MVVM 軟體架構模式 - 透過 EventCommand 再進化傳遞事件參數 (註冊 Command/CommandParameter/InvokeParameter 三個依賴屬性)
這裡把前一篇的 EventCommandAction 拓展改寫,這就叫它 EventCommand 吧~少打幾個字~XD
先看改造過的 EventCommand 類別
public class EventCommand : TriggerAction<DependencyObject>
{
private string commandName;
public ICommand Command
{
get
{
return (ICommand)this.GetValue(CommandProperty);
}
set
{
this.SetValue(CommandProperty, value);
}
}
public string CommandName
{
get
{
return this.commandName;
}
set
{
if (this.CommandName != value)
{
this.commandName = value;
}
}
}
public object CommandParameter
{
get
{
return this.GetValue(CommandParameterProperty);
}
set
{
this.SetValue(CommandParameterProperty, value);
}
}
public object InvokeParameter
{
get
{
return this.GetValue(InvokeParameterProperty);
}
set
{
this.SetValue(InvokeParameterProperty, value);
}
}
public object Sender { get; set; }
public static readonly
DependencyProperty CommandProperty =
DependencyProperty.Register(nameof(Command),
typeof(ICommand),
typeof(EventCommand),
null);
public static readonly
DependencyProperty CommandParameterProperty =
DependencyProperty.Register(nameof(CommandParameter),
typeof(object),
typeof(EventCommand),
null);
public static readonly
DependencyProperty InvokeParameterProperty =
DependencyProperty.Register(nameof(InvokeParameter),
typeof(object),
typeof(EventCommand),
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);
}
}
}
private ICommand ResolveCommand()
{
ICommand command = null;
if (this.Command != null)
{
return this.Command;
}
var frameworkElement = this.AssociatedObject as FrameworkElement;
if (frameworkElement != null)
{
object dataContext = frameworkElement.DataContext;
if (dataContext != null)
{
PropertyInfo commandPropertyInfo = dataContext
.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(
p =>
typeof(ICommand).IsAssignableFrom(p.PropertyType) &&
string.Equals(p.Name, this.CommandName, StringComparison.Ordinal)
);
if (commandPropertyInfo != null)
{
command = (ICommand)commandPropertyInfo.GetValue(dataContext, null);
}
}
}
return command;
}
}
最大改動是多了兩個依賴屬性 CommandParameter 與 InvokeParameter 來處理事件參數
前一篇例子是只能參數留空 null(此時為事件自身參數)或是自訂綁定值,而這不同的是建立一個 ResolveCommand 方法來解析參數來源,可以指定傳遞 Visual Tree 上元件屬性或參數
這有兩個前臺 XAML 使用範例,使用前依然別忘記引入命名空間
xmlns:root="clr-namespace:MVVM"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
範例1 - 在 Grid 上放下物件時,把事件參數傳遞給觸發命令的方法 (DragEventArgs)
<Grid AllowDrop = "True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<root:EventCommand
CommandName="DropCommand"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=InvokeParameter}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid >
範例2 - 在 ComboBox 上選擇事件時,把事件參數傳遞給觸發命令的方法 (SelectionChangedEventArgs)
<ComboBox>
<ComboBoxItem Content="Foo option 1"/>
<ComboBoxItem Content="Foo option 2"/>
<ComboBoxItem Content="Foo option 3"/>C
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<root:EventCommand
Command="{Binding SubmitFormCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=InvokeParameter}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
雖然這兩個範例都是引用自身事件參數,但有一個不同是範例1 是使用 CommandName 指定處理方法,範例2 使用 Command 指定,要注意使用的語法差異外還不要同時混用
另外要怎麼使用傳遞進去的參數呢?請參考『[WPF] MVVM 軟體架構模式 - 透過 DelegateCommand 綁定方法 (傳遞事件參數)』囉~不負責任結束~XD