简体   繁体   English

如何为每个TreeViewItem设置事件?

[英]How to set event for every TreeViewItem?

I want to set an event for every TreeViewItem i'm creating dynamically in code. 我想为我在代码中动态创建的每个TreeViewItem设置一个事件。 I'm using the following XAML code: 我正在使用以下XAML代码:

<Window.Resources>
        <Style TargetType="TreeViewItem">
            <EventSetter Event="Selected" Handler="TreeViewItemSelectionEvent"/>
            <EventSetter Event="MouseRightButtonDown" Handler="TreeViewItemRightClickEvent"/>
        </Style>
</Window.Resources>

But this only works on a root TreeViewItem in the TreeView. 但这仅适用于TreeView中的根TreeViewItem。 If i click on another item, which is a child of the root, then i allways get back the root. 如果我单击另一个项目,它是根的子级,那么我总是返回根。

Can i make this work somehow? 我可以以某种方式进行这项工作吗? I'd love this method, thus you don't need to handle the events in your code which makes it look cleaner. 我很喜欢这种方法,因此您无需处理代码中的事件,从而使它看起来更干净。

TL;DR: Use HierarchicalDataTemplate and Styles to display data in a TreeView. TL; DR:使用HierarchicalDataTemplate和样式在TreeView中显示数据。 Dynamically loading data in your viewmodel will automatically update the TreeView. 在视图模型中动态加载数据将自动更新TreeView。

Do not manually create TreeViewItems. 不要手动创建TreeViewItems。

In MVVM, what matters the most is the data and the architecture of your data. 在MVVM中,最重要的是数据和数据的体系结构。 The general pattern is to define your data and separately define how the view will display your data. 通常的模式是定义数据,并分别定义视图如何显示数据。 Your view is dependent on your data, not the other way around. 您的视图取决于数据,而不是相反。

So let's create a ProductViewModel which has a ProductName , a list of sub products and a IsSelected property. 因此,让我们创建一个ProductViewModel ,它具有一个ProductName ,一个子产品列表和一个IsSelected属性。 We equip this class with a method LoadSubProductsCollectionFromDataSource which retrieves data from whatever data source you may have. 我们为此类配备了方法LoadSubProductsCollectionFromDataSource ,该方法可从您可能拥有的任何数据源中检索数据。 Here, I just load somme dummy items. 在这里,我只加载一些虚拟物品。

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

In your MainWindow.xaml.cs, initialize and expose a collection of view model objects like this: 在您的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" });
    }
}

This collection would normally be stored in a main viewmodel object but for simplicity I just created it in the MainWindow. 该集合通常将存储在主viewmodel对象中,但为简单起见,我只是在MainWindow中创建了它。 Notice how I expose it as a property (to allow Binding) and as an ObservableCollection (to automatically notify the view when the collection changes). 请注意,我是如何将其作为属性(允许绑定)和ObservableCollection(在集合更改时自动通知视图)公开的。

Finally, tell your view how to display your ProductViewModel objects using a TreeView: 最后,告诉您的视图如何使用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>

Now, every time you select a TreeViewItem in your TreeView , its IsSelected property is set to true (this is the behavior of a TreeViewItem ). 现在,每次在TreeViewItem中选择TreeView ,其IsSelected属性都设置为true(这是TreeViewItem的行为)。 Because of our binding, it also sets to true the IsSelected property of the corresponding ProductViewModel . 由于我们的绑定,它还将相应ProductViewModelIsSelected属性设置为true。 In the setter of this property, we make a call to populate the list of subproducts. 在此属性的设置器中,我们进行了调用以填充子产品列表。 Because this list is actually an ObservableCollection , it notifies the View (the TreeView ) which knows it should update itself with new TreeViewItems . 因为此列表实际上是一个ObservableCollection ,所以它通知View( TreeView ),后者知道应该使用新的TreeViewItems更新自身。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何为ContextMenu设置TreeViewItem的DataContext - How to Set DataContext for TreeViewItem for ContextMenu 如何在C#上将MouseDoubleClick事件添加到TreeViewItem - How to add a MouseDoubleClick event on C# to a TreeViewItem WPF-如何获取触发TreeViewItem.Loaded事件的TreeViewItem? - Wpf- How can I get the TreeViewItem that triggered the TreeViewItem.Loaded event? 如何在wpf中的treeviewitem中访问treeviewitem? - How to access a treeviewitem with in a treeviewitem in wpf? 带有 TreeViewItem.Header 的 TreeViewItem 上的 C# WPF TreeViewItem MouseDoubleClick 事件 - C# WPF TreeViewItem MouseDoubleClick event on TreeViewItem with TreeViewItem.Header TreeViewItem 展开事件 - TreeViewItem expanded event WPF如何在selecteditemchanged事件中获取选定的treeviewitem? - WPF how do i get the selected treeviewitem on selecteditemchanged event? 如何使用 WPF 中的 PreviewMouseLeftButtonDown 事件忽略 TreeViewItem 扩展器上的鼠标单击? - How to ignore mouse click on TreeViewItem expander, using PreviewMouseLeftButtonDown event in WPF? 在TreeViewItem WPF中设置SelectionActive - Set SelectionActive in TreeViewItem WPF 如何在列表框的类似TreeView的TreeView中,以递归方式在按钮单击事件中找到treeviewitem的Next / Previous TreeViewItem? - How can I find the Next/Previous TreeViewItem of a treeviewitem on a button click event recursively in a TreeView like listview item in a listbox?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM