当前位置: 首页 > news >正文

WPF入门到精通:3.MVVM简单应用及全局异常处理

MVVM简介

在WPF应用程序开发中,MVVM(Model-View-ViewModel)是一种非常流行的架构模式。它为应用程序的设计提供了良好的分层结构和可扩展性。

结构分为下列三部分

  1. Model:定义了应用程序的数据模型 就是系统中的对象,可包含属性和行为(是一个class实体,是对现实中事物的抽象,开发过程中涉及到的事物都可以抽象为Model,例如用户的账号、密码、电话等),负责从数据源中获取数据并将其提供给ViewModel。

  2. ViewModel:封装了应用程序的业务逻辑,通过View类的DataContext属性绑定到View,负责将数据从Model传递到View,并将用户交互事件传递回Model。显示数据对应ViewMode中的Property,执行命令对应ViewModel中的Command。

  3. View:用xaml实现的界面,接收用户输入,把数据展现给用户,并与ViewModel交互以便进行数据绑定和命令绑定。

在MVVM模式中,ViewModel的主要职责是将数据从Model传递到View,并响应View的用户交互事件。ViewModel通过命令绑定和数据绑定与View进行交互。

MVVM优点

  1. 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  2. 灵活扩展:可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑;
  3. 易测试:可以针对ViewModel来写测试用例
  4. 独立开发:开发人员可以专注于业务逻辑和数据的开发(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应用程序开发中&#xff0c;MVVM&#xff08;Model-View-ViewModel&#xff09;是一种非常流行的架构模式。它为应用程序的设计提供了良好的分层结构和可扩展性。 结构分为下列三部分 Model&#xff1a;定义了应用程序的数据模型 就是系统中的对象&#xff0c;…...

Springboot+mybatis-plus+dynamic-datasource+Druid 多数据源 分布式事务

Springbootmybatis-plusdynamic-datasourceDruid 多数据源事务&#xff0c;分布式事务 文章目录 Springbootmybatis-plusdynamic-datasourceDruid 多数据源事务&#xff0c;分布式事务0.前言1. 基础介绍ConnectionFactoryAbstractRoutingDataSource 动态路由数据源的抽象类 Dyn…...

673. 最长递增子序列的个数

673. 最长递增子序列的个数 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;方法一&#xff1a;动态规划方法二&#xff1a;贪心 前缀和 二分查找 参考代码&#xff1a;__673最长递增子序列的个数__动态规划__673最长递增子序列的个数__贪心_前缀和_二分查找…...

Android12之ABuffer数据处理(三十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…...

whisper 语音识别项目部署

1.安装anaconda软件 在如下网盘免费获取软件&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1zOZCQOeiDhx6ebHh5zNasA 提取码&#xff1a;hfnd 2.使用conda命令创建python3.8环境 conda create -n whisper python3.83.进入whisper虚拟环境 conda activate whisper4.…...

实例044 在关闭窗口前加入确认对话框

实例说明 用户对程序进行操作时&#xff0c;难免会有错误操作的情况&#xff0c;例如不小心关闭程序&#xff0c;如果尚有许多资料没有保存&#xff0c;那么损失将非常严重&#xff0c;所以最好使程序具有灵活的交互性。人机交互过程一般都是通过对话框来实现的&#xff0c;对话…...

子查询和事务隔离以及用户管理

一、子查询 子查询是另一个语句中的select语句嵌套在另一个select中。注意子查询语法上必须使用()包起来。 嵌套的那个语句返回的结果有可能是&#xff1a; 一个字段&#xff0c;一行记录&#xff0c;一个列或一个表。嵌套的位置 where / having语句里面作为条件使用在from语…...

uniapp 滚动到指定元素的位置(锚点)

需求&#xff1a;在页面中&#xff0c;不管位于何处&#xff0c;点击按钮页面滚动到对应的标题位置。 最简单有效的方式&#xff08;直接复制改数据就行&#xff09; 使用 scroll-view 标签的属性&#xff1a;scroll-top(距离值 num) 或 scroll-into-view(子元素的id,不能以…...

Spring AOP 的 afterReturing 返回值是否能修改问题

文章目录 结论举例子原因外传 结论 最近要搞脱敏信息&#xff0c;所以&#xff0c;想了几种方案&#xff0c;最后使用全局的接口拦截&#xff0c;但是&#xff0c;又不能用注解的方式&#xff0c;毕竟是几年的老产品&#xff0c;有很多限制。 中间尝试过使用Spring AOP 的 aft…...

MyBatis分页插件PageHelper的使用及特殊字符的处理

目录 一、PageHelper简介 1.什么是分页 2.PageHelper是什么 3.使用PageHelper的优点 二、PageHelper插件的使用 原生limit查询 1. 导入pom依赖 2. Mybatis.cfg.xml 配置拦截器 3. 使用PageHelper进行分页 三、特殊字符的处理 1.SQL注入&#xff1a; 2.XML转义&#…...

[语音识别] 基于Python构建简易的音频录制与语音识别应用

语音识别技术的快速发展为实现更多智能化应用提供了无限可能。本文旨在介绍一个基于Python实现的简易音频录制与语音识别应用。文章简要介绍相关技术的应用&#xff0c;重点放在音频录制方面&#xff0c;而语音识别则关注于调用相关的语音识别库。本文将首先概述一些音频基础概…...

Matlab彩色图像转索引图像

索引图像 索引图像是一种把像素值直接作为RGB调色板下标的图像。索引图像包括一个数据矩阵X&#xff0c;一个调色板矩阵map&#xff0c;也称为颜色映像矩阵。其中&#xff0c;数据矩阵X可以是8位无符号整型、16位无符号整型或双精度类型。调色板矩阵map是一个m3的数据阵列&…...

测试框架pytest教程(11)-pytestAPI

常量 pytest.__version__ #输出pytest版本 pytest.version_tuple #输出版本的元组形式 功能 pytest.approx pytest.approx 是一个用于进行数值近似比较的 pytest 断言工具。 在测试中&#xff0c;有时候需要对浮点数或其他具有小数部分的数值进行比较。然而&#xff0c;由于…...

Docker自学:利用FastAPI建立一个简单的web app

环境配置&#xff1a;下载Docker Desktop 文件一&#xff1a;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&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1EKggbyC4ZW-ufnDW45eKzA 提取码&#xff1a;k3b2 baseline 链接&#xff1a;https://pan.baidu.com/s/12hkZNJjQ__FGAHiF3fifvQ 提取码&#xff1a;88tb 数据…...

Springboot中sharding-jdbc的API模式并使用自定义算法

Springboot中sharding-jdbc的API模式并使用自定义算法 可配合AbstractRoutingData使用切换数据源 程序用到了AbstractRoutingData来切换数据源&#xff08;数据源是自定义的格式编写并没有用springboot的自动装配的格式写&#xff09;&#xff0c;但是又用到sharding-jdbc进行…...

MySQL回表是什么?哪些情况下会回表

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责…...

VR、AR、MR 傻傻分不清楚?区别的底层逻辑?

VR是一种能够制作虚拟物体并与人互动的基础技术。它与操作者所处的环境无关。AR可以让在特定位置出现或消失。MR可以让虚拟物体与真实物体进行互动。 AR和MR的大部分应用场景都是随机的&#xff0c;所以硬件基本都采用手机和眼镜。提升了便携性。牺牲了性能。这就导致了AR与MR…...

VScode运行C语言出现的调试问题 lauch:program does not exist 解决方法

"lauch:program does not exist"错误通常表示编译器或调试器无法找到指定的可执行文件。这可能是由于几个原因引起的。首先&#xff0c;确保你的源代码文件夹路径不包含中文字符&#xff0c;因为这可能导致编译器无法识别文件。其次&#xff0c;检查你的launch.json文…...

云原生安全:保护现代化应用的新一代安全策略

随着云计算和容器技术的快速发展&#xff0c;云原生应用已成为现代化软件开发和部署的主流趋势。然而&#xff0c;随之而来的安全挑战也变得更加复杂和严峻。本文将深入探讨云原生安全的概念、原则和最佳实践&#xff0c;帮助您理解如何有效保护云原生应用和敏感数据。 第一部…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇

根据 QYResearch 发布的市场报告显示&#xff0c;全球市场规模预计在 2031 年达到 9848 万美元&#xff0c;2025 - 2031 年期间年复合增长率&#xff08;CAGR&#xff09;为 3.7%。在竞争格局上&#xff0c;市场集中度较高&#xff0c;2024 年全球前十强厂商占据约 74.0% 的市场…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...