电脑技术学习

了解WPF中的路由事件和命令

dn001
; 超越路由命令

由于路由命令存在一定的限制,许多用 WPF 构建复杂 UI 的公司已转为使用自定义 ICommand 实现,这些实现能为它们提供自己的路由机制,特别是与可视树无关联且支持多个命令处理程序的机制。

创建自定义命令实现并不困难。针对类实现 ICommand 界面后,会为挂接命令处理程序提供一种方式,然后可在调用命令时执行路由。您还必须确定使用何种标准确定引发 CanExecuteChanged 事件的时机。

创建自定义命令时最好先使用委托。委托已支持调用目标方法,并支持多个订户。

图 6 显示了名为 StringDelegateCommand 的命令类,它使用委托来允许挂接多个处理程序。它支持向处理程序传递字符串参数,并使用调用程序的 CommandParameter 确定向处理程序传递的消息。

public class StringDelegateCommand : ICommand {; Action<string> m_ExecuteTargets = delegate { }Func<bool> m_CanExecuteTargets = delegate { return false; }bool m_Enabled = falsepublic bool CanExecute(object parameter) {;Delegate[] targets = m_CanExecuteTargets.GetInvocationList();;foreach (Func<bool> target in targets) {;;;m_Enabled = false;;;;bool localenable = target.Invoke();;;;if (localenable) {;;;;;m_Enabled = true break;;;;};};return m_Enabled}; public void Execute(object parameter) {;if (m_Enabled);;;m_ExecuteTargets(parameter != null ? parameter.ToString() : null)}; public event EventHandler CanExecuteChanged = delegate { }...}如 您所见,我选择使用 Func<bool> 委托挂接确定是否启用命令的处理程序。在 CanExecute 实现中,类遍历挂接到 m_CanExecuteTargets 委托的处理程序,查看是否有处理程序想执行的委托。如果有,它为要启用的 StringDelegateCommand 返回 true。调用 Execute 方法时,它仅需检查是否启用了命令,如启用,则调用所有挂接到 m_ExecuteTargets Action<string> 委托的处理程序。

要将处理程序挂接到 CanExecute 和 Execute 方法,StringDelegateCommand 类公开图 7 中所示的事件访问器,从而允许处理程序从基础委托轻松订阅或取消订阅。注意,您还可以在处理程序订阅或取消订阅时使用事件访问器触发 CanExecuteChanged 事件。

public event Action<string> ExecuteTargets {; add {;m_ExecuteTargets += value}; remove {;m_ExecuteTargets -= value}}public event Func<bool> CanExecuteTargets {; add {;m_CanExecuteTargets += value;;CanExecuteChanged(this, EventArgs.Empty)}; remove {;m_CanExecuteTargets -= value;;CanExecuteChanged(this, EventArgs.Empty)}}

路由处理程序示例

在 代码下载的示例应用程序中,我挂接了这个类。该示例有一个简单视图,隐含一个表示器(沿用 MVP,但没有模型)。表示器向视图公开一个表示模型以绑定数据(您可将表示模型想象成位于表示器和视图之间,而 MVP 模型位于表示器之后)。表示模型通常公开视图可以绑定数据的属性。在本例中,它仅公开了一个命令属性,以便可以通过数据绑定在视图的 XAML 中轻松实现挂接。

<Window x:Class="CustomCommandsDemo.SimpleView" ...><Grid>;<Button Command="{Binding CookDinnerCommand}";;;;CommandParameter="Dinner is served!" ...>Cook Dinner</Button>;<Button Click="OnAddHandler" ...>Add Cook Dinner Handler</Button></Grid></Window> Binding 声明只查找当前 DataContext 的属性(名为 CookDinnerCommand),如找到,则将它传给 Icommand。我们在前面提到过 CommandParameter,调用程序可以用它随同命令传递某些数据。在本例中,请注意我只传递了将通过 StringDelegateCommand 传递给处理程序的字符串。

此处所示为视图的代码隐藏(Window类):

public partial class SimpleView : Window {; SimpleViewPresenter m_Presenter = new SimpleViewPresenter()public SimpleView() {;InitializeComponent();;DataContext = m_Presenter.Model}; private void OnAddHandler(object sender, RoutedEventArgs e) {;m_Presenter.AddCommandHandler()}}视图构建其表示器,从表示器取得表示模型,然后将其设置为 DataContext。它还有按钮 Click 的处理程序,该处理程序调入表示器,让它为命令添加处理程序。

图 8 显示了运行中的这一应用程序。第一个窗口处于初始状态,未挂接命令处理程序。由于没有命令处理程序,所以您会看到第一个按钮(调用程序)被禁用。按第二个 按钮时,它会调入表示器并挂接新的命令处理程序。此时会启用第一个按钮,您再单击它时,它会调用其通过数据绑定松散联接的命令处理程序和基础命令的订户列 表。


图8运行中的自定义命令示例(单击图像可查看大图)

表示器代码如图 9 中所示。您可以看到表示器构建了表示模型,并通过 Model 属性将其公开给视图。从视图调用 AddCommandHandler 时(响应第二个按钮 Click 事件),它会向模型的 CanExecuteTargets 和 ExecuteTargets 添加一个订户。这些订阅方法是表示器中的简单方法,它们分别返回 true 并显示 MessageBox。

public class SimpleViewPresenter {; public SimpleViewPresenter() {;Model = new SimpleViewPresentationModel()}; public SimpleViewPresentationModel Model { get; set; }; public void AddCommandHandler() {;Model.CookDinnerCommand.CanExecuteTargets += CanExecuteHandler;;Model.CookDinnerCommand.ExecuteTargets += ExecuteHandler}; bool CanExecuteHandler() {;return true}; void ExecuteHandler(string msg) {;MessageBox.Show(msg)}}本 例显示数据绑定、UI 模式和自定义命令的组合将为您带来清晰独立的命令途径,可以摆脱路由命令的限制。由于命令是通过绑定以 XAML 形式挂接的,您甚至可以通过此方式完全用 XAML 定义视图(没有代码隐藏)、从 XAML 使用绑定命令触发表示模型中的操作、启动您原本需要表示模型代码隐藏执行的操作。

您 需要控制器来构建视图并为其提供表示模型,但您不用代码隐藏就能编写交互视图。如果无需代码隐藏,在代码隐藏文件中添加相互纠结、不可测试的复杂代码的机 率会大大降低,在 UI 应用程序中,这种情况经常出现。此方式刚在 WPF 中试用。但它的确值得考虑,您应该了解更多的示例。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wmjcom/archive/2009/05/22/4208406.aspx