如何在单击项时展开WPF TreeView

15

现在您必须双击或单击 + 图标。 有没有办法使用户单击节点的任何位置即可展开?

7个回答

19

我曾遇到这个问题,通过另一个StackOverflow帖子找到了一个好的解决方案。

在control.xaml的TreeView元素中,你可以直接连接到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
+1 对于 XAML 代码。完美解决了问题。(我也点赞了被接受的答案)。 - Avada Kedavra
2
在我看来,这是最好的方法!只有一个缺陷:当你尝试通过点击折叠图标来折叠一个未选中的项时,它会被选中并且事件处理程序会阻止其折叠。你需要将事件处理程序封装在 if (!e.Handled) {...} 中,并在切换 IsExpanded 属性后立即设置 e.Handled = true; - kroimon
1
另一个问题是,当您单击已选择的树视图时,它不会折叠或展开。 - Josh Noe

6
也许这不是最优雅的解决方案,但它有效:
    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的魔法来自于这里:在显示上下文菜单之前,选择TreeView节点

敬礼


1
将一个非常古老的答案顶起来。我正在尝试这个解决方案,但是我有一个带有AccessText标签的标签,而我得到的OriginalSource是System.Windows.Documents.Run元素。这不是一个Visual或Visual3D元素,因此GetParent会抛出异常。有任何想法如何处理吗? - Piyush Parashar

5
我遇到了同样的问题,我使用了Style功能来解决它,这样你就不需要处理事件。
我为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 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;
}

1

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

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模式,这是一个很好的方法,因为您不需要codebehind。
感谢Markust的VisualUpwardSearch(DependencyObject source)。

这不是另一种方法,而是唯一的方法。 - Alexandru Dicu

0

被接受的解决方案在使用键盘导航时有奇怪的行为,并且在已经选择该项时不会折叠该项。或者,只需从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);
}

-1
<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);
        }

这是答案,希望您喜欢

回答者:阿里·拉希米


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接