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文…...
云原生安全:保护现代化应用的新一代安全策略
随着云计算和容器技术的快速发展,云原生应用已成为现代化软件开发和部署的主流趋势。然而,随之而来的安全挑战也变得更加复杂和严峻。本文将深入探讨云原生安全的概念、原则和最佳实践,帮助您理解如何有效保护云原生应用和敏感数据。 第一部…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...
