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

C# 如何将键盘输入命令绑定到主窗口?,c#,wpf,mvvm,keyboard,icommand,C#,Wpf,Mvvm,Keyboard,Icommand,我对WPF绑定比较陌生,在绑定ICommand实现的类(用于检测窗口中的用户键盘输入)时遇到困难?起初,我认为需要将它绑定到XAML上的窗口,但该窗口似乎没有命令 这是我的ViewModel类 public class NoteBoxViewModel { public MusicalNotation MusicalNotation { get; set; } public ICommand KeyboardHotkeyCommand { get; private set; }

我对WPF绑定比较陌生,在绑定
ICommand
实现的类(用于检测窗口中的用户键盘输入)时遇到困难?起初,我认为需要将它绑定到XAML上的窗口,但该窗口似乎没有
命令

这是我的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;
    }
}
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--;
        }
    }
}
这是我的KeyboardHotkeyCommand类

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;
    }
}
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--;
        }
    }
}
这是我的笔记枚举

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

  • 如何将
    KeyboardHotkeyCommand
    绑定到我的XAML类,以便检测用户输入(input不会首先进入
    Textbox
    或任何类型的文本编辑器
    )?我还尝试将它绑定到
    窗口_IsKeyDown
    (我知道这是一个糟糕的做法),但最终失败了。是否可以在没有
    事件的情况下实现它
  • 在我的
    KeyboardHotkeyCommand
    中,有一个名为
    CanExecuteChanged
    的事件。我在上面填上了确切的方向,上面说的是:。但我不知道为什么会这样。谁能给我解释一下吗
  • 我还研究了这里的教程:他似乎可以用
    ActionCommand
    直接实例化
    ICommand
    ,但在我的VisualStudio中找不到。有人知道为什么吗
  • 注:

  • 尽管我是MVVM新手(昨天才知道),但我希望尽可能多地使用MVVM模式,因此,我希望避免使用事件(当然,如果可能的话),因为我读到这不是一个好的MVVM实践
  • 我认为,无论我的
    MusicalNotation
    类是什么样子,它都不会影响这个问题的解决,因为它只是一个“模型”。但如果需要的话,我也会百分之百地向你展示

  • 使用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>
    
    <Window.InputBindings>
        <KeyBinding Key="A"  Command="{Binding KeyBoardHotKeyCommand}" CommandParameter="N1"/>
    </Window.InputBindings>
    
    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;
    }
    
        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;
        }
    
    使用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;
        }
    
    您的“Notes”枚举很好。您的
    ICommand
    代理在MVVM方面走的是正确的道路,但需要从
    Keyboard.IsKeyDown中抽象出来,因为这些引用不容易测试。写得好的视图模型与硬件无关,并不真正关心事件是否发生在键盘或其他设备(如触摸屏)上

    对于最后一个问题,我使用Josh Smith的
    RelayCommand
    。看起来像这样

    <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>
    
    <Window.InputBindings>
        <KeyBinding Key="A"  Command="{Binding KeyBoardHotKeyCommand}" CommandParameter="N1"/>
    </Window.InputBindings>
    
    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;
    }
    
        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的一部分
    )基本上与Josh Smith的
    RelayCommand
    相同。它们都使用相同的原则来实现
    ICommand
    接口。

    如果可能,请您也回答第2个问题好吗?第2个和第3个问题已修改如果可能,您能否进一步解释一下:
    添加{CommandManager.RequerySuggested+=value;}
    删除{CommandManager.RequerySuggested-=value;}
    ?谢谢。你能读一下吗?如果有什么不清楚的地方,那就形成另一个问题?我有。嗯。那么,value是要绑定的元素吗?如果是,那我就有点明白了。