I have the following keybindings in my MainWindow:
<KeyBinding Command="{Binding OpenCommand}" Gesture="Ctrl+O"/>
<KeyBinding Command="{Binding SaveCommand}" Gesture="Ctrl+S"/>
<KeyBinding Command="{Binding CopyCommand}" Gesture="Ctrl+C"/>
<KeyBinding Command="{Binding PasteCommand}" Gesture="Ctrl+V"/>
<KeyBinding Command="{Binding CutCommand}" Gesture="Ctrl+X"/>
The Open and the Save keybindings work fine... the rest do nothing when I hit the key combination. There are no binding errors in the output. I also have buttons on my menu bound to the same commands and they work. Is there an issue using commands that have a CanExecute method associated with them? I an using .Net 4.0. Any ideas as to why the clipboard actions wouldn't work?
Update:
If I bind something else (like OpenCommand) to Ctrl+C it works. If I bind CopyCommand to a different gesture it still does not work. So it seems to be a problem with the command. That is strange though because my copy button works fine bound to the same CopyCommand. Here is the CopyCommand code that it is bound to:
public ICommand CopyCommand
{
get
{
if (this.copyCommand == null)
{
this.copyCommand = new RelayCommand(
param => this.Copy(),
param => this.Copy_CanExecute());
}
return this.copyCommand;
}
}
You can only execute commands where CanExecute returns true, might be one reason why they do no execute.
Another possible reason is local handling of the respective gestures, as TextBoxes do by default. You can override this by re-declaring the KeyBindings locally with your own command.
this works fine. In my MainWindow.xaml file, I add two keyBinding commands for illustration
<Window x:Class="MainWindowCommandBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.InputBindings>
<KeyBinding Command="{Binding OpenCommand}" Gesture="Ctrl+O"/>
<!--<KeyBinding Command="{Binding SaveCommand}" Gesture="Ctrl+S"/>-->
<KeyBinding Command="{Binding CopyCommand}" Gesture="Ctrl+C"/>
<!--<KeyBinding Command="{Binding PasteCommand}" Gesture="Ctrl+V"/>
<KeyBinding Command="{Binding CutCommand}" Gesture="Ctrl+X"/>-->
</Window.InputBindings>
<Grid>
</Grid>
</Window>
In my MainWindow.xaml.cs file, I initialize my DataContext as follow.
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowContext();
}
The MainWindowContext class is defined as follow
class MainWindowContext
{
RelayCommand _openCommand;
public ICommand OpenCommand
{
get
{
if (_openCommand == null)
{
_openCommand = new RelayCommand(
param => this.Open(),
param => this.Open_CanExecute());
}
return _openCommand;
}
set { _openCommand = (RelayCommand) value; }
}
RelayCommand _copyCommand;
public ICommand CopyCommand
{
get
{
if (_copyCommand == null)
{
_copyCommand = new RelayCommand(
param => this.Copy(),
param => this.Copy_CanExecute());
}
return _copyCommand;
}
set { _copyCommand = (RelayCommand)value; }
}
private bool Copy_CanExecute()
{
return true;
}
private object Copy()
{
Console.Out.WriteLine("Copy command executed");
return null;
}
private bool Open_CanExecute()
{
return true;
}
private object Open()
{
Console.Out.WriteLine("Open command executed");
return null;
}
}
When I execute, it works fine. You can see which command has been executed in your console.
Please tell me if I miss something.
There are some key combinations you can not use because windows already uses them. I think Ctrl+c/v is one of them.
Related
I'm trying to get the four arrow keys to be bound to a command in my ViewModel, but they are not working. I have a ContentControl in a Window with InputBindings like so:
<ContentControl.InputBindings>
<KeyBinding Command="{Binding EndCmd}" Key="Esc" />
<KeyBinding Command="{Binding PanUpCmd}" Key="Up" />
<KeyBinding Command="{Binding PanDownCmd}" Key="Down" />
<KeyBinding Command="{Binding PanLeftCmd}" Key="Left" />
<KeyBinding Command="{Binding PanRightCmd}" Key="Right" />
</ContentControl.InputBindings>
In my ViewModel:
public RelayCommand EndCmd { get; set; }
public RelayCommand PanUpCmd { get; set; }
public RelayCommand PanDownCmd { get; set; }
public RelayCommand PanLeftCmd { get; set; }
public RelayCommand PanRightCmd { get; set; }
public MainViewModel()
{
EndCmd = new RelayCommand(End);
PanUpCmd = new RelayCommand(PanUp);
PanDownCmd = new RelayCommand(PanDown);
PanLeftCmd = new RelayCommand(PanLeft);
PanRightCmd = new RelayCommand(PanRight);
}
//functions that the commands call here
Now, the Escape key works fine, but the four arrow keys do not. Why is this? They are set up exactly the same. I thought maybe it was something to do with the DataContext so I put the KeyBindings in the WindowsInputBindings` but it was the same issue.
Edit: I've tested every key on my keyboard. Every key fires properly except the four arrow keys. I checked if the Content of the ContentControl was swallowing the events, and it was not. In fact, the Control that is the Content has it's own keydown event, which is also never called, nor is the previewkeydown, with the arrow keys.
I copied your code and it seems to work fine.
The only reason I can think for this not to work in your case (especially if Esc works, but not the other keys) is that whatever content you're using inside of the ContentControl also contains input bindings for the direction keys.
In this case, the bindings in the content would override the bindings you've set for the ContentControl itself.
Arrow keys are handled by KeyboardNavigation default.
You should disable KeyboardNavigation and make sure control focusable.
<Grid Background="{Binding Background}" KeyboardNavigation.ControlTabNavigation="None" Focusable="True">
<Grid.InputBindings>
<KeyBinding Key="Left" Command="local:OpsCommands.MoveLeft" />
<KeyBinding Key="Up" Command="local:OpsCommands.MoveUp" />
<KeyBinding Key="Right" Command="local:OpsCommands.MoveRight" />
<KeyBinding Key="Down" Command="local:OpsCommands.MoveDown" />
</Grid.InputBindings>
</Grid>
public ICommand PanRightCmd
{
get { return (ICommand)GetValue(SearchBarEnterCmdProperty); }
set { SetValue(SearchBarEnterCmdProperty, value); }
}
...
PanRightCmd= new RelayCommand(o => PanRightCmdExecute());
https://www.c-sharpcorner.com/UploadFile/20c06b/icommand-and-relaycommand-in-wpf/
I'm trying to implement a dialer in WPF. I have a window, and inside it a user control. The user control has lots of buttons, but the user can also use the num pad to enter numbers.
I created a small sample project to show where I'm at:
MainWindow.xaml
<Window x:Class="wpf_dialer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:wpf_dialer"
Title="MainWindow" Height="200" Width="525">
<Window.Resources>
<local:DialerViewModel x:Key="myViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource myViewModel}">
<local:Dialer />
</Grid>
</Window>
Dialer.xaml
<UserControl x:Class="wpf_dialer.Dialer"
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:local="clr-namespace:wpf_dialer"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="300"
d:DataContext="{d:DesignInstance local:DialerViewModel}"
Loaded="UserControl_Loaded">
<UserControl.InputBindings>
<KeyBinding Key="A" Command="{Binding CommandDialValue, Mode=OneTime}" CommandParameter="." />
<KeyBinding Key="Back" Command="{Binding CommandDialValue, Mode=OneTime}" CommandParameter="Back" />
<KeyBinding Key="Enter" Command="{Binding CommandAcceptValue, Mode=OneTime}" CommandParameter="KeyBinding" />
</UserControl.InputBindings>
<UniformGrid Columns="1">
<TextBox IsEnabled="False" Text="{Binding DialedValue, Mode=OneWay}" MinWidth="200" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10"/>
<Button Content="OK" Margin="60,30" Command="{Binding CommandAcceptValue, Mode=OneTime}" CommandParameter="Button" />
</UniformGrid>
</UserControl>
DialerViewModel.cs
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace wpf_dialer
{
class DialerViewModel : INotifyPropertyChanged
{
private Random RAND = new Random();
private string _dialed_value = "00";
public string DialedValue
{
get { return _dialed_value; }
set
{
if (_dialed_value == value) return;
_dialed_value = value;
RaisePropertyChanged("DialedValue");
}
}
public ICommand CommandDialValue { get { return new CommandImpl(DialValue); } }
public ICommand CommandAcceptValue { get { return new CommandImpl(Alert); } }
private void DialValue(object parameter)
{
if (parameter.ToString() == "Back")
{
if (DialedValue.Length > 0)
{
DialedValue = DialedValue.Substring(0, DialedValue.Length - 1);
}
}
else
{
DialedValue += RAND.Next(0, 10).ToString();
}
}
private void Alert(object parameter)
{
System.Windows.MessageBox.Show(parameter.ToString());
}
#region INotifyPropertyChanged
protected void RaisePropertyChanged(string property_name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private class CommandImpl : ICommand
{
private readonly Action<object> _action = null;
public CommandImpl(Action<object> action)
{
_action = action;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter) { _action(parameter); }
}
}
}
Objectives:
As soon as the window is loaded, when the user presses the A key, the CommandDialValue is executed;
When the user presses Enter, a message box is displayed with the text "KeyBinding". The CommandAcceptValue must be called from the KeyBinding, and NOT from the button;
Problems:
When the window is loaded, the KeyBindings don't execute. They are executed when I click a button somewhere in the UserControl;
When I press Enter, the button's command is executed, but I want the user control's KeyBinding to be executed;
This dialer must be held in a UserControl (or a ControlTemplate, or DataTemplate), because it's contained in a very elaborate window.
I don't want to put the KeyBindings on the Window, because then the UserControl is not reusable, and because its DataContext is not the same as the user control.
UPDATE:
I solved the second problem by setting Focusable="False" on all buttons.
To prevent the buttons from gaining focus, I set Focusable="False" for all buttons.
To set the focus when the window opens, I set Focusable="True" on the UserControl, and on the Loaded event I called Focus().
Dialer.xaml
d:DataContext="{d:DesignInstance local:DialerViewModel}"
Loaded="UserControl_Loaded" Focusable="True">
<UserControl.InputBindings>
Dialer.xaml.cs
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
Focus();
}
I found no combination of FocusManager.FocusedElement that worked. I tried {Binding ElementName=myUserControl}, and {Binding RelativeSource={RelativeSource Self}}.
It's a question of Focus. When your window is loaded for the first time, your user control does not have Focus. So key bindings' gesture will not intercept your keypress. You have, at the first app loading time, to give Focus to your user control. (Focusable ="True"(i don't know if this helps but i am sure the FocusManager will helps)). Then your key gestures will work well.
I need to bind the visibility of a control on a WPF UserControl to the state of the Alt Key, if somehow possible via a converter. The Button should only be visible if the ALT Key is being pressed down, the solution should not be integrated in the Code Behind file, since I'm working in a strict MVVM pattern using PRISM/Unity.
A perfect solution would include writing a new converter that would be able to convert the state of a keyboard key to the Visiblity property of a user control, but I have little experience in converters and wasn't able to come up with a solution by myself.
Here is a complete example
Xaml
<Window x:Class="Playground.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Playground"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="350" Width="525">
<Window.InputBindings>
<KeyBinding Modifiers="Alt" Key="LeftAlt" Command="{Binding AltPressedCommand}" />
</Window.InputBindings>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="boolToVisibilityConverter"/>
</Window.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyUp">
<i:InvokeCommandAction Command="{Binding AltUnpressedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Button Content="My Button" Visibility="{Binding IsAltPressed, Converter={StaticResource boolToVisibilityConverter}}"/>
</Grid>
</Window>
ViewModel
public class MainWindowViewModel : NotificationObject
{
public MainWindowViewModel()
{
AltPressedCommand = new DelegateCommand(() => IsAltPressed = true);
AltUnpressedCommand = new DelegateCommand(() => IsAltPressed = false);
}
public DelegateCommand AltPressedCommand { get; set; }
public DelegateCommand AltUnpressedCommand { get; set; }
private bool _IsAltPressed;
public bool IsAltPressed
{
get { return _IsAltPressed; }
set
{
if (value != _IsAltPressed)
{
_IsAltPressed = value;
RaisePropertyChanged("IsAltPressed");
}
}
}
}
Explanation
The visibility of the control is binded to a boolean property via BooleanToVisibilityConverter.
Then I use two commands. One fired when the Alt key is being pressed using KeyBinding, and the second is fired when the key up occurs. I left out a check of the Alt key when the key up occurs that you should add. If you want to purely use MVVM this could get tricky because you need to send a parameter to the command stating the key being pressed up.
Edit
I use the following behavior to pass the key parameter from the PreviewKeyUp event
public class PreviewKeyUpBehavior : Behavior<UIElement>
{
#region Properties
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(PeviewKeyUpBehavior));
#endregion
#region Methods
protected override void OnAttached()
{
AssociatedObject.PreviewKeyUp += OnPreviewKeyUp;
base.OnAttached();
}
private void OnPreviewKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (Command == null) return;
// Execute command and send the key as the command parameter
Command.Execute(e.Key == Key.System ? e.SystemKey : e.Key);
}
#endregion
}
This will raise the binded command when the PreviewKeyUp is fired and send the key as the commands parameter. I then altered the code in the View and ViewModel as follows:
<!-- Used behaviors instead of triggers -->
<i:Interaction.Behaviors>
<local:PreviewKeyUpBehavior Command="{Binding KeyUnpressedCommand}"/>
</i:Interaction.Behaviors>
Changed the command to take a nullable key parameter
public DelegateCommand<Key?> KeyUnpressedCommand { get; set; }
And implemented it
KeyUnpressedCommand = new DelegateCommand<Key?>(key =>
{
if (key == Key.LeftAlt)
IsAltPressed = false;
});
Hope this helps
I've got a textbox where I have this:
<KeyBinding Command="{Binding MyCommand}" Key="Tab"/>
Problem is it swallows the Tab and doesn't tab to the next control.
How can I trap the Tab for the textbox and still preserve tabbing to the next control in the tab order?
Edit: I'm also using MVVM and MyCommand is in the ViewModel code, so that's where I need to re-throw the Tab.
It's easy to achieve, just don't use KeyBinding for this. Handle your TextBox's OnKeyDown event:
<TextBox KeyDown="UIElement_OnKeyDown" ...
Then on the code-behind, execute your command whenever Tab is pressed. Unlike KeyBinding, this won't swallow the TextInput event so it should work.
private void OnKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Tab:
// Execute your command. Something similar to:
((YourDataContextType)DataContext).MyCommand.Execute(parameter:null);
break;
}
}
I cannot find a way to set focus to a control given your question as a purely XAML solution.
I choose to create an attacted property and then through binding set the focus to next control from the Command associated with your KeyBinding in the ViewModel.
Here is the View:
<Window x:Class="WarpTab.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WarpTab.Commands"
xmlns:Views="clr-namespace:WarpTab.Views"
xmlns:local="clr-namespace:WarpTab.ViewModels"
Title="Main Window" Height="400" Width="800">
<Window.Resources>
<c:CommandReference x:Key="MyCommandReference" Command="{Binding MyCommand}" />
</Window.Resources>
<DockPanel>
<ScrollViewer>
<WrapPanel >
<TextBox Text="First text value" >
<TextBox.InputBindings>
<KeyBinding Command="{StaticResource MyCommandReference}" Key="Tab"/>
</TextBox.InputBindings>
</TextBox>
<TextBox Text="Next text value" local:FocusExtension.IsFocused="{Binding FocusControl}" />
<Button Content="My Button" />
</WrapPanel>
</ScrollViewer>
</DockPanel>
</Window>
Here is the ViewModel:
using System.Windows.Input;
using WarpTab.Commands;
namespace WarpTab.ViewModels
{
public class MainViewModel : ViewModelBase
{
public ICommand MyCommand { get; set; }
public MainViewModel()
{
MyCommand = new DelegateCommand<object>(OnMyCommand, CanMyCommand);
}
private void OnMyCommand(object obj)
{
FocusControl = true;
// process command here
// reset to allow tab to continue to work
FocusControl = false;
return;
}
private bool CanMyCommand(object obj)
{
return true;
}
private bool _focusControl = false;
public bool FocusControl
{
get
{
return _focusControl;
}
set
{
_focusControl = value;
OnPropertyChanged("FocusControl");
}
}
}
}
Here is the code to define the attached property that I found in the following answer.
using System.Windows;
namespace WarpTab.ViewModels
{
public static class FocusExtension
{
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached(
"IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus(); // Don't care about false values.
}
}
}
}
Why don't you just use this code in your command handler?
private void MyCommandHandler(){
// Do command's work here
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
control.MoveFocus(request);
}
That's basically what 'Tab' does, so if you do the same, you're good to go. (Of course reverse the direction if you have a command with Shift-Tab.
I actually wrapped this into an extension method like so...
public static class NavigationHelpers{
public static void MoveFocus(this FrameworkElement control, FocusNavigationDirection direction = FocusNavigationDirection.Next, bool wrap = true) {
TraversalRequest request = new TraversalRequest(direction);
request.Wrapped = wrap;
control.MoveFocus(request);
}
}
...meaning the prior code becomes even simpler, like this...
private void MyCommandHandler(){
// Do command's work here
Control.MoveFocus();
}
...and if you don't know what the currently focused control is, you can just do this...
(Keyboard.FocusedElement as FrameworkElement).MoveFocus();
Hope this helps! If so, much appreciated if you vote me up or mark it as accepted!
Had the same problem, came across this thread and took me a while to find the best answer. Reference: Use EventTrigger on a specific key
Define this class:
using System; using System.Windows.Input; using System.Windows.Interactivity;
public class KeyDownEventTrigger : EventTrigger
{
public KeyDownEventTrigger() : base("KeyDown")
{
}
protected override void OnEvent(EventArgs eventArgs)
{
var e = eventArgs as KeyEventArgs;
if (e != null && e.Key == Key.Tab)
{
this.InvokeActions(eventArgs);
}
}
}
The xaml for your text box:
<TextBox x:Name="txtZip"
Text="{Binding Zip, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding ZipLookup.GetAddressByZipKeyCommand}" CommandParameter="{Binding ElementName=txtZip, Path=Text}" />
</TextBox.InputBindings>
<i:Interaction.Triggers>
<iCustom:KeyDownEventTrigger EventName="KeyDown">
<i:InvokeCommandAction Command="{Binding ZipLookup.GetAddressByZipKeyCommand}" CommandParameter="{Binding ElementName=txtZip, Path=Text}" />
</iCustom:KeyDownEventTrigger>
</i:Interaction.Triggers>
</TextBox>
In your window or user control root tag include these attributes:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:iCustom="clr-namespace:[NAMESPACE FOR CUSTOM KEY DOWN CLASS]"
I need to create hot-keys for every control + number combination and would prefer not to have create ten commands. Is there any way to do this?
If I understand your question, you have a single command, say MyCommand, and you want to fire it if the user presses CTRL+0 through CTRL+9, and give the command a different parameter for each combination.
In that case, just create 10 key bindings in your window, all bound to MyCommand, and give them a parameter:
<Window.InputBindings>
<KeyBinding Command="MyCommand" Gesture="Ctrl+0" CommandParameter="0"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+1" CommandParameter="1"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+2" CommandParameter="2"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+3" CommandParameter="3"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+4" CommandParameter="4"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+5" CommandParameter="5"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+6" CommandParameter="6"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+7" CommandParameter="7"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+8" CommandParameter="8"/>
<KeyBinding Command="MyCommand" Gesture="Ctrl+9" CommandParameter="9"/>
</Window.InputBindings>
Yes, you can create a custom KeyBinding that does this. The code would look something like this:
[ContentProperty("Keys")]
public class MultiKeyBinding : InputBinding
{
public ModifierKeys Modifiers;
public List<Key> Keys = new List<Key>();
private Gesture _gesture;
public override InputGesture Gesture
{
get
{
if(_gesture==null) _gesture = new MultiKeyGesture { Parent = this };
return _gesture;
}
set { throw new InvalidOperationException(); }
}
class MultiKeyGesture : InputGesture
{
MultiKeyBinding Parent;
public override bool Matches(object target, InputEventArgs e)
{
bool match =
e is KeyEventArgs &&
Parent.Modifiers == Keyboard.Modifiers &&
Parent.Keys.Contains( ((KeyEventArgs)e).Key );
// Pass actual key as CommandParameter
if(match) Parent.CommandParameter = ((KeyEventArgs)e).Key;
return match;
}
}
}
It would be used like this:
<local:MultiKeyBinding Command="..." Modifiers="Control">
<Key>D0</Key>
<Key>D1</Key>
...
</local:MultiKeyBinding>
Hope this helps.