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
Related
I want to create a simple wpf user control which has 2 commands, each with a single CommandParameter which is set by a DependencyProperty in the XAML.
However the value of the property bound to the CommandParameter isn't passed through to the CommandHandler. I'm using ReactiveUI to implement the Commands.
The commands are being called as expected, however the CommandParameter is always 0. So I have 2 questions:
Question 1
Even if I have a single command DependencyProperty called "Command" and a single DependencyProperty parameter called "CommandParameter", the parameter isn't passed through.
Question2
How are Command and CommandParameter properties linked together? Is it a naming convention and if so does it support multiple commands (e.g. is Command1 linked to Command1Parameter?).
I'm aware of passing multiple parameters to a single command, but this is a different scenario where I have 2 separate commands. In this case I actually want to pass the same value to both commands.
I've created a simple test app to demo the problem; The MainWindow.xaml is
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<local:WidgetUserControl x:Name="Widget1" Command="{Binding WidgetCommand1}"
CommandParameter="1" Command2="{Binding WidgetCommand2}" />
<local:WidgetUserControl x:Name="Widget2" Command="{Binding WidgetCommand1}"
CommandParameter="1" Command2="{Binding WidgetCommand2}"/>
</StackPanel>
</Grid>
</Window>
The MainViewModel is:
public class MainViewModel : ReactiveObject
{
public ReactiveCommand<int, Unit> WidgetCommand1 { get; }
public ReactiveCommand<int, Unit> WidgetCommand2 { get; }
public MainViewModel()
{
WidgetCommand1 = ReactiveCommand.Create<int>(ExecuteWidgetCommand1);
WidgetCommand2 = ReactiveCommand.Create<int>(ExecuteWidgetCommand2);
}
private void ExecuteWidgetCommand1(int arg)
{
MessageBox.Show($"Widget Command 1: Parameter {arg}");
}
private void ExecuteWidgetCommand2(int arg)
{
MessageBox.Show($"Widget Command 2: Parameter {arg}");
}
}
The WidgetUserControl XAML is:
<UserControl x:Class="WpfApp1.WidgetUserControl"
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:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border BorderBrush="Aqua" BorderThickness="2">
<StackPanel Orientation="Horizontal" Margin="5">
<Button Content="Button1"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}, Path=Command}"/>
<Button Content="Button2"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}, Path=Command2}"/>
</StackPanel>
</Border>
</Grid>
</UserControl>
The WidgetUserControl code behind looks like this:
public partial class WidgetUserControl : UserControl
{
public WidgetUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(WidgetUserControl));
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(WidgetUserControl),
new PropertyMetadata(OnCommandParameterCallback));
public static readonly DependencyProperty Command2Property = DependencyProperty.Register("Command2", typeof(ICommand), typeof(WidgetUserControl));
public static readonly DependencyProperty Command2ParameterProperty = DependencyProperty.Register("Command2Parameter", typeof(object), typeof(WidgetUserControl),
new PropertyMetadata(OnCommandParameterCallback));
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public ICommand Command2
{
get => (ICommand)GetValue(Command2Property);
set => SetValue(Command2Property, value);
}
public object Command2Parameter
{
get => GetValue(Command2ParameterProperty);
set => SetValue(Command2ParameterProperty, value);
}
private static void OnCommandParameterCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Not sure if this is needed
var control = sender as WidgetUserControl;
control.CommandParameter = e.NewValue;
}
}
You can use the CommandParameter in the xaml binding to pass along a parameter to the command. This is available on the Button or similar controls that support Command's.
In your code, you assigned two commands to each button. When those buttons executed the command which they had assigned to, they will pass its own command parameter.Below is the source code from ButtonBase and MS.Internal.Commands.CommandHelpers that actually do the work.
protected virtual void OnClick()
{
RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent, this);
RaiseEvent(newEvent);
MS.Internal.Commands.CommandHelpers.ExecuteCommandSource(this);
}
[SecurityCritical, SecurityTreatAsSafe]
internal static void ExecuteCommandSource(ICommandSource commandSource)
{
CriticalExecuteCommandSource(commandSource, false);
}
[SecurityCritical]
internal static void CriticalExecuteCommandSource(ICommandSource commandSource, bool userInitiated)
{
ICommand command = commandSource.Command;
if (command != null)
{
object parameter = commandSource.CommandParameter;
IInputElement target = commandSource.CommandTarget;
RoutedCommand routed = command as RoutedCommand;
if (routed != null)
{
if (target == null)
{
target = commandSource as IInputElement;
}
if (routed.CanExecute(parameter, target))
{
routed.ExecuteCore(parameter, target, userInitiated);
}
}
else if (command.CanExecute(parameter))
{
command.Execute(parameter);
}
}
}
So basically, if you want to write a UserControl to support ICommand, you must fill your own logic like when and how to execute them. Or at least, pass the CommandParameter to the control that actually execute the command.
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.
In my View, I try to tie an event to the Enter key by the following XAML:
<TextBox x:Name="txtFields" Text="{Binding FieldsTextProperty, UpdateSourceTrigger=PropertyChanged}" Height="23" TextWrapping="NoWrap" Background="#FFCBEECD" AcceptsReturn="False" >
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding AddFieldCommand}"></KeyBinding>
</TextBox.InputBindings>
</TextBox>
The AddFieldCommand exists in my ViewModel as a property:
public ICommand AddFieldCommand { get; private set; }
In the ViewModel Constructor the following RelayCommand exists.
AddFieldCommand = new RelayCommand(AddField);
And from the RelayCommand the method AddField is called.
public void AddField()
{
Console.WriteLine("AddField Method")
}
This doesn't work - the AddField method is never called. Can anybody help?
I wonder if the the .InputBindings aren't working in this scenario. Keyboard input handling is probably being hijacked by the TextBox.
Assuming you want to stick to the MVVM pattern and avoid event-handling code in the code-behind, I'd probably choose to create a custom implementation of the TextBox - call it a 'SubmitTextBox'
The custom SubmitTextBox could automatically hook up to the PreviewKeyDown event, and monitor the Enter key.
You could further adhere to MVVM by adding an ICommand DP to handle the 'Submit' event.
Something like this ...
public class SubmitTextBox : TextBox
{
public SubmitTextBox()
: base()
{
PreviewKeyDown += SubmitTextBox_PreviewKeyDown;
}
private void SubmitTextBox_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
if (this.SubmitCommand != null && this.SubmitCommand.CanExecute(this.Text))
{
// Note this executes the command, and returns
// the current value of the textbox.
this.SubmitCommand.Execute(this.Text);
}
}
}
/// <summary>
/// The command to execute when the text is submitted (Enter is pressed).
/// </summary>
public ICommand SubmitCommand
{
get { return (ICommand)GetValue(SubmitCommandProperty); }
set { SetValue(SubmitCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for SubmitCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SubmitCommandProperty =
DependencyProperty.Register("SubmitCommand", typeof(ICommand), typeof(SubmitTextBox), new PropertyMetadata(null));
}
And your XAML would end up looking like this:
<custom:SubmitTextBox
x:Name="txtFields"
Text="{Binding FieldsTextProperty}"
SubmitCommand="{Binding AddFieldCommand}"
Height="23"
TextWrapping="NoWrap"
Background="#FFCBEECD" />
Hope that helps :)
UPDATE: To clarify, the SubmitCommand I created returns the current text in the textbox as a parameter. In order to use this with the MVVM-Light toolkit, you'll need to create a RelayCommand that can accept the type 'string'.
public RelayCommand<string> AddFieldCommand { get; private set; }
public ViewModelConstructor()
{
AddFieldCommand = new RelayCommand<string>(AddField);
}
private void AddField(string text)
{
// Do something
}
I hope that clears things up a litte :)
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'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]"