2012-09-11 30 views
2

我想寫一個自定義的WPF/MVVM TreeView控件,當父項展開時,它將自動滾動(儘可能多的)子項到視圖中。WPF TreeView - 將擴展項滾動到視圖中

我找到了這篇文章,WPF TreeView - How to scroll so expanded branch is visible,但它似乎只適用於樹項目實際上從TreeViewItem下降。

我的樹項目類只是一個簡單的C#對象 - 沿Josh Smith的博客http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode的行,它們是在Tree Item類中的IsExpanded屬性設置器中加載的。

進入OnExpand事件處理程序,我可以看到原始源是一個TreeViewItem(由幕後框架創建),它在標題字段中有我的樹項目對象。這個TreeViewItem只有一個可視化子元素,一個網格,它又有3個可視化子元素,ToggleButton,Border和ItemsPresenter。

有沒有人有一個提示,我可以如何獲得我的子項目,我可以然後調用BringIntoView()相應的可視化組件?

+0

任何這運氣? – AGhosT

回答

0

五年是在編程方面具有很長的時間......

我最近被授予徽章2500條意見後,回到了這個問題。剛好在我的博客上發佈了一篇文章,提出了這些問題的解決方案 - http://peregrinesview.uk/wpf-behaviors-part-2-treeview/

我知道只有鏈接的答案通常不會在stackoverflow上獲得批准,所以我也會在這裏發佈關鍵元素。

1)TreeViewItem的助手包含兩個附加屬性,一個用於將選定項目滾動到視圖中,另一個用於在展開項目時儘可能多地滾動查看。

public static class perTreeViewItemHelper 
{ 
    public static bool GetBringSelectedItemIntoView(TreeViewItem treeViewItem) 
    { 
     return (bool)treeViewItem.GetValue(BringSelectedItemIntoViewProperty); 
    } 

    public static void SetBringSelectedItemIntoView(TreeViewItem treeViewItem, bool value) 
    { 
     treeViewItem.SetValue(BringSelectedItemIntoViewProperty, value); 
    } 

    public static readonly DependencyProperty BringSelectedItemIntoViewProperty = 
     DependencyProperty.RegisterAttached(
      "BringSelectedItemIntoView", 
      typeof(bool), 
      typeof(perTreeViewItemHelper), 
      new UIPropertyMetadata(false, BringSelectedItemIntoViewChanged)); 

    private static void BringSelectedItemIntoViewChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
    { 
     if (!(args.NewValue is bool)) 
      return; 

     var item = obj as TreeViewItem; 

     if (item == null) 
      return; 

     if ((bool)args.NewValue) 
      item.Selected += OnTreeViewItemSelected; 
     else 
      item.Selected -= OnTreeViewItemSelected; 
    } 

    private static void OnTreeViewItemSelected(object sender, RoutedEventArgs e) 
    { 
     var item = e.OriginalSource as TreeViewItem; 
     item?.BringIntoView(); 

     // prevent this event bubbling up to any parent nodes 
     e.Handled = true; 
    } 

    public static bool GetBringExpandedChildrenIntoView(TreeViewItem treeViewItem) 
    { 
     return (bool)treeViewItem.GetValue(BringExpandedChildrenIntoViewProperty); 
    } 

    public static void SetBringExpandedChildrenIntoView(TreeViewItem treeViewItem, bool value) 
    { 
     treeViewItem.SetValue(BringExpandedChildrenIntoViewProperty, value); 
    } 

    public static readonly DependencyProperty BringExpandedChildrenIntoViewProperty = 
     DependencyProperty.RegisterAttached(
      "BringExpandedChildrenIntoView", 
      typeof(bool), 
      typeof(perTreeViewItemHelper), 
      new UIPropertyMetadata(false, BringExpandedChildrenIntoViewChanged)); 

    private static void BringExpandedChildrenIntoViewChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
    { 
     if (!(args.NewValue is bool)) 
      return; 

     var item = obj as TreeViewItem; 

     if (item == null) 
      return; 

     if ((bool)args.NewValue) 
      item.Expanded += OnTreeViewItemExpanded; 
     else 
      item.Expanded -= OnTreeViewItemExpanded; 
    } 

    private static void OnTreeViewItemExpanded(object sender, RoutedEventArgs e) 
    { 
     var item = e.OriginalSource as TreeViewItem; 

     if (item == null) 
      return; 

     // use DispatcherPriority.ContextIdle, so that we wait for all of the UI elements for any newly visible children to be created 

     // first bring the last child into view 
     Action action =() => 
     { 
      var lastChild = item.ItemContainerGenerator.ContainerFromIndex(item.Items.Count - 1) as TreeViewItem; 
      lastChild?.BringIntoView(); 
     }; 

     item.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); 

     // then bring the expanded item (back) into view 
     action =() => { item.BringIntoView(); }; 
     item.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); 

     // prevent this event bubbling up to any parent nodes 
     e.Handled = true; 
    } 
} 

2)樹型視圖樣式包括這些屬性

<Style x:Key="perExpandCollapseToggleStyle" 
     TargetType="ToggleButton"> 
    <Setter Property="Focusable" Value="False" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="ToggleButton"> 
       <Grid Width="10" 
         Height="10" 
         Background="Transparent"> 
        <Path x:Name="ExpanderGlyph" 
          Margin="1" 
          HorizontalAlignment="Left" 
          VerticalAlignment="Center" 
          Data="M 0,3 L 0,5 L 3,5 L 3,8 L 5,8 L 5,5 L 8,5 L 8,3 L 5,3 L 5,0 L 3,0 L 3,3 z" 
          Fill="LightGreen" 
          Stretch="None" /> 
       </Grid> 

       <ControlTemplate.Triggers> 
        <Trigger Property="IsChecked" Value="True"> 
         <Setter TargetName="ExpanderGlyph" Property="Data" Value="M 0,0 M 8,8 M 0,3 L 0,5 L 8,5 L 8,3 z" /> 
         <Setter TargetName="ExpanderGlyph" Property="Fill" Value="Red" /> 
        </Trigger> 

        <Trigger Property="IsEnabled" Value="False"> 
         <Setter TargetName="ExpanderGlyph" Property="Fill" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<Style x:Key="perTreeViewItemContainerStyle" 
     TargetType="{x:Type TreeViewItem}"> 

    <!-- Link the properties of perTreeViewItemViewModelBase to the corresponding ones on the TreeViewItem --> 
    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> 
    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}" /> 

    <!-- Include the two "Scroll into View" behaviors --> 
    <Setter Property="vhelp:perTreeViewItemHelper.BringSelectedItemIntoView" Value="True" /> 
    <Setter Property="vhelp:perTreeViewItemHelper.BringExpandedChildrenIntoView" Value="True" /> 

    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TreeViewItem}"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto" 
              MinWidth="14" /> 
         <ColumnDefinition Width="*" /> 
        </Grid.ColumnDefinitions> 
        <Grid.RowDefinitions> 
         <RowDefinition Height="Auto" /> 
         <RowDefinition Height="*" /> 
        </Grid.RowDefinitions> 
        <ToggleButton x:Name="Expander" 
            Grid.Row="0" 
            Grid.Column="0" 
            ClickMode="Press" 
            IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" 
            Style="{StaticResource perExpandCollapseToggleStyle}" /> 

        <Border x:Name="PART_Border" 
          Grid.Row="0" 
          Grid.Column="1" 
          Padding="{TemplateBinding Padding}" 
          Background="{TemplateBinding Background}" 
          BorderBrush="{TemplateBinding BorderBrush}" 
          BorderThickness="{TemplateBinding BorderThickness}"> 

         <ContentPresenter x:Name="PART_Header" 
              Margin="0,2" 
              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
              ContentSource="Header" /> 

        </Border> 

        <ItemsPresenter x:Name="ItemsHost" 
            Grid.Row="1" 
            Grid.Column="1" /> 
       </Grid> 

       <ControlTemplate.Triggers> 
        <Trigger Property="IsExpanded" Value="false"> 
         <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" /> 
        </Trigger> 

        <Trigger Property="HasItems" Value="false"> 
         <Setter TargetName="Expander" Property="Visibility" Value="Hidden" /> 
        </Trigger> 

        <!-- Use the same colors for a selected item, whether the TreeView is focussed or not --> 
        <Trigger Property="IsSelected" Value="true"> 
         <Setter TargetName="PART_Border" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> 
         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" /> 
        </Trigger> 

        <Trigger Property="IsEnabled" Value="false"> 
         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<Style TargetType="{x:Type TreeView}"> 
    <Setter Property="ItemContainerStyle" Value="{StaticResource perTreeViewItemContainerStyle}" /> 
</Style>