c# - 使用 CommandParameter 在 TreeView 项上进行键绑定(bind)

标签 c# .net wpf xaml mvvm

我正在尝试使用带有 KeyBinding 的 TreeViewItem 和 MenuContext 来执行位于我的 ViewModel 上的命令。

目前,使用上下文菜单,在正确的 ViewModel 实例上调用命令。 但是,当我选择一个 TreeViewItem 并按下“C”键时,将在“根”ViewModel 上调用该命令。

我也尝试扩展 KeyBinding 类(Keybinding a RelayCommand),但没有成功。

也许我走错了路:如果我使用上下文菜单或按键,我只想显示正确的 MessageBox。

名为 WpfTest 的 WPF 项目的代码示例。

MainWindow.xaml

<Window x:Class="WpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:WpfTest"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TreeView ItemsSource="{Binding}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Child}" DataType="{x:Type vm:ViewModel}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="ContextMenu">
                        <Setter.Value>
                            <ContextMenu>
                                <MenuItem Header="{Binding Name}" Command="{Binding SomeCommand}" CommandParameter="{Binding}"/>
                            </ContextMenu>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="vm:MyAttached.InputBindings">
                        <Setter.Value>
                            <InputBindingCollection>
                                <KeyBinding Key="C" Command="{Binding SomeCommand}" CommandParameter="{Binding}"/>
                            </InputBindingCollection>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>
    </Grid>
</Window>

MainWindow.xaml.cs:

namespace WpfTest
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Input;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new List<ViewModel>
            {
                new ViewModel
                {
                    Name = "Parent",
                    Child = new ObservableCollection<ViewModel>
                    {
                        new ViewModel { Name = "Child 1" },
                        new ViewModel { Name = "Child 2" },
                        new ViewModel { Name = "Child 3" }
                    }
                }
            };
        }
    }

    public class ViewModel
    {
        public string Name { get; set; }
        public ObservableCollection<ViewModel> Child { get; set; }
        public ICommand SomeCommand { get; set; }

        public ViewModel()
        {
            this.SomeCommand = new RelayCommand<ViewModel>(OnCommandExecuted);
        }

        private void OnCommandExecuted(ViewModel parameter)
        {
            MessageBox.Show("CommandExecuted on " + Name + " with parameter " + parameter.Name);
        }
    }

    public class MyAttached
    {
        public static readonly DependencyProperty InputBindingsProperty =
            DependencyProperty.RegisterAttached("InputBindings", typeof(InputBindingCollection), typeof(MyAttached),
            new FrameworkPropertyMetadata(new InputBindingCollection(),
            (sender, e) =>
            {
                var element = sender as UIElement;
                if (element == null) return;
                element.InputBindings.Clear();
                element.InputBindings.AddRange((InputBindingCollection)e.NewValue);
            }));

        public static InputBindingCollection GetInputBindings(UIElement element)
        {
            return (InputBindingCollection)element.GetValue(InputBindingsProperty);
        }

        public static void SetInputBindings(UIElement element, InputBindingCollection inputBindings)
        {
            element.SetValue(InputBindingsProperty, inputBindings);
        }
    }

    public class RelayCommand<T> : ICommand
    {
        readonly Action<T> _execute = null;
        public RelayCommand(Action<T> execute)  { _execute = execute; }
        public bool CanExecute(object parameter) { return true; }
        public void Execute(object parameter) { _execute((T)parameter); }
        public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
    }
}

最佳答案

这里是问题所在:Style 只为所有 ListViewItems 创建一个 InputBindingCollection,你必须非常小心 Setter .Values 出于这个原因。

这里是修复:

    <TreeView ItemsSource="{Binding}">
        <TreeView.Resources>
            <!-- x:Shared="False" forces the new creation of that object whenever referenced -->
            <InputBindingCollection x:Shared="False" x:Key="InputBindings">
                <KeyBinding Key="C" Command="{Binding SomeCommand}" CommandParameter="{Binding}" />
            </InputBindingCollection>
        </TreeView.Resources>
        <!-- ... -->
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <!-- ... -->
                <Setter Property="vm:MyAttached.InputBindings" Value="{StaticResource InputBindings}"/>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

关于c# - 使用 CommandParameter 在 TreeView 项上进行键绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7565428/

相关文章:

c# - 程序集、命名空间、DAL;什么类属于什么地方?

c# - NServicebus 添加到旧版 Windows 服务

c# - 如何为 Windows Phone 7 解析以下 JSON?

wpf - 将菜单项添加到 FlowDocumentReader ContextMenu

c# - 如何访问 .csx 脚本中的命令行参数?

c# - 使数组常量产生 "use of null is not valid in this context"错误

c# - NET Core 2 MVC - 为 GET 方法存储对象

.net - 具有现在(日期)默认值的可选参数

c# - WPF HTTPWebRequest 底层连接已关闭 : An unexpected error occurred on a receive

c# - WPF 错误 : Property elements cannot be in the middle of an element's content. 它们必须在内容之前或之后