CommandParameter of event trigger using InvokeDelegateCommandAction
I am using the class InvokeDelegateCommandAction from AlexeyZakharov's weblog on the basis of some advice from guys that this is the best way to send back a parameter from a View to a ViewModel from an EventTrigger.
Here's what I have.
In the View (a DataGrid to be specific):
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<cmnwin:InvokeDelegateCommandAction
Command="{Binding SelectedExcludedItemChangedCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=SelectedItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
In the ViewModel:
public DelegateCommandWithParameter SelectedActiveItemChangedCommand
{
get
{
return selectedActiveItemChangedCommand ??
(selectedActiveItemChangedCommand = new DelegateCommandWithParameter(DoSelectedActiveItemsChanged, CanDoSelectedActiveItemsChanged));
}
}
public bool CanDoSelectedActiveItemsChanged(object param)
{
return true;
}
public void DoSelectedActiveItemsChanged(object param)
{
if (param != null && param is List<Object>)
{
var List = param as List<Object>;
MyLocalField = List;
}
}
The new kind of DelegateCommand that allows me to pass objects as args:
public class DelegateCommandWithParameter : ICommand
{
#region Private Fields
private Func<object, bool> canExecute;
private Action<object> executeAction;
private bool canExecuteCache;
#endregion
#region Constructor
public DelegateCommandWithParameter(Action<object> executeAction, Func<object, bool&g开发者_运维技巧t; canExecute)
{
this.executeAction = executeAction;
this.canExecute = canExecute;
}
#endregion
#region ICommand Members
public bool CanExecute(object parameter)
{
bool temp = canExecute(parameter);
if (canExecuteCache != temp)
{
canExecuteCache = temp;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, new EventArgs());
}
}
return canExecuteCache;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
executeAction(parameter);
}
#endregion
}
Whenever my code gets to the DoSelectedActiveItemsChanged, the arg is always NULL.... am I being a complete doofus here? Where does the CommandParamter get linked to the command args? AKA, why does the View pass nothing back to the command? Please help.
I did it with a ListBox
instead, but I got the same thing. The following is fine, as it passes CommandParameter
instead of the invoke parameter. So why is CommandParameter
null
?
protected override void Invoke( object parameter ) {
this.InvokeParameter = parameter;
if ( this.AssociatedObject != null ) {
ICommand command = this.ResolveCommand();
if ( ( command != null ) && command.CanExecute( this.CommandParameter ) ) {
command.Execute( this.CommandParameter );
}
}
}
CommandParameter
doesn't seem to be working appropriately because your binding is setting it to null
. {RelativeSource Self}
resolves to an InvokeDelegateCommandAction
, and that doesn't have a SelectedItems
property. Instead, use this binding:
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=SelectedItems}"
Then CommandParameter
will pass in a SelectedItemCollection
from the ListBox
.
There is one other issue that you'll quickly discover. DoSelectedActiveItemsChanged()
's param
will be an instance of SelectedItemCollection
, not List<Object>
.
I have solved the problem with the aid of Joel's observations... For those that may be interested, here's how. Although obviously I have credited Joel rightfully with the correct answer, it seems right to put this info as a answer rather than an edit to the question.
I discovered the generic DelegateCommand, so got rid of the DelegateCommandWithParameter
public ICommand SelectedObjectsChangedCommand
{
get
{
return selectedObjectsChangedCommand ??
(selectedObjectsChangedCommand = new DelegateCommand<MyType>(DoSelectedObjectsChangedCommand , CanDoSelectedObjectsChangedCommand ));
}
}
The 'Do' (Execute method) now with the return of the SelectionChangedEventArgs...
public void DoSelectedObjectsChangedCommand(object param)
{
if (param != null && param is SelectionChangedEventArgs)
{
foreach (MyType object in ((SelectionChangedEventArgs)param).AddedItems.Cast<MyType>().ToList())
{
selectedObjects.Add(object);
}
foreach (MyType object in ((SelectionChangedEventArgs)param).RemovedItems.Cast<MyType>().ToList())
{
selectedObjects.Remove(object);
}
UpdateAllCanDos();
}
}
Along with the rest of my business logic it makes for a very smooth and intuitive UX. Many thanks to all the answer-ers. I have been doing WPF and MVVM with NHibernate not for only a month and I can't help but acknowledge that the SO community are getting me over the learning curves in the best and most enriching ways possible.
精彩评论