MVVM框架与项目实例

  • 一、MVVM框架
    • 1. 概述
    • 2. 核心组件与优势
  • 一、MVVM项目
    • 1.普通项目
    • 2. MVVM架构
    • 3. MVVM项目实例
      • 1. 项目准备
      • 2. LoginViewModel与Login
      • 2. MainWindowViewModel
    • 4. MVVM项目优化
      • 1. BaseViewModel
      • 2. RealyCommand
      • 3. 效果展示
  • 总结


一、MVVM框架

1. 概述

官方文档:https://learn.microsoft.com/zh-cn/dotnet/architecture/maui/mvvm

在这里插入图片描述

2. 核心组件与优势

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、MVVM项目

1.普通项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. MVVM架构

在这里插入图片描述

3. MVVM项目实例

1. 项目准备

以一个简单的图书管理系统为例,包含登录和图书删除两个功能。
在这里插入图片描述
Model创建Book、DAL、User,内容如下:

namespace Model
{//书籍类public class Book{public Book(string name, string isbn, string author){Name = name;Isbn = isbn;Author = author;}public string Name { get; set; }public string Isbn { get; set; }public string Author { get; set; }}
}
namespace Model
{//数据访问层,操作数据库public class DAL{//模拟查询数据库书籍表数据public ObservableCollection<Book> GetBookList(){ObservableCollection<Book> list = new ObservableCollection<Book>();list.Add(new Book("Java高级编程", "111", "李老师"));list.Add(new Book("C++高级编程", "222", "黄老师"));list.Add(new Book("C#高级编程", "333", "马老师"));return list;}//模拟查询数据库用户表数据public List<User> GetUserList(){List<User> list = new List<User>();list.Add(new User("admin", "123", "1111111"));list.Add(new User("张三", "123", "2222222"));return list;}//通过用户名查找用户public User FindUserByName(String name){List<User> users = GetUserList();foreach (User u in users){if (u.Name == name)return u;}return null;}}
namespace Model
{//用户类public class User{public User(string name, string pwd, string phone){Name = name;Pwd = pwd;Phone = phone;}public string Name { get; set; }public string Pwd { get; set; }public string Phone { get; set; }}
}

View创建Login,内容如下:

    <Grid><Label x:Name="label" Content="用户名" HorizontalAlignment="Left" Height="30" Margin="245,115,0,0" VerticalAlignment="Top" Width="100"/><TextBox x:Name="textBoxName" Text="" HorizontalAlignment="Left" Height="30" Margin="360,115,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195"/><Label x:Name="label2" Content="密码" HorizontalAlignment="Left" Height="30" Margin="245,175,0,0" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.402,2.66"/><TextBox x:Name="textBoxPwd" Text="" HorizontalAlignment="Left" Height="30" Margin="360,175,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="195"/><Button x:Name="buttonLogin" Content="登录" HorizontalAlignment="Left" Height="40" Margin="340,310,0,0" VerticalAlignment="Top" Width="140"  /><Label x:Name="labelError" Content="" HorizontalAlignment="Left" Height="33" Margin="225,235,0,0" VerticalAlignment="Top" Width="390" Foreground="#FFF30303"/> </Grid>

2. LoginViewModel与Login

ViewModel创建LoginViewModel,为界面上的用户名和密码输入框设计属性,并继承InotifyPertyChanged接口,实现数据双向更新
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接来对窗口使用DataContext进行上下文数据绑定。
在这里插入图片描述将View绑定到ViewModel上
在这里插入图片描述

创建LoginCommand类,继承接口ICommand并实现。
其对LoginCommand类的Execute函数改造,实现命令处理逻辑,以及检查登录信息是否正确。
在这里插入图片描述

在这里插入图片描述

using Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;namespace ViewModel
{//自定义命令类class LoginCommand : ICommand{public event EventHandler CanExecuteChanged;private LoginViewModel vm;public LoginCommand(LoginViewModel vm){this.vm = vm;}public bool CanExecute(object parameter){return true;}public void Execute(object parameter){//Execute方法中实现命令处理逻辑string Name = vm.Name;string Pwd = vm.Pwd;if(string.IsNullOrEmpty(vm.Name) || string.IsNullOrEmpty(vm.Pwd)){vm.Error = "用户名密码不能为空!";return;}//检查登录信息User u = vm.dal.FindUserByName(vm.Name);if (u != null && u.Pwd == vm.Pwd){Window w = parameter as Window;w.DialogResult = true;//关闭窗口}elsevm.Error = "用户名密码错误!";}}internal class LoginViewModel : INotifyPropertyChanged{//属性对应,界面上用户名TextBox控件private string name = "admin";public string Name{get { return name; }set{name = value;OnPropertyChanged("Name");}}//属性对应,界面上密码TextBox控件private string pwd;public string Pwd{get { return pwd; }set{pwd = value;OnPropertyChanged("Pwd");}}//属性对应,界面上报错Label控件  private string error = "暂无信息";public string Error{get { return error; }set{error = value;OnPropertyChanged("Error");}}//命令属性,对应界面上的登录事件public LoginCommand LoginCommand { get; set; }//数据访问层public DAL dal = new DAL();public event PropertyChangedEventHandler PropertyChanged;//通知属性更改public void OnPropertyChanged(string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}//构造函数public LoginViewModel(){LoginCommand = new LoginCommand(this);}}
}
<Window x:Class="View.Login"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:View"mc:Ignorable="d"Title="Login" Height="450" Width="800" FontSize="18"  Closing="Window_Closing"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="3*"/><ColumnDefinition Width="5*"/></Grid.ColumnDefinitions><Label x:Name="label" Content="用户名"  HorizontalAlignment="Left" Height="30" Margin="245,115,0,0" VerticalAlignment="Top" Width="100" Grid.ColumnSpan="2"/><TextBox x:Name="textBoxName" Text="{Binding Name}" HorizontalAlignment="Left" Height="30" Margin="60,115,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195" Grid.Column="1"/><Label x:Name="label2" Content="密码" HorizontalAlignment="Left" Height="30" Margin="245,175,0,0" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.402,2.66" Grid.ColumnSpan="2"/><TextBox x:Name="textBoxPwd" Text="{Binding Pwd}" HorizontalAlignment="Left" Height="30" Margin="60,175,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="195" Grid.Column="1"/><Button x:Name="buttonLogin" Content="登录" Command="{Binding LoginCommand}"CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window} } }"HorizontalAlignment="Left" Height="40" Margin="40,310,0,0" VerticalAlignment="Top" Width="140" Grid.Column="1"  /><Label x:Name="labelError" Content="{Binding Error}" HorizontalAlignment="Left" Height="33" Margin="225,235,0,0" VerticalAlignment="Top" Width="390" Foreground="#FFF30303" Grid.ColumnSpan="2"/></Grid>
</Window>

2. MainWindowViewModel

在ViewModel创建图书管理的MainWindowViewModel,实现图书删除。

在这里插入图片描述
在这里插入图片描述
接来对窗口使用DataContext进行上下文数据绑定。
在这里插入图片描述
将View绑定到ViewModel上

在这里插入图片描述
创建DelCommand类,继承接口ICommand并实现。
其对DelCommand类的Execute函数改造,实现命令处理逻辑,删除书籍。
在这里插入图片描述
在这里插入图片描述

using Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;namespace ViewModel
{class DelCommand : ICommand{public event EventHandler CanExecuteChanged;private MainWindowViewModel vm;public DelCommand(MainWindowViewModel vm){this.vm = vm;}public bool CanExecute(object parameter){return true;}//传入的参数是命令的绑定的参数Book类的对象public void Execute(object parameter){Book  b = parameter as Book;//删除执行的内容vm.Books.Remove(b);}}internal class MainWindowViewModel : INotifyPropertyChanged{//数据访问层public DAL dal = new DAL();public MainWindowViewModel(){ Books = dal.GetBookList() ;DelCommand  = new DelCommand(this);}//对应界面上的  ListView显示的数据//ObservableCollection 可以通知界面更新的private ObservableCollection<Book> books; public ObservableCollection<Book> Books{get { return books; }set { books = value;OnPropertyChanged("Books");}}//删除命令 ,对应界面的删除按钮的点击操作public  DelCommand  DelCommand { get; set; }//通知属性已经更改public event PropertyChangedEventHandler PropertyChanged;public void OnPropertyChanged(string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}
}
<Window x:Class="View.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:View"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"  FontSize="18" ><Grid><ListView x:Name="listView"   ItemsSource="{Binding  Books}"HorizontalAlignment="Center" Height="275" VerticalAlignment="Center" Width="556" d:ItemsSource="{d:SampleData ItemCount=5}"><ListView.View><GridView><GridViewColumn Header="书名" Width="200" DisplayMemberBinding="{Binding Name}" /><GridViewColumn Header="ISBN" Width="100" DisplayMemberBinding="{Binding Isbn}" /><GridViewColumn Header="作者" Width="Auto" DisplayMemberBinding="{Binding Author }" /><GridViewColumn Header="操作" Width="Auto" ><GridViewColumn.CellTemplate><DataTemplate><Button Width="60" Content="删除"  Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}},Path=DataContext.DelCommand}" CommandParameter="{Binding  }"   /></DataTemplate></GridViewColumn.CellTemplate> </GridViewColumn></GridView></ListView.View></ListView></Grid>
</Window>

4. MVVM项目优化

在这里插入图片描述

1. BaseViewModel

在这里插入图片描述

2. RealyCommand

在这里插入图片描述

3. 效果展示

在这里插入图片描述
修改LoginCommand
在这里插入图片描述
修改DelCommand

下面为优化后的实际代码
创建BaseViewModel

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ViewModel
{internal class BaseViewMode : INotifyPropertyChanged{//通知属性已经更改public event PropertyChangedEventHandler PropertyChanged;public void OnPropertyChanged(string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}
}

创建RelayCommand

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;namespace ViewModel
{internal class RelayCommand : ICommand{//命令执行的函数,参数为object,无返回值的private Action<object> executeAction;//判断命名能否执行的函数,参数为object,返回值为 boolprivate Func<object, bool> canExecuteFunc;public event EventHandler CanExecuteChanged;//构造函数传入参数,以后不用为每个命令单独写一个类了,每个命令都可以使用这个类//只不过执行的内容不同public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null){executeAction = execute;canExecuteFunc = canExecute;}public bool CanExecute(object parameter){if (canExecuteFunc != null){ return canExecuteFunc(parameter);}return true;}public void Execute(object parameter) { if (executeAction != null){ executeAction(parameter);} }}
}

优化LoginViewModel

using Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;namespace ViewModel
{   internal class LoginViewModel : BaseViewMode{//构造函数public LoginViewModel(){LoginCommand = new RelayCommand(parameter =>{//Execute方法中实现命令处理逻辑if (string.IsNullOrEmpty(Name) || string.IsNullOrEmpty(Pwd)){Error = "用户名密码不能为空!";return;}//检查登录信息User u = dal.FindUserByName(Name);if (u != null && u.Pwd == Pwd){Window w = parameter as Window;w.DialogResult = true;//关闭窗口}elseError = "用户名密码错误!";});}//属性对应,界面上用户名TextBox控件private string name = "admin";public string Name{get { return name; }set{name = value;OnPropertyChanged("Name");}}//属性对应,界面上密码TextBox控件private string pwd;public string Pwd{get { return pwd; }set{pwd = value;OnPropertyChanged("Pwd");}}//属性对应,界面上报错Label控件  private string error = "暂无信息";public string Error{get { return error; }set{error = value;OnPropertyChanged("Error");}}//命令属性,对应界面上的登录事件public ICommand LoginCommand { get; set; }//数据访问层public DAL dal = new DAL();}
}

优化MainWindowViewModel

using Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;namespace ViewModel
{class DelCommand : ICommand{public event EventHandler CanExecuteChanged;private MainWindowViewModel vm;public DelCommand(MainWindowViewModel vm){this.vm = vm;}public bool CanExecute(object parameter){return true;}//传入的参数是命令的绑定的参数Book类的对象public void Execute(object parameter){Book  b = parameter as Book;//删除执行的内容vm.Books.Remove(b);}}internal class MainWindowViewModel : INotifyPropertyChanged{//数据访问层public DAL dal = new DAL();public MainWindowViewModel(){ Books = dal.GetBookList() ;DelCommand  = new DelCommand(this);}//对应界面上的  ListView显示的数据//ObservableCollection 可以通知界面更新的private ObservableCollection<Book> books; public ObservableCollection<Book> Books{get { return books; }set { books = value;OnPropertyChanged("Books");}}//删除命令 ,对应界面的删除按钮的点击操作public  DelCommand  DelCommand { get; set; }//通知属性已经更改public event PropertyChangedEventHandler PropertyChanged;public void OnPropertyChanged(string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}
}

总结

  • MVVM有效的帮助界面与实现的分离,易维护,可重用,易测试,可并行开发
  • ViewModel继承InotifyPertyChanged接口,实现数据双向更新
  • 继承接口ICommand实现自定义命令替代事件,对Execute函数改造,实现命令处理逻辑。
  • ObservableCollection类表示一个动态数据集合,它实现了INotifyPropertyChanged 接囗的数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL实操

## 基于MySQL#先启动MySQL服务#第一次登录[rootlocalhost ~]# mysql -uroot -P3306#密码登录[rootlocalhost ~]# mysql -uroot -pEnter password: Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 9Server version: 8.0.41 Source dist…

ez_rust_writeup

一道简单的[[rust逆向]] #rust逆向 #位运算 题目信息 文件名&#xff1a;ezrust.exe 题目附件&#xff1a;https://wwfj.lanzoul.com/iczMR30k5j4h 密码:bueq 题目分析 1. 初步分析 这是一道Rust编写的逆向题目。通过IDA分析可以看到&#xff0c;这是一个典型的flag验证程序。 …

【QT】-隐式转换 explicit用法

通俗易懂的解释:隐式转换 vs 显式转换 什么是隐式转换? 隐式转换就是编译器偷偷帮你做的类型转换,你甚至都没意识到它发生了。 例子: cpp 运行 double x = 5; // 隐式:int → double(5 变成 5.0) int y = x * 2.5; // 隐式:double → int(截断小数部分) 构造函数的隐…

Django核心知识点详解:JSON、AJAX、Cookie、Session与用户认证

1. JSON数据格式详解1.1 什么是JSON&#xff1f;JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;具有以下特点&#xff1a;独立于语言&#xff0c;几乎所有编程语言都支持易于人阅读和编写易于机器解析和生成基于文本&#xff…

[特殊字符] Python 实战 | 批量统计中文文档词频并导出 Excel

本文展示如何用 Python 脚本&#xff1a; 批量读取文件夹中的多篇中文文档&#xff1b; 用 jieba 分词并统计词频&#xff08;过滤停用词与单字符&#xff09;&#xff1b; 将各文档词频输出为对应 Excel 文件&#xff1b; 是文本分析、内容审查、报告编写中的实用技巧。 &…

共享打印机(详细操作+常见问题:需输入用户名密码、无法连接等)

文章目录一、设置打印机共享的准备工作二、Windows系统下打印机共享设置1. 启用主机打印机共享2. 客户端添加共享打印机三、我所遇到的问题及解决方法客户机遇到输入用户名、密码错误代码 0x0000011b一、错误代码 0x0000011b 的含义二、解决方法添加打印机没成功其他问题此次打…

在 Windows 系统上配置 [go-zero](https://go-zero.dev) 开发环境教程

&#x1f4bb; 在 Windows 系统上配置 go-zero 开发环境教程 本教程将详细介绍如何在 Windows 系统上配置 go-zero 微服务框架的开发环境&#xff0c;包括依赖安装、路径配置、常见问题等。 &#x1f9f1; 一、前置环境安装 1. 安装 Go 下载地址&#xff1a;https://go.dev/…

开源=白嫖?

国内有一个非常浓重的思想&#xff0c;开源&#xff0c;开源就是免费&#xff0c;就是白嫖&#xff0c;就是不花钱&#xff0c;白给。那么什么是开源&#xff1f;“源代码”是软件中大多数计算机用户从未见过的部分;它是计算机程序员可以操纵的代码&#xff0c;以改变一个软件(…

2048-控制台版本

2048控制台版 文章目录2048控制台版实现效果&#xff1a;在这里插入图片描述库函数使用&#xff1a;初始化变量功能函数实现&#xff1a;状态判断函数int Judge&#xff08;&#xff09;&#xff1b;数字生成函数 bool CtreateNumber&#xff08;&#xff09;打印游戏界面 void…

提取出Wallpaper Engine壁纸的mpkg类静态壁纸

github 地址 https://github.com/notscuffed/repkg先下载软件2853…26目录这样获取有的直接mp4格式&#xff0c;就不能用这方法准备好后 cmd 进入repkg目录 执行 repkg extract ./294...333/scene.pkg

AI健康小屋“15分钟服务圈”:如何重构社区健康生态?

AI健康小屋作为“15分钟服务圈”的核心载体&#xff0c;通过技术赋能与场景重构&#xff0c;正推动社区健康生态从被动治疗向主动预防、从单一服务向全周期管理转型。那我们应该如何重构社区健康生态呢&#xff1f;服务模式创新1.全时段覆盖AI健康小屋通过分时段服务满足不同群…

[netty5: WebSocketFrame]-源码分析

WebSocketFrame WebSocketFrame 是 Netty 中用于表示 WebSocket 消息帧的抽象基类&#xff0c;封装了帧的内容、分片标志和扩展位信息&#xff0c;供各类具体帧&#xff08;如文本、二进制、控制帧&#xff09;继承使用。 public abstract class WebSocketFrame extends Buffer…

【加解密与C】非对称加解密(三)ECC椭圆曲线

ECC椭圆曲线的基本概念椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff0c;ECC&#xff09;是一种基于椭圆曲线数学的公钥密码体制。与传统的RSA相比&#xff0c;ECC在相同安全级别下使用更短的密钥&#xff0c;计算效率更高&#xff0c;适用于资源受限的环境。…

力扣网编程150题:加油站(贪心解法)

一. 简介 前面一篇文章使用暴力解法来解决力扣网150 题目&#xff1a;加油站。文章如下&#xff1a; 力扣网编程150题&#xff1a;加油站&#xff08;暴力解法&#xff09;-CSDN博客 暴力解法就是遍历了所有元素作为起始点的可能&#xff0c;算法时间复杂度为 O(n*n)&#x…

windwos 设置redis长久密码不生效

1、设置长久密码redis.windows.conf 文件修改对应的设置密码2、启动时设置对应的加载配置文件

物联网(IoT)领域存在多种协议

物联网&#xff08;IoT&#xff09;领域存在多种协议&#xff0c;主要是因为不同的应用场景对通信的需求差异很大&#xff0c;包括实时性、带宽、功耗、设备兼容性、安全性等。以下从协议多样性的原因和你提到的具体协议&#xff08;如 dc3-driver-* 模块&#xff09;展开说明&…

二、encoders

文章目录一、batch_encoder (用于 BFV)1. 概述2. 数学原理3. 使用方法4. 代码示例二、ckks_encoder (用于 CKKS)在 1. bfv_basics.cpp 中&#xff0c;我们展示了如何使用BFV方案执行非常简单的计算。计算是在 plain_modulus 参数的模下执行的&#xff0c;并且 只使用了 BFV 明文…

数据一致性解决方案总结

数据一致性解决方案总结 我们在系统中&#xff0c;主要进行了数据冗余&#xff0c;那么就会带来数据一致性的问题。常见的数据一致性问题有&#xff1a;数据库主从同步延迟导致的读数据不一致&#xff1b;数据库主主之间数据的不一致&#xff1b;缓存和数据库之间的数据不一致。…

17.Spring Boot的Bean详解(新手版)

文章目录1. 什么是Bean&#xff1f;从零开始理解1.1 Bean的定义1.2 为什么需要Bean&#xff1f;1.3 Bean vs 普通对象的区别2. Spring容器&#xff1a;Bean的家2.1 什么是Spring容器&#xff1f;2.2 容器的工作流程3. Bean的声明方式详解3.1 使用Component及其专门化注解3.1.1 …

cherryStudio electron因为环境问题无法安装解决方法或打包失败解决方法

$env:ELECTRON_MIRROR"https://npmmirror.com/mirrors/electron/"; $env:ELECTRON_CUSTOM_DIR"{{ version }}"; yarn install1. 上面是关于electron安装失败的解决方法. 也可以通过到git上下面包,解压后,放到对应的目录下面,并把里面的build文件夹删除, b…