WPF入门到精通:3.MVVM简单应用及全局异常处理
MVVM简介
在WPF应用程序开发中,MVVM(Model-View-ViewModel)是一种非常流行的架构模式。它为应用程序的设计提供了良好的分层结构和可扩展性。
结构分为下列三部分
-
Model:定义了应用程序的数据模型 就是系统中的对象,可包含属性和行为(是一个class实体,是对现实中事物的抽象,开发过程中涉及到的事物都可以抽象为Model,例如用户的账号、密码、电话等),负责从数据源中获取数据并将其提供给ViewModel。
-
ViewModel:封装了应用程序的业务逻辑,通过View类的DataContext属性绑定到View,负责将数据从Model传递到View,并将用户交互事件传递回Model。显示数据对应ViewMode中的Property,执行命令对应ViewModel中的Command。
-
View:用xaml实现的界面,接收用户输入,把数据展现给用户,并与ViewModel交互以便进行数据绑定和命令绑定。
在MVVM模式中,ViewModel的主要职责是将数据从Model传递到View,并响应View的用户交互事件。ViewModel通过命令绑定和数据绑定与View进行交互。
MVVM优点
- 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 灵活扩展:可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑;
- 易测试:可以针对ViewModel来写测试用例
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于xaml页面设计。
MVVM示例
以下是一个简单的WPF MVVM登录示例:
底层通用实体
ViewModelBase
添加一个viewmodelbase方便后续数据双向绑定更新
using System.ComponentModel;
using System.Runtime.CompilerServices;namespace YourProjectName.Comm
{public class ViewModelBase : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;public void PC(string propertyName){this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}public void PCEH([CallerMemberName] string propertyName = null){this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}
}
CommandBase
添加一个继承ICommand实体,方便后续绑定command
using System;
using System.Windows.Input;namespace YourProjectName.Comm
{public class CommandBase : ICommand{private readonly Action<object> _execute;private readonly Func<object, bool> _canExecute;public event EventHandler CanExecuteChanged{add{CommandManager.RequerySuggested += value;}remove{CommandManager.RequerySuggested -= value;}}public CommandBase(Action<object> execute): this(execute, null){}public CommandBase(Action<object> execute, Func<object, bool> canExecute){_execute = execute ?? throw new ArgumentNullException("execute");_canExecute = canExecute ?? ((Func<object, bool>)((object x) => true));}public bool CanExecute(object parameter){return _canExecute(parameter);}public void Execute(object parameter){_execute(parameter);}public void Refresh(){CommandManager.InvalidateRequerySuggested();}}
}
ViewModel及Model
在ViewModel文件夹下,创建一个名为LoginViewModel的类。 因为只有两个字段测试所以未新建Model,实际开发中注意新建
using System.Windows.Input;
using YourProjectName.Comm;namespace YourProjectName.ViewModel
{public class LoginViewModel : ViewModelBase {private string _username;private string _password;private bool _rememberMe;public string Username {get { return _username; }set { _username= value; PCEH();}}public string Password {get { return _password; }set { _password= value; PCEH();}}public bool RememberMe {get { return _rememberMe; }set { _rememberMe= value; PCEH();}}public ICommand LoginCommand;private void Login() {LoginCommand= new CommandBase(async l =>{// TODO: Add login logic.
});}}
}
View
在View文件夹下,创建一个名为LoginView的XAML和CS文件。
<Window x:Class="YourProjectName.View.LoginView"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:YourProjectName.View"xmlns:vm="clr-namespace:YourProjectName.ViewModel"mc:Ignorable="d"Title="LoginView" Height="300" Width="400"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions><Label Grid.Row="0" Grid.Column="0" Content="Username:" /><TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Username}" /><Label Grid.Row="1" Grid.Column="0" Content="Password:" /><PasswordBox Grid.Row="1" Grid.Column="1" Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}" /><CheckBox Grid.Row="2" Grid.Column="1" Content="Remember me" IsChecked="{Binding RememberMe}" /><Button Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Content="Login" Command="{Binding LoginCommand}" /><TextBlock Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Text="Don't have an account? Sign up." /></Grid>
</Window>
初始化DataContext ,也可在页面中初始化
using System.Windows;namespace YourProjectName.View
{public partial class LoginView : Window {private readonly LoginViewModel model;public LoginView() {InitializeComponent();DataContext = model = new();}}
}
启动应用程序并测试登录页面。在“TODO”注释的位置添加实际的登录代码。
全局异常处理
在WPF应用程序中,全局异常处理非常重要。全局异常处理可以帮助我们捕获应用程序中的所有未处理异常,防止程序异常崩溃并提供更好的用户体验。在WPF中,可以通过在应用程序的App.xaml.cs文件中添加以下代码来实现全局异常处理:
异常处理示例
public partial class App : Application
{protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;DispatcherUnhandledException += App_DispatcherUnhandledException;TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;}private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e){//处理UI线程上的未处理异常e.Handled = true;MessageBox.Show("发生错误:" + e.Exception.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);}private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e){//处理非UI线程上的未处理异常Exception ex = (Exception)e.ExceptionObject;MessageBox.Show("发生错误:" + ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);}/// <summary>/// Task线程内未捕获异常处理事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs ex){MessageBox.Show("Task线程异常:" + ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);//设置该异常已察觉(这样处理后就不会引起程序崩溃)e.SetObserved();}
}
以上代码会在应用程序启动时为当前域的未处理异常事件和UI线程上的未处理异常事件及task线程异常注册处理程序。在发生异常时,会弹出一个消息框来通知用户并不会造成程序崩溃。
总结:
MVVM模式是WPF应用程序开发中非常重要的主题。通过理解和实战练习,我们可以更好地开发出强大和稳定的应用程序。
全局异常处理是程序中必不可少的一步,能保障程序在异常发生过程中正常运行。
相关文章:
WPF入门到精通:3.MVVM简单应用及全局异常处理
MVVM简介 在WPF应用程序开发中,MVVM(Model-View-ViewModel)是一种非常流行的架构模式。它为应用程序的设计提供了良好的分层结构和可扩展性。 结构分为下列三部分 Model:定义了应用程序的数据模型 就是系统中的对象,…...
Springboot+mybatis-plus+dynamic-datasource+Druid 多数据源 分布式事务
Springbootmybatis-plusdynamic-datasourceDruid 多数据源事务,分布式事务 文章目录 Springbootmybatis-plusdynamic-datasourceDruid 多数据源事务,分布式事务0.前言1. 基础介绍ConnectionFactoryAbstractRoutingDataSource 动态路由数据源的抽象类 Dyn…...
673. 最长递增子序列的个数
673. 最长递增子序列的个数 原题链接:完成情况:解题思路:方法一:动态规划方法二:贪心 前缀和 二分查找 参考代码:__673最长递增子序列的个数__动态规划__673最长递增子序列的个数__贪心_前缀和_二分查找…...
Android12之ABuffer数据处理(三十四)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…...
whisper 语音识别项目部署
1.安装anaconda软件 在如下网盘免费获取软件: 链接:https://pan.baidu.com/s/1zOZCQOeiDhx6ebHh5zNasA 提取码:hfnd 2.使用conda命令创建python3.8环境 conda create -n whisper python3.83.进入whisper虚拟环境 conda activate whisper4.…...
实例044 在关闭窗口前加入确认对话框
实例说明 用户对程序进行操作时,难免会有错误操作的情况,例如不小心关闭程序,如果尚有许多资料没有保存,那么损失将非常严重,所以最好使程序具有灵活的交互性。人机交互过程一般都是通过对话框来实现的,对话…...
子查询和事务隔离以及用户管理
一、子查询 子查询是另一个语句中的select语句嵌套在另一个select中。注意子查询语法上必须使用()包起来。 嵌套的那个语句返回的结果有可能是: 一个字段,一行记录,一个列或一个表。嵌套的位置 where / having语句里面作为条件使用在from语…...
uniapp 滚动到指定元素的位置(锚点)
需求:在页面中,不管位于何处,点击按钮页面滚动到对应的标题位置。 最简单有效的方式(直接复制改数据就行) 使用 scroll-view 标签的属性:scroll-top(距离值 num) 或 scroll-into-view(子元素的id,不能以…...
Spring AOP 的 afterReturing 返回值是否能修改问题
文章目录 结论举例子原因外传 结论 最近要搞脱敏信息,所以,想了几种方案,最后使用全局的接口拦截,但是,又不能用注解的方式,毕竟是几年的老产品,有很多限制。 中间尝试过使用Spring AOP 的 aft…...
MyBatis分页插件PageHelper的使用及特殊字符的处理
目录 一、PageHelper简介 1.什么是分页 2.PageHelper是什么 3.使用PageHelper的优点 二、PageHelper插件的使用 原生limit查询 1. 导入pom依赖 2. Mybatis.cfg.xml 配置拦截器 3. 使用PageHelper进行分页 三、特殊字符的处理 1.SQL注入: 2.XML转义&#…...
[语音识别] 基于Python构建简易的音频录制与语音识别应用
语音识别技术的快速发展为实现更多智能化应用提供了无限可能。本文旨在介绍一个基于Python实现的简易音频录制与语音识别应用。文章简要介绍相关技术的应用,重点放在音频录制方面,而语音识别则关注于调用相关的语音识别库。本文将首先概述一些音频基础概…...
Matlab彩色图像转索引图像
索引图像 索引图像是一种把像素值直接作为RGB调色板下标的图像。索引图像包括一个数据矩阵X,一个调色板矩阵map,也称为颜色映像矩阵。其中,数据矩阵X可以是8位无符号整型、16位无符号整型或双精度类型。调色板矩阵map是一个m3的数据阵列&…...
测试框架pytest教程(11)-pytestAPI
常量 pytest.__version__ #输出pytest版本 pytest.version_tuple #输出版本的元组形式 功能 pytest.approx pytest.approx 是一个用于进行数值近似比较的 pytest 断言工具。 在测试中,有时候需要对浮点数或其他具有小数部分的数值进行比较。然而,由于…...
Docker自学:利用FastAPI建立一个简单的web app
环境配置:下载Docker Desktop 文件一:main.py from typing import Unionfrom fastapi import FastAPIimport uvicornapp FastAPI()app.get("/") def read_root():return {"Hello": "World"}app.get("/items/{item…...
微调bert做学术论文分类(以科大讯飞学术论文分类挑战赛为例)
代码 12-How to Fine-Tune BERT for Text Classification:链接:https://pan.baidu.com/s/1EKggbyC4ZW-ufnDW45eKzA 提取码:k3b2 baseline 链接:https://pan.baidu.com/s/12hkZNJjQ__FGAHiF3fifvQ 提取码:88tb 数据…...
Springboot中sharding-jdbc的API模式并使用自定义算法
Springboot中sharding-jdbc的API模式并使用自定义算法 可配合AbstractRoutingData使用切换数据源 程序用到了AbstractRoutingData来切换数据源(数据源是自定义的格式编写并没有用springboot的自动装配的格式写),但是又用到sharding-jdbc进行…...
MySQL回表是什么?哪些情况下会回表
🏆作者简介,黑夜开发者,全栈领域新星创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责…...
VR、AR、MR 傻傻分不清楚?区别的底层逻辑?
VR是一种能够制作虚拟物体并与人互动的基础技术。它与操作者所处的环境无关。AR可以让在特定位置出现或消失。MR可以让虚拟物体与真实物体进行互动。 AR和MR的大部分应用场景都是随机的,所以硬件基本都采用手机和眼镜。提升了便携性。牺牲了性能。这就导致了AR与MR…...
VScode运行C语言出现的调试问题 lauch:program does not exist 解决方法
"lauch:program does not exist"错误通常表示编译器或调试器无法找到指定的可执行文件。这可能是由于几个原因引起的。首先,确保你的源代码文件夹路径不包含中文字符,因为这可能导致编译器无法识别文件。其次,检查你的launch.json文…...
云原生安全:保护现代化应用的新一代安全策略
随着云计算和容器技术的快速发展,云原生应用已成为现代化软件开发和部署的主流趋势。然而,随之而来的安全挑战也变得更加复杂和严峻。本文将深入探讨云原生安全的概念、原则和最佳实践,帮助您理解如何有效保护云原生应用和敏感数据。 第一部…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
