2013-08-03 46 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> 

這工作正常,但有一個問題。頁腳不像列標題和其他項目那樣水平滾動。我已經將額外的GridViewRowPresenter放入ScrollViewer中,就像GridViewHeaderRowPresenter一樣。它看起來像標題行必須有某種鉤子跟隨滾動偏移量。

如何使這項工作?那麼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如何做到這一點?

你可以在RenewEvents()方法中找到神奇的方法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); 
     } 
    } 
    ... 

正如你可以看到GridViewHeaderRowPresenter掛鉤起來ScrollChanged事件的Parent(在HeaderScrollViewer)和TemplatedParent(該MainScrollViewer),然後處理與ScrollToHorizontalOffset法的事件太(從線1034上)。

// 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