C#/WPF:按键绑定未触发命令

11

我已经声明了<InputBindings>

<UserControl.InputBindings>
    <KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyImageCommand}" />
    <KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteImageCommand}" />
</UserControl.InputBindings>

为了测试目的,我添加了绑定到这些命令的按钮

<Button Command="{Binding CopyImageCommand}" Content="Copy" />
<Button Command="{Binding PasteImageCommand}" Content="Paste" />
我注意到当启用粘贴按钮时,按Ctrl-V键没有任何反应。Ctrl-C似乎可以工作。此时列表框中的项目被选中,我不确定这是否有任何区别。有人知道为什么我的PasteImageCommand没有触发吗?
我顺便提一下,我正在使用.NET 4。
更新:
以下是更完整的代码片段:
<UserControl x:Class="QuickImageUpload.Views.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:QuickImageUpload.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.InputBindings>
        <KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyImageCommand}" />
        <KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteImageCommand}" />
    </UserControl.InputBindings>
    <UserControl.DataContext>
        <vm:ShellViewModel />
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />

更新

我发现我需要将 KeyBindings 放在主窗口 (MainWindow) 中,但是命令在视图模型 (ViewModel) 中,如何在 ShellView 中设置键绑定,然后将其绑定到 ShellViewModel 的命令中?


请问您能否发布InputBinding在哪里指定的信息?可能您将其放错了位置。 - Euphoric
@Euphoric,我已经将我的InputBindings放在了UserControl ShellView中。我发现当我把它们放在MainWindow中时它可以工作,但是我需要将视图模型设置为ShellViewModel,这样做不太正确,我该如何处理? - Jiew Meng
1
@JiewMeng:嗨,Jiew!我几乎有同样的问题。你找到任何解决方案了吗? - Jalal
@Jalax,我已经有一段时间没有做C#了,恐怕我记不清我是否解决了这个问题:( - Jiew Meng
7个回答

6
确保您没有绑定错误。您设置了用户控件的DataContext,请确保命令可以绑定到它。有时,WPF只使用出现的顺序,DataContext比命令后设置。
可能VS的输出窗口已经显示了命令的绑定错误。尝试将DataContext定义放在顶部(并教自己在所有视图中都这样做)。

1
将输入绑定移动到用户控件底部对我很有效。 - joe blogs
1
我有多个绑定正在进行,这篇文章帮助我意识到只需逐个标记并为每个组件定义确切的数据上下文。谢谢! - cwiggo

3
为了避免硬编码键位绑定,我使用了Josh Smith的RelayCommand-Class并添加了与快捷键相关的内容:
class UIRelayCommand : RelayCommand, INotifyPropertyChanged
{
    private static Dictionary<ModifierKeys, string> modifierText = new Dictionary<ModifierKeys, string>()
    {
        {ModifierKeys.None,""},
        {ModifierKeys.Control,"Ctrl+"},
        {ModifierKeys.Control|ModifierKeys.Shift,"Ctrl+Shift+"},
        {ModifierKeys.Control|ModifierKeys.Alt,"Ctrl+Alt+"},
        {ModifierKeys.Control|ModifierKeys.Shift|ModifierKeys.Alt,"Ctrl+Shift+Alt+"},
        {ModifierKeys.Windows,"Win+"}
    };

    private Key _key;
    public Key Key
    {
        get { return _key; }
        set { _key = value; RaisePropertyChanged("Key"); RaisePropertyChanged("GestureText"); }
    }

    private ModifierKeys _modifiers;
    public ModifierKeys Modifiers
    {
        get { return _modifiers; }
        set { _modifiers = value; RaisePropertyChanged("Modifiers"); RaisePropertyChanged("GestureText");}
    }

    public string GestureText
    {
        get { return modifierText[_modifiers] + _key.ToString(); }
    }

    public UIRelayCommand(Action<object> execute, Predicate<object> canExecute, Key key, ModifierKeys modifiers)
        : base(execute, canExecute)
    {
        _key = key;
        _modifiers = modifiers;
    }


    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

然后在ViewModel中创建命令:

private ICommand _newFileCommand;
public ICommand NewFileCommand
{
    get
    {
        if (_newFileCommand == null)
            _newFileCommand = new UIRelayCommand(p => OnNewFile(p), p => CanNewFile(p), Key.N, ModifierKeys.Control);
        return _newFileCommand;
    }
}
protected void OnNewFile(object p)
{
    //open file...
}
protected bool CanNewFile(object p)
{
    return true;
}

并将其绑定在视图中:

<Window.InputBindings>
    <KeyBinding Command="{Binding NewFileCommand}" Key="{Binding NewFileCommand.Key}" Modifiers="{Binding NewFileCommand.Modifiers}"  />
</Window.InputBindings>
...
<MenuItem Header="New File" Command="{Binding NewFileCommand}" InputGestureText="{Binding NewFileCommand.GestureText}" />

采用这种方法,我可以允许用户在运行时(在我的配置窗口中)调整快捷方式。


1
+1...非常好的解决方案!最后一段代码片段中有一个小错别字:Modifiers="{Binding NewFileCommand.Modifier}" 应该是 Modifiers="{Binding NewFileCommand.Modifiers}"。 - SWalters

1
答案可以在这个链接中找到。

https://social.msdn.microsoft.com/Forums/vstudio/en-US/395046c8-2cc8-48b4-9642-341ce0c99cc9/key-binding-does-not-work-always-in-mvvm-application?forum=wpf

由于焦点问题,用户控件中的输入绑定应该使用用户控件代码后台分配给主窗口。
public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
        this.Loaded += UserControl1_Loaded;
    }

    void UserControl1_Loaded(object sender, RoutedEventArgs e)
    {
        Window window = Window.GetWindow(this);
        foreach (InputBinding ib in this.InputBindings)
        {
            window.InputBindings.Add(ib);
        }
    }
}

0
在这种情况下,您可以在RoutedCommand声明中提供键绑定:
public static RoutedCommand PasteImageCommand = new RoutedCommand("PasteImageCommand", typeof(YourType), new InputGestureCollection { new KeyGesture(Key.V, ModifierKeys.Control)});

这应该可以工作。


0
我曾经遇到过类似的情况,即键相关事件只在Shell View中被监听,而没有传递到实际按下键的视图中。 为了解决这个问题,我编写了一个小的附加行为,将焦点设置到用户控件或框架元素上,以便在初始加载时接收焦点,这样按键就可以被我想要监听的UI元素所监听。
public class FocusBehavior
{
    public static readonly DependencyProperty IsFocusedProperty = 
        DependencyProperty.RegisterAttached("IsFocused", typeof(bool?),typeof(FocusBehavior),
        new UIPropertyMetadata(false, new PropertyChangedCallback(OnFocusChanged)));
    public static bool? GetIsFocused(DependencyObject obj)
    {
        return (bool?)obj.GetValue(IsFocusedProperty);
    }
    public static void SetIsFocused(DependencyObject obj, bool? value)
    {
        obj.SetValue(IsFocusedProperty, value);
    }
    private static void OnFocusChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        var frameworkElement = sender as FrameworkElement;
        if (frameworkElement == null) return;
        if (args.OldValue == null) return;
        if (args.NewValue == null) return;
        if ((bool)args.NewValue)
        {
            frameworkElement.Loaded += OnFrameworkElementLoaded;
        }
    }

    private static void OnFrameworkElementLoaded(object sender, RoutedEventArgs args)
    {
        var frameworkElement = sender as FrameworkElement;
        frameworkElement.Focus();
        frameworkElement.Loaded -= OnFrameworkElementLoaded;
        var textControl = frameworkElement as JHATextEditor;
        if (textControl == null) return;
        textControl.SelectAll();
    }
}

而在我的列表视图中,我像下面这样使用它 -

<GridViewColumn Width="Auto" Header="Value">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid HorizontalAlignment="Stretch" MinWidth="100">
                                <TextBlock Text="{Binding FieldValue}" />
                            </Grid>
                            <DataTemplate.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="True">
                                    <Setter Property="local:FocusBehavior.IsFocused" TargetName="FieldValueEditor" Value="True" />
                                </DataTrigger>
                            </DataTemplate.Triggers>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

希望这能有所帮助。
-VJ

0
你在使用3.5还是4版本?
在3.5版本中,"feature"是这样的。UserControl.InputBindings不属于dataContext树,因此无法绑定到父类绑定的类的项。例如,DataBinding将无法工作,您需要手动在代码中设置DataBinding或整个KeyBinding。
在4版本中已经修复了这个问题。

我更新了我的帖子,并添加了更完整的代码片段,展示了输入绑定的声明位置。 - Jiew Meng

0

试试这个:

<UserControl.InputBindings>
        <KeyBinding Key="C" Modifiers="Control" Command="{Binding CopyImageCommand}" />
        <KeyBinding Key="V" Modifiers="Control" Command="{Binding PasteImageCommand}" />
    </UserControl.InputBindings>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接