I'm little confused about RelayCommand and EventToCommand in Mvvmlight.
It seems that EventToCommand handle the EventTrigger and call a RelayCommand to do job.
Such as:
<i:Interaction.Triggers>
<i:EventTrigger x:Uid="i:EventTrigger_1" EventName="MouseLeftButtonUp">
<cmd:EventToCommand x:Uid="cmd:EventToCommand_1" Command="{Binding Form_MouseLeftButtonUpCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Is my understanding correct?
So, can we use RelayCommand directly with EventTrigger, no need to use EventToCommand?
Thanks for your help!
EventToCommand is a custom behavior. It is first provided by Expression blend team and now Part of WPF 4. If you're not using WPF4. you require Blend SDK from here.
Behaviors encapsulates functionality as reusable components. These are to be used when feature is not present by default. For example Adding Command support to Label, Combobox etc.
can we use RelayCommand directly with EventTrigger, no need to use EventToCommand?
No. RelayCommand is a shortcut to avoid code redudnency to define custom commands.It extends ICommand whose delegates can be attached for Execute(T) and CanExecute(T). It is similar to DelegateCommand of Prism library.
<cmd:EventToCommand x:Uid="cmd:EventToCommand_1" Command="{Binding Form_MouseLeftButtonUpCommand}" PassEventArgsToCommand="True"/>
In above line cmd:EventToCommand is additional feature to the underlying control. Form_MouseLeftButtonUpCommand is the Command it executes. This command can be encapsulated as RelayCommand.
Thank Tilak for your useful answer.
With the explanation from Tilak, I did mess up like putting a binding in a event handler (such as Button GotFocus="{Binding DoJob}") --> Build failed and found that Event Handler like this does not support Binding. We can only bind in Command (such as Button Command="{Binding DoJob}" /> and default event is invoked in this situation, with button, it should be Click event).
Do something stupid will help me to understand the life more - LOL
can we use RelayCommand directly with EventTrigger, no need to use
EventToCommand?
Actually, I intend NOT to use EventToCommand, and I found the solution for that: use InvokeCommandAction instead (belongs to System.Windows.Interactivity - Mvvm-light also refers to this assembly).
Related
I am trying to use an attached event to invoke an ICommand.
I am using the Microsoft.Toolkit.Mvvm NuGet package, along with the Microsoft.Xaml.Behaviors.Wpf Nuget package.
I have had success starting a Storyboard using the <BeginStoryBoardAction /> within the <FOO.Triggers /> by defining an <EventTrigger /> and setting the RoutedEvent equal to the name of the attached event in question.
However, to my knowledge, there exists no way to invoke an ICommand using anything provided within the <EventTrigger />. By which I mean, there is nothing that I can use within the body of the <EventTriggers.Actions /> block (similar to <Behaviors.InvokeCommandAction />) which will result in the ICommand being invoked.
In compliance with the Minimal, Complete and Verifiable example, to demonstrate what I am trying to achieve, you can reference the project on GitHub - https://github.com/AbbottWC/MCVE_AttachedEventFailure.
Or
Open Visual Studio. Create a WPF Application (Targeting .Net Core 3.1)
Tools -> NuGet Package Manager -> Manage Packages for this solution
Add the Microsoft.Toolkit.Mvvm (for the RelayCommand class) and the Microsoft.Xaml.Behaviors.Wpf packages.
In the App.Xaml.cs
private RelayCommand testCommand = new RelayCommand(( ) => MessageBox.Show("Event Captured!", "Success!"));
public RelayCommand TestCommand => testCommand;
In the MainWindow.xaml
Define the namespace xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
Rename local to l
Add the following to the body of the XAML before the closing </Window> tag:
<i:Interaction.Triggers>
<!--This will work, and is present only to prove the problem lies not with the Command or how it is being accessed.-->
<i:EventTrigger EventName="MouseEnter">
<i:EventTrigger.Actions>
<i:InvokeCommandAction Command="{Binding TestCommand, Source={x:Static l:App.Current}}" />
</i:EventTrigger.Actions>
</i:EventTrigger>
<!--This will not work, and is meant to provide an example of the problem.-->
<i:EventTrigger EventName="Mouse.MouseEnter">
<i:EventTrigger.Actions>
<i:InvokeCommandAction Command="{Binding TestCommand, Source={x:Static l:App.Current}}" />
</i:EventTrigger.Actions>
</i:EventTrigger>
</i:Interaction.Triggers>
So to restate my question, is what I am trying to achieve here possible? Is it just not possible to use an attached event in this way?
Thanks.
From what I understand, the EventTrigger can only listen to events that the source object "owns". So to answer your question, this is not possible using the EventTrigger class.
If you look at the source, when you pass Mouse.MouseEnter, it tries to get that event from the target type. Also, since you did not specify the target, it defaults to the AssociatedObject, which is Window in your case.
Type targetType = obj.GetType();
EventInfo eventInfo = targetType.GetEvent(eventName);
Now the problem I find, is if it cannot find the event, it only throws an exception when the source object is not null, and in your case you haven't specified one, so it silently fails. Not sure why the designers made it this way.
Now, I did find this blog post, which describes exactly how you can workaround this problem:
https://sergecalderara.wordpress.com/2012/08/23/how-to-attached-an-mvvm-eventtocommand-to-an-attached-event/
Basically, you inherit from EventTriggerBase<T> and in the OnAttached method you need to call AddHandler on your AssociatedObject to add the event.
I am creating a .Net WPF application that is a dashboard.
I need the ability to communicate events between different View Models on the dashboard.
This to me feels like a pub/sub events model.
What is the best way to implement a solution which fits well with the MVVM FrameWork?
I started to look at Prism, but am wondering if that is a little heavy handed for my needs.
Can someone recommend a best practices approach and point me to some simple
examples of implementation?
Thanks,
JohnB
Please refer to the following blog post about how to use the event aggregator pattern to communicate between view models in a loosely coupled fashion: https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/.
This should answer your question.
Another option is to use a shared service: https://social.msdn.microsoft.com/Forums/en-US/22907a0f-d805-4195-8272-7c284b72d2ee/example-of-using-shared-services-prism?forum=wpf
There is an example of how to use the EventAggregator class in the latest version of Prism available on GitHub: https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/EventAggregation
for MVVM, i would definitely prefer to use MVVMLIGHTLIBS. First of all, there is an eventtrigger functionality, where you can do mousedown, mouseup, selectionchanged, etc.. it makes your life a lot easier when you try to bind a command for eventtrigger
Example would be:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=employeeListBox, Path=SelectedValue}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
MyCommand will be implement in your viewmodel either using relaycommand or commandhandler, whichever you prefer.
Also, knowledge regarding binding clr property, dependency property and attached property for controls are extremely important as well if you want to follow MVVM pattern.
There are several examples that i would like to share:
---- event triggers---------
http://www.c-sharpcorner.com/blogs/example-of-eventtrigger-in-mvvm-application1
----- attached property and dependency property----
To summarize:
Attached properties are for the container elements. For example, you can have grid, and then you can create grid.rowdefinition and grid.columndefinition to have the number of row and column change dynamically
Dependency properties are properties of classes that derive from DependencyObject, and they're special in that rather than simply using a backing field to store their value, they use some helper methods on DependencyObject.
Those are pretty basic thing about wpf and mvvm, which involve model, view and viewmodel.
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.
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
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