C#WPFMVVM实战-1

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#WPFMVVM实战-1
前⾔
MVVM 就是 Model – View – ViewModel 三组功能(类)分割的设计模式。

废话不多说,不知道的⾃⼰上⽹查。

⽤ MVVM 我认为最⼤好处是能对 ViewModel 做单元测试。

另外,MVVM 分⼯也⽐较明显,⽅便安排程序员分组分⼯进⾏项⽬,基本设计有了之后可以各⾃敲。

这样的话,写出来,类(class)最起码有三个。

⽐如 Window1 作为 View,Window1ViewModel 作为 ViewModel,实际业务类⽐如 Sales Order 销售订单作为 Model。

View 不⼀定要是 System.Control.Window,UserControl 也可以,Page也⾏,总之,是 UI ⽤来显⽰⽤的。

常⽤基类
有两个基类,做 MVVM 你有的话会⽅便⼀些:
1. ViewModelBase
2. RelayCommand / DelegateCommand
我以下代码都不会列这两个出来。

ViewModelBase 代码⽐较常见,搜索然后抄下来就可以了,然后写 ViewModel ⽐如 Window1ViewModel 类时候,继承⾃ViewModelBase 即可。

它的主要作⽤,是提供 OnPropertyChange(string propertyName) 这⽅法,告诉视图 View 知道,值发⽣变化需要更新显⽰。

它的代码,⽐如如下:
/// <summary>
/// Provides common functionality for ViewModel classes
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
public virtual string DisplayName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#region IDisposable Members
public void Dispose()
{
this.OnDispose();
}
protected virtual void OnDispose()
{
}
#endregion
}
个⼈爱好,删掉 DisplayName 或者⾃⼰加其他属性请⾃便,以它为基类做 ViewModel 时候代码会简单⼀些。

RelayCommand / DelegateCommand 代码也是⽐较常见,搜索⼀下抄下来就是。

它是⼀个实现了 ICommand 接⼝的类。

做命令的绑定,⽐如 Button 中的 Command 属性,绑定时它的类型要求是 ICommand 的东西。

ICommand ⽐起点击事件优胜的地⽅是,ICommand 除了委托执⾏⽅法以外,还有⼀个 CanExecute 的委托,可以⾃动 Enable / Disable 按钮。

代码⽐如:
/// <summary>
/// Base Relay Command implements ICommand for easy delegation
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
WPF ⼊门请点这⾥:
要注意的杂项
有⼏点要注意:
1. ICommand ⼀般不传参数,不是不可以,只是⼀般来说没必要。

⼀切值都在 ViewModel 内的时候,你不⽤让 View 再告诉你什么新鲜

2. ⼀般来说⼀个 ViewModel 对⼀个 View
3. WPF 的 PasswordBox 做不了绑定,据说是安全性原因,没办法
4. 不⼀定在 View 的背后代码⼀句都不写才叫做 MVVM,但操作数据的不会在 View 内出现,操作 View 的不会在 ViewModel 出现
⽰范
来个简单的⽰范:
View 部分
1. <Window x:Class="WPF_Binding_Example.MainView"
2. xmlns="/winfx/2006/xaml/presentation"
3. xmlns:x="/winfx/2006/xaml"
4. Title="Window1" Height="300" Width="300">
5.
6. <Window.Resources>
7. <Style x:Key="myStyle" TargetType="StackPanel">
8. <Style.Triggers>
9. <DataTrigger Binding="{Binding Path=Age}"
10. Value="0">
11. <Setter Property="Background"
12. Value="Yellow"/>
13. </DataTrigger>
14. </Style.Triggers>
15. </Style>
16. <DataTemplate x:Key="PersonTemplate">
17. <StackPanel Margin="2"
18. Orientation="Horizontal"
19. Style="{StaticResource myStyle}">
20. <TextBlock Text="{Binding Name}"
21. Width="50"/>
22. <TextBlock Text="{Binding Age}"/>
23. </StackPanel>
24. </DataTemplate>
25. </Window.Resources>
26.
27. <Grid>
28. <ListView Margin="38,50,33,59"
29. Name="listView1"
30. ItemsSource="{Binding PersonList}"
31. ItemTemplate="{StaticResource PersonTemplate}"
32. />
33. <Button Command="{Binding AddRowCommand}"
34. Height="23"
35. Width="75"
36. HorizontalAlignment="Right"
37. Margin="0,0,11,10"
38. VerticalAlignment="Bottom"
39. Content="加⼀⾏" />
40. </Grid>
41. </Window>
画这界⾯的同事,只需要知道三件事:
1. ViewModel 内会有⼀个叫做 PersonList 的公共类集合(30 ⾏),要⽤ ListView 显⽰,按内容做些样式
2. PersonList 类集合内的单个对象,有两个公共属性,分别是 Name 和 Age (20、22⾏),但不需要知道实际是什么类
3. ViewModel 内会有⼀个实现了 ICommand 接⼝的实例引⽤,名字是 AddRowCommand(33⾏)
然后,ViewModel 部分:
1. using System.Collections.ObjectModel;
2. using System.Windows.Input;
3. using WPF_Binding_Example.Domain;
4. using WPF_Binding_Example.Infrastructure;
5.
6. namespace WPF_Binding_Example
7. {
8. public class MainViewModel : ViewModelBase
9. {
10. public MainViewModel()
11. {
12. personList = new ObservableCollection<Person>();
13.
14. //演⽰⽤
15. Add_Dummy_Data();
16. }
17.
18. ///<summary>
19. ///演⽰⽤
20. ///</summary>
21. private void Add_Dummy_Data()
22. {
23. PersonList.Add(
24. new Person { Name = "张三", Age = 26 }
25. );
26. PersonList.Add(
27. new Person { Name = "李四", Age = 24 }
28. );
29. }
30.
31. private ObservableCollection<Person> personList;
32. public ObservableCollection<Person> PersonList
33. {
34. get { return personList; }
35. }
36.
37. private RelayCommand addRowCommand;
38. public ICommand AddRowCommand
39. {
40. get
41. {
42. if (addRowCommand == null)
43. {
44. addRowCommand =
45. new RelayCommand(x => this.AddRow());
46. }
47. return addRowCommand;
48. }
49. }
50.
51. private void AddRow()
52. {
53. this.PersonList.Add(
54. new Person { Name = "我是新⼈", Age = 0 }
55. );
56. }
57. }
58. }
负责这个类的同事,不需要知道界⾯的任何东西,这类的代码也⾮常简单。

ObservableCollection 在 System.Collections.ObjectModel 内,它与其他集合的分别是,集合有变化时,⽐如加减 Item 等,它会发出通知告诉视图。

如果你有⾃⼰很有个性的 Collection,要做绑定的话,让它实现 INotifyCollectionChanged 和 INotifyPropertyChanged 即可。

RelayCommand 上⾯说了,是实现了 ICommand,所以出现了 44-47 ⾏这样的写法。

刚才说了 ICommand 还有个 CanExecute 的委托,这⾥没⽤到。

RelayCommand 的设计是,构造函数参数只有⼀个⽅法委托时候,CanExecute 默认返回 True,即永远可执⾏。

然后是 Model 部分
1. namespace WPF_Binding_Example.Domain
2. {
3. public class Person
4. {
5. private string name;
6. public string Name
7. {
8. get { return name; }
9. set
10. {
11. if (value != name)
12. {
13. name = value;
14. }
15. }
16. }
17.
18. private int age;
19. public int Age
20. {
21. get { return age; }
22. set
23. {
24. if (value != age)
25. {
26. age = value;
27. }
28. }
29. }
30. }
31. }
View 连接 ViewModel
说了半天,除了 ViewModel 和 Model 在上⾯代码有点关系以外,View 不认识 ViewModel,ViewModel 不认识 View,怎样连在⼀起?
这样:
1. using System.Windows;
2.
3. namespace WPF_Binding_Example
4. {
5. ///<summary>
6. /// Interaction logic for App.xaml
7. ///</summary>
8. public partial class App : Application
9. {
10. protected override void OnStartup(StartupEventArgs e)
11. {
12. base.OnStartup(e);
13. MainViewModel vm = new MainViewModel();
14. MainView view = new MainView();
15. view.DataContext = vm;
16. view.Show();
17. }
18. }
19. }
请把 WPF 新建项⽬时,模板⾃带 app.xaml 的 XAML 代码中 StartupUri 属性删除,然后在 App 类重写 OnStartup ⽅法,如上。

把 View 和 ViewModel 连在⼀起的,就⼀句 view.DataContext = vm; 有⼈会⽤ view 构造函数注⼊ ViewModel,也可以反过来在ViewModel 构造函数注⼊ view,然后在 IoC 注册后直接 resolve 出来⽤,或者写在 XAML 内也⾏,但都离不开这⼀句 DataContext = vm。

本例⼦中是在 Application 类重写 OnStartup ⽅法,把两者创建实例,然后 view.Show 来实现。

接下去会介绍怎样单元测试,和各种情景各种数据结构和控件,怎样⽤ MVVM 模式做绑定。

我在这群⾥,欢迎加⼊交流:
.Net 开发交流 595769918
开发板玩家群 578649319
硬件创客 (10105555)。

相关文档
最新文档