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 中的控件进行交互真的很难。而且我不知道保留您的开发方法的答案。但是知道如何用其他方式完成它。
我建议改用 MVVM 和 Binding
。我已经创建了一个演示项目来展示它是如何完成的。
这不是 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();
}
}
我尝试使用 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 中的控件进行交互真的很难。而且我不知道保留您的开发方法的答案。但是知道如何用其他方式完成它。
我建议改用 MVVM 和 Binding
。我已经创建了一个演示项目来展示它是如何完成的。
这不是 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();
}
}