MVVM模式下实现事件到命令的绑定

在MVVM模式中,通常将值属性和命令声明在ViewModel中,并通过DataBinding在视图上使用。这样可以实现"数据驱动UI"的功能。Metro风格的应用程序虽然使用XAML和WinRT组件,但仍然可以使用MVVM作为其架构。Metro支持DataBinding,可以让"数据"驱动"Metro UI"。

虽然在ViewModel中实现属性和命令相对容易,但将UI控件的事件分配给命令却不太容易。在WPF中,可以使用Blend SDK的行为来将事件分配给命令,或者设计一个AttachedCommand,如下所示: 。 但是,检查Metro的MVVM Light Toolkit,它不能提供EventToCommand行为,Blend 5 Developer Preview版本也不提供该行为。因此,需要为Metro设计一个AttachedCommand。

在AttachedCommand类中,应该声明两个附加属性:Command和RoutedEvent属性。 public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached( "Command", typeof(Object), typeof(AttachedCommand).FullName, new PropertyMetadata(DependencyProperty.UnsetValue)); public static ICommand GetCommand(DependencyObject d) { return (ICommand)d.GetValue(CommandProperty); } public static void SetCommand(DependencyObject d, ICommand value) { d.SetValue(CommandProperty, value); } public static readonly DependencyProperty RoutedEventProperty = DependencyProperty.RegisterAttached( "RoutedEvent", typeof(String), typeof(AttachedCommand).FullName, new PropertyMetadata(String.Empty, new PropertyChangedCallback(OnRoutedEventChanged))); public static String GetRoutedEvent(DependencyObject d) { return (String)d.GetValue(RoutedEventProperty); } public static void SetRoutedEvent(DependencyObject d, String value) { d.SetValue(RoutedEventProperty, value); } 应该通过类型的字符串在Metro中注册附加属性(类似于Silverlight解决方案),对于接口,应该使用"Object"而不是接口。不确定是否会在下一个Windows 8版本中解决,但是对于接口,它会引发NullReferenceException。

下面是RoutedEvent属性的属性更改回调: private static void OnRoutedEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { String routedEvent = (String)e.NewValue; if (!String.IsNullOrEmpty(routedEvent)) { EventHooker eventHooker = new EventHooker(); eventHooker.AttachedCommandObject = d; EventInfo eventInfo = GetEventInfo(d.GetType(), routedEvent); if (eventInfo != null) { eventInfo.AddEventHandler(d, eventHooker.GetEventHandler(eventInfo)); } } } 需要通过反射Metro的DependencyObject来获取EventInfo。但在Metro中,反射只能列出当前类型直接声明的成员。它不能列出从基类型继承的所有成员。因此,应该使用一种方法来从其基类型中搜索事件成员: private static EventInfo GetEventInfo(Type type, string eventName) { EventInfo eventInfo = null; eventInfo = type.GetTypeInfo().GetDeclaredEvent(eventName); if (eventInfo == null) { Type baseType = type.GetTypeInfo().BaseType; if (baseType != null) return GetEventInfo(type.GetTypeInfo().BaseType, eventName); else return eventInfo; } return eventInfo; }

当控件上触发特定事件时,应该返回事件处理程序。因此,有一个EventHooker可以返回一个"OnEventRaised"方法,并在其中执行命令: internal sealed class EventHooker { public DependencyObject AttachedCommandObject { get; set; } public Delegate GetEventHandler(EventInfo eventInfo) { Delegate del = null; if (eventInfo == null) throw new ArgumentNullException("eventInfo"); if (eventInfo.EventHandlerType == null) throw new ArgumentNullException("eventInfo.EventHandlerType"); if (del == null) del = this.GetType().GetTypeInfo().GetDeclaredMethod("OnEventRaised").CreateDelegate(eventInfo.EventHandlerType, this); return del; } private void OnEventRaised(object sender, object e) { ICommand command = (ICommand)(sender as DependencyObject).GetValue(AttachedCommand.CommandProperty); if (command != null) command.Execute(null); } }

如何使用:应该为Metro添加一个DelegateCommand或RelayCommand(ICommand),这可以帮助在ViewModel中返回ICommand。可以将这个ICommand属性绑定到AttachedCommand.Command属性上。下面是一个适用于Metro的DelegateCommand。MVVM Light Toolkit也提供了适用于Metro的RelayCommand。 public class DelegateCommand : ICommand { private readonly Predicate _canExecute; private readonly Action _execute; public event Windows.UI.Xaml.EventHandler CanExecuteChanged; public DelegateCommand(Action execute) : this(execute, null) { } public DelegateCommand(Action execute, Predicate canExecute) { _execute = execute; _canExecute = canExecute; } public bool CanExecute(object parameter) { if (_canExecute == null) return true; return _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, null); } } } 请注意,WinRT EventHandler与System.EventHandler不同。Windows.UI.Xaml.EventHandler有两个object参数,第二个参数不是EventArgs。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485