如何将键盘输入命令绑定到主窗口?

时间:2014-06-19 11:01:10

标签: c# wpf mvvm keyboard icommand

我对WPF Binding相对较新,并且难以绑定ICommand实现的Class,以检测窗口中的用户键盘输入?起初我认为我需要将它绑定到XAML上的Window,但是,看起来Window没有Command

这是我的ViewModel类

public class NoteBoxViewModel
{
    public MusicalNotation MusicalNotation { get; set; }
    public ICommand KeyboardHotkeyCommand { get; private set; }
    public bool IsSelected { get; set; }

    public NoteBoxViewModel()
    {
        MusicalNotation = new MusicalNotation();
        KeyboardHotkeyCommand = new KeyboardHotkeyCommand(this);
        IsSelected = true;
    }
}

这是我的KeyboardHotkeyCommand Class

public class KeyboardHotkeyCommand : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    private NoteBoxViewModel _vm;

    public KeyboardHotkeyCommand(NoteBoxViewModel vm)
    {
        _vm = vm;
    }

    public bool CanExecute(object parameter)
    {
        return _vm.IsSelected;
    }

    public void Execute(object parameter)
    {
        if (Keyboard.IsKeyDown(Key.OemPeriod))
        {
            _vm.MusicalNotation.Note = Notes.Blank;
        }
        else if (Keyboard.IsKeyDown(Key.D0))
        {
            _vm.MusicalNotation.Note = Notes.Rest;
        }
        else if (Keyboard.IsKeyDown(Key.D1))
        {
            _vm.MusicalNotation.Note = Notes.N1;
        }
        else if (Keyboard.IsKeyDown(Key.D2))
        {
            _vm.MusicalNotation.Note = Notes.N2;
        }
        else if (Keyboard.IsKeyDown(Key.D3))
        {
            _vm.MusicalNotation.Note = Notes.N3;
        }
        else if (Keyboard.IsKeyDown(Key.D4))
        {
            _vm.MusicalNotation.Note = Notes.N4;
        }
        else if (Keyboard.IsKeyDown(Key.D5))
        {
            _vm.MusicalNotation.Note = Notes.N5;
        }
        else if (Keyboard.IsKeyDown(Key.D6))
        {
            _vm.MusicalNotation.Note = Notes.N6;
        }
        else if (Keyboard.IsKeyDown(Key.D7))
        {
            _vm.MusicalNotation.Note = Notes.N7;
        }
        else if (Keyboard.Modifiers == ModifierKeys.Control && Keyboard.IsKeyDown(Key.OemPlus))
        {
            _vm.MusicalNotation.Octave++;
        }
        else if (Keyboard.Modifiers == ModifierKeys.Control && Keyboard.IsKeyDown(Key.OemMinus))
        {
            _vm.MusicalNotation.Octave--;
        }
    }
}

这是我的Notes枚举

public enum Notes
{
    N1,
    N2,
    N3,
    N4,
    N5,
    N6,
    N7,
    Rest,
    Blank
}

问题:

  1. 如何将KeyboardHotkeyCommand绑定到我的XAML类,以便检测用户输入(输入不会Textbox或任何类型的文本编辑器)?我也尝试将它绑定到Window_IsKeyDown(这是一个不好的做法,我知道),但它毕竟失败了。是否有可能在没有event
  2. 的情况下实现它
  3. 在我的KeyboardHotkeyCommand中,有一个名为CanExecuteChanged的事件。我在这里告诉我的确切方向:http://blog.lab49.com/archives/2650。但我不知道为什么它会这样填充。谁能向我解释一下?
  4. 我也在这里研究了这个教程:http://reedcopsey.com/series/windows-forms-to-mvvm/似乎他可以用ICommand直接实例化ActionCommand,但我在Visual Studio中找不到任何内容。谁知道为什么?
  5. 注意:

    1. 即使我是MVVM的新手(昨天才知道),但我想尽可能多地使用MVVM模式,所以,我想避免使用事件(如果可能的话,当然),因为我读到这不是一个好的MVVM练习。
    2. 我认为,无论我的MusicalNotation类看起来如何,它都不会影响这个问题的解决方案,因为它只是一个'模型'。但如果需要,我也愿意为你展示。

1 个答案:

答案 0 :(得分:3)

使用MVVM模式,您可以使用主窗口上的InputBindings将键击绑定到VM命令。示例Xaml代码段看起来像这样......

<Window.InputBindings>
    <KeyBinding Key="Right" Command="{Binding NavigateCommand}" CommandParameter="f"/>
    <KeyBinding Key="Left" Command="{Binding NavigateCommand}" CommandParameter="b"/>
    <KeyBinding Key="Delete"  Command="{Binding NavigateCommand}"  CommandParameter="delete"/>
    <KeyBinding Key="F5" Command="{Binding GetAllCommand}"/>
</Window.InputBindings>

KeyBinding类有几个参数:键本身,它是一个枚举,以及任何修饰符,如CTRL,ALT和SHIFT。

直接感兴趣的两个参数是Command参数,它被绑定到VM,以及CommandParameter,它在命令代理中显示为参数。

在此示例中,Xaml将三个不同的击键绑定到NavigateCommand,并使用CommandParameter让VM知道按下了哪个键。

InputBindings的文档在这里http://msdn.microsoft.com/en-us/library/system.windows.input.inputbinding(v=vs.110).aspx有更多的使用样本。

注意:用法假定Window的DataContext已设置为实现这些命令的VM实例,否则将出现数据绑定错误。

你的实现看起来像这样......

<Window.InputBindings>
    <KeyBinding Key="A"  Command="{Binding KeyBoardHotKeyCommand}" CommandParameter="N1"/>
</Window.InputBindings>

与ICommand代表一样......

    private void ExecuteKeyboardHotKeyCommand(object obj)
    {
        Notes note;
        if (obj!=null && Enum.TryParse(obj.ToString(), out note))
        {
            Console.WriteLine(@"User pressed {0}", note);
        }
    }
    private bool CanExecuteKeyboardHotKeyCommand(object obj)
    {
        return true;
    }

你的笔记&#39;枚举很好。您的ICommand代表是MVVM的正确轨道,但需要从Keyboard.IsKeyDown抽象出来,因为这些引用无法轻松测试。写得很好的视图模型是硬件无关的,并不关心事件是否发生在键盘或其他设备(如触摸屏)上。

关于你的上一个问题,我使用的是Josh Smith的RelayCommand。它看起来像这样......

public class RelayCommand : ICommand
{   //http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void Execute(object parameter)
    {
        _execute(parameter);
    }
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;
}

CanExecuteChanged是WPF绑定引擎(或其他一些外源类)可以订阅的事件。实现看起来像这样......

    public ICommand KeyBoardHotKeyCommand { get; set; }
    public ViewModel()
    {
        KeyBoardHotKeyCommand = new RelayCommand(ExecuteKeyboardHotKeyCommand, CanExecuteKeyboardHotKeyCommand);
    }
    private void ExecuteKeyboardHotKeyCommand(object obj)
    {
        Notes note;
        if (obj!=null && Enum.TryParse(obj.ToString(), out note))
        {
            Console.WriteLine(@"User pressed {0}", note);
        }
    }
    private bool CanExecuteKeyboardHotKeyCommand(object obj)
    {
        return true;
    }

最后,关于Reed的实施问题,当美国的成员醒来时,Reed可能会出现。但就目前而言,他的ActionCommand(现在是Expression Studio http://msdn.microsoft.com/en-us/library/microsoft.expression.interactivity.core.actioncommand(v=expression.40).aspx的一部分 )与Josh Smith的RelayCommand基本相同。他们都使用相同的原则来实现ICommand接口。