key strokes events dont execute in C# WPF - c#

I'm using WPF MVVM pattern with Prism
im trying to bind keybind to some command
----View---
<Canvas Background="Red" Grid.Row="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<prism:InvokeCommandAction Command="{Binding KeyDownCmd}"/>
</i:EventTrigger>
<i:EventTrigger EventName="KeyDown">
<prism:InvokeCommandAction Command="{Binding KeyUpCmd}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
----View Model----
#region Commands
public DelegateCommand<KeyEventArgs> KeyDownCmd { get; private set; }
public DelegateCommand<KeyEventArgs> KeyUpCmd { get; private set; }
#endregion
#region Ctor
public GameViewModel()
{
KeyDownCmd = new DelegateCommand<KeyEventArgs>(KeyDownExecute);
KeyUpCmd = new DelegateCommand<KeyEventArgs>(KeyUpExecute);
}
private void KeyUpExecute(KeyEventArgs obj)
{
//some code here
}
private void KeyDownExecute(KeyEventArgs obj)
{
//some code here
}
i also tried to bind to code-behind like this KeyDown="Canvas_KeyDown" and nothing
tried to use PreviewKeyDown /PreviewKeyUp and nothing
also tried to bind the key command to the gird above the canvas and to the userControl and nothing
P.S
im navigating between pages with viewInjection as described here

A Canvas doesn't raise any key stroke events unless it's Focusable and focused.
You can make it focusable by setting the property in the XAML but you still have to focus it at some point, for example when it's clicked:
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Canvas canvas = (Canvas)sender;
Keyboard.Focus(canvas);
}
XAML:
<Canvas Focusable="True" Background="Red"
PreviewKeyDown="Canvas_PreviewKeyDown"
MouseLeftButtonDown="Canvas_MouseLeftButtonDown">
...
</Canvas>
You should probably reconsider your approach and handle the PreviewKeyDown of a parent element or a focusable child element of the Canvas instead.

Related

WPF Button Command not firing ICommand in ViewModel

I have a View with a button as follows:
<Button Grid.Column="2" Grid.Row="1" Content="Test" Margin="10,4"
Command="{Binding DataContext.CmdTestButtonClicked}"
CommandParameter="{Binding}" />
In the view's code-behind, I set the DataContext to the ViewModel:
public GlobalSettings()
{
InitializeComponent();
...
DataContext = Helpers.IoCHelper.GlobalSettingsVM;
...
}
My ViewModel derives from a base class which exposes the ICommand:
public class GlobalSettingsVM : CollectionViewModel<GlobalSettings> { ... }
public abstract class CollectionViewModel<TModel> : IInstallModuleViewModel, INotifyPropertyChanged,
INotifyDataErrorInfo where TModel : Model, new()
{
...
public ICommand CmdTestButtonClicked
{
get
{
return _testButtonClicked ??
(_testButtonClicked = new RelayCommand(TestButtonClicked));
}
}
protected virtual void TestButtonClicked(object o)
{
// I never get here
}
}
I don't have any other issues using this pattern throughout my application, however all my other implementations have the Button within a ListView, so there I have to use RelativeSource={RelativeSource AncestorType={x:Type ListView}}.
Why would this command never fire? Do I need to set a RelativeSource here as well?
This
Command="{Binding DataContext.CmdTestButtonClicked}"
Implies that the Command will look for a property called DataContext in the object to which the button is bound.
If the DataContext of the button is a GlobalSettingsVM this should work:
Command="{Binding CmdTestButtonClicked}"
You could also use the MVVM Light toolkit wich is very convenient and helping on these situations.
You would get Something like this :
<Button Grid.Column="2" Grid.Row="1" Content="Test" Margin="10,4"
<i:Interaction.Triggers>
<i:EventTrigger EventName="OnClick" >
<Command:EventToCommand Command="{Binding DataContext.CmdTestButtonClicked}" CommandParameter="{Binding}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
In my case, I was listening to PreviewMouseLeftButtonDown under constructor of xaml.cs class which was stopping command event callback to the view model.
this.PreviewMouseLeftButtonDown += (s, e) => DragMove();
Instead of above line, in xaml file for window added MouseLeftButtonDown="Window_MouseLeftButtonDown" click handler and handled window drag within it, as below
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}

Disposing a timer inside a ViewModel, upon closing the main window

Currently in my application, I have a RelayCommand which calls an action which starts a Timer. I have a separate command which the calls the Dispose() method to get rid of/release the timer. This all sits in the ViewModel counterpart of the project.
Using the MVVM pattern, how would I dispose of this Timer upon closing the window, as well as carrying out the regular/default operation of closing the window?
I am also using the MVVM-Light toolkit, if this is of any help.
An example of my current solution is shown below:
ViewModel
private static Timer dispatchTimer;
public MainViewModel()
{
this.StartTimerCommand = new RelayCommand(this.StartTimerAction);
this.StopTimerCommand = new RelayCommand(this.StopTimerAction);
}
public RelayCommand StartTimerCommand { get; private set; }
public RelayCommand StopTimerCommand { get; private set; }
private void StartTimerAction()
{
dispatchTimer = new Timer(new TimerCallback(this.IgnoreThis), null, 0, 15);
}
private void StopTimerAction()
{
dispatchTimer.Dispose();
}
View (xaml)
....
Height="896"
Width="1109"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="mainGrid"
Width="Auto"
Height="Auto">
<Button x:Name="startTimerButton"
Content="Start Timer"
Command="{Binding StartTimerCommand}"/>
<Button x:Name="stopTimerButton"
Content="Stop Timer"
Command="{Binding StopTimerCommand}"/>
</Grid>
</Window>
View (Code-Behind)
public MainWindow()
{
this.InitializeComponent();
//Would a 'Closing +=' event be called here?
}
Closing the windows may or may not dispose your viewmodel, depending how you register it. One approach would be bind to the Loaded/Unloaded events from the view and do the work there:
View:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding PageLoadedCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unloaded">
<i:InvokeCommandAction Command="{Binding PageUnLoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
ViewModel:
public RelayCommand PageUnLoadedCommand { get; private set; }
...
PageUnLoadedCommand = new RelayCommand(() => OnPageUnLoadedCommand());
...
private void OnPageUnLoadedCommand()
{
//Unsubscribe and dispose here
}
You can add a destructor for your ViewModel like below
~public MainViewModel()
{
dispatchTimer.Dispose();
}

Wpf MVVM How to handle TextBox "paste event" in the ViewModel

I develop application with using MVVM pattern. I using MVVMLight library to do this. So if I need to handle TextBox TextChange event I write in XAML:
<I:EventTrigger EventName="TextChanged">
<I:InvokeCommandAction Command="{Binding PropertyGridTextChange}"/>
</I:EventTrigger>
where PropertyGridTextChange is Command in ViewModel. But TextBox has no Paste event!
This solution only works if application don't use MVVM pattern, because you need to have link on TextBox.
<DataTemplate x:Key="StringTemplate">
<TextBox Text="{Binding Value, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</DataTemplate>
Important detail - TextBox placed within DataTemplate.
I have no idea how can I handle "paste event".
I want PasteCommand to be invoked when I paste text into TextBox. And I need that TextBox.Text or TextBox itself to be passed as parameter into PasteCommandMethod.
private RelayCommand<Object> _pasteCommand;
public RelayCommand<Object> PasteCommand
{
get
{
return _pasteCommand ?? (_pasteCommand =
new RelayCommand<Object>(PasteCommandMethod));
}
}
private void PasteCommandMethod(Object obj)
{
}
I can suggest answer on my question.
Class-helper.
public class TextBoxPasteBehavior
{
public static readonly DependencyProperty PasteCommandProperty =
DependencyProperty.RegisterAttached(
"PasteCommand",
typeof(ICommand),
typeof(TextBoxPasteBehavior),
new FrameworkPropertyMetadata(PasteCommandChanged)
);
public static ICommand GetPasteCommand(DependencyObject target)
{
return (ICommand)target.GetValue(PasteCommandProperty);
}
public static void SetPasteCommand(DependencyObject target, ICommand value)
{
target.SetValue(PasteCommandProperty, value);
}
static void PasteCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var textBox = (TextBox)sender;
var newValue = (ICommand)e.NewValue;
if (newValue != null)
textBox.AddHandler(CommandManager.ExecutedEvent, new RoutedEventHandler(CommandExecuted), true);
else
textBox.RemoveHandler(CommandManager.ExecutedEvent, new RoutedEventHandler(CommandExecuted));
}
static void CommandExecuted(object sender, RoutedEventArgs e)
{
if (((ExecutedRoutedEventArgs)e).Command != ApplicationCommands.Paste) return;
var textBox = (TextBox)sender;
var command = GetPasteCommand(textBox);
if (command.CanExecute(null))
command.Execute(textBox);
}
}
Using in XAML. In TextBox as attribute.
TextBoxPasteBehavior.PasteCommand="{Binding PropertyGridTextPasted}"
PropertyGridTextPasted - Command in the ViewModel.
I've been struggling with this kind of problem too in recent days. My first approach would be to have a property in the VM that is bound to the text box (which I am sure you already have). Then bind an ICommand to an event to handle the on paste event:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowEditEnding">
<i:InvokeCommandAction Command="{Binding DocRowEdit}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
you need to define the namespace in the proper part of the XAML code, and then put the interaction triggers in as part of the textbox definition. Here I am capturing the RowEditEnding event to do some stuff similar to what you are attempting.
The command binding is another piece, let me know if you need more information on how that needs to be set up.

WPF: How to bind a command to the ListBoxItem using MVVM?

I have just started learning MVVM. I've made the application from scratch by following this MVVM tutorial (I highly recommend it to all MVVM beginners out there). Basically, what I have created so far is a couple of text boxes where user adds his or her data, a button to save that data which subsequently populates the ListBox with all entries made.
Here's where I got stuck: I want to be able to double-click on a ListBoxItem and to trigger a command that I have created and added to my ViewModel. I don't know how to finish the XAML side, i.e. I don't know how to bind that command to the ListBox(Item).
Here's XAML:
...
<ListBox
Name="EntriesListBox"
Width="228"
Height="208"
Margin="138,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Entries}" />
...
Here's ViewModel:
public class MainWindowViewModel : DependencyObject
{
...
public IEntriesProvider Entries
{
get { return entries; }
}
private IEntriesProvider entries;
public OpenEntryCommand OpenEntryCmd { get; set; }
public MainWindowViewModel(IEntriesProvider source)
{
this.entries = source;
...
this.OpenEntryCmd = new OpenEntryCommand(this);
}
...
}
And finally, here's the OpenEntryCommand that I want to be executed once the user double-clicks the item in the EntriesListBox:
public class OpenEntryCommand : ICommand
{
private MainWindowViewModel viewModel;
public OpenEntryCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return parameter is Entry;
}
public void Execute(object parameter)
{
string messageFormat = "Subject: {0}\nStart: {1}\nEnd: {2}";
Entry entry = parameter as Entry;
string message = string.Format(messageFormat,
entry.Subject,
entry.StartDate.ToShortDateString(),
entry.EndDate.ToShortDateString());
MessageBox.Show(message, "Appointment");
}
}
Please help, I'd appreciate it.
Unfortunately, only ButtonBase derived controls have the possibility for binding ICommand objects to their Command properties (for the Click event).
However, you can use an API provided by Blend to map an event (like in your case MouseDoubleClick on the ListBox) to an ICommand object.
<ListBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding YourCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
You'll have to define: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" and have a reference to System.Windows.Interactivity.dll.
-- EDIT --
This is part of WPF4, but u can use Microsoft.Windows.Interactivity if you're not using WPF4. This dll is from Blend SDK, which doesn't require Blend, from here:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=f1ae9a30-4928-411d-970b-e682ab179e17&displaylang=en
Update: I found something that should help you. check this link on MVVM Light Toolkit which contains a walkthrough on how to do this, along with a link to the needed libraries. MVVM Light Toolkit is a very interesting framework for applying MVVM with Silverlight, WPF, and WP7.
Hope this helps :)
This is made tricky because of the DoubleClick event. There are a few ways to do this:
Handle the double-click event in code behind, and then manually invoke a command/method on your ViewModel
Use an attached behavior to route the DoubleClick event to your Command
Use a Blend Behavior to map the DoubleClick event to your command
2 and 3 might be more pure, but frankly, 1 is easier, less complex, and not the worst thing in the world. For a one-off case, I'd probably use approach #1.
Now, if you changed your requirements to use, say, a hyperlink on each item, it would be easier. Start out by naming the root element in your XAML - e.g., for a Window:
<Window .... Name="This">
Now, in the DataTemplate for your ListBox items, use something like this:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Hyperlink
Command="{Binding ElementName=This, Path=DataContext.OpenEntryCmd}"
Text="{Binding Path=Name}"
/>
The ElementName binding lets you resolve the OpenEntryCmd from the context of your ViewModel, rather than the specific data item.
EDIT: I wrote this post as an inexperienced WPF developer, nowadays I'd either use a framework that provides event to command binding, or simply use a button and restyle it. Of course for maximum flexibility this is maybe better.
I find the best way to do this is to create a simple user control wrapper for my content, with dependency properties for the command and parameter.
The reason I did this was due to the Button not bubbling the click event to my ListBox which prevented it from selecting the ListBoxItem.
CommandControl.xaml.cs:
public partial class CommandControl : UserControl
{
public CommandControl()
{
MouseLeftButtonDown += OnMouseLeftButtonDown;
InitializeComponent();
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (Command != null)
{
if (Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand),
typeof(CommandControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object),
typeof(CommandControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
CommandControl.xaml:
<UserControl x:Class="WpfApp.UserControls.CommandControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="Transparent">
</UserControl>
Usage:
<ListBoxItem>
<uc:CommandControl Command="{Binding LoadPageCommand}"
CommandParameter="{Binding HomePageViewModel}">
<TextBlock Text="Home" Margin="0,0,0,5" VerticalAlignment="Center"
Foreground="White" FontSize="24" />
</uc:CommandControl>
</ListBoxItem>
The Content can be whatever, and when the control is clicked, it will execute the command.
EDIT: Added Background="Transparent" to UserControl to enable click events on the entire area of the control.
This is a bit of a hack, but it works well and allows you to use commands and avoid code behind. This also has the added benefit of not triggering the command when you double-click (or whatever your trigger is) in the empty ScrollView area assuming your ListBoxItems don't fill the entire container.
Basically, just create a DataTemplate for your ListBox that is composed of a TextBlock and bind the width of the TextBlock to the width of the ListBox, set the margins and padding to 0, and disable horizontal scrolling (because the TextBlock will bleed beyond the visible bounds of the ScrollView triggering the horizontal scroll bar otherwise). The only bug I've found is that the command won't fire if the user clicks precisely on the border of the ListBoxItem, which I can live with.
Here is an example:
<ListBox
x:Name="listBox"
Width="400"
Height="150"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ItemsSource="{Binding ItemsSourceProperty}"
SelectedItem="{Binding SelectedItemProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Padding="0"
Margin="0"
Text="{Binding DisplayTextProperty}"
Width="{Binding ElementName=listBox, Path=Width}">
<TextBlock.InputBindings>
<MouseBinding
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectProjectCommand}"
Gesture="LeftDoubleClick" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I recently needed to trigger an ICommand upon double clicking a ListBoxItem as well.
Personally, I don't like the DataTemplate method as it is binding to the content inside the ListBoxItem container, and not the container itself. I've opted to use an Attached Property to assign an InputBinding on the container. It takes a little more elbow grease, but it works well.
First, we need to create an attached property class. I've created mine a little more generically towards any class that derives from FrameworkElement, just in case I run into this again with a different visual.
public class FrameworkElementAttachedProperties : DependencyObject
{
public static readonly DependencyProperty DoubleClickProperty = DependencyProperty.RegisterAttached("DoubleClick", typeof(InputBinding),
typeof(FrameworkElementAttachedProperties), new PropertyMetadata(null, OnDoubleClickChanged));
public static void SetDoubleClick(FrameworkElement element, InputBinding value)
{
element.SetValue(DoubleClickProperty, value);
}
public static InputBinding GetDoubleClick(FrameworkElement element)
{
return (InputBinding)element.GetValue(DoubleClickProperty);
}
private static void OnDoubleClickChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = obj as FrameworkElement;
/// Potentially throw an exception if an object is not a FrameworkElement (is null).
if(e.NewValue != null)
{
element.InputBindings.Add(e.NewValue as InputBinding);
}
if(e.OldValue != null)
{
element.InputBindings.Remove(e.OldValue as InputBinding);
}
}
}
Then the final step is to override the base container style for the ListBoxItem.
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
BasedOn="{StaticResource ListBoxItem}">
<Setter Property="local:FrameworkElementAttachedProperties.DoubleClick">
<Setter.Value>
<MouseBinding Command="{Binding OnListBoxItemDoubleClickCommand}"
MouseAction="LeftDoubleClick"/>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
Now, anytime a ListBoxItem is double clicked, it will fire our OnListBoxItemDoubleClickCommand.
If you're looking for a nice simple solution that uses interactions instead of mucking about with user controls, code behind, input bindings, custom attached properties, etc.
And you want something that works at the ListBoxItem level, i.e. not ListBox level as per the (incorrectly) accepted solution.
Then here's a snippet for a simple 'button like' click action..
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<!-- insert your visuals here -->
<b:Interaction.Triggers>
<b:EventTrigger EventName="MouseUp">
<b:InvokeCommandAction Command="{Binding YourCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note, background="Transparent" is required to ensure the entire Grid is clickable and not just the contents inside.

WPF: KeyBinding for Tab, Swallows Tab and Doesn't Pass It Along

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]"

Categories

Resources