简体   繁体   English

自定义 Treeview 用户控件 MVVM 双击冒泡事件 WPF

[英]Custom Treeview User Control MVVM Double Click Bubbling Event WPF

I'm trying to make a custom TreeView and make it a user control.我正在尝试制作自定义 TreeView 并使其成为用户控件。 When I wrap the user control in another window, I tried to get the TreeView item double click event in the main window.当我将用户控件包装在另一个窗口中时,我试图在主窗口中获取 TreeView 项目双击事件。

<Window xmlns:avalondock="http://avalondock.codeplex.com"     x:Class="WellsVisualizationWPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:well="clr-namespace:VisualizationWPF.ViewModel.ViewUserControl"
    Title="e-IFD" Height="408" Width="558" WindowState="Maximized"
    >
 <Grid MinWidth="100" **TreeViewItem.MouseLeftButtonClick=<EventHandler>**>   <-- Trying to override but failed :p                                        
     <local:CustomTreeView />
 </Grid>

I tried to get the bubbling mouse double click from CustomTreeView item and intercept the event in the grid wrapper outside the usercontrol.我试图从 CustomTreeView 项目中获取冒泡鼠标双击,并在用户控件外的网格包装器中拦截事件。 I tried to add TreeViewItem.我试图添加 TreeViewItem。 TreeViewItem.MouseLeftButtonDown="Grid_MouseLeftButtonDown and failed. Any ideas to solve my problem? TreeViewItem.MouseLeftButtonDown="Grid_MouseLeftButtonDown 失败了。有什么办法可以解决我的问题吗?

Here is my code of custom user control for treeview这是我的树视图自定义用户控件代码

<UserControl x:Class="WellsVisualizationWPF.ViewModel.ViewUserControl.WellsTreeView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"           
         xmlns:local="clr-namespace:VisualizationWPF.ViewModel"           
         >
<Grid MinWidth="100">
    <Grid.RowDefinitions>
        <RowDefinition MaxHeight="500" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="1">
        <TextBlock TextWrapping="Wrap" FontSize="12">
        Text1
        </TextBlock>
        <Button Height="24" Content="Add New" Name="btn_add" Click="btn_add_Click" />
    </StackPanel>
    <ScrollViewer>
        <DockPanel>
            <TreeView  Grid.Row="0" ItemsSource="{Binding Data}">
                <TreeView.Resources>
                    <HierarchicalDataTemplate
                    DataType="{x:Type local:MainViewModel}"
                    ItemsSource="{Binding Children}"
                    >
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Name}" />
                        </StackPanel>
                    </HierarchicalDataTemplate>
                    <DataTemplate DataType="{x:Type local:ParamsViewModel}">
                        <TextBlock Text="{Binding Name}" />                            
                    </DataTemplate>
                </TreeView.Resources>                       
            </TreeView>
        </DockPanel>
    </ScrollViewer>
</Grid>

You don't need to publish double-click event outside from the user control at all.您根本不需要在用户控件之外发布双击事件。 You need to add some InputBinding ( MouseBinding in this particular case) into InputBindings collection of the TreeView.SelectedItem .您需要将一些InputBinding (在此特定情况下为MouseBinding )添加到TreeView.SelectedItemInputBindings集合中。

The problem is that you can't do that in normal, obvious way - set InputBindings via TreeView.ItemContainerStyle , because InputBindings collection is read-only.问题是你不能以正常、明显的方式做到这一点——通过TreeView.ItemContainerStyle设置InputBindings ,因为InputBindings集合是只读的。 Sad, but true.悲伤,但真实。

Good news is that you can use attached property to accomplish that.好消息是您可以使用附加属性来实现这一点。 The sample:例子:

View models.查看模型。
a) this is what will be displayed as items in tree view: a) 这是将在树视图中显示为项目的内容:

public class Node : ViewModelBase
{
    public String Text
    {
        get { return text; }
        set
        {
            if (text != value)
            {
                text = value;
                OnPropertyChanged("Text");
            }
        }
    }
    private String text;

    public ObservableCollection<Node> Nodes { get; set; }
}

b) this is "main" view model: b) 这是“主”视图模型:

public class ViewModel : ViewModelBase
{
    public ViewModel()
    {
        this.selectedNodeDoubleClickedCommand = new RelayCommand<Node>(node => 
        {
            Debug.WriteLine(String.Format("{0} clicked!", node.Text));
        });
    }

    public ObservableCollection<Node> Nodes { get; set; }

    public RelayCommand<Node> SelectedNodeDoubleClickedCommand
    {
        get { return selectedNodeDoubleClickedCommand; }
    }
    private readonly RelayCommand<Node> selectedNodeDoubleClickedCommand;
}

User control code-behind.用户控制代码隐藏。 Basic idea - we're adding one attached property to set input binding though it in XAML, and another one - to allow external world bind command, when input binding fires:基本思想——我们添加一个附加属性来通过它在 XAML 中设置输入绑定,而另一个属性——在输入绑定触发时允许外部世界绑定命令:

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public ICommand SelectedItemDoubleClickedCommand
    {
        get { return (ICommand)GetValue(SelectedItemDoubleClickedCommandProperty); }
        set { SetValue(SelectedItemDoubleClickedCommandProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemDoubleClickedCommandProperty = DependencyProperty.Register(
        "SelectedItemDoubleClickedCommand", typeof(ICommand), 
        typeof(UserControl1), 
        new UIPropertyMetadata(null));

    public static ICommand GetSelectedItemDoubleClickedCommandAttached(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(SelectedItemDoubleClickedCommandAttachedProperty);
    }

    public static void SetSelectedItemDoubleClickedCommandAttached(DependencyObject obj, ICommand value)
    {
        obj.SetValue(SelectedItemDoubleClickedCommandAttachedProperty, value);
    }

    public static readonly DependencyProperty SelectedItemDoubleClickedCommandAttachedProperty = DependencyProperty.RegisterAttached(
        "SelectedItemDoubleClickedCommandAttached", 
        typeof(ICommand), typeof(UserControl1), 
        new UIPropertyMetadata(null, SelectedItemDoubleClickedCommandAttachedChanged));

    private static void SelectedItemDoubleClickedCommandAttachedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var item = d as TreeViewItem;
        if (item != null)
        {
            if (e.NewValue != null)
            {
                var binding = new MouseBinding((ICommand)e.NewValue, new MouseGesture(MouseAction.LeftDoubleClick));

                BindingOperations.SetBinding(binding, InputBinding.CommandParameterProperty, new Binding("SelectedItem")
                {
                    RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(TreeView), 1)
                });

                item.InputBindings.Add(binding);
            }
        }
    }
}

User control XAML:用户控制 XAML:

<Grid>
    <TreeView ItemsSource="{Binding Nodes}">
        <TreeView.Resources>
            <HierarchicalDataTemplate ItemsSource="{Binding Nodes}" DataType="{x:Type local:Node}">
                <TextBlock Text="{Binding Text}"/>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="local:UserControl1.SelectedItemDoubleClickedCommandAttached" 
                        Value="{Binding SelectedItemDoubleClickedCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
            </Style>
        </TreeView.ItemContainerStyle>

    </TreeView>
</Grid>

Main window XAML:主窗口 XAML:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:UserControl1 SelectedItemDoubleClickedCommand="{Binding SelectedNodeDoubleClickedCommand}"/>
    </Grid>
</Window>

Main window code-behind:主窗口代码隐藏:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel
        {
            Nodes = new ObservableCollection<Node>
            {
                new Node
                {
                    Text = "Parent 1",
                    Nodes = new ObservableCollection<Node>
                    {
                        new Node { Text = "Child 1.1"},
                        new Node { Text = "Child 1.2"},
                    }
                },
                new Node
                {
                    Text = "Parent 2",
                    Nodes = new ObservableCollection<Node>
                    {
                        new Node { Text = "Child 2.1"},
                        new Node { Text = "Child 2.2"},
                    }
                },
            }
        };
    }
}

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM