【问题标题】:In WPF, how do you programmatically deselect all Items in a Treeview?在 WPF 中,如何以编程方式取消选择 Treeview 中的所有项目?
【发布时间】:2013-12-20 16:21:05
【问题描述】:

我正在尝试创建一种递归方法来取消选择 WPF TreeView 中的所有项目。使事情复杂化的是每个 TreeViewItem 都不是 mini-TreeView。这导致您必须来回进行大量转换。所以,这是我尝试过的:

TreeViewDeselectAll(myTreeView.Items);      

// Must send in ItemCollection to allow the recursive call
private void TreeViewDeselectAll(ItemCollection myTreeViewItems)
{
    // The only way to get to the IsSelected property is to turn it back into a TreeViewItem
    foreach (TreeViewItem currentItem in myTreeViewItems)
    {
        currentItem.IsSelected = false;
        if (currentItem.HasItems)
        {
             // Recursify!
             TreeViewDeselectAll(currentItem.Items);
        }
    }
}

有没有人成功取消选择 TreeView 中的所有项目?您甚至能够以递归方式遍历 TreeView 吗?

Winforms TreeView 有一个 Nodes 集合,它实际上是一个 mini-TreeView。这允许递归就好了。但是 WPF TreeView 没有节点。

在 .Net 4.0 中工作。

【问题讨论】:

  • 我还想指出,除非您做了一些特别的事情,否则 TreeView 只允许选择一项。
  • @JoelLucsy 没错。我尝试过使用ItemContainerStyle,却发现使用TreeView 是不可能的。不过,OP 肯定需要多项选择,这可以通过将选择状态移动到 Model/ViewModel 来实现。看我的回答。

标签: c# wpf treeview


【解决方案1】:

好的。删除所有代码并重新开始。

如果您正在使用 WPF,那么您确实需要抛弃所有来自恐龙技术(如 winform)的过时做法,并理解并接受 The WPF Mentality。

在 WPF 中,您不会以编程方式“选择 TreeViewItem”,这仅仅是因为 UI is Not Data。

UI 不负责跟踪数据项的选择状态,这些数据项显示在 TreeView 或任何其他 UI 元素中。

相反,您创建一个适当的 DataModel 和一个 ViewModel 来分别保存数据和应用程序逻辑。

Josh Smith 有一个非常有趣的article 解释了如何在 WPF 中以正确的方式处理 TreeView。

基本上是这样的:

<Window x:Class="MiscSamples.MVVMTreeViewSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MVVMTreeViewSample" Height="300" Width="300">
    <DockPanel>
        <Button Content="Select All" Click="SelectAll" DockPanel.Dock="Top"/>
        <Button Content="Select None" Click="SelectNone" DockPanel.Dock="Top"/>

        <TreeView ItemsSource="{Binding}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <CheckBox IsChecked="{Binding IsSelected}" Content="{Binding DisplayName}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </DockPanel>
</Window>

代码背后:

public partial class MVVMTreeViewSample : Window
{
    private ObservableCollection<HierarchicalData> Data; 

    public MVVMTreeViewSample()
    {
        InitializeComponent();
        DataContext = Data = CreateData();
    }

    private void Select(IEnumerable<HierarchicalData> items, bool isselected)
    {
        while (items.Any())
        {
            items.ToList().ForEach(x => x.IsSelected = isselected);
            items = items.SelectMany(x => x.Children);
        }  
    }

    private void SelectAll(object sender, RoutedEventArgs e)
    {
        Select(Data, true);
    }

    private void SelectNone(object sender, RoutedEventArgs e)
    {
        Select(Data, false);
    }

    private ObservableCollection<HierarchicalData> CreateData()
    {
        return new ObservableCollection<HierarchicalData>
        {
            //... Dummy Data here
        }
    }
}

数据项:

public class HierarchicalData : System.ComponentModel.INotifyPropertyChanged
{

    public string DisplayName { get; set; }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }

    public ObservableCollection<HierarchicalData> Children { get; private set; }

    public HierarchicalData()
    {
        Children = new ObservableCollection<HierarchicalData>();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

结果:

  • 看看如何没有一行代码可以操作任何 UI 元素。一切都通过DataBinding 完成Simple、Simple Properties 和 INotifyPropertyChanged。这就是您在 WPF 中编程的方式。不需要复杂的VisualTreeHelper.Whatever() 东西,不需要由于 UI 虚拟化等导致各种问题的复杂操作。
  • 在SelectAll() 和SelectNone() 方法中,我只是通过Data Items 而不是UI 元素进行迭代,并相应地设置它们的值。然后 WPF 通过 DataBinding 引擎更新 UI。
  • 了解如何无需将任何内容强制转换为任何内容,以及我如何使用自己的简单类而不是处理复杂、神秘的 WPF 对象模型。
  • 忘记winforms。它是一个无用的恐龙,什么都不支持。
  • WPF 摇滚。只需将我的代码复制并粘贴到File -&gt; New Project -&gt; WPF Application 中,然后自己查看结果。请注意,您需要在 CreateData() 方法中将项目添加到集合中。
  • 如果您需要进一步的帮助,请告诉我。

【讨论】:

  • 好帖子。我确实喜欢 WPF,尽管我认为数据绑定被许多程序员滥用。许多人编写代码时都假设数据库始终非常健壮,并且网络始终可靠。
  • @ClayAcord 我无法理解您的评论。 WPF 的 DataBinding 与数据库或网络连接完全无关。
  • 我也很担心微软现在采取的方向,或者说缺乏方向。 .Net 和 WPF 在 Windows 9 中会被淘汰吗?谁真的知道? 我很高兴 MS 建议在 ASP.NET 中转向 MVC,以便消除大量的 VIEWSTATE。我需要那个房间来填充我需要的 3 或 4 个 JavaScript 库。
  • @ClayAcord 如果 .Net / WPF 已过时,我会将所有 XAML + MVVM 直接移植到 WinRT XAML。甚至是非基于 XAML 的框架,因为我的 ViewModel 实际上与 View 无关。而非 MVVM 代码更难移植。
  • @ClayAcord 明确表示,如果从数据库加载数据后要执行数据一致性检查,则由Data Access Layer 负责,而不是 UI 层。这同样适用于从网络获取的数据,其一致性应该由Service Layer 或Communications Layer 来保证,而不是UI Layer。 WPF 是一个 UI 框架,它负责 UI 问题,而不是其他所有问题。
【解决方案2】:

每个 TreeViewItem 都是一个 Mini-TreeView。你有错误的印象,或者你读错了关于 TreeViewItems 的内容。

您做错了什么是传递不包含 TreeViewItem 类型的子项的 Items 集合,而是那些是数据项。

通过 children 集合,你应该做的很好。

像这样:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

    }

    private bool @switch = false;

    private void TreeViewDeselectAll(IEnumerable myTreeViewItems, bool value)
    {
        if (myTreeViewItems != null)
        {
            foreach (var currentItem in myTreeViewItems)
            {
                if (currentItem is TreeViewItem)
                {
                    TreeViewItem item = (TreeViewItem)currentItem;
                    item.IsSelected = value;
                    if (item.HasItems)
                    {
                        TreeViewDeselectAll(LogicalTreeHelper.GetChildren(item), value);
                    }
                }
            }
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.TreeViewDeselectAll(LogicalTreeHelper.GetChildren(this.treeView), this.@switch);
    }
}

XAML 将如下所示:

<StackPanel>
    <TreeView Name="treeView">
        <TreeViewItem Header="First Level">
            <TreeViewItem Header="Second Level"/>
        </TreeViewItem>
    </TreeView>
    <Button Click="Button_Click">select/unselect all</Button>
</StackPanel>

我稍微更改了您的 TreeViewDeselectAll 方法。根据开关,您可以全选或取消全选。

【讨论】:

    猜你喜欢
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 2015-05-30
    • 2010-11-07
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多