Attach command with parameter to KeyUp event - c#

I am currently transforming a medium size WPF project to MVVM and I ran into a problem that I wasn't able to solve, yet. Maybe you could help me out?
The target framework is .NET 3.5.1.
I have a list view that gets its items from the underlying view model. That view model is exposing a command to remove the selected items from the list view. Therefore the command parameter is bound to the SelectedItems property of the list view.
<ListView ItemsSource="{Binding MyItems}"
x:Name="MyListView"
SelectionMode="Extended">
</ListView>
<Button x:Name="MyRemoveButton"
Content="Remove item"
Command="{Binding RemoveItemCommand}"
CommandParameter="{Binding ElementName=MyListView, Path=SelectedItems}">
My intention is to execute this command not only when pressing a button, but also when the KeyUp event is fired on the list view and the pressed key is "delete".
I was close to finding the solution when I stumbled upon interaction triggers in this example:
http://joyfulwpf.blogspot.com/2009/05/mvvm-invoking-command-on-attached-event.html?showComment=1250325648481#c3867495357686026904
Now the problem with this demo is that the command parameter is the pressed key, but in my case I need the command parameter to be the SelectedItems property and I need the command to execute only on a specific key.
Is there any way to do this without much overhead and in the MVVM way?
Something like this would be awesome:
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<local:CommandAction Command="{Binding RemoveItemCommand}"
CommandParameter={Binding ElementName=MyListView, Path=SelectedItems}
EventArgument="Key.Delete"/>
</i:EventTrigger>
</i:Interaction.Triggers>

To do it in the MVVM way you need to bind "SelectedItems" property of the ListView to your ViewModel, so you could use it from your commands and wouldn't need to pass it via CommandParameter.

How strict is your separation requirement? If you don't have designers using Blend, then put a call to a ViewModel method into the KeyUp or PreviewKeyUp event handler in your code-behind.

Related

Mvvm light EventToCommand - Setting the Handled property?

I have a tabbed GUI with each tab containing a Frame. I use the EventToCommand with the SelectionChangedEvent whenever a new TabItem is selected. I do this to update the state of the app. This all works fine - a little bit too fine, the event gets fired too often. Here is my problem:
How can I prevent the event from bubbling up the visual tree by setting the Handled property on the event when using the mvvm light eventToCommand feature?
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<mvvm:EventToCommand
Command="{Binding YourCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Your command needs to provide parameter from method like RelayCommand . This parameter is event args providing Handled property. More here.

Force Binding update after ComboBox selection has completed MVVM

I am having some trouble with an editable ComboBox and the updating of a binding. Currently I have a ComboBox where its UpdateSourceTrigger=LostFocus this is because I need to wait for the user to finish inputting somthing before I decide if the value is a new value (and thus create a new one).
Unfortunately, I have another feature which requires the binding to update when the value has changed aswell. Aka, LostFocus is no good to me in this case. As selecting a new value within the ComboBox it doesn't cause the LostFocus to fire (obviously). So I need to find a way to force update the binding.
I have looked into the SelectionChanged and forcing the update on the binding:
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ParentConversation.ViewModel.ComboSelectionChanged}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:StoryForgeComboBox}}}"/>
</i:EventTrigger>
And updating the binding in code behind like so:
be = BindingOperations.GetBindingExpression(ele, ComboBox.TextProperty);
if (be != null)
{
be.UpdateSource();
}
Unfortunately I CANNOT update the binding at this point as the value has yet to change. See this stackoverflow topic: ComboBox- SelectionChanged event has old value, not new value
There is a trick where you can possibly use the DropDownClosed event and then update the binding, this works but does not work if you use the Up/Down arrow keys which never opens the ComboBox. Also hooking into KeyUp and KeyDown is too early. The binding cant be updated yet.
So my question is, when is a good time to say "Hey Mr Combo Box, You can update your bindings now".
Cheers.
You can change the SelectionChanged event trigger to LostFocus:
<ComboBox
IsEditable="True"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="LostFocus">
<i:InvokeCommandAction
Command="{Binding Command}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
When the user types, Text is updated.
When the user types and the ComboBox finds a match in the Items, SelectedItem is changed.
When the user types, the ComboBox does not find a match and a item was previously selected, SelectedItem is set to null.
When the user selects an item, both SelectedItem and Text are updated.
When the user leaves the ComboBox (losing focus), Command is triggered.
When the user has typed text and opens the drop down list, Command is triggered.
EDIT: strangely enough Command is also triggered when it receives focus.
Is this the behavior you want?

WPF MVVM event to command - invoke command based on event args

I have a WPF app which uses DevExpress controls and MVVM with PRISM.
I'm using DockLayoutManager's 'DockOperationCompleted' event to invoke a command on my view model like this:
<dxd:DockLayoutManager x:Name="dockContainer">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DockOperationCompleted">
<i:InvokeCommandAction Command="{Binding DataContext.SaveLayoutCommand, ElementName=dockContainer}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<dxd:LayoutGroup/>
</dxd:DockLayoutManager>
The purpose of the 'SaveLayoutCommand' command is to save the layout so it can be restored later in time.
The 'DockOperationCompleted' event is raised after a DockItem gets docked or closed (there are other cases but they are irrelevant).
The problem is that when I close the main window, the dock items in my DockLayoutManager are getting closed one by one and thus 'SaveLayoutCommand' gets invoked for every closed dock item and I don't want this to happen.
The 'DockOperationCompletedEventArgs' with which the event gets raised has a 'DockOperation' property which I can check agains, but I'm not sure where exactly should this code fit in.
What I am trying to achieve is that the command should be invoked only in one case - when the item is docked
My question is : is there a way to 'filter' when the command gets invoked based on the event's event args?
Thanks :)
In addition to aKzenTs answer I want to point out that with DevExpress its rather easy to pass the event args to a command.
You should use EventToCommand from their MVVM library (PassEventArgsToCommand-Property). If you want to keep your viewmodel clean of DevExpress you can additionally use a Converter to transform the event args to an arbitrary object.
<dxmvvm:Interaction.Triggers>
<dxmvvm:EventToCommand Command="{Binding YOURCOMMAND}"
EventName="THEEVENT"
EventArgsConverter="{StaticResource YOUREVENTARGSCONVERTER}"
PassEventArgsToCommand="true" />
</dxmvvm:Interaction.Triggers>
There is no builtin way to filter the events that are raised before invoking an action. You can however implement your own custom trigger action that does the filtering.
Unfortunately it's also not easy to access the event args and passing them to the command. See this question as a reference:
MVVM Passing EventArgs As Command Parameter

How can I bind a Hold/DoubleTap event of a button to a property in ViewModel

I have a view containing a button. And i want to perform an action on Hold event. How can i do this in mvvm? For Tap event i can Bind it to a Command property. Is it possible to do this with same way?
I would go with Braulio's answer - MVVM Light is what I would use, but back in the Silverlight 3 days I used custom attached properties to achieve this. See here for an example of custom attached properties: http://umairsaeed.com/2010/04/22/custom-attached-properties-in-silverlight/
You could create a custom attached property for the hold event to bind the command to and then use it like so:
<Border local:MyTextBoxControl.HoldEventCommand="{Binding HoldCommand}"/>
This is a lot of work compared with including the mvvm light toolkit in your project and then doing this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Hold">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding YourCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Not sure if it supports the command, if not you can use MVVM Light Toolkit (free and open source) behavior: EventToCommand

How would you know which element of an ItemsControl sends an event in MVVM?

Let's say I currently have an ItemsControl whose DataTemplate is a bunch of buttons. I'm wiring up these buttons' click events, but how am I to know which button was clicked? Should I not use a ItemsControl?
I'm trying to have no code-behind, but being pragmatic may be necessary.
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding ItemsControlButtonClicked, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to know what Item was clicked, then pass {Binding } as the CommandParameter and it will pass the selected object to your Command
If you want to know what Button was clicked, I would do that in the code-behind since ViewModels do not need to know anything about the UI, and that includes buttons.
Also since your control is a Button, you should use the Command property instead of a Click trigger.
<Button Command="{Binding ItemsControlButtonClicked}" />
You can send parameters along with the command and based on these parameters you can find out which button was clicked
In my project I also use the MVVM Light I has an dropdown with collection of items, and a button which user press and action depend on selected item from drop down
you should create a Relay command with parameter look at the example from my code
public RelayCommand<Project> StartTimer { get; private set; }//declare command
StartTimer = new RelayCommand<Project>(OnStartTimer);
private void OnStartTimer(Project project)
{
if (project != null)
{
currentProject = project;
if (!timer.IsTimerStopped)
{
timer.StopTimer();
}
else
{
Caption = "Stop";
timer.StartTimer();
}
}
on the view I bind the drop down with collection of class Project
and for button command parameter I bind the selected item form drop down
look at the code
<ComboBox Name="projectcomboBox" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="FullName"
SelectedValuePath="Name" SelectedIndex="0" >
</ComboBox>
<Button Name="timerButton" Content="{Binding Path=Caption}" Command="{Binding Path=StartTimer}"
CommandParameter="{Binding ElementName=projectcomboBox, Path=SelectedItem}" ></Button>
pay attention to Command and CommandParameter binding
also you can use this approache not only for drop down
Well, you can use the Sender.DataContext which is the actual data.
Create command properties in your view model class (using Josh Smith's RelayCommand pattern is the simplest way to do this) and bind each button's Command to the appropriate one. Not only is this straightforward to do and simple to maintain, it also gives you an easy way of implementing the enable/disable behavior when you need to.

Categories

Resources