
在WPF的MVVM模式中,View和ViewModel之间数据和命令的关联都是通过绑定实现的,绑定后View和ViewModel并不产生直接的依赖。具体就是View中出现数据变化时会尝试修改绑定的目标。同样View执行命令时也会去寻找绑定的Command并执行。反过来,ViewModel在Property发生改变时会发个通知说“名字叫XXX的Property改变了,你们这些View中谁绑定了XXX也要跟着变啊!”,至于有没有View收到是不是做出变化也不关心。ViewModel中的Command脱离View就更简单了,因为Command在执行操作过程中操作数据时,根本不需要操作View中的数据,只需要操作ViewModel中的Property就可以了,Property的变化通过绑定就可以反映到View上。这样在测试Command时也不需要View的参与。这也是我在接触WPF初期时根本理解不了的所谓数据驱动。
这样一来ViewMode可以在完全没有View的情况下测试,View也可以在完全没有ViewModel的情况下测试(当然只是测试界面布局和动画等业务无关的内容)。
1、App中的代码:
|
public App()
{
CalculatorView view = new CalculatorView();
view.DataContext = new CalculatorViewModel();
view.Show();
}
|
2、Model层中CauculatorModel的代码
|
class CauculatorModel
{
public string FirstOperand { get; set; }
public string SecondOperand { get; set; }
public string Operation { get; set; }
public string Result { get; set; }
}
|
3、Command
|
public class DeletgateCommand<T>:ICommand
{
private readonly Action<T> _executeMethod = null;
private readonly Func<T,bool> _canExecuteMethod = null;
public DeletgateCommand(Action<T> executeMethod)
: this(executeMethod, null)
{ }
public DeletgateCommand(Action<T> executeMethod, Func<T,bool> canExecuteMethod)
{
if (executeMethod == null)
throw new ArgumentNullException("executeMetnod");
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#region ICommand 成员
/// <summary>
/// Method to determine if the command can be executed
/// </summary>
public bool CanExecute(T parameter)
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod(parameter);
}
return true;
}
/// <summary>
/// Execution of the command
/// </summary>
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
#endregion
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
#region ICommand 成员
public bool CanExecute(object parameter)
{
if (parameter == null && typeof(T).IsValueType)
{
return (_canExecuteMethod == null);
}
return CanExecute((T)parameter);
}
public void Execute(object parameter)
{
Execute((T)parameter);
}
#endregion
}
|
4、ViewModwl层,为了简化,此处Add方法采用硬编码的形式
|
public class CalculatorViewModel
{
CauculatorModel calculatorModel;
private DeletgateCommand<string> addCommand;
public CalculatorViewModel()
{
calculatorModel = new CauculatorModel();
}
#region Public Properties
public string FirstOperand
{
get { return calculatorModel.FirstOperand; }
set { calculatorModel.FirstOperand = value; }
}
public string SecondOperand
{
get { return calculatorModel.SecondOperand; }
set { calculatorModel.SecondOperand = value; }
}
public string Operation
{
get { return calculatorModel.Operation; }
set { calculatorModel.Operation = value; }
}
public string Result
{
get { return calculatorModel.Result; }
set { calculatorModel.Result = value; }
}
#endregion
public ICommand AddCommand
{
get
{
if (addCommand == null)
{
addCommand = new DeletgateCommand<string>(Add, CanAdd);
}
return addCommand;
}
}
public void Add(string x)
{
FirstOperand = x;
SecondOperand = x;
Result = (double.Parse(FirstOperand) + double.Parse(SecondOperand)).ToString();
Operation = "+";
MessageBox.Show( FirstOperand+ Operation +SecondOperand +"=" + Result);
}
private static bool CanAdd(string num)
{
return true;
}
}
|
ViewModelBase中的代码:
|
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
|
5、View层
|
<Grid>
<TextBox Height="23" Margin="12,63,0,0" Name="textBox1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="120" />
<Label Margin="12,25,95,0" Name="label2" Height="32" VerticalAlignment="Top">请输入x的值! x+x=? </Label>
<Button Height="23" Command="{Binding AddCommand}"
CommandParameter="{Binding ElementName=textBox1,Path=Text}" HorizontalAlignment="Left" Margin="12,102,0,0" Name="button1" VerticalAlignment="Top" Width="75">
确定</Button>
</Grid>
|
CommandParameter里传递的是一个参数,当然可以传递多个参数。