I am developing an MVVM application. I have a main Window, which looks more or less like this:
<Window>
<ContentControl Content={Binding ContentViewModel} />
</Window>
Then I have this ViewModel, which exposes a certain number of Commands, and I want these commands to be available to the user both from the UI (with buttons, etc), AND from the keyboard, using KeyBindings.
The commands work properly from the UI buttons. But the Keybindings don't always work, it'd seem to me that the problem is that the loaded view is not always in focus. This is the code for the view.
<UserControl>
<UserControl.InputBindings>
<KeyBinding Key="Delete" Command="{Binding RemoveEntityCommand, ElementName=Designer}" />
</UserControl.InputBindings>
<Grid>
<namespace:Designer x:Name="Designer" />
</Grid>
</UserControl>
How to solve this permanently for an MVVM application? I've encountered this problem multiple times.
Note: all namespace declarations removed for simplicity.
Thanks.
I would probably attach a Command to the KeyDown or KeyUp event of the Window
instead of the UserControl
, and route it from there.
It can either be routed to the ShellViewModel
, which will in turn pass it to the current ContentViewModel
if needed, or perhaps use some kind of Messaging system that broadcasts special key combinations, and ViewModel's can subscribe to them.
What I would do is implement a PreviewKeyUp event and use that to call a method on my view model, something like this:
protected void PreviewKeyUp(object sender, KeyEventArgs args)
{
args.Handled = myViewModel.HandleKeyUp(args.Key);
}
public bool HandleKeyUp(Key key)
{
// Determine if you should execute a command
if(myCommands.ShouldExecuteOnKey(key))
{
// Execute the commad
return true;
}
return false;
}
A lot of people seem to think that MVVM means no code-behind , but that's not always true, or even possible.
Yeah, key bindings are a pain. And I agree with Rachel that you likely want something available at the window level in this specific case.
You can avoid code behind and get the goodness of mvvm by doing the following:
This is obviously a bit of infrastructure work tho it is testable and reusable once you have it in place. In this case, since you want the bindings available at the window level, your ShellVm holds the commands and delegates to child view models as needed. I've left just enough code below to give you a feel for the idea
HTH,
Berryl
<Window.InputBindings>
<cmdRef:KeyBindingEx CommandReference="{Binding AddCommand}"/>
</Window.InputBindings>
public class KeyBindingEx : KeyBinding
{
public static readonly DependencyProperty CommandReferenceProperty = DependencyProperty
.Register("CommandReference", typeof(CommandReference), typeof(KeyBindingEx),
new PropertyMetadata(OnCommandReferenceChanged));
private static void OnCommandReferenceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var kb = (KeyBinding) d;
var cmdRef = e.NewValue as VmCommand;
if(cmdRef==null) return;
kb.Key = cmdRef.GestureKey;
kb.Modifiers = cmdRef.GestureModifier;
kb.Command = cmdRef;
}
public CommandReference CommandReference
{
get { return (CommandReference)GetValue(CommandReferenceProperty); }
set { SetValue(CommandReferenceProperty, value); }
}
}
public class CommandReference : PropertyChangedBase { ...
public Key GestureKey
{
get { return _gestureKey; }
set
{
if (_gestureKey == value) return;
_gestureKey = value;
NotifyOfPropertyChange(() => GestureKey);
}
}
private Key _gestureKey;
}
/// <summary>A command whose primary purpose is to relay its functionality to other objects by invoking delegates.</summary>
public class VmCommand : CommandReference, ICommand
{
...
/// <summary>Action to be called when the Execute method of the command gets called</summary>
public Action ExecuteDelegate { get; protected set; }
/// <summary>Predicate to execute when the CanExecute of the command gets called (default is <c>true</c>)</summary>
public Func<bool> CanExecuteDelegate { get; protected set; }
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.