2013-08-03 2 views
3

나머지 항목과 수직으로 스크롤하지 않는 바닥 글 행이있는 ListView를 원했지만 항상 표시되어야합니다. 아래 템플릿을 사용하여이 작업을 수행했습니다.ListView 바닥 글 행 - 가로 스크롤

<Style x:Key="FrozenRowListView" TargetType="ListView"> 
    <Setter Property="SnapsToDevicePixels" Value="true" /> 
    <Setter Property="OverridesDefaultStyle" Value="true" /> 
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" /> 
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" /> 
    <Setter Property="ScrollViewer.CanContentScroll" Value="true" /> 
    <Setter Property="VerticalContentAlignment" Value="Center" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="ListView"> 
       <Border Name="Border" BorderThickness="1"> 
        <ScrollViewer Style="{StaticResource FrozenRowScrollViewer}"> 
         <ItemsPresenter /> 
        </ScrollViewer> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<Style x:Key="FrozenRowScrollViewer" TargetType="ScrollViewer"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="ScrollViewer"> 
       <Grid Background="{TemplateBinding Background}"> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="*" /> 
         <ColumnDefinition Width="Auto" /> 
        </Grid.ColumnDefinitions> 
        <Grid.RowDefinitions> 
         <RowDefinition Height="*" /> 
         <RowDefinition Height="Auto" /> 
        </Grid.RowDefinitions> 

        <DockPanel Margin="{TemplateBinding Padding}"> 
         <ScrollViewer DockPanel.Dock="Bottom" 
             HorizontalScrollBarVisibility="Hidden" 
              VerticalScrollBarVisibility="Hidden" 
              Focusable="false"> 
          <GridViewRowPresenter 
           Margin="2,0,2,0" 
           Content="{Binding Path=TemplatedParent.ItemsSource, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource summaryConverter}}" 
           Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}" 
           SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> 
         </ScrollViewer> 
         <ScrollViewer DockPanel.Dock="Top" 
             HorizontalScrollBarVisibility="Hidden" 
             VerticalScrollBarVisibility="Hidden" 
             Focusable="false"> 
          <GridViewHeaderRowPresenter DockPanel.Dock="Top" 
            Margin="2,0,2,0" 
            Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}" 
            ColumnHeaderContainerStyle="{Binding Path=TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}" 
            ColumnHeaderTemplate="{Binding Path=TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}" 
            ColumnHeaderTemplateSelector="{Binding Path=TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}" 
            AllowsColumnReorder="{Binding Path=TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}" 
            ColumnHeaderContextMenu="{Binding Path=TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}" 
            ColumnHeaderToolTip="{Binding Path=TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}" 
            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> 
         </ScrollViewer> 

         <ScrollContentPresenter Name="PART_ScrollContentPresenter" 
               KeyboardNavigation.DirectionalNavigation="Local" 
               CanContentScroll="True" 
               CanHorizontallyScroll="False" 
               CanVerticallyScroll="False" /> 
        </DockPanel> 

        <ScrollBar Name="PART_HorizontalScrollBar" 
           Orientation="Horizontal" 
           Grid.Row="1" 
           Maximum="{TemplateBinding ScrollableWidth}" 
           ViewportSize="{TemplateBinding ViewportWidth}" 
           Value="{TemplateBinding HorizontalOffset}" 
           Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" /> 

        <ScrollBar Name="PART_VerticalScrollBar" 
           Grid.Column="1" 
           Maximum="{TemplateBinding ScrollableHeight}" 
           ViewportSize="{TemplateBinding ViewportHeight}" 
           Value="{TemplateBinding VerticalOffset}" 
           Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" /> 

       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

이 방법은 문제가 없지만 한 가지 문제가 있습니다. 머리글 및 기타 항목처럼 바닥 글이 가로로 스크롤되지 않습니다. GridViewHeaderRowPresenter와 같은 ScrollViewer에 별도의 GridViewRowPresenter를 추가했습니다. 머리글 행에 스크롤 오프셋을 따르는 후크가 있어야합니다.

이 작품을 만드는 방법? GridViewHeaderRowPresenter는 스크롤을 어떻게 처리합니까?

답변

2

바닥 글 행 ScrollViewer.HorizontalOffsetPART_HorizontalScrollBar.Value으로 동기화해야합니다. 그러나 여기서 this 같은 솔루션은 작동하지 않습니다. 첨부 된 속성을 사용하는 것이 좋습니다. 복잡한 것처럼 보이지만 그렇지 않습니다.

public static class ScrollViewerBinding 
{ 
    public static double GetHorizontalOffset(DependencyObject depObj) 
    { 
     return (double)depObj.GetValue(HorizontalOffsetProperty); 
    } 

    public static void SetHorizontalOffset(DependencyObject depObj, double value) 
    { 
     depObj.SetValue(HorizontalOffsetProperty, value); 
    } 

    public static readonly DependencyProperty HorizontalOffsetProperty = 
     DependencyProperty.RegisterAttached("HorizontalOffset", 
              typeof(double), 
              typeof(ScrollViewerBinding), 
              new PropertyMetadata(OnHorizontalOffsetPropertyChanged)); 

    private static void OnHorizontalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ScrollViewer sv = d as ScrollViewer; 
     if (sv != null) 
     { 
      sv.ScrollToHorizontalOffset((double)e.NewValue); 
     } 
    } 
} 

그런 다음 두 번째 질문에 지금이

<ScrollViewer DockPanel.Dock="Bottom" 
       local:ScrollViewerBinding.HorizontalOffset="{Binding Value, ElementName=PART_HorizontalScrollBar}" 

처럼 결합 할 수있다 : GridViewHeaderRowPresenter는이 작업을 수행하는 방법?

방법의 마법은 GridViewHeaderRowPresenter.cs (946 번 줄에서)에서 찾을 수 있습니다.

// find needed elements and hook up events 
private void RenewEvents() 
{ 
    ScrollViewer oldHeaderSV = _headerSV; 
    _headerSV = Parent as ScrollViewer; 
    if (oldHeaderSV != _headerSV) 
    { 
     if (oldHeaderSV != null) 
     { 
      oldHeaderSV.ScrollChanged -= new ScrollChangedEventHandler(OnHeaderScrollChanged); 
     } 
     if (_headerSV != null) 
     { 
      _headerSV.ScrollChanged += new ScrollChangedEventHandler(OnHeaderScrollChanged); 
     } 
    } 

    ScrollViewer oldSV = _mainSV; // backup the old value 
    _mainSV = TemplatedParent as ScrollViewer; 

    if (oldSV != _mainSV) 
    { 
     if (oldSV != null) 
     { 
      oldSV.ScrollChanged -= new ScrollChangedEventHandler(OnMasterScrollChanged); 
     } 

     if (_mainSV != null) 
     { 
      _mainSV.ScrollChanged += new ScrollChangedEventHandler(OnMasterScrollChanged); 
     } 
    } 
    ... 

당신이 ScrollChangedParent의 이벤트합니다 (HeaderScrollViewer)와 TemplatedParent합니다 (MainScrollViewer를) GridViewHeaderRowPresenter 후크을 본 후 (라인 1034에서에) 너무 ScrollToHorizontalOffset 메소드를 사용하여 이벤트를 처리 할 수있다.

// The following two scroll changed methods will not be called recursively and lead to dead loop. 
// When scrolling _masterSV, OnMasterScrollChanged will be called, so _headerSV also scrolled 
// to the same offset. Then, OnHeaderScrollChanged be called, and try to scroll _masterSV, but 
// it's already scrolled to that offset, so OnMasterScrollChanged will not be called. 

// When master scroll viewer changed its offset, change header scroll viewer accordingly 
private void OnMasterScrollChanged(object sender, ScrollChangedEventArgs e) 
{ 
    if (_headerSV != null && _mainSV == e.OriginalSource) 
    { 
     _headerSV.ScrollToHorizontalOffset(e.HorizontalOffset); 
    } 
} 

// When header scroll viewer changed its offset, change master scroll viewer accordingly 
private void OnHeaderScrollChanged(object sender, ScrollChangedEventArgs e) 
{ 
    if (_mainSV != null && _headerSV == e.OriginalSource) 
    { 
     _mainSV.ScrollToHorizontalOffset(e.HorizontalOffset); 
    } 
} 
+0

와우, 고마워요. 어떻게 든 알아낼 수 없었습니다. 작동합니다! 그러나 나는 명시 적 바인딩이 없어도 열 머리글을 가로로 스크롤하는 방법이 여전히 궁금합니다. – serine

+0

GridViewHeaderRowPresenter에는 장점이 있으며, 두 ScrollViewer에 쉽게 도달 할 수 있습니다. 내 편집을 참조하십시오. – LPL

+0

덕분에 이제는 의미가 있습니다 :) – serine