【问题标题】:How to expand WPF TreeView on single click of item如何在单击项目时展开 WPF TreeView
【发布时间】:2019-05-24 16:58:09
【问题描述】:

现在您必须双击或单击 + 图标。如果用户点击它展开的节点上的任何位置,有什么办法可以做到这一点?

【问题讨论】:

    标签: wpf treeview


    【解决方案1】:

    感谢another StackOverflow post,我遇到了同样的问题并找到了一个很好的解决方案。

    在control.xaml的TreeView元素中,可以直接hook到TreeViewItem的Selected事件中:

    <TreeView ItemsSource="{StaticResource Array}" TreeViewItem.Selected="TreeViewItem_Selected"/>
    

    然后在您的 control.xaml.cs 代码中,您可以从 RoutedEventArgs 中获取选定的 TreeViewItem 并将其设置为 IsExpanded:

    private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
    {
        TreeViewItem tvi = e.OriginalSource as TreeViewItem;
    
        if (tvi == null || e.Handled) return;
    
        tvi.IsExpanded = !tvi.IsExpanded;
        e.Handled = true;
    }
    

    干净整洁。希望对某人有所帮助!

    【讨论】:

    • +1 表示 XAML 代码。完美解决了问题。 (接受的答案也是如此(我也赞成)。
    • 在我看来,这是最好的方法!只有一个缺陷:当您尝试通过单击折叠图标来折叠未选中的项目时,它会被选中并且事件处理程序会阻止折叠它。您必须在if (!e.Handled) {...} 中封装事件处理程序,并在切换IsExpanded 属性后立即设置e.Handled = true;
    • 另一个问题是,当您单击已选择的树视图时,它不会折叠或展开。
    【解决方案2】:

    也许不是最优雅的解决方案,但这是可行的:

        static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
        {
            while (source != null && source.GetType() != typeof(T))
                source = VisualTreeHelper.GetParent(source);
    
            return source;
        }
    

    然后在 TreeViewItem.Selected 处理程序中:

            private void Treeview_Selected(object sender, RoutedEventArgs e)
            {
                var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
                if (treeViewItem != null) treeViewItem.IsExpanded = true;
            }
    

    VisualUpwardSearch 魔法取自这里:Select TreeView Node on right click before displaying ContextMenu

    问候

    【讨论】:

    • 撞了一个很老的答案。我正在尝试这个解决方案,但我有一个带有 AccessText 的标签,并且我将 System.Windows.Documents.Run 元素作为 OriginalSource。这不是视觉或视觉3d 元素,因此 GetParent 会引发异常。知道该怎么做吗?
    【解决方案3】:

    我遇到了同样的问题,我使用样式功能做到了,这样您就不需要处理事件了。

    我为 TreeViewItem 定义了一个样式

    <Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
        <!--<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>-->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TreeViewItem}">
                            <CheckBox Style="{StaticResource TreeViewItemCB}" IsChecked="{Binding Path=IsExpanded,Mode=OneWayToSource,RelativeSource={RelativeSource TemplatedParent}}"  ClickMode="Press">
                                <Grid Background="{StaticResource TreeViewItemBackground}" Margin="0">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions> 
                                <Border Name="Bd">
                                        <ContentPresenter x:Name="PART_Header" ContentSource="Header"/>
                                </Border>
                                <ItemsPresenter x:Name="ItemsHost" Grid.Row="1"/>
                                </Grid>
                            </CheckBox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    重要的部分是在 ControlTemplate 中定义 checkBox 并与之绑定。勾选 CheckBox 后,项目一键展开。

    <CheckBox Style="{StaticResource TreeViewItemCB}" IsChecked="{Binding Path=IsExpanded,Mode=OneWayToSource,RelativeSource={RelativeSource TemplatedParent}}"  ClickMode="Press">
    

    这是复选框的样式,因此它可以拉伸并且不显示带有笔划的框。

    <Style x:Key="TreeViewItemCB" TargetType="CheckBox" BasedOn="{StaticResource baseStyle}">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="VerticalAlignment" Value="Stretch"/>
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="CheckBox">
                    <ContentPresenter VerticalAlignment="Stretch" HorizontalAlignment="Stretch" RecognizesAccessKey="True"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    【讨论】:

      【解决方案4】:

      @BJennings 提供了一个很好的答案。但是,如果您想展开或折叠已选择的项目,则它不起作用。为了改善这一点,您可以简单地添加: tvi.IsSelected = false; (如果您不关心项目是否处于选中状态。)

      所以,整个代码如下所示:

      private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
      {
          TreeViewItem tvi = e.OriginalSource as TreeViewItem;
      
          if (tvi == null || e.Handled) return;
      
          tvi.IsExpanded = !tvi.IsExpanded;
          tvi.IsSelected = false;
          e.Handled = true;
      }
      

      【讨论】:

        【解决方案5】:

        另一种方法是使用附加属性。

        public class VirtualOneClickExpandButtonBehavior : DependencyObject
        {
            public static bool GetEnabled(DependencyObject obj)
            {
                return (bool)obj.GetValue(EnabledProperty);
            }
        
            public static void SetEnabled(DependencyObject obj, bool value)
            {
                obj.SetValue(EnabledProperty, value);
            }
        
        
            public static readonly DependencyProperty EnabledProperty =
                DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(VirtualOneClickExpandButtonBehavior), 
                    new UIPropertyMetadata(false, EnabledPropertyChangedCallback
                        ));
        
            private static void EnabledPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
            {
                var treeView = dependencyObject as TreeView;
        
                if (treeView == null) return;
        
                treeView.MouseUp += TreeView_MouseUp;
            }
        
            private static void TreeView_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
                if (treeViewItem != null) treeViewItem.IsExpanded = !treeViewItem.IsExpanded;
            }
        
            static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
            {
                while (source != null && source.GetType() != typeof(T))
                    source = VisualTreeHelper.GetParent(source);
        
                return source;
            }
        }
        

        然后你就可以这样使用了。

        <TreeView controls:VirtualOneClickExpandButtonBehavior.Enabled="true" ItemsSource="{Binding HierarchicalModel}"/>
        

        如果您使用 MVVM 模式,这是一个很好的方法,因为您不需要代码隐藏。

        感谢 Markust 的 VisualUpwardSearch(DependencyObject 源)

        【讨论】:

        • 这不是另一种方法,这是方法。
        【解决方案6】:

        当您使用键盘导航时,接受的解决方案会出现奇怪的行为,并且在项目已被选中时不会折叠它。或者,只需从 TreeViewItem 派生一个新类并覆盖 MouseLeftButtonDown 方法。您还需要将 TreeView.ItemsSource 设置为新 TreeViewItem 类的集合。

        protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            if (!e.Handled && base.IsEnabled)
            {
                this.IsExpanded = !this.IsExpanded;
                e.Handled = true;
            }
            base.OnMouseLeftButtonDown(e);
        }
        

        【讨论】:

          【解决方案7】:
          <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
              <Setter Property="Cursor" Value="Hand" />
              <EventSetter Event="MouseUp" Handler="TreeViewItem_Click"/>
            </Style>
          </TreeView.ItemContainerStyle>
          
          
          private void TreeViewItem_Click(object sender, MouseButtonEventArgs e)
                  {
                      ((TreeViewItem) sender).IsExpanded = !((TreeViewItem) sender).IsExpanded;
                      Thread.Sleep(700);
                  }
          

          这就是答案,尽情享受吧

          回答者:阿里·拉希米

          【讨论】:

            猜你喜欢
            • 2019-04-27
            • 2011-02-16
            • 1970-01-01
            • 2011-11-22
            • 1970-01-01
            • 1970-01-01
            • 2021-04-07
            • 1970-01-01
            • 2016-08-14
            相关资源
            最近更新 更多