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>
Related
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>
I am using MVVM light to refacter my code. My old code looks like this-
<Grid x:Name="root" ButtonBase.Click="LayoutRoot_Click">
The LayoutRoot_Click will handle all the RoutedEvent from child controls.
Because the grid have no command interface ,so the new code like this
<Grid x:Name="root" ButtonBase.Click="LayoutRoot_Click">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ButtonBase.Click">
<mvvm:EventToCommand Command="{Binding InputCommand}" CommandParameter="{Binding ElementName=button1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
The viewmodel like this-
public class SomeViewModel : ViewModelBase
{
public SomeViewModel()
{
InputCommand = new RelayCommand(InputCode);
}
public ICommand InputCommand { get; set; }
private void InputCode()
{
string input = string.Empty;
}
}
But this doesn't work. The command did not get the RoutedEvent.
Try adding PassEventArgsToCommand="True" to your command binding
<Grid x:Name="root" ButtonBase.Click="LayoutRoot_Click">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ButtonBase.Click">
<mvvm:EventToCommand Command="{Binding InputCommand}" CommandParameter="{Binding ElementName=button1}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Your command would then have to have a parameter of the correct type which I think is RoutedEventArgs
public RelayCommand<RoutedEventArgs> SomeCommand { get; private set; }
private void SomeCode(RoutedEvent routedEvent)
{
//Do something with the routed event
}
I have a ListView/GridView setup and I want to handle a right click on the dislayed items. Is there are Databinding-way of doing this? I have seen complicated workarounds like handling the super-elements event and poking around to find its origin, but that feels awfully bloated for such basic request.
What I'd love to see is something like binding the event to an action of the item's ViewModel - is there a way to do that? Similar to this, but I can't quite wrap my head around how to adapt that to work on a single ListView item (I am not even sure that's possible, tbh).
Rough outline:
<ListView>
<ListView.View>
<GridView />
</ListView.View>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
</Style>
</ListView.Resources>
</ListView/>
There is a way using the Interactivity assembly form the Blend SDK. It will provide an EventTrigger which executes a command when an event is raised.
<!--
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
-->
<Button Content="Click me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ClickCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Edit:
A possible solution for your problem could look like this:
View:
<ListView x:Name="listView">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonUp">
<i:InvokeCommandAction Command="{Binding RightClickOnItemCommand}"
CommandParameter={Binding SelectedItem, ElementName=listView} />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
ViewModel:
public ICommand RightClickOnItemCommand { get; set; }
public void RightClickOnItem(object item)
{
}
You could try to create a style template for the list view item, and add an attached behaviour to it to handle mouse clicks.
public static readonly DependencyProperty PreviewMouseLeftButtonDownCommandProperty =
DependencyProperty.RegisterAttached("PreviewMouseLeftButtonDownCommand", typeof (ICommand),
typeof (MouseBehaviour), new FrameworkPropertyMetadata(PreviewMouseLeftButtonDownCommandChanged));
private static void PreviewMouseLeftButtonDownCommandChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs args)
{
var element = (FrameworkElement) dependencyObject;
element.PreviewMouseLeftButtonDown += Element_PreviewMouseLeftButtonDown;
}
private static void Element_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs args)
{
var element = (FrameworkElement) sender;
ICommand command = GetPreviewMouseLeftButtonDownCommand(element);
if (command != null)
{
command.Execute(args);
}
}
public static void SetPreviewMouseLeftButtonDownCommand(UIElement element, ICommand value)
{
element.SetValue(PreviewMouseLeftButtonDownCommandProperty, value);
}
public static ICommand GetPreviewMouseLeftButtonDownCommand(UIElement element)
{
return (ICommand) element.GetValue(PreviewMouseLeftButtonDownCommandProperty);
}
I have a button
<Button Command="{Binding MyCommand}" />
But MyCommand is being hit when I long-press the button as well as when I just click it. Is there any way around this?
thanks
What you could do is the following:
Reference the System.Windows.Interactivity dll.
Define the namespace in your xaml code:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then in your xaml code wire up this event trigger:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap" SourceName="btnTest">
<i:InvokeCommandAction Command="{Binding DoSomething}" />
</i:EventTrigger>
</i:Interaction.Triggers>
The EventName in this case is "Tap", the SourceName is the x:Name of the button you want to watch. Like this:
<Button Content="Click me" x:Name="btnTest"/>
Then in your ViewModel, you can wire it up to an ICommand, I typically use a RelayCommand:
private ICommand _DoSomething;
public ICommand DoSomething
{
get
{
if (_DoSomething == null)
{
_DoSomething = new RelayCommand(DoSomethingExecute);
}
return _DoSomething;
}
}
private void DoSomethingExecute()
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("btnTest on the tap event");
});
}
I tested, only the tap event is captured, not the long press event.
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.