WPF MVVM系统入门-上

news2024/12/28 23:17:14

WPF MVVM系统入门-上

Models:存放数据的模型,实体对象

Views:可视化界面

ViewModels:业务逻辑。ViewModels与Models的联系会更紧密,而Views页面会主动绑定ViewModels中的数据,原则上ViewModels不要直接去操作Views,被动的被Views来获取数据即可。

一般遵循MVVM模式的项目下,都会有Models、Views、ViewModels三个文件夹来存放不同的代码工程。

案例入门

实现一个加法计算器,输入两个值,进行相加,并返回结果。

Model

一共需要三个double类型,两个输入,一个输出。

public class MainModel
{
    public double Value1 { get; set; }
    public double Value2 { get; set; }
    public double Value3 { get; set; }
}

ViewModel

编写相加的逻辑代码

public class MainViewModel
{
    //声明一个Model类型的属性
    public MainModel mainModel { get; set; } = new MainModel();
	//业务逻辑
    public void Add()
    {
        mainModel.Value3 = mainModel.Value2 + mainModel.Value1;
    }
    public MainViewModel()
    {
        //业务逻辑的调用
        //为了便于观察,延迟3s后调用方法
        Task.Factory.StartNew(() =>
        {
            Task.Delay(3000).Wait();
            Add();
        });
    }
}

View

view中,利用一个Textbox和一个slider作为输入分别绑定Value1和value2,使用另一个TextBox绑定value3

<Window.DataContext>
    <vm:MainViewModel />
</Window.DataContext>
<Grid>
    <StackPanel>
        <TextBox Text="{Binding mainModel.Value1, UpdateSourceTrigger=PropertyChanged}" />
        <Slider
            Maximum="100"
            Minimum="0"
            Value="{Binding mainModel.Value2}" />
        <TextBox Text="{Binding mainModel.Value3}" />
    </StackPanel>
</Grid>

这样等待3秒,下面的TextBox中的数值一直没有更新

img

造成这样的原因是,Value3虽然更新了,但是并没有通知绑定他的控件,所以Value3作为输出,需要在更新时触发一个事件,让该事件的订阅者(也就是绑定该值得控件)进行响应。

改进Model

public class MainModel:INotifyPropertyChanged
{
    public double Value1 { get; set; }
    public double Value2 { get; set; }

    private double _value3;

    public double Value3
    {
        get { return _value3; }
        set
        {
            _value3 = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value3"));
        }
    }
    public event PropertyChangedEventHandler? PropertyChanged;
}

img

在本案例中,只有三个属性,但是属性很多,难道都需要这样触发事件吗?有没有简单的方式?

PropertyChanged.Fody插件

PropertyChanged.Fody插件可让用户简化事件的通知,Nuget安装PropertyChanged.Fody

[AddINotifyPropertyChangedInterface]
public class MainModel
{
    public double Value1 { get; set; }
    public double Value2 { get; set; }
    public double Value3 { get; set; }
}

只需要这样就可以完成上面的工作,它的原理是在编译的时候,把所有的属性都会使用PropertyChanged.Invoke来触发事件。但有时有些属性不需要进行触发,比如本案例的Value1和Value2,所以可以这样

[AddINotifyPropertyChangedInterface]
public class MainModel
{
    [DoNotNotify]
    public double Value1 { get; set; }
    [DoNotNotify]
    public double Value2 { get; set; }
    public double Value3 { get; set; }
}
//其他的特性
/// AlsoNotifyFoAttribute     实现通知的时候,同时通知其属性
/// DoNotNotify 指定不需要通知相关的代码
/// DependsOn 指定哪些属性变化的时候,通知当前属性变化
/// DoNotCheckEquality    强制不做旧值比对(默认情况会自动添加比对代码)

命令Command

在上面案例中是使用了构造函数调用了Add方法,但是如果我想增加一个按钮,在点击的时候才执行Add方法要怎么办,当然可以绑定按钮的Click事件,但是这样的话Click事件要放置在View的后台类中,不能很好的利用绑定与ViewModel建立联系,所以引出了命令Command

命令的用途

  1. 将调用命令的对象与执行命令的逻辑分开,这允许多个源调用相同的命令逻辑
  2. 可以指示命令是否可用,如登陆时,用户名为空则登陆按钮不可用

命令要实现ICommand接口,该接口中包含

  • CanExecute:是否可执行方法
  • Execute:主要执行逻辑
  • CanExecuteChanged:触发检查是否可执行

案例入门

  1. 定义一个类实现ICommand接口
public class CommandBase : ICommand
{
    public event EventHandler? CanExecuteChanged;

    public bool CanExecute(object? parameter)
    {
        return true;//返回true表示命令可用
    }

    //这样做可以在外部给DoExecute委托赋值,根据不同的逻辑业务赋予不同的值
    public Action<object> DoExecute { get; set; }
    public void Execute(object? parameter)
    {
        DoExecute?.Invoke(parameter);
    }
}
  1. 在ViewModel中定义一个CommandBase属性
public class MainViewModel
{
    public MainModel mainModel { get; set; } = new MainModel();

    public void Add(object obj)
    {
        mainModel.Value3 = mainModel.Value2 + mainModel.Value1;
    }

    public CommandBase BtnCommand { get; set; }//命令
    public MainViewModel()
    {
        BtnCommand = new CommandBase() {DoExecute   = new Action<object>(Add) };
    }
}
  1. 页面增加一个Button按键并进行绑定<Button Command="{Binding BtnCommand}" Content="Ok" />

img

检测是否可执行

上面的CommandBase中,直接将CanExecute返回为true,其实可以利用这个方法来实现检测是当前按钮是否可以使用

将上面的CommandBase改为

public Func<object,bool> DoCanExecute { get; set; }
public bool CanExecute(object? parameter)
{
    return DoCanExecute?.Invoke(parameter) == true;
}

更改ViewModel

public class MainViewModel
{
    public MainModel mainModel { get; set; } = new MainModel();

    public void Add(object obj)
    {
        mainModel.Value3 = mainModel.Value2 + mainModel.Value1;
    }

    public bool CanCal(object obj)
    {
        return mainModel.Value1 != 0;
    }

    public CommandBase BtnCommand { get; set; }//命令
    public MainViewModel()
    {
        BtnCommand = new CommandBase() {
            DoExecute = new Action<object>(Add),
            DoCanExecute = new Func<object, bool>(CanCal) };
    }
}

运行后可以看出OK按钮为不可用状态,但是将上面的文本框改为非零状态,仍然是不可用,这是因为在更改value1后,并没有触发检测事件CanExecuteChanged也就是说此时并没有再次执行CanExecute方法。所以需要触发CanExecuteChanged事件,框架会自动执行CanExecute方法。

img

因为事件必须在本类中进行触发,所以在CommandBase中增加DoCanExecuteChanged方法并触发CanExecuteChanged事件。

public void DoCanExecuteChanged()
{
    CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

因为需要在Value1赋值的时候,触发 CanExecuteChanged事件,所以需要将Model中定义一个CommandBase。并将ViewModel中的CommandBase删除。

public class MainModel:INotifyPropertyChanged
{
    private double _value1;

    public double Value1
    {
        get { return _value1; }
        set {
            _value1 = value;
            BtnCommand.DoCanExecuteChanged();//在更改时触发CanExecuteChanged事件,该事件触发后会执行CanExecute方法
        }
    }

    public double Value2 { get; set; }
    private double _value3;
    public double Value3
    {
        get { return _value3; }
        set
        {
            _value3 = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value3"));
        }
    }
    public event PropertyChangedEventHandler? PropertyChanged;

    public CommandBase BtnCommand { get; set; }//命令

    public MainModel()
    {
        BtnCommand = new CommandBase()
        {
            DoExecute = new Action<object>(Add),
            DoCanExecute = new Func<object, bool>(CanCal)
        };
    }

    public void Add(object obj)
    {
        Value3 =Value2 + Value1;
    }

    public bool CanCal(object obj)
    {
        return Value1 != 0;
    }
}

View中的Button更换command绑定<Button Command="{Binding mainModel.BtnCommand}" Content="Ok" />

img

但是这样的话就必须将Command定义在Model中。那Command如何定义在ViewModel中,请看MVVM系统入门-下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

教唆chat ai 吵架--chatGPT和chatBing体验

教唆chat ai 吵架–chatGPT和chatBing体验 请注意&#xff0c;本文主观性非常高&#xff0c;只是一个参考性文章&#xff0c;无任何其他含义。 当我们谈到人工智能对话模型时&#xff0c;ChatGPT和ChatBing是两个备受关注的模型。它们都是自然语言处理领域中的重要里程碑&…

hive学习(仅供参考)

hive搭建Hive什么是hiveHive的优势和特点hive搭建解压、改名修改环境变量添加hive-site.xml将maven架包拷贝到hive替换一下gua包使环境变量生效初始化安装成功Hive 什么是hive 将结构化的数据文件映射为数据库表 提供类sql的查询语言HQL(Hive Query Language) Hive让更多的人…

【C++内存管理机制】学习笔记(4):重载operate new/::operator new..../new()

目录 简介C++应用程序 分配内存的途径重载::operator new/::operator delete重载operator new/operator delete重载new()/delete()结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简…

自学软件测试?一般人“别来沾边”...

本人7年测试经验&#xff0c;在学测试之前对电脑的认知也就只限于上个网&#xff0c;玩个办公软件。这里不能跑题&#xff0c;我为啥说&#xff1a;自学软件测试&#xff0c;一般人我还是劝你算了吧&#xff1f;因为我就是那个一般人&#xff01; 软件测试基础真的很简单&…

mac环境,安装NMP遇到的问题

一 背景 项目开发中,公司项目需要使用本地的环境运行,主要是php这块的业务。没有使用docker来处理,重新手动撸了一遍。记录下其中遇到的问题; 二 遇到的问题 2.1 Nginx的问题 brew install nginx后,启动nginx,报错如下:nginx: [emerg] no "ssl_certificate" …

数据结构与算法(二):线性表

上一篇《数据结构与算法&#xff08;一&#xff09;&#xff1a;概述》中介绍了数据结构的一些基本概念&#xff0c;并分别举例说明了算法的时间复杂度和空间复杂度的求解方法。这一篇主要介绍线性表。 一、基本概念 线性表是具有零个或多个数据元素的有限序列。线性表中数据…

零售电子标签解决方案

电子货架标签系统&#xff08;ESLs&#xff09;&#xff0c;是一种放置在货架上、可替代传统纸质价格标签的电子显示装置&#xff0c; 每一个电子货架标 签通过有线或者无线网络与商场计算机数据库相连&#xff0c; 并将最新的商品价格通过电子货架标签上的屏显示出来。 电子…

2023年数学建模美赛D题(Prioritizing the UN Sustainability Goals)分析与编程

2023年数学建模美赛D题分析建模与编程 重要说明&#xff1a; 本文介绍2023年美赛题目&#xff0c;并进行简单分析&#xff1b;本文首先对 D题进行深入分析&#xff0c;其它题目分析详见专题讨论&#xff1b;本文及专题分析将在 2月17日每3小时更新一次&#xff0c;完全免费&am…

使用chatgpt生成快速入眠笔记

以下是使用chatgpt生成快速入眠笔记的简单过程 可以发现&#xff0c;增加详细两个字&#xff0c;可以让它表述的更明白。 通过询问“还有其他方法吗”&#xff0c;获取更多可能性&#xff0c;当然你也可以直接说继续 但实测继续有时候不会记住上一条提问 详细讲解一下程序员怎…

类似LeetCode的登录页面(小程序版)

前言每一个项目都会有用户端的注册和登录页面&#xff0c;对于刚入门的小白来说&#xff0c;在UI设计方面不太擅长&#xff0c;就算大致的UI界面设计出来了&#xff0c;但是落实到代码上来实现的时候就很容易卡住。这篇博客主要介绍的就是仿作一个类似LeetCode登录的简约大方页…

离线环境轻量级自动化部署

流程图&#xff1a; 常规系统发布的痛点 服务器频繁重启&#xff0c;上面部署的应用服务不能随之重启&#xff0c;导致服务时常宕机应用手动部署相对比较麻烦&#xff0c;步骤繁琐应用发布环境取决于发布人本地环境&#xff0c;导致不同发布人每次发布环境不一致&#xff0c;导…

【玩转多核异构】双核高速率CAN-FD评测——飞凌嵌入式

为了能够让更多的工程师朋友了解多核异构处理器&#xff0c;飞凌嵌入式特别推出了【玩转多核异构】专题&#xff0c;帮助大家解决在多核异构处理器的开发过程中遇到的问题。【玩转多核异构】专题持续更新中&#xff0c;欢迎您的持续关注。引言凭借实时性、抗干扰性和安全性等优…

Redis 开发规范

原创 | Java 2021 超神之路&#xff0c;很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 Elastic-Job 源码解析分布式事务中间件 TCC-Transaction 源…

详细总结Ansible中使用playbook

文章目录前言一、Playbook的功能二、YAML三、playbook执行命令1.使用ansible-playbook部署ftp服务&#xff0c;并开启匿名用户访问权利2.使用ansible-playbook部署apache服务&#xff0c;设定默认发布文件内容为www.westos.org3.tags&#xff1a;标签四、使用vim解决yaml书写格…

使用git中可能出现的问题

问题1&#xff1a;如果遇到自己的文件在远程仓库dev分支被别人修改了&#xff0c;自己在本地仓库test分支继续在写代码先拉取最新的代码 覆盖本地dev分支 TortoiseGit->Pull被修改如图2.拉取最新的代码(拉取成功后 本地dev分支user有四条属性)3.切换到自己的分支tortoiseGit…

C++009-C++循环结构while

文章目录C009-C循环结构whilewhile循环while循环举例题目描述 对折多少次能超过nmm题目描述 输入整数和超过n题目描述 输入若干个大写字母&#xff0c;输出对应的小写字母题目描述 输入整数&#xff0c;逆序输出作业在线练习&#xff1a;总结C009-C循环结构while 在线练习&…

模型转换 PyTorch转ONNX 入门

前言 本文主要介绍如何将PyTorch模型转换为ONNX模型&#xff0c;为后面的模型部署做准备。转换后的xxx.onnx模型&#xff0c;进行加载和测试。最后介绍使用Netron&#xff0c;可视化ONNX模型&#xff0c;看一下网络结构&#xff1b;查看使用了那些算子&#xff0c;以便开发部署…

计算机网络第1章(概述)学习笔记

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

PPS文件如何转换成PPT?附两种方法

在工作中&#xff0c;PPS文件的使用还是很广泛的&#xff0c;因为作为幻灯片放映文件&#xff0c;点击后就能直接播放&#xff0c;十分方便。但如果想要修改PPS里的内容&#xff0c;PPS是无法编辑的&#xff0c;我们需要把文件转换成PPT&#xff0c;再进行修改。 那PPS文件如何…

详细解读ChatGPT:如何调用ChatGPT的API接口到官方例子的说明以及GitHub上的源码应用和csdn集成的ChatGPT

文章目录1. 解读ChatGPT1.1 词语解释1.2 功能解读2. GitHub上ChatGPT的应用源码3. 调用ChatGPT的API4. 官方例子说明5. 集成ChatGPT自ChatGPT出来到如今&#xff0c;始终走在火热的道路上&#xff0c;如今日活用户破亿&#xff0c;他为何有如此大的魅力&#xff0c;深受广大用户…