如何为每个 TreeViewItem 设置事件?
How to set event for every TreeViewItem?
我想为我在代码中动态创建的每个 TreeViewItem 设置一个事件。我正在使用以下 XAML 代码:
<Window.Resources>
<Style TargetType="TreeViewItem">
<EventSetter Event="Selected" Handler="TreeViewItemSelectionEvent"/>
<EventSetter Event="MouseRightButtonDown" Handler="TreeViewItemRightClickEvent"/>
</Style>
</Window.Resources>
但这只适用于 TreeView 中的根 TreeViewItem。如果我点击另一个项目,它是根的 child,那么我总是会找回根。
我能以某种方式完成这项工作吗?我喜欢这种方法,因此您不需要处理代码中的事件,这使它看起来更干净。
TL;DR:使用 HierarchicalDataTemplate 和样式在 TreeView 中显示数据。在您的视图模型中动态加载数据将自动更新 TreeView。
不要手动创建 TreeViewItems。
在 MVVM 中,最重要的是数据和数据的架构。一般模式是定义您的数据并单独定义视图将如何显示您的数据。您的观点取决于您的数据,而不是相反。
那么让我们创建一个 ProductViewModel
,它有一个 ProductName
、一个子产品列表和一个 IsSelected
属性。我们为这个 class 配备了一个方法 LoadSubProductsCollectionFromDataSource
,它可以从您拥有的任何数据源中检索数据。在这里,我只加载了一些虚拟项目。
public class ProductViewModel {
/// <summary>
/// Backing field for the IsSelected property
/// </summary>
private bool _isSelected;
/// <summary>
/// Gets or sets the collection of materials used to build this Product.
/// </summary>
public ObservableCollection<ProductViewModel> SubProducts { get; set; } = new ObservableCollection<ProductViewModel>();
/// <summary>
/// Gets or sets the name of this product.
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// Gets or sets the selected state of this product.
/// </summary>
public bool IsSelected {
get => _isSelected;
set {
//The product has been selected or deselected.
if (!_isSelected && SubProducts.Count == 0) {
//We load data into it if not already done.
LoadSubProductsCollectionFromDataSource();
}
_isSelected = value;
}
}
/// <summary>
/// Loads sub products data into this product.
/// </summary>
private void LoadSubProductsCollectionFromDataSource() {
//..
//Do stuff to retrieve your data dynamically and
//add them to the SubProducts collection.
//...
for (int i = 0; i < 5; i++) {
//Add dummy items
SubProducts.Add(new ProductViewModel() { ProductName = "Some product " + i.ToString() });
}
}
}
在您的 MainWindow.xaml.cs 中,像这样初始化并公开视图模型对象的集合:
public partial class MainWindow : Window {
/// <summary>
/// Exposes the root product of the tree
/// </summary>
public ObservableCollection<ProductViewModel> RootProducts { get; } = new ObservableCollection<ProductViewModel>();
public MainWindow() {
InitializeComponent();
RootProducts.Add(new ProductViewModel() { ProductName = "Root product" });
}
}
这个集合通常存储在主视图模型对象中,但为简单起见,我只是在 MainWindow 中创建它。请注意我如何将其公开为 属性(以允许绑定)和 ObservableCollection(以在集合更改时自动通知视图)。
最后,告诉您的视图如何使用 TreeView 显示您的 ProductViewModel 对象:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
x:Name="window"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<!--Tell the treeview how to hierarchically display and organize ProductViewModel items-->
<HierarchicalDataTemplate DataType="{x:Type local:ProductViewModel}" ItemsSource="{Binding SubProducts}">
<TextBlock Text="{Binding ProductName}"></TextBlock>
</HierarchicalDataTemplate>
<!--Tell each treeviewitem to bind IsSelected to the PoductViewModel.ISSelected property.-->
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"></Setter>
</Style>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding ElementName=window, Path=RootProducts}"/>
</Grid>
</Window>
现在,每次你 select 一个 TreeViewItem
在你的 TreeView
中,它的 IsSelected
属性 被设置为真(这是行为TreeViewItem
)。由于我们的绑定,它还将相应 ProductViewModel
的 IsSelected
属性 设置为 true。在此 属性 的 setter 中,我们调用以填充子产品列表。因为这个列表实际上是一个 ObservableCollection
,它通知视图(TreeView
)知道它应该用新的 TreeViewItems
.
更新自己
我想为我在代码中动态创建的每个 TreeViewItem 设置一个事件。我正在使用以下 XAML 代码:
<Window.Resources>
<Style TargetType="TreeViewItem">
<EventSetter Event="Selected" Handler="TreeViewItemSelectionEvent"/>
<EventSetter Event="MouseRightButtonDown" Handler="TreeViewItemRightClickEvent"/>
</Style>
</Window.Resources>
但这只适用于 TreeView 中的根 TreeViewItem。如果我点击另一个项目,它是根的 child,那么我总是会找回根。
我能以某种方式完成这项工作吗?我喜欢这种方法,因此您不需要处理代码中的事件,这使它看起来更干净。
TL;DR:使用 HierarchicalDataTemplate 和样式在 TreeView 中显示数据。在您的视图模型中动态加载数据将自动更新 TreeView。
不要手动创建 TreeViewItems。
在 MVVM 中,最重要的是数据和数据的架构。一般模式是定义您的数据并单独定义视图将如何显示您的数据。您的观点取决于您的数据,而不是相反。
那么让我们创建一个 ProductViewModel
,它有一个 ProductName
、一个子产品列表和一个 IsSelected
属性。我们为这个 class 配备了一个方法 LoadSubProductsCollectionFromDataSource
,它可以从您拥有的任何数据源中检索数据。在这里,我只加载了一些虚拟项目。
public class ProductViewModel {
/// <summary>
/// Backing field for the IsSelected property
/// </summary>
private bool _isSelected;
/// <summary>
/// Gets or sets the collection of materials used to build this Product.
/// </summary>
public ObservableCollection<ProductViewModel> SubProducts { get; set; } = new ObservableCollection<ProductViewModel>();
/// <summary>
/// Gets or sets the name of this product.
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// Gets or sets the selected state of this product.
/// </summary>
public bool IsSelected {
get => _isSelected;
set {
//The product has been selected or deselected.
if (!_isSelected && SubProducts.Count == 0) {
//We load data into it if not already done.
LoadSubProductsCollectionFromDataSource();
}
_isSelected = value;
}
}
/// <summary>
/// Loads sub products data into this product.
/// </summary>
private void LoadSubProductsCollectionFromDataSource() {
//..
//Do stuff to retrieve your data dynamically and
//add them to the SubProducts collection.
//...
for (int i = 0; i < 5; i++) {
//Add dummy items
SubProducts.Add(new ProductViewModel() { ProductName = "Some product " + i.ToString() });
}
}
}
在您的 MainWindow.xaml.cs 中,像这样初始化并公开视图模型对象的集合:
public partial class MainWindow : Window {
/// <summary>
/// Exposes the root product of the tree
/// </summary>
public ObservableCollection<ProductViewModel> RootProducts { get; } = new ObservableCollection<ProductViewModel>();
public MainWindow() {
InitializeComponent();
RootProducts.Add(new ProductViewModel() { ProductName = "Root product" });
}
}
这个集合通常存储在主视图模型对象中,但为简单起见,我只是在 MainWindow 中创建它。请注意我如何将其公开为 属性(以允许绑定)和 ObservableCollection(以在集合更改时自动通知视图)。
最后,告诉您的视图如何使用 TreeView 显示您的 ProductViewModel 对象:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
x:Name="window"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<!--Tell the treeview how to hierarchically display and organize ProductViewModel items-->
<HierarchicalDataTemplate DataType="{x:Type local:ProductViewModel}" ItemsSource="{Binding SubProducts}">
<TextBlock Text="{Binding ProductName}"></TextBlock>
</HierarchicalDataTemplate>
<!--Tell each treeviewitem to bind IsSelected to the PoductViewModel.ISSelected property.-->
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"></Setter>
</Style>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding ElementName=window, Path=RootProducts}"/>
</Grid>
</Window>
现在,每次你 select 一个 TreeViewItem
在你的 TreeView
中,它的 IsSelected
属性 被设置为真(这是行为TreeViewItem
)。由于我们的绑定,它还将相应 ProductViewModel
的 IsSelected
属性 设置为 true。在此 属性 的 setter 中,我们调用以填充子产品列表。因为这个列表实际上是一个 ObservableCollection
,它通知视图(TreeView
)知道它应该用新的 TreeViewItems
.