WPF Override / Reset Keybinding on child control - c#

I have a window on which I have commands bound to the numpad keys like this:
<!-- Set keybindings -->
<controls:MetroWindow.InputBindings>
<!-- NumPad Shortcuts for selecting reasons -->
<KeyBinding Key="NumPad0" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="0" />
<KeyBinding Key="NumPad1" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="1" />
<KeyBinding Key="NumPad2" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="2" />
<KeyBinding Key="NumPad3" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="3" />
<KeyBinding Key="NumPad4" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="4" />
<KeyBinding Key="NumPad5" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="5" />
<KeyBinding Key="NumPad6" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="6" />
<KeyBinding Key="NumPad7" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="7" />
<KeyBinding Key="NumPad8" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="8" />
<KeyBinding Key="NumPad9" Command="{Binding OnReasonShortcutKeyPressedCommand}" CommandParameter="9" />
<!-- Others -->
<KeyBinding Key="Back" Command="{Binding OnReasonGoBackClickedCommand}" />
<KeyBinding Key="Escape" Command="{Binding OnEscapeClickedCommand}" />
</controls:MetroWindow.InputBindings>
In the backend, this is handled as:
ICommand _onReasonShortcutKeyPressedCommand;
public ICommand OnReasonShortcutKeyPressedCommand
{
get
{
return _onReasonShortcutKeyPressedCommand ??
(_onReasonShortcutKeyPressedCommand = new RelayCommand(OnReasonShortcutKeyPressedCommand_Execute));
}
}
private void OnReasonShortcutKeyPressedCommand_Execute(object param)
{
//Find which key was presses by command param
int keyPressed = Int32.Parse((string)param);
// Do something bla bla bla
}
Now, this window also contains some textboxes in which numbers have to be entered. Ofcourse, the keybindings on the window level result in the commands being triggered instead of the actual number being printed. Is there anyway in which I can override this?

Not sure whether there is better way to solve this, but my propose is to use attach property to archive this.
Here is example:
In XAML you should attach new behavior (LimitBindings) to TextBox:
XAML:
<TextBox behavs:KeyBindingBehavior.LimitKeyBindings="True"></TextBox>
and create your behavior class (+ Helper method to get parent window -it should be placed somewhere else :)):
public static class KeyBindingBehavior
{
//to keep window's bindings.
private static InputBindingCollection windowBindings;
public static bool GetLimitKeyBindings(DependencyObject obj)
{
return (bool)obj.GetValue(LimitKeyBindingsProperty);
}
public static void SetLimitKeyBindings(DependencyObject obj, bool value)
{
obj.SetValue(LimitKeyBindingsProperty, value);
}
public static readonly DependencyProperty LimitKeyBindingsProperty =
DependencyProperty.RegisterAttached(
"LimitKeyBindings",
typeof(bool),
typeof(KeyBindingBehavior),
new PropertyMetadata(false, PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
TextBox textBox = dependencyObject as TextBox;
if (textBox != null)
{
textBox.GotKeyboardFocus += textBox_GotKeyboardFocus;
textBox.LostKeyboardFocus += textBox_LostKeyboardFocus;
}
}
static void textBox_LostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
var window = GetParentWindow(sender as DependencyObject);
window.InputBindings.AddRange(windowBindings);
}
static void textBox_GotKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
var window = GetParentWindow(sender as DependencyObject);
windowBindings = new InputBindingCollection(window.InputBindings);
window.InputBindings.Clear();
}
// This helper method should be in seperate class
public static Window GetParentWindow(DependencyObject child)
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null)
{
return null;
}
Window parent = parentObject as Window;
if (parent != null)
{
return parent;
}
else
{
return GetParentWindow(parentObject);
}
}
}
Don't forget to add reference in XAML to point to behavior class :
xmlns:behavs="clr-namespace:WpfApplication1"

Related

How to trigger KeyUp Event in MVVM using ICommand

I have created this simple view:
Now when I press the RETURN key while the cursor being inside one of these 2 textboxes, I want the "Suchen" = SEARCH button to trigger (KeyUp Event).
I know how to easily do this in the code behind but I want to do it in MVVM (in my view model class) with an ICommand. In the code behind I used the (autogenerated) KeyEventArgs parameter.
I tried it in MVVM using ICommand but the Command method gives me an error claiming that I would need to instantiate an object for the KeyEventArgs argument. In the code behind (non-mvvm-like) I did not need to instantiate anything because the KeyEventArgs parameter was "autogenerated" just like the method. So I didn't have to worry about that.
How do I make the KeyUp event work in my MVVM project?
For this question to answer I provide you the following shortened code:
XAML-View:
<StackPanel Height="423" VerticalAlignment="Bottom">
<Label Name="lblArtikelbezeichnung" Content="Artikelbezeichnung:" Margin="20, 20, 20, 0"></Label>
<TextBox Name="txtArtikelbezeichnung"
Width="Auto"
Margin="20, 0, 20, 0"
IsEnabled="{Binding BezEnabled}"
Text="{Binding BezText}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextChangedBez}" />
</i:EventTrigger>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding KeyUpBez}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<!--TextChanged="txtArtikelbezeichnung_TextChanged"
KeyUp="txtArtikelbezeichnung_KeyUp"-->
<Label Name="lblLieferant" Content="Lieferant:" Margin="20, 0, 20, 0"></Label>
<TextBox Name="txtLieferant"
Width="Auto"
Margin="20, 0, 20, 0"
IsEnabled="{Binding LiefEnabled}"
Text="{Binding LiefText}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextChangedLief}" />
</i:EventTrigger>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding KeyUpLief}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<!--TextChanged="txtLieferant_TextChanged"
KeyUp="txtLieferant_KeyUp"-->
<Button Name="btnSuchen"
Content="Suchen"
Width="100" Height="25"
Margin="20, 10,240, 10"
Command="{Binding GefilterteSuche}">
</Button>
...
<StackPanel>
Code Behind:
using System.Windows;
namespace Lieferscheine
{
/// <summary>
/// Interaktionslogik für artikelHinzu.xaml
/// </summary>
public partial class artikelHinzu : Window
{
public artikelHinzu()
{
InitializeComponent();
DataContext = new ArtikelHinzuViewModel();
}
}
}
View Model:
public class ArtikelHinzuViewModel : INotifyPropertyChanged
{
//ICommands (shortened)
public ICommand GefilterteSuche => new DelegateCommand<object>(SucheArtikel);
public ICommand KeyUpLief => new DelegateCommand<KeyEventArgs>(KeyUpLieferant);
public ICommand KeyUpBez => new DelegateCommand<KeyEventArgs>(KeyUpBezeichnung);
//INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//Konstruktor
public ArtikelHinzuViewModel()
{
}
//ICommand methods (shortened for reasons of simplicity)
//KeyUp Events (THIS PART IS MY PROBLEM)
private void KeyUpBezeichnung(KeyEventArgs e) //the argument is obligatory but it does not have an instantiated object which is why an error fires...
{
//since I need to create an object for KeyEventArgs I tried this but it is useless...
/*e = new KeyEventArgs(Keyboard.PrimaryDevice,
Keyboard.PrimaryDevice.ActiveSource,
0, Key.Back);
//I need to access this e.Key property but don't know how in my case! That is the actual problem...
if (e.Key == Key.Return)
{
object o = new object();
SucheArtikel(o);
}
*/
}
//same problem here as above...
private void KeyUpLieferant(KeyEventArgs e)
{
/*
e = new KeyEventArgs(Keyboard.PrimaryDevice,
Keyboard.PrimaryDevice.ActiveSource,
0, Key.Back);
if (e.Key == Key.Return)
{
object o = new object();
SucheArtikel(o);
}
*/
}
}
Using InputBindings is easier:
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}" />
</TextBox.InputBindings>
</TextBox>

How to read TextBox focus from ViewModel - MVVM Light

I have two textBoxes and in my ViewModel I would like to be able to keep track of which box is currently in focus.
<TextBox x:Name="textBox1" Text="Text Box 1"/>
<TextBox x:Name="textBox2" Text="Text Box 2"/>
How can I read/identify which textBox is currently in focus from my ViewModel?
There are several ways how you can achieve this, some of them:
1) Use behavior:
You need System.Windows.Interactivity.dll
Behavior (setting IsFocused property will not make element focused, you need slightly extend behavior in order to achieve this)
public class FocusChangedBehavior : Behavior<UIElement>
{
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.Register(
nameof(IsFocused),
typeof(bool),
typeof(FocusChangedBehavior),
new FrameworkPropertyMetadata(default(bool),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool IsFocused
{
get { return (bool)this.GetValue(IsFocusedProperty); }
set { this.SetValue(IsFocusedProperty, value); }
}
/// <inheritdoc />
protected override void OnAttached()
{
this.AssociatedObject.GotFocus += this.AssociatedObjectFocused;
this.AssociatedObject.LostFocus += this.AssociatedObjectUnfocused;
}
/// <inheritdoc />
protected override void OnDetaching()
{
this.AssociatedObject.GotFocus -= this.AssociatedObjectFocused;
this.AssociatedObject.LostFocus -= this.AssociatedObjectUnfocused;
}
private void AssociatedObjectFocused(object sender, RoutedEventArgs e)
{
this.IsFocused = true;
}
private void AssociatedObjectUnfocused(object sender, RoutedEventArgs e)
{
this.IsFocused = false;
}
}
In XAML you bind IsFocused to property in ViewModel.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBox x:Name="textBox1" Text="Text Box 1">
<i:Interaction.Behaviors>
<local:FocusChangedBehavior IsFocused="{Binding IsFocusedTxt1}" />
</i:Interaction.Behaviors>
</TextBox>
<TextBox x:Name="textBox2" Text="Text Box 2">
<i:Interaction.Behaviors>
<local:FocusChangedBehavior IsFocused="{Binding IsFocusedTxt2}" />
</i:Interaction.Behaviors>
</TextBox>
Finally in View-Model create properties
public bool IsFocusedTxt1 { get; set; }
public bool IsFocusedTxt2 { get; set; }
2) Alternatively you could you use EventTrigger in the XAML
You need System.Windows.Interactivity.dll and MicrosoftExpressionInteractions (For the ActionCommand)
Event Triggers:
<TextBox x:Name="textBox1" Text="Text Box 1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedTxt1Command}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
In ViewModel create command NotifyFocusedReceivedTxt1Command
public ICommand NotifyFocusedReceivedTxt1Command { get; }
// in constructor
this.NotifyFocusedReceivedTxt1Command = new ActionCommand(this.FocusedReceivedTxt1);
// and method
private void FocusedReceivedTxt1()
{
// Your logic
}
Also, if you don't want introduce many command/properties you could use same command and pass different textboxes by setting CommandParameter (slightly breaks MVVM, but not critically)
<TextBox x:Name="textBox1" Text="Text Box 1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedCommand}"
CommandParameter="{Binding ., ElementName=textBox1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBox x:Name="textBox2" Text="Text Box 2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedCommand}"
CommandParameter="{Binding ., ElementName=textBox2}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
and
public ICommand NotifyFocusedReceivedCommand { get; }
// in constructor
this.NotifyFocusedReceivedCommand = new ActionCommand(this.FocusedReceived);
// and method
private void FocusedReceived(object control)
{
var txt = (TextBox)control;
bool isFocused = txt.IsFocused;
string name = txt.Name;
}
public static DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached(
"IsFocused",
typeof(bool),
typeof(TextBoxProperties),
new UIPropertyMetadata(false,OnIsFocusedChanged)
);
public static bool GetIsFocused(DependencyObject dependencyObject) {
return (bool)dependencyObject.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject dependencyObject, bool value) {
dependencyObject.SetValue(IsFocusedProperty, value);
}
you can use this property
This can not be done via the ViewModel on Server-side, a workaround would look like this:
View Code: (js & html)
function updateFocus(textboxNr) {
$.ajax({
type: "POST",
url: '#Url.Action("Index", "Controller")',
data: {
Focus: textboxNr
},
contentType: "application/json; charset=utf-8",
dataType: "json",
});
}
<textarea id="1" name="1" onfocus="updateFocus(1)">Text Box 1</textarea>
<textarea id="2" name="2" onfocus="updateFocus(2)">Text Box 2</textarea>

WPF Binding Application Commands to ViewModel ICommand

Learning WPF with a small editor project and designing it with MVVM in mind.
The following code is throwing "Provide value on 'System.Windows.Data.Binding' threw an exception." at run time when the XAML is first parsed. No Build errors.
How best to bind my ICommands to Application Commands Close, Save, Save As, Open, New etc.
Currently I have just the Close and New setup.
XAML Code:
<Window x:Class="Editor.Views.EditorView"
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:Editor.Views"
xmlns:vm="clr-namespace:Editor.ViewModels"
xmlns:userControls="clr-namespace:Editor.UserControls"
mc:Ignorable="d"
Title="EditorView" Height="600" Width="800" WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:DocumentViewModel}">
<ContentControl Content="{Binding DocTextBox}" />
</DataTemplate>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
Executed="{Binding ExitCommand}" />
<CommandBinding Command="ApplicationCommands.New"
Executed="{Binding NewDocumentCommand}" />
<!--<CommandBinding Command="ApplicationCommands.Open"
Executed="OpenDocument" />
<CommandBinding Command="ApplicationCommands.Save"
CanExecute="SaveDocument_CanExecute"
Executed="SaveDocument" />
<CommandBinding Command="ApplicationCommands.SaveAs"
Executed="SaveDocumentAs" />-->
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="N" Modifiers="Control" Command="{Binding NewDocumentCommand}" />
<KeyBinding Key="F4" Modifiers="Control" Command="{Binding CloseDocumentCommand}" />
</Window.InputBindings>
<DockPanel>
<userControls:Menu x:Name="menu"
DockPanel.Dock="Top" />
<TabControl ItemsSource="{Binding Documents}" SelectedIndex="{Binding SelectedIndex}">
<TabControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding FileName}" />
<Button Command="{Binding CloseCommand}" Content="X" Margin="4,0,0,0" FontFamily="Courier New" Width="17" Height="17" VerticalContentAlignment="Center" />
</WrapPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DockPanel>
</Window>
The ViewModel Code:
public class EditorViewModel : ViewModelBase
{
private static int _count = 0;
public EditorViewModel()
{
Documents = new ObservableCollection<DocumentViewModel>();
Documents.CollectionChanged += Documents_CollectionChanged;
}
#region Event Handlers
void Documents_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count != 0)
foreach (DocumentViewModel document in e.NewItems)
document.RequestClose += this.OnDocumentRequestClose;
if (e.OldItems != null && e.OldItems.Count != 0)
foreach (DocumentViewModel document in e.OldItems)
document.RequestClose -= this.OnDocumentRequestClose;
}
private void OnDocumentRequestClose(object sender, EventArgs e)
{
CloseDocument();
}
#endregion
#region Commands
private RelayCommand _exitCommand;
public ICommand ExitCommand
{
get { return _exitCommand ?? (_exitCommand = new RelayCommand(() => Application.Current.Shutdown())); }
}
private RelayCommand _newDocumentCommand;
public ICommand NewDocumentCommand
{
get { return _newDocumentCommand ?? (_newDocumentCommand = new RelayCommand(NewDocument)); }
}
private void NewDocument()
{
_count++;
var document = new DocumentViewModel { FileName = "New " + _count, DocTextBox = new RichTextBox() };
Documents.Add(document);
SelectedIndex = Documents.IndexOf(document);
}
private RelayCommand _closeDocumentCommand;
public ICommand CloseDocumentCommand
{
get { return _closeDocumentCommand ?? (_closeDocumentCommand = new RelayCommand(CloseDocument, param => Documents.Count > 0)); }
}
private void CloseDocument()
{
Documents.RemoveAt(SelectedIndex);
SelectedIndex = 0;
}
#endregion
#region Public Members
public ObservableCollection<DocumentViewModel> Documents { get; set; }
private int _selectedIndex = 0;
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
_selectedIndex = value;
OnPropertyChanged();
}
}
#endregion
}
When you are using CommandBinding, arguably you are configuring commands that the view should be handling. As such, it's not clear to me that it makes sense to implement the command in the view model. Conversely, if the view model should own the command, then use its command, not a pre-defined one.
It doesn't make sense to ask to bind your ICommand object to an application command. The ApplicationCommands objects are themselves ICommand implementations! (RoutedUICommand, to be specific.)
If your view model already implements ICommand for the standard commands, then just bind to those:
<CommandBinding Command="{Binding ExitCommand}"/>
If you really want to use the ApplicationCommands commands, then you'll need to subscribe an event handler method to the Executed and CanExecute events and then delegate those to the view model. For example:
<CommandBinding Command="ApplicationCommands.Close"
Executed="Close_Executed" />
Then in code-behind, something like this:
void Close_Executed(object sender, ExecutedRoutedEventArgs e)
{
ICommand command = (ICommand)e.Parameter;
command.Execute(null);
}
Note that you'd have to make sure in this case that you set the CommandParameter at the source of the command itself. I.e. include CommandParameter={Binding ExitCommand} in the InputBinding and Button where you invoke the command. This could get tedious.
Alternatively, you could assume that the DataContext of the Source object is your view model and get the command directly from that:
void Close_Executed(object sender, ExecutedRoutedEventArgs e)
{
EditorViewModel viewModel = (EditorViewModel)((FrameworkElement)e.Source).DataContext;
ICommand command = viewModel.ExitCommand;
command.Execute(e.Parameter);
}

Issue with doubleclick on datagrid

I have the following on a datagrid in my C# code:
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding CmdTransUnitFillerRowDblClick}" />
</DataGrid.InputBindings>
It works for the most part except if user first selects the row (single click) and then tries double-clicking the row. In this situation the CmdTransUnitFillerRowDblClick code is never fired for processing.
So, how can I get the CmdTransUnitFillerRowDblClick to fire correctly on a double-click when the row is already selected?
Since someone may ask:
private void ExecutecmdTransUnitFillerRowDblClick(object parameter)
{
if (DgTransUnitFillerSelectedItem != null)
TransUnitFillerDoubleClick(DgTransUnitFillerSelectedItem.CollectionRowId);
}
See my answer to another related question. The problem is that the datagrid no longer has the focus after the user selects a row (or cell, actually); the cell that the user clicked in the datagrid does. So you have to change the focus back to the datagrid to allow this.
Change:
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding CmdTransUnitFillerRowDblClick}" />
</DataGrid.InputBindings>
To:
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding CmdTransUnitFillerRowDblClick}" />
<MouseBinding Gesture="LeftClick" Command="{Binding CmdTransUnitFillerRowClick}" />
</DataGrid.InputBindings>
...and add:
private void ExecutecmdTransUnitFillerRowClick(object parameter)
{
if (DgTransUnitFillerSelectedItem != null)
The_Name_Of_Your_DataGrid.Focus();
}
On top of your existing InputBinding, you can use a Style to attach the InputBinding to each cell:
<Style TargetType="DataGridCell">
<Setter Property="local:MouseCommands.LeftDoubleClick" Value="ApplicationCommands.New" />
</Style>
This requires use of the MouseCommands class from here.
public static class MouseCommands
{
private static void LeftDoubleClickChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var control = (Control)sender;
if (args.NewValue != null && args.NewValue is ICommand)
{
var newBinding = new MouseBinding(args.NewValue as ICommand, new MouseGesture(MouseAction.LeftDoubleClick));
control.InputBindings.Add(newBinding);
}
else
{
var oldBinding = control.InputBindings.Cast<InputBinding>().First(b => b.Command.Equals(args.OldValue));
control.InputBindings.Remove(oldBinding);
}
}
public static readonly DependencyProperty LeftDoubleClickProperty =
DependencyProperty.RegisterAttached("LeftDoubleClick",
typeof(ICommand),
typeof(MouseCommands),
new UIPropertyMetadata(LeftDoubleClickChanged));
public static void SetLeftDoubleClick(DependencyObject obj, ICommand value)
{
obj.SetValue(LeftDoubleClickProperty, value);
}
public static ICommand GetLeftDoubleClick(DependencyObject obj)
{
return (ICommand)obj.GetValue(LeftDoubleClickProperty);
}
}
Though I think a cleaner way is to just handle the MouseDoubleClick event in the code-behind and manually raise the Command execution by calling your ViewModel directly, or calling .Execute() on the command.

Pass KeyUp as parameter WPF Command Binding Text Box

I've a Text box KeyUp Event Trigger Wired up to a command in WPF.
I need to pass the actual key that was pressed as a command parameter.
The command executes fine, but the code that handles it needs to know the actual key that was pressed (remember this could be an enter key or anything not just a letter, so I can't get it from the TextBox.text).
Can't figure out how to do this.
XAML:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
XAML:
<TextBox Height="23" Name="TextBoxSelectionSearch" Width="148" Tag="Enter Selection Name" Text="{Binding Path=SelectionEditorFilter.SelectionNameFilter,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding SelectionEditorSelectionNameFilterKeyUpCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
I don't think that's possible with InvokeCommandAction but you can quickly create your own Behavior which could roughly look like this one:
public class KeyUpWithArgsBehavior : Behavior<UIElement>
{
public ICommand KeyUpCommand
{
get { return (ICommand)GetValue(KeyUpCommandProperty); }
set { SetValue(KeyUpCommandProperty, value); }
}
public static readonly DependencyProperty KeyUpCommandProperty =
DependencyProperty.Register("KeyUpCommand", typeof(ICommand), typeof(KeyUpWithArgsBehavior), new UIPropertyMetadata(null));
protected override void OnAttached()
{
AssociatedObject.KeyUp += new KeyEventHandler(AssociatedObjectKeyUp);
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.KeyUp -= new KeyEventHandler(AssociatedObjectKeyUp);
base.OnDetaching();
}
private void AssociatedObjectKeyUp(object sender, KeyEventArgs e)
{
if (KeyUpCommand != null)
{
KeyUpCommand.Execute(e.Key);
}
}
}
and then just attach it to the TextBox:
<TextBox Height="23" Name="TextBoxSelectionSearch" Width="148" Tag="Enter Selection Name" Text="{Binding Path=SelectionEditorFilter.SelectionNameFilter,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Behaviors>
<someNamespace:KeyUpWithArgsBehavior
KeyUpCommand="{Binding SelectionEditorSelectionNameFilterKeyUpCommand}" />
</i:Interaction.Behaviors>
</TextBox>
With just that you should receive the Key as a parameter to the command.

Categories