如何利用 MVVM Light EventToCommand 在 XAML 中绑定很多相同的事件?
How to take advantage of MVVM Light EventToCommand to bind a lot of same events in XAML?
让我解释一下我的要求:
我有一个包含多个复选框的网格,如下所示:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox>1</CheckBox>
<CheckBox
Grid.Column="1">2</CheckBox>
</Grid>
我清楚地知道如何使用 mvvm light EventToCommand 绑定到命令(在我的 VM 或其他任何地方定义)。
但是,众所周知,我有不止 1 个复选框。所以我必须这样做:
<CheckBox
Content="1">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="Checked">
<ml:EventToCommand
Command="{Binding SomeCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
<CheckBox
Grid.Column="1"
Content="2">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="Checked">
<ml:EventToCommand
Command="{Binding SomeCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
如果我有更多复选框,我必须一次又一次地 C+V 以使每个复选框具有相同的 EventToCommand 行为。
更烦人的是,我们还有另一个名为 Unchecked 的事件。通常 Checked 和 Uncheck 的回调是完全一样的。所以我不得不一次又一次的C+V.......
最后我的 XAML 又长又复杂!
=========================================== =
我正在考虑这样的解决方法:
我查看了Checked和Unchecked事件的源代码,发现它们都是RoutedEvent,模式为Bubble,这意味着这个路由事件调用冒泡到包含它们的网格。
所以我这样写 XAML 代码:
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger
EventName="ToggleButton.Checked">
<ml:EventToCommand
Command="{Binding TestCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
......several check boxes......
</Grid>
但是上面的代码是行不通的。 Checked 和 Unchecked 都不是 ATTACHED 事件。
然后我删除了"ToggleButton.",像这样:
<i:EventTrigger
EventName="Checked">
<ml:EventToCommand
Command="{Binding TestCommand}" />
</i:EventTrigger>
还是不行! :(
事件应该在网格上冒泡,不是吗?
所以,有人对此有什么好的想法吗?
谢谢!
您可以为 Grid
中的所有 CheckBoxes
定义隐式 Style
:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="CheckBox">
<Setter Property="Command" Value="{Binding SomeCommand}" />
</Style>
</Grid.Resources>
<CheckBox>1</CheckBox>
<CheckBox Grid.Column="1">2</CheckBox>
</Grid>
而不是处理 Checked
和 Unchecked
事件,您可以将每个 CheckBox
的 IsChecked
属性 绑定到源 属性:
<CheckBox IsChecked="{Binding IsFirstChecked}">1</CheckBox>
感谢您的建议。这真的很方便。
我还在这个网站上找到了另一个解决方案 post:
link
上面post中提到了另一种解决方案:
using System;
using System.Windows;
using System.Windows.Interactivity;
namespace NaiveMepUi.GuiCommon
{
public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
public RoutedEventTrigger() { }
public RoutedEvent RoutedEvent { get; set; }
protected override void OnAttached()
{
Behavior behavior = base.AssociatedObject as Behavior;
FrameworkElement associatedElement =
base.AssociatedObject as FrameworkElement;
if (behavior != null)
{
associatedElement =
((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
}
if (associatedElement == null)
{
throw new ArgumentException
("RoutedEventTrigger.AssociatedObject is not FrameworkElement.");
}
if (RoutedEvent != null)
{
associatedElement.AddHandler(
RoutedEvent,
new RoutedEventHandler(this.OnRoutedEvent));
}
}
/// <summary>
/// 路由事件被引发,继而引发基类的事件回调;
/// 但是这一步会丢弃<paramref name="sender"/>。
/// 如果事件绑定到命令,则命令只能接收到<paramref name="args"/>;
/// 作为在 ViewModel 中定义的命令,原则上不应该 View 中定义的元素。
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void OnRoutedEvent(object sender, RoutedEventArgs args)
{
base.OnEvent(args);
}
protected override string GetEventName()
{
return RoutedEvent.Name;
}
}
}
然后我可以像这样在 XAML 中使用它:
<Grid>
<i:Interaction.Triggers>
<guicommon:RoutedEventTrigger
RoutedEvent="CheckBox.Checked">
<ml:EventToCommand
Command="{Binding }" />
<!--todo 测试一下这种设计模式是否可行-->
</guicommon:RoutedEventTrigger>
</i:Interaction.Triggers>
</Grid>
让我解释一下我的要求:
我有一个包含多个复选框的网格,如下所示:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox>1</CheckBox>
<CheckBox
Grid.Column="1">2</CheckBox>
</Grid>
我清楚地知道如何使用 mvvm light EventToCommand 绑定到命令(在我的 VM 或其他任何地方定义)。 但是,众所周知,我有不止 1 个复选框。所以我必须这样做:
<CheckBox
Content="1">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="Checked">
<ml:EventToCommand
Command="{Binding SomeCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
<CheckBox
Grid.Column="1"
Content="2">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="Checked">
<ml:EventToCommand
Command="{Binding SomeCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
如果我有更多复选框,我必须一次又一次地 C+V 以使每个复选框具有相同的 EventToCommand 行为。 更烦人的是,我们还有另一个名为 Unchecked 的事件。通常 Checked 和 Uncheck 的回调是完全一样的。所以我不得不一次又一次的C+V.......
最后我的 XAML 又长又复杂!
=========================================== =
我正在考虑这样的解决方法:
我查看了Checked和Unchecked事件的源代码,发现它们都是RoutedEvent,模式为Bubble,这意味着这个路由事件调用冒泡到包含它们的网格。 所以我这样写 XAML 代码:
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger
EventName="ToggleButton.Checked">
<ml:EventToCommand
Command="{Binding TestCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
......several check boxes......
</Grid>
但是上面的代码是行不通的。 Checked 和 Unchecked 都不是 ATTACHED 事件。
然后我删除了"ToggleButton.",像这样:
<i:EventTrigger
EventName="Checked">
<ml:EventToCommand
Command="{Binding TestCommand}" />
</i:EventTrigger>
还是不行! :(
事件应该在网格上冒泡,不是吗?
所以,有人对此有什么好的想法吗? 谢谢!
您可以为 Grid
中的所有 CheckBoxes
定义隐式 Style
:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="CheckBox">
<Setter Property="Command" Value="{Binding SomeCommand}" />
</Style>
</Grid.Resources>
<CheckBox>1</CheckBox>
<CheckBox Grid.Column="1">2</CheckBox>
</Grid>
而不是处理 Checked
和 Unchecked
事件,您可以将每个 CheckBox
的 IsChecked
属性 绑定到源 属性:
<CheckBox IsChecked="{Binding IsFirstChecked}">1</CheckBox>
感谢您的建议。这真的很方便。 我还在这个网站上找到了另一个解决方案 post: link 上面post中提到了另一种解决方案:
using System;
using System.Windows;
using System.Windows.Interactivity;
namespace NaiveMepUi.GuiCommon
{
public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
public RoutedEventTrigger() { }
public RoutedEvent RoutedEvent { get; set; }
protected override void OnAttached()
{
Behavior behavior = base.AssociatedObject as Behavior;
FrameworkElement associatedElement =
base.AssociatedObject as FrameworkElement;
if (behavior != null)
{
associatedElement =
((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
}
if (associatedElement == null)
{
throw new ArgumentException
("RoutedEventTrigger.AssociatedObject is not FrameworkElement.");
}
if (RoutedEvent != null)
{
associatedElement.AddHandler(
RoutedEvent,
new RoutedEventHandler(this.OnRoutedEvent));
}
}
/// <summary>
/// 路由事件被引发,继而引发基类的事件回调;
/// 但是这一步会丢弃<paramref name="sender"/>。
/// 如果事件绑定到命令,则命令只能接收到<paramref name="args"/>;
/// 作为在 ViewModel 中定义的命令,原则上不应该 View 中定义的元素。
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void OnRoutedEvent(object sender, RoutedEventArgs args)
{
base.OnEvent(args);
}
protected override string GetEventName()
{
return RoutedEvent.Name;
}
}
}
然后我可以像这样在 XAML 中使用它:
<Grid>
<i:Interaction.Triggers>
<guicommon:RoutedEventTrigger
RoutedEvent="CheckBox.Checked">
<ml:EventToCommand
Command="{Binding }" />
<!--todo 测试一下这种设计模式是否可行-->
</guicommon:RoutedEventTrigger>
</i:Interaction.Triggers>
</Grid>