我正在使用C#,WPF,ReactiveUI和Prism来创建具有许多不同视图(用户控件)的应用程序。在某些视图中,有一些按钮/菜单项绑定到视图模型中的命令。我希望这些按钮也可以使用按键组合激活,例如ctrl + s等....
我尝试了什么
InputBindings
但只有在定义这些输入绑定的视图具有焦点时才有效。 ApplicationCommands
预定义命令(如ApplicationCommands.Close
)似乎很有用。我可以在视图和视图模型中引用它们,但我不知道如何在视图模型中订阅它们。似乎我必须先“激活”命令,或者至少更改CanExecute,因为任何绑定到此命令的按钮都会被禁用。我希望
假设我的视图代表顶部菜单栏MenuView
带有按钮myButton
,相应的视图模型MenuViewModel
带有命令myCommand
。我想将myButton
绑定到myCommand
,将键盘快捷键ctrl+u
绑定到myCommand
,而MenuView
不知道其视图模型的实现。只要包含MenuView
的窗口具有焦点,键盘快捷键就可以正常工作。
我并不关心键盘快捷键是在视图模型还是视图模型中。
答案 0 :(得分:1)
您可以创建一个附加的Blend行为来处理父窗口的PreviewKeyDown
事件:
public class KeyboardShortcutBehavior : Behavior<FrameworkElement>
{
private Window _parentWindow;
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(nameof(Command), typeof(ICommand),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(null));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty ModifierKeyProperty =
DependencyProperty.Register(nameof(ModifierKey), typeof(ModifierKeys),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(ModifierKeys.None));
public ModifierKeys ModifierKey
{
get { return (ModifierKeys)GetValue(ModifierKeyProperty); }
set { SetValue(ModifierKeyProperty, value); }
}
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register(nameof(Key), typeof(Key),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(Key.None));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
AssociatedObject.Unloaded += AssociatedObject_Unloaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
_parentWindow = Window.GetWindow(AssociatedObject);
if(_parentWindow != null)
{
_parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
}
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(Command != null && ModifierKey != ModifierKeys.None && Key != Key.None && Keyboard.Modifiers == ModifierKey && e.Key == Key)
Command.Execute(null);
}
private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
{
if(_parentWindow != null)
{
_parentWindow.PreviewKeyDown -= ParentWindow_PreviewKeyDown;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObject_Loaded;
AssociatedObject.Unloaded -= AssociatedObject_Loaded;
}
}
样本用法:
<TextBox xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Behaviors>
<local:KeyboardShortcutBehavior ModifierKey="Ctrl" Key="U" Command="{Binding myCommand}" />
</i:Interaction.Behaviors>
</TextBox>
答案 1 :(得分:0)
在代码背后容易。创建一些实用程序函数,最终导致父窗口键事件的可观察。请注意,您将需要ReactiveUI.Events库。
一些用于处理加载和卸载控件的工具。
public static void LoadUnloadHandler
( this FrameworkElement control
, Func<IDisposable> action
)
{
var state = false;
var cleanup = new SerialDisposable();
Observable.Merge
(Observable.Return(control.IsLoaded)
, control.Events().Loaded.Select(x => true)
, control.Events().Unloaded.Select(x => false)
)
.Subscribe(isLoadEvent =>
{
if (!state)
{
// unloaded state
if (isLoadEvent)
{
state = true;
cleanup.Disposable = new CompositeDisposable(action());
}
}
else
{
// loaded state
if (!isLoadEvent)
{
state = false;
cleanup.Disposable = Disposable.Empty;
}
}
});
}
public static IObservable<T> LoadUnloadHandler<T>(this FrameworkElement control, Func<IObservable<T>> generator)
{
Subject<T> subject = new Subject<T>();
control.LoadUnloadHandler(() => generator().Subscribe(v => subject.OnNext(v)));
return subject;
}
和一个专门用于处理加载控件的窗口
public static IObservable<T> LoadUnloadHandler<T>
(this FrameworkElement control, Func<Window, IObservable<T>> generator)
{
Subject<T> subject = new Subject<T>();
control.LoadUnloadHandler(() => generator(Window.GetWindow(control)).Subscribe(v => subject.OnNext(v)));
return subject;
}
最后是任何控件的父窗口的键处理程序
public static IObservable<KeyEventArgs> ParentWindowKeyEventObservable(this FrameworkElement control)
=> control.LoadUnloadHandler((Window window) => window.Events().PreviewKeyDown);
现在你可以做到
Button b;
b.ParentWindowKeyEventObservable()
.Subscribe( kEvent => {
myCommand.Execute();
}
看起来有点复杂但我在大多数用户控件上使用LoadUnloadHandler来随着UI生命周期的进展获取和处理资源。
答案 2 :(得分:0)
您想要使用KeyBindings。这允许您将键盘键组合绑定到命令。阅读此处的文档:https://msdn.microsoft.com/en-us/library/system.windows.input.keybinding(v=vs.110).aspx