WPF / MVVM – 如何在ViewModel中双击TreeViewItems?

(注意 – 这是一个重新发布,因为我的第一个问题是在错误的标题下发布的:对不起!)

我有一个标准的WPF树视图,并有绑定项目来查看模型类。

我现在希望在双击项目时处理行为(打开文档visual-studio-style)。

我可以在树视图(显示xaml)的控件中触发事件处理程序,但是如何绑定到视图模型类上的特定行为 – 例如ProjectViewModel?

优先受ICommand实施者约束,因为这在别处使用…

                                   

稍微更新我的答案。

我已经尝试了很多不同的方法,我仍然觉得附加行为是最好的解决方案。 虽然在开始时它可能看起来很多开销但实际上并非如此。 我将ICommands所有行为保存在同一个地方,每当我需要支持另一个事件时,只需要复制/粘贴并更改PropertyChangedCallback的事件。

我还添加了CommandParameter的可选支持。

在设计师中,只需选择所需的事件即可

在此处输入图像描述

您可以在TreeViewTreeViewItem或您喜欢的任何其他位置设置此项。

例。 在TreeView上设置它

  

例。 在TreeViewItem上设置它

      

这是附加行为 MouseDoubleClick

 public class MouseDoubleClick { public static DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(MouseDoubleClick), new UIPropertyMetadata(CommandChanged)); public static DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(MouseDoubleClick), new UIPropertyMetadata(null)); public static void SetCommand(DependencyObject target, ICommand value) { target.SetValue(CommandProperty, value); } public static void SetCommandParameter(DependencyObject target, object value) { target.SetValue(CommandParameterProperty, value); } public static object GetCommandParameter(DependencyObject target) { return target.GetValue(CommandParameterProperty); } private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) { Control control = target as Control; if (control != null) { if ((e.NewValue != null) && (e.OldValue == null)) { control.MouseDoubleClick += OnMouseDoubleClick; } else if ((e.NewValue == null) && (e.OldValue != null)) { control.MouseDoubleClick -= OnMouseDoubleClick; } } } private static void OnMouseDoubleClick(object sender, RoutedEventArgs e) { Control control = sender as Control; ICommand command = (ICommand)control.GetValue(CommandProperty); object commandParameter = control.GetValue(CommandParameterProperty); command.Execute(commandParameter); } } 

我迟到了,但我只是使用了不同的解决方案。 再一次,它可能不是最好的,但这就是我这样做的方式。

首先,来自Meleak的前一个答案很酷,但我觉得被迫添加AttachedBehaviors只是为了像MouseDoubleClick那样基本的东西是非常沉重的。 这会迫使我在我的应用程序中使用新模式,甚至会使一切变得更复杂。

我的目标是尽可能保持简单。 因此我做了一些非常基本的事情(我的例子是针对DataGrid,但你可以在很多不同的控件上使用它):

    

在代码隐藏中:

 private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) { //Execute the command related to the doubleclick, in my case Edit (this.DataContext as VmHome).EditAppCommand.Execute(null); } 

为什么我觉得它不会破坏MVVM模式? 因为在我看来,你应该在代码隐藏中放置的唯一东西是你的viewModel的桥梁,这是你的UI特有的东西。 在这种情况下,它只是说,如果你双击,触发相关的命令。 它几乎与Command =“{Binding EditAppCommand}”相同,我只是模拟了这种行为。

请随意向我发表您对此的看法,我很高兴听到一些批评者对这种思维方式的看法,但是现在我相信这是在不破坏MVVM的情况下实现它的最简单方法。

Meleak和ígor的建议都很棒,但是当双击事件处理程序绑定到TreeViewItem将为所有项目的父元素(而不仅仅是单击的元素)调用事件处理程序。 如果不需要,这是另一个补充:

 private static void OnMouseDoubleClick(object sender, RoutedEventArgs e) { Control control = sender as Control; ICommand command = (ICommand)control.GetValue(CommandProperty); object commandParameter = control.GetValue(CommandParameterProperty); if (sender is TreeViewItem) { if (!((TreeViewItem)sender).IsSelected) return; } if (command.CanExecute(commandParameter)) { command.Execute(commandParameter); } } 

它非常简单,这就是我在TreeView上双击的方法:

          

System.Windows.Interactivity.dll取自C:\ Program Files(x86)\ Microsoft SDKs \ Expression \ Blend.NETFramework \ v4.0 \ Libraries \ System.Windows.Interactivity.dll或NuGet

我的观点模型:

 public class TreeViewModel : INotifyPropertyChanged { private List departments; public TreeViewModel() { Departments = new List() { new Department("Department1"), new Department("Department2"), new Department("Department3") }; } public List Departments { get { return departments; } set { departments = value; OnPropertyChanged("Departments"); } } public void SomeMethod() { MessageBox.Show("*****"); } } 

Meleak解决方案很棒!,但我添加了检查

  private static void OnMouseDoubleClick(object sender, RoutedEventArgs e) { Control control = sender as Control; ICommand command = (ICommand)control.GetValue(CommandProperty); object commandParameter = control.GetValue(CommandParameterProperty); //Check command can execute!! if(command.CanExecute(commandParameter )) command.Execute(commandParameter); } 

只是为了好奇:如果我接受Frederiks的一​​部分,但是直接将它作为行为来实现呢?

 public class MouseDoubleClickBehavior : Behavior { public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof (ICommand), typeof (MouseDoubleClickBehavior), new PropertyMetadata(default(ICommand))); public ICommand Command { get { return (ICommand) GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof (object), typeof (MouseDoubleClickBehavior), new PropertyMetadata(default(object))); public object CommandParameter { get { return GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } protected override void OnAttached() { base.OnAttached(); AssociatedObject.MouseDoubleClick += OnMouseDoubleClick; } protected override void OnDetaching() { AssociatedObject.MouseDoubleClick -= OnMouseDoubleClick; base.OnDetaching(); } void OnMouseDoubleClick(object sender, RoutedEventArgs e) { if (Command == null) return; Command.Execute(/*commandParameter*/null); } } 

我达到的最佳方法是在双向模式下将IsSelected属性从TreeViewItem绑定到ViewModel,并在属性设置器中实现逻辑。 然后,您可以定义在值为true或false时要执行的操作,因为只要用户单击某个项,此属性就会更改。

 class MyVM { private bool _isSelected; public bool IsSelected { get { return _isSelected; } set { if (_isSelected == null) return; _isSelected = vale; if (_isSelected) { // Your logic goes here. } else { // Your other logic goes here. } } } 

这避免了很多代码。

此外,此技术允许您仅在真正需要它的ViewModel中实现“onclick”行为。

TextBlock上的鼠标绑定

在View的TreeView.Resources中:

            

在该View的ViewModel中(DiscoveryUrlViewModel.cs):

 private RelayCommand _doubleClickCommand; public ICommand DoubleClickCopyCommand { get { if (_doubleClickCommand == null) _doubleClickCommand = new RelayCommand(OnDoubleClick); return _doubleClickCommand; } } private void OnDoubleClick(object obj) { var clickedViewModel = (DiscoveryUrlViewModel)obj; }