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/
Related
I have the following code:
<Button Focusable="True">
<Button.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding ButtonClickedCommand}" />
<MouseBinding Gesture="CTRL+LeftClick" Command="{Binding ButtonClickedWithCtrlCommand}" />
</Button.InputBindings>
</Button>
the view model is like this
public ICommand ButtonClickedCommand { get; }
public ICommand ButtonClickedWithCtrlCommand { get; }
...
{
ButtonClickedCommand = new DelegateCommand(() => { });
ButtonClickedWithCtrlCommand = new DelegateCommand(() => { });
}
Now if I click the button, the command is correctly executed. But somehow the focus of the button does not get set. Can someone tell me why?
It seems that the Command prevents the base functionality of the click (like the button down visalization as well). Is there a way to invoke that when using InputBindings?
PS: I know that I can achieve the same with behaviors and "event to command binding" (in fact that's how I fixed it). Maybe I'm abusing the input binding and I should not use it in the first place for "things" which are already bound (like click etc.)?
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'm new to C# and this is my first WPF project. I am following this tutorial (using their implementation of RelayCommand and attempting to use MVVM. I am implementing a clone of the standard Windows calculator. I would like to find a way to group functionality of similar buttons as what I am doing seems clumsy.
For exmaple, here is my my XAML of three buttons
<Button Content="_7" Focusable ="False" Command="{Binding Seven}" Style="{DynamicResource NumberButton}" Margin="0,134,184,129"/>
<Button Content="_8" Focusable ="False" Command="{Binding Eight}" Style="{DynamicResource NumberButton}" Margin="46,134,138,129"/>
<Button Content="_9" Focusable ="False" Command="{Binding Nine}" Style="{DynamicResource NumberButton}" Margin="92,134,92,129"/>
Here is the ICommands for those:
public ICommand Nine { get { return new RelayCommand(NineExecute); } }
public ICommand Eight { get { return new RelayCommand(EightExecute); } }
public ICommand Seven { get { return new RelayCommand(SevenExecute); } }
and the methods:
void NineExecute()
{
NumberPressed("9");
}
void EightExecute()
{
NumberPressed("8");
}
void SevenExecute()
{
NumberPressed("7");
}
What should I investigate in order to group similar function buttons such as numbers into a single ICommand, with a single method that can determine the sender - while still not putting code behind in the Window class as the article warns against.
Xlam code for a button (supposing that you defined your data context):
<....DataContext>
<loc:Commands/>
</....DataContext>
<Button Content="_9"
Command="{Binding Path=ShowMeABox}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Content}"/>
Our dummy command (using RelayCommand<T> from the provided link):
public class Commands
{
private static readonly ICommand _showShowBoxCommand =
new RelayCommand<string>(str => MessageBox.Show(str));
public static ICommand ShowMeABox { get { return _showShowBoxCommand; } }
}
That's it.
FYI.
It's seems that you explicitly specify button size which is generally a bad practice. To position your buttons use stack or wrap panel, or grid/uniformgrid.
Read info on styles and templates to increase code reuse.
Example:
<UniformGrid Columns="3" Rows="3">
<UniformGrid.DataContext>
<loc:Commands/>
</UniformGrid.DataContext>
<UniformGrid.Resources>
<Style TargetType="Button">
<Setter Property="Command" Value="{Binding ShowMeABox}"/>
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Content}"/>
</Style>
</UniformGrid.Resources>
<Button Content="_1"/>
<Button Content="_2"/>
<Button Content="_3"/>
<Button Content="_4"/>
<Button Content="_5"/>
<Button Content="_6"/>
<Button Content="_7"/>
<Button Content="_8"/>
<Button Content="_9"/>
</UniformGrid>
May be it's possible to bind Enumerable.Range(0,10) to populate control automatically in the MVVM fashion.
Good luck!
Use the CommandParameter property - that way you can bind all of your buttons to the same Command but with different CommandParameter for each number (ie, the CommandParameter should be an integer representing which button as actually pressed)
I would personally write this using a constructor:
public YourType()
{
this.Seven = new RelayCommand( () => NumberPressed("7"));
this.Eight = new RelayCommand( () => NumberPressed("8"));
this.Nine = new RelayCommand( () => NumberPressed("9"));
}
public ICommand Nine { get; private set; }
public ICommand Eight { get; private set; }
public ICommand Seven { get; private set; }
Note that it may also make sense to use the CommandParameter, and a single command. Many frameworks provide a variation on RelayCommand/ActionCommand/etc which accept a command parameter, which you can then specify in XAML. That specific implementation doesn't allow it, but many do, and will map to a method which gets the CommandParameter as a parameter directly.
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.
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.