WPF MVVM从入门到精通3:数据绑定express
WPF MVVM从入门到精通5:PasswordBox的绑定ide
WPF MVVM从入门到精通6:RadioButton等一对多控件的绑定this
这一部分咱们要作的事情,是把点击登陆按钮的事件也在ViewModel里实现。若不是用MVVM模式,可能XAML文件里是这样的:xml
<Button Grid.Row="3" Grid.ColumnSpan="2" Content="登陆" Width="200" Height="30" Click="Button_Click"/>
而跟XAML文件相关的CS文件里则是这样的:
private void Button_Click(object sender, RoutedEventArgs e) { //业务处理逻辑代码 }
如此一来,前端和后端的代码又耦合在一块儿了。其实,命令和事件都是能够绑定的,就像数据同样。
咱们先来了解一下命令。ICommand是全部命令的接口,它主要完成两件事情,这个命令可否被执行,以及执行命令。
event EventHandler CanExecuteChanged; public bool CanExecute(object parameter); public void Execute(object parameter);
例如当用户名为空时,咱们可能会禁用按钮。当登陆按钮跟一个命令绑定在一块儿时,CanExecute会不断被执行,若是返回false,按钮的IsEnabled属性也会被置为false。
通常状况下,咱们须要继承ICommand接口来进行开发。
using System; using System.Windows.Input; namespace LoginDemo.ViewModel.Common { /// <summary> /// 命令基类 /// </summary> public class BaseCommand : ICommand { public event EventHandler CanExecuteChanged { add { if (_canExecute != null) { CommandManager.RequerySuggested += value; } } remove { if (_canExecute != null) { CommandManager.RequerySuggested -= value; } } } public bool CanExecute(object parameter) { if (_canExecute == null) { return true; } return _canExecute(parameter); } public void Execute(object parameter) { if (_execute != null && CanExecute(parameter)) { _execute(parameter); } } private Func<object, bool> _canExecute; private Action<object> _execute; public BaseCommand(Action<object> execute, Func<object, bool> canExecute) { _execute = execute; _canExecute = canExecute; } public BaseCommand(Action<object> execute) : this(execute, null) { } } }
BaseCommand的功能很简单,就是执行命令前先判断一下命令能不能执行。
而后咱们就能够绑定命令了,在后端这样写:
private BaseCommand clickLoginCommand; public BaseCommand ClickLoginCommand { get { if(clickLoginCommand==null) { clickLoginCommand = new BaseCommand(new Action<object>(o => { //执行登陆逻辑 })); } return clickLoginCommand; } }
前端这样写:
<Button Grid.Row="3" Grid.ColumnSpan="2" Content="登陆" Width="200" Height="30" Command="{Binding ClickLoginCommand}"/>
点击按钮执行登陆逻辑的代码就这样完成了。但不要急着复制代码,由于咱们不打算使用命令。
咱们知道,对于按钮的操做,不必定是点击,多是鼠标划过,多是鼠标右击。那Command触发的是什么呢?就是点击,没有其余了。对于其余控件,例如是输入框,Command又表明什么呢?文本改变事件能用Command吗?这些问题让咱们感到困惑,因此通常在项目中,我都只会使用事件,而不会使用命令(即便是单击事件)。
BaseCommand这个类还能够留着,咱们后面还须要使用的。在引入事件以前,咱们须要先引用一个dll:System.Windows.Interactivity.dll。这个dll并非.NET Framework的标配,它是Blend的一个类库。能够在扩展的程序集里找到:
若是没有找到(我安装VS2017后就没有找到),须要安装如下库才有:
好了,引用了System.Windows.Interactivity.dll后,咱们就能够开始讲事件了。
有些事件是有参数的,例如鼠标移动这个事件,会带上鼠标的位置。但咱们以前使用的命令,默认传入的参数是null。为了可以传递参数,咱们须要先定义一个事件基类:
using System.Windows; using System.Windows.Input; using System.Windows.Interactivity; namespace LoginDemo.ViewModel.Common { /// <summary> /// 事件命令 /// </summary> public class EventCommand : TriggerAction<DependencyObject> { protected override void Invoke(object parameter) { if (CommandParameter != null) { parameter = CommandParameter; } if (Command != null) { Command.Execute(parameter); } } /// <summary> /// 事件 /// </summary> public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null)); /// <summary> /// 事件参数,若是为空,将自动传入事件的真实参数 /// </summary> public object CommandParameter { get { return (object)GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(null)); } }
如今,咱们能够在ViewModel里增长以下代码:
private BaseCommand loginClick; /// <summary> /// 登陆事件 /// </summary> public BaseCommand LoginClick { get { if(loginClick==null) { loginClick = new BaseCommand(new Action<object>(o => { //执行登陆逻辑 })); } return loginClick; } }
而后在XAML文件里,先加入i这个命名空间:xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity",而后修改按钮的代码:
<Button Grid.Row="3" Grid.ColumnSpan="2" Content="登陆" Width="200" Height="30"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <c:EventCommand Command="{Binding LoginClick}"/> </i:EventTrigger> </i:Interaction.Triggers> </Button>
上面的代码指出,Click这个事件,绑定到了LoginClick这个属性。当咱们点击按钮的时候,LoginClick里面的Action就会被执行。