wpf-mvvm开发学习1
最近在做wpf的程序 写到中期越来越恶心 所以尝试重构为MVVM模式
MVVM相关知识
0x01 什么是MVVM
1.Model
Model就是一个class,是对现实中事物的抽象,开发过程中涉及到的事物都可以抽象为Model,例如客户,客户的姓名、编号、电话、住址等属性也对应了class中的Property,客户的下订单、付款等行为对应了class中的方法。
2. View
View很好理解,就是界面。
3. ViewModel
上面说过Model抽象,那么ViewModel就是对View的抽象。显示的数据对应着ViewMode中的Property,执行的命令对应着ViewModel中的Command。
0x02 WPF中MVVM的解耦方式
在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的情况下测试(当然只是测试界面布局和动画等业务无关的内容)。
0x3 MVVM框架需要解决的问题
从图中可以看出如果要实现一套MVVM框架,需要解决的最基本的问题就是数据绑定和命令绑定。此外由于UI中会产生大量的事件,因此还需要将事件绑定到MVVM中的命令上。
实战开始
安装
Microsoft.Toolkit.Mvvm
Microsoft.Xaml.Behaviors.Wpf
PropertyChanged.Fody
构建如图项目结构
一个简单demo
ProcessWindowModel.cs Model层
应该声明所需要的类 数据结构)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Toolkit.Mvvm.ComponentModel;
namespace lltool_MVVM.Model {
internal class ProcessWindowModel {
}
public class myProcess : ObservableObject {
private int _pid;
private string _pidName;
private bool _isChecked;
private int _processSize;
public int pid
{
get { return _pid; }
set { _pid = value; }
}
public string pidName
{
get { return _pidName; }
set { _pidName = value; }
}
public bool isChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
public int processSize
{
get { return _processSize; }
set { _processSize = value; }
}
}
}
ViewModel层
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using lltool_MVVM.Model;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.DependencyInjection;
using Microsoft.Toolkit.Mvvm.Input;
using Microsoft.Toolkit.Mvvm.Messaging;
using Microsoft.Toolkit.Mvvm.Messaging.Messages;
namespace lltool_MVVM.ViewModel {
public class myProcessViewModel : ObservableObject {
public myProcessViewModel() {
//将命令接口与真正的函数绑定
UpdatePidNameCommand = new RelayCommand(UpdatePidNameExecute);
}
private string _pidName;
//提供的SetProperty<T>(ref T, T, string)方法检查属性的当前值,如果不同则更新它,然后还会自动引发相关事件。
//属性名称是通过使用[CallerMemberName]属性自动捕获的,因此无需手动指定要更新的属性。
//暴露出pidName 便于外部进行绑定
public string pidName
{
get { return _pidName; }
set
{
SetProperty(ref _pidName , value);
}
}
void UpdatePidNameExecute() {
this.pidName = "new name";
MessageBox.Show(this.pidName);
}
//一个接口 用于外部的button绑定
public ICommand UpdatePidNameCommand { get; }
}
}
View层
<metro:MetroWindow
x:Class="lltool_MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Aduskin="clr-namespace:AduSkin;assembly=AduSkin"
xmlns:ViewModel="using:lltool_MVVM.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:lltool_MVVM"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:metro="clr-namespace:AduSkin.Controls.Metro;assembly=AduSkin"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<TextBlock Margin="20" Text="{Binding pidName}" />
<Button Width="100" Height="100" Command="{Binding UpdatePidNameCommand}" Content="点我" />
</Grid>
</metro:MetroWindow>
MainWindow.xaml.cs
using AduSkin.Controls.Metro;
using lltool_MVVM.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace lltool_MVVM {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : MetroWindow {
public MainWindow() {
InitializeComponent();
//绑定数据上下文
DataContext = new myProcessViewModel
{
pidName = "2333"
};
}
}
}
结果