wpf相当于winform的treeview.node.find

wpf equivalent to treeview.node.find of winform

我尝试使用 treeview 控件将 winform 转换为 wpf,但我不知道 wpf 中的哪个方法等同于 treeview.node.find

在winform中,我可以通过

轻松找到树节点
 Dim MyNode() As TreeNode
 MyNode = TreeView1.Nodes.Find("10.8", True)

但是在 wpf c# 中我如何找到树节点(也许在 wpf 中调用 treeviewitem)

已解决

我终于通过使用递归循环找到了适合自己的解决方案。我知道这可能不是查找节点的最佳方法,但目前它工作正常

不过我认为并尝试采用另一种方式,例如 aepot 的 post :)

private TreeViewItem SearchTreeView(string p_sSearchTerm, ItemCollection p_Nodes)
{
    TreeViewItem returnValue = null;
    foreach (TreeViewItem node in p_Nodes)
    {
        if (string.Equals(node.Name.ToString(), p_sSearchTerm) ==true)
        {
            returnValue = node;
            return returnValue;
        }

        if (node.Items.Count > 0) returnValue = SearchTreeView(p_sSearchTerm, node.Items);
    }
    return returnValue;
}

然后,我们就可以使用它了

var MyNode = SearchTreeView("A10_8", treeView.Items);
if (node != null)
{
    Console.Write(MyNode.Name);
}

直接与 WPF 中的控件进行交互真的很难。而且我不知道保留您的开发方法的答案。但是知道如何用其他方式完成它。

我建议改用 MVVMBinding。我已经创建了一个演示项目来展示它是如何完成的。

这不是 Silver Bullet,而是一个演示。

由于 MVVM 模式方法,我们需要几个助手 classes.

// INPC Interface implementation for deriving in ViewModels
public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

// ICommand interface implementation for easy commands use
public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    public void Execute(object parameter) => _execute(parameter);
}

应用程序功能

  • 按名称或部分名称查找并select节点
  • 删除 selected 节点
  • 将子节点添加到 selected 节点

你可以改进它:

  • 添加并存储Parent 属性以避免上层搜索的东西

数据分层class(意味着它包含自己的集合)

public class MyTreeNode : NotifyPropertyChanged
{
    private ObservableCollection<MyTreeNode> _items = new ObservableCollection<MyTreeNode>();
    private string _nodeName;
    private bool _isSelected;

    public ObservableCollection<MyTreeNode> Items
    {
        get => _items;
        set
        {
            _items = value;
            OnPropertyChanged();
        }
    }

    public string NodeName
    {
        get => _nodeName;
        set
        {
            _nodeName = value;
            OnPropertyChanged();
        }
    }

    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            _isSelected = value;
            OnPropertyChanged();
        }
    }
}

然后是非常重要的main class MainViewModel 它将提供属性给MainWindow.

public class MainViewModel : NotifyPropertyChanged
{
    private ObservableCollection<MyTreeNode> _treeItems;
    private ICommand _searchCommand;
    private ICommand _addCommand;
    private ICommand _removeCommand;

    private string _text;

    public string Text
    {
        get => _text;
        set
        {
            _text = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        TreeItems = new ObservableCollection<MyTreeNode>();

        // demo values for initial test
        TreeItems.Add(new MyTreeNode { NodeName = "Node1" });
        MyTreeNode node = new MyTreeNode { NodeName = "Node2", IsSelected = true };
        TreeItems.Add(node);
        node.Items.Add(new MyTreeNode { NodeName = "SubNode1.1" });
        node.Items.Add(new MyTreeNode { NodeName = "SubNode1.2" });
        node.Items.Add(new MyTreeNode { NodeName = "SubNode1.3" });
        TreeItems.Add(new MyTreeNode { NodeName = "Node3" });
        TreeItems.Add(new MyTreeNode { NodeName = "Node4" });
    }

    public ObservableCollection<MyTreeNode> TreeItems
    {
        get => _treeItems;
        set
        {
            _treeItems = value;
            OnPropertyChanged();
        }
    }

    // search by node name implementation
    private MyTreeNode SearchItemByName(ObservableCollection<MyTreeNode> nodes, string searchText)
    {
        if (searchText?.Length > 0)
        {
            foreach (MyTreeNode node in nodes)
            {
                if (node.NodeName.Contains(searchText, StringComparison.InvariantCultureIgnoreCase))
                {
                    return node;
                }
                if (node.Items.Count > 0)
                {
                    MyTreeNode result = SearchItemByName(node.Items, searchText);
                    if (result != null) return result;
                }
            }
        }
        return null;
    }

    // need for remove action to find the collection that contains the required item
    private ObservableCollection<MyTreeNode> FindParentCollection(ObservableCollection<MyTreeNode> nodes, MyTreeNode searchNode)
    {
        if (searchNode != null)
        {
            foreach (MyTreeNode node in nodes)
            {
                if (node.Equals(searchNode))
                {
                    return nodes;
                }
                if (node.Items.Count > 0)
                {
                    ObservableCollection<MyTreeNode> result = FindParentCollection(node.Items, searchNode);
                    if (result != null) return result;
                }
            }
        }
        return null;
    }

    // Commands where buttons are attached to.
    public ICommand SearchCommand => _searchCommand ?? (_searchCommand = new RelayCommand(parameter =>
    {
        MyTreeNode result = SearchItemByName(TreeItems, Text);
        if (result != null)
            result.IsSelected = true;
    }));

    public ICommand AddCommand => _addCommand ?? (_addCommand = new RelayCommand(parameter =>
    {
        MyTreeNode newNode = new MyTreeNode { NodeName = Text };
        if (parameter is MyTreeNode node)
            node.Items.Add(newNode);
        else
            TreeItems.Add(newNode);
    }));

    public ICommand RemoveCommand => _removeCommand ?? (_removeCommand = new RelayCommand(parameter =>
    {
        MyTreeNode node = parameter as MyTreeNode;
        ObservableCollection<MyTreeNode> nodes = FindParentCollection(TreeItems, node);
        nodes.Remove(node);
    }, parameter => parameter is MyTreeNode));
}

以及有助于重现整个应用程序的完整标记

<Window x:Class="WpfApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainViewModel/><!-- MainViewModel instantiated here -->
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBox Text="{Binding Text}" Margin="5" Width="300"/>
            <Button Margin="5" Content="Search" Command="{Binding SearchCommand}"/>
            <Button Margin="5" Content="Add" Command="{Binding AddCommand}" CommandParameter="{Binding SelectedItem, ElementName=MyTreeView}"/>
            <Button Margin="5" Content="Remove" Command="{Binding RemoveCommand}" CommandParameter="{Binding SelectedItem, ElementName=MyTreeView}"/>
        </StackPanel>
        <TextBlock Grid.Row="1" Margin="5" Text="{Binding SelectedItem.NodeName, ElementName=MyTreeView}"/>
        <TreeView x:Name="MyTreeView" Grid.Row="2" Margin="5" ItemsSource="{Binding TreeItems}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                    <TextBlock Text="{Binding NodeName}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
            <TreeView.Resources>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
                    <Setter Property="IsExpanded" Value="True"/>
                </Style>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</Window>

以及 MVVM 新手的传统做法:代码隐藏 class

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