WPF MvvmToolkit入门

最新.net6 wpf MVVMToolkit 8.0 工程搭建。

MVVMToolkit是一个轻量级MVVM框架,在框架下我们第一个要做的就是搞清在此框架下的一些常规操作:属性绑定和通知,命令绑定,消息传递。搞懂这些处理流程,然后就可以写自己业务的逻辑。

1.安装mvvmtoolkit

1.1Nuget下载CommunityToolkit.Mvvm

 1.2调整目录结构

添加3个文件夹:Views Models ViewModels。

 Views下放界面的xmal文件

Models下放数据模型

ViewModels下放逻辑代码

并把MainWindow.xaml移动到Views文件夹下,需要修改3个地方:

App.xaml.cs:

 Mainwindow.xaml:

 MainWindow.xaml.cs

创建一个类处理逻辑,命名MainViewModel,放在ViewModels下

创建一个类描述数据模型,命名CalculateModel,放在Models下

 UI界面设计:

 CalculateModel类:

界面上的textbox分别绑定CalculateModel中的a b c

 public class CalculateModel
    {
        //计算a+b=c
        public int a { get; set; }
        public int b { get; set; }
        public int c { get; set; }

    }
}

布局代码:

<Grid>
        <StackPanel >
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="   a" Width="50"/>
                <TextBox Width="100" Text="{Binding  calculateModel.a}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="   b" Width="50"/>
                <TextBox Width="100" Text="{Binding calculateModel.b}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="   c" Width="50"/>
                <TextBox Width="100" Text="{Binding calculateModel.c}"/>
            </StackPanel>
             
            <Button Content="计算" Height="30" Width="50" HorizontalAlignment="Left" />
        </StackPanel>
    </Grid>

MainViewModel代码:

public class MainViewModel
    {
    //在MianViewModel中写处理逻辑,这个类 类似于在不分mvvm模式下的后台的代码
public CalculateModel? calculateModel { get; set; } public MainViewModel() { calculateModel = new CalculateModel(); calculateModel.a = 1; calculateModel.b = 1; calculateModel.c = calculateModel.a + calculateModel.b; } }

在MainWindow.xaml.cs中添加DataContext

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainViewModel();
        }
    }

运行结果:可以看到能绑定成功了,然后就是介绍 属性绑定通知,命令绑定,消息传递的功能了。

 需要注意的:

1.一个View对应一个ViewModel,usercontrol也算一种view

2.新建的MainViewModel是一个独立的类,和界面没有关联,为了能够让UI界面找到MainViewModel类中的属性,则需要设置DataContext

this.DataContext = new MainViewModel();这里的this就是指MainWindow。

2.属性的绑定和通知

属性想要实现通知功能,属性所在的类需要继承ObservableObject,并在属性的set访问器中添加SetProperty

把上面的CalculateModel修改为可通知的:

public class CalculateModel: ObservableObject
    {
        //计算a+b=c
        private int _a;
        private int _b;
        private int _c;
                
        public int a
        {
            get { return _a; }
            set {
                SetProperty(ref _a, value);
            }
        }
        public int b
        {
            get { return _b; }
            set
            {
                SetProperty(ref _b, value);
            }
        }
        public int c
        {
            get { return _c; }
            set
            {
                SetProperty(ref _c, value);
            }
        }
    }

通过调用add方法,修改a b c的值,界面也会自动通知

public void add()
        {
            calculateModel.a = 11;
            calculateModel.b = 11;
            calculateModel.c = calculateModel.a + calculateModel.b;

        }

默认:

 调用add方法后:

 可以看到实现了数据通知的功能,命令的使用方法在后面。

几个需要注意的地方:

1.可通知的属性必须是属性,不能是字段,由get set访问器的是属性,并且访问权限是public。

     //定义属性
        public int MyProperty { get; set; }
        //定义字段
        public int MyProperty1;

2.被绑定的属性在哪个类中,该类就得继承ObservableObject

这个例子中a b c这3个属性在CalculateModel类中,所以是CalculateModel类继承ObservableObject

如果a b c这3个属性直接定义在MainViewModel中,则MainViewModel类需要继承ObservableObject

一个datacontext的问题:

目前这个例子的逻辑是这样:

 假如现在MainViewModel中也有abc3个属性了,UI界面想绑定MainViewModel中的abc,而不是去绑定CalculateModel中的abc,则需要这样绑定

 <TextBox Width="100" Text="{Binding b}"/>

3.命令的绑定

不使用mvvm模式时,都是直接处理各种事件,Click DoubleClick等等,在使用mvvm模式下,使用Command代替事件处理。

第2部分介绍了属性的绑定。命令的绑定也和属性的绑定一样简单,把命令当成属性来处理就行了,属性和命令都属于某个类。结合属性的绑定,命令也需要是public的,必须带get set访问器。

看一个简单的例子:

 <Button Content="计算" Command="{Binding addCommand}" Height="30" Width="50" HorizontalAlignment="Left" />

当点击Button时执行 Command,Command绑定了一个addCommand(这个是我们自己写的),

定义命令:

public class MainViewModel
    {
        public CalculateModel? calculateModel { get; set; }


        public MainViewModel()
        {
            calculateModel = new CalculateModel();
            calculateModel.a = 1;
            calculateModel.b = 1;
            calculateModel.c = calculateModel.a + calculateModel.b;
       //第三步:给命令赋值一个方法,当命令被触发时会自动调用赋值的方法
            addCommand = new RelayCommand(add);
        }
    //第一步:定义一个命令
public RelayCommand addCommand { get; set; }
    //顶二步:定义一个普通的方法
public void add() { calculateModel.a = 11; calculateModel.b = 11; calculateModel.c = calculateModel.a + calculateModel.b; } }

总结就3点:定义一个方法,定义一个命令,把方法和命令关联起来。

当点击button时会调用add方法。

疑惑的点:命令要定义在哪里?

解释:可以定义在Model中,也可以定义在ViewModel中,都可以。

4.MvvmToolkit框架下任意命令的绑定

只有Button由Command,其他控件没有怎么处理?

参考这篇文章,多种方法

WPF 任意事件绑定 - 薛定谔的小灯泡 - 博客园 (cnblogs.com)

5.消息传递

在mvvm模式下,项目的组成可能是这样的:

 

 一般是这样:

一个View对应一个ViewModel,操作View的数据的逻辑,写在对应的ViewModle中,操作View界面的和业务无关的逻辑写在View.xaml.cs中

ViewModel中定义Model,然后处理

View和ViewModel之间可以互相传递消息。在mvvmtoolkit框架下,提供了传递消息的机制

5.1 WeakReferenceMessenger基本理解

既然是传递消息,就会有2方:收消息的一方和发消息的一方

收消息:WeakReferenceMessenger.Default.Register

发消息:WeakReferenceMessenger.Default.Send

a给b发送消息,b必须提前注册好这个消息,也就是说b是提前知道a会给它发消息的。

假如上面的例子实现这样一个功能,

点击“计算”按钮后,计算出a+b的结果,并使用MessageBox弹出这个结果。

其中的逻辑:“计算”按钮的点击绑定了ViewModel中的命令,在ViewModel中计算结果,弹窗需要在View.xmal.cs中处理,所以在ViewModel中给View.xaml.cs发送结果,在View.xmal.cs中接收到消息后,弹出结果

ViewModel代码:

发消息:

 MainWindow.xaml.cs代码:

收消息:

 运行结果:

这里传递的消息类型必须是引用,int double之类的非引用类型无法传递

发消息和收消息的2方靠什么识别是否接收呢?

如果没有写Token,则以消息的类型判断,双方消息的类型一致则能成功发送消息

如果写了Token,则判断Token和消息类型

Token的写法:

 

6.MvvmToolkit框架下IOC的处理

mvvmtoolkit框架没有提供IOC容器,需要额外处理,这里使用微软的 SeverCollection (ServiceProvider)仿照mvvmlight的Locator写一个,引用Nuget包microsoft.extensions.dependencyinjection

第1步:添加microsoft.extensions.dependencyinjection,在ViewModels文件夹下创建一个Locator类

 

Locator代码:

public class Locator
    {
        public static IServiceProvider? ServiceProvide { get; set; }
        public Locator()
        {
            ServiceProvide = GetService();
        }

        private IServiceProvider GetService()
        {
            var service = new ServiceCollection();
            service.AddSingleton<MainViewModel>();
            return service.BuildServiceProvider();
        }
        public MainViewModel MainViewModel
        {
            get
            {
                return ServiceProvide.GetService<MainViewModel>();
            }
        }
    }

第2步:修改dataContext

 第3步:修改UI界面绑定时的层级关系

修改前:

 修改后:

 

posted @ 2022-08-24 22:19  薛定谔的小灯泡  阅读(7437)  评论(5编辑  收藏  举报