WPF【11_8】WPF实战-重构与美化(UI 与视图模型的联动,实现INotifyPropertyChanged)

11-13 【重构】INotifyPropertyChanged 与 ObservableCollection

现在我们来完成新建客户的功能。
当用户点击“客户添加”按钮以后系统会清空当前所选定的客户,客户的详细信息以及客户的预约记录会从 UI 中被清除。然后我们就可以在输入框中输入新的客户信息了,最后按下保存按钮这个时候新客户就被保存进数据库并且显示在客户列表中了。

--\MainWindow.xaml
    <StackPanel Grid.Row="1" Grid.Column="0">
        <Button Content="添加客户" Click="ClearSelectedCustomer_Click"/>
        <ListView ItemsSource="{Binding Customers, Mode=OneWay}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}" />
    </StackPanel>

--\MainWindow.xaml.cs
    private MainViewModel _viewModel;
    …………
    private void ClearSelectedCustomer_Click(object sender, RoutedEventArgs e)
    {
        _viewModel.ClearSelectedCustomer();
    }

如何清空客户呢我们还是得从视图模型入手打开 MainViewModel 创建一个清空当前客户的方法 并直接把 _selectedCustomer 设置为空就好了。
--\ViewModels\MainViewModel.cs
    public void ClearSelectedCustomer()
    {
        _selectedCustomer = null;
    }

运行一下代码试试看,程序跑起来选择一个客户,点击“添加客户”翻车了,即使我们在代码中把视图模型中的 _selectedCustomer 清空了,但是 UI 并没有发生改变!这是为什么呢?
虽然我们在客户信息的 UI 绑定过程中使用了双向绑定,但是在 ViewModel 中改变 _selectedCustomer 的数据以后,我们依然需要通知 UI 数据的变化过程,也就是要一个 ViewModel 与 UI 的联动过程。

那么在 WPF 中处理 UI 与视图模型的联动过程,我们可以通过实现 INotifyPropertyChanged 这个接口来实现。打开 MainViewModel 让这个类实现接口 INotifyPropertyChanged 这个接口。使用 Visual Studio 来自动实现这个接口的代码,可以看到这个 INotifyPropertyChanged 实现,实际上就是一个委托或者说是一个事件,这个事件将会发送给视图。

视图在接收到事件以后会根据事件的内容做出 UI 的调整,事件则是通知 UI 视图模型属性发生了变化。所以我们创一个私有的方法(RaisePropertyChanged)来处理这个事件。这个方法将会告诉 UI 到底是哪个属性发生了变化。
方法中我们调用 PropertyChanged ,尤其仅当它不为 null 的时候我们调用 Invoke 方法。通过 Invoke 向 UI 发送事件。 Invoke 方法的第一个参数就是视图模型本身 this ,而第二个参数则是实例化一个 PropertyChangedEventArgs 传入参数的属性名称 propertyName 。

--\ViewModels\MainViewModel.cs

public class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    …………

    public void ClearSelectedCustomer()
    {
        _selectedCustomer = null;
        RaisePropertyChanged(nameof(SelectedCustomer));
    }
}


接下来我们就可以在清空当前选择客户以后调用这个方法通知 UI 了。参数传入 nameof(SelectedCustomer) 。
在客户选择的过程中同样也要调用这个 UI 的事件。
在 SelectedCustomer 的 set 中更新了当前客户以后,向 UI 发送客户更新通知。

--\ViewModels\MainViewModel.cs
    private CustomerViewModel _selectedCustomer;

    public CustomerViewModel SelectedCustomer
    {
        get => _selectedCustomer; 
        set
        {
            if (value != _selectedCustomer)
            {
                _selectedCustomer = value;
                RaisePropertyChanged(nameof(SelectedCustomer));
                LoadAppointments(SelectedCustomer.Id);
            }
        }
    }

接下来我们来完成客户的添加功能。
它具体的业务逻辑是什么呢?这一次我们需要把两个业务混合在同一个方法中,如果当前选定的客户 _selectedCustomer 不为空,那么我们就执行数据的更新工作。否则我们就添加一个新的客户。

--\ViewModels\MainViewModel.cs
    public void SaveCustomer(string name, string idNumber, string address)
    {
        if(SelectedCustomer != null)
        {
            // 更新客户数据
            using (var db = new AppDbContext())
            {
                var customer = db.Customers.Where(c => c.Id == SelectedCustomer.Id).FirstOrDefault();
                customer.Name = name;
                customer.IdNnumber = idNumber;
                customer.Address = address;
                db.SaveChanges();
            }
        }
        else
        {
            // 添加新客户
            using (var db = new AppDbContext())
            {
                var newCustomer = new Customer()
                {
                    Name = name,
                    IdNnumber = idNumber,
                    Address = address
                };
                db.Customers.Add(newCustomer);
                db.SaveChanges();
            }
            LoadCustomers();
        }
    }


其实客户数据的更新和添加在之前的课程中我们就实现了,代码非常简单。

添加客户同样也是使用 using 来托管数据库先创建一个 newCustomer ,…… 完成新客户添加以后我们还要刷新这个客户列表 LoadCustomers 。不过在 LoadCustomers 中还有一个 bug 需要更新,因为我们需要的是刷新 customer 列表,按照目前的逻辑客户列表只会增加不会减少,因此每次加载客户数据的时候我们都应该先重置 customer 列表,然后再添加新数据。

    public void LoadCustomers()
    {
        Customers.Clear();
        …………
    }


接下来处理页面逻辑,打开主页 xaml 文件双击客户“保存”按钮创建点击事件重命名一下这个点击事件 SaveCustomer_Click ,记得在 XML 空间中也需要改一下名字。
因为要访问数据库所以我们需要使用 try catch 来处理一下异常,客户的名称、身份证、住址 均来自文本框 TextBox ,所以我们也需要给这三个文本框加上名字 <TextBox Name="NameTextBox" …… />。

--\MainWindow.xaml
    <StackPanel Grid.Row="1" Grid.Column="1">
        <TextBlock Text="姓名" Margin="10 10 10 0"/>
        <TextBox Name="NameTextBox" Margin="10" Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Text="身份证" Margin="10 10 10 0"/>
        <TextBox Name="IdTextBox" Margin="10" Text="{Binding SelectedCustomer.IdNnumber, Mode=TwoWay}" />
        <TextBlock Text="地址" Margin="10 10 10 0"/>
        <TextBox Name="AddressTextBox" Margin="10" Text="{Binding SelectedCustomer.Address, Mode=TwoWay}" />
        <Button Content="保存" Margin="10 10 10 30" VerticalAlignment="Bottom" HorizontalAlignment="Left" Click="SaveCustomer_Click" />
    </StackPanel>


--\MainWindow.xaml.cs
    private void SaveCustomer_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            string name = NameTextBox.Text.Trim(); 
            string idNumber = IdTextBox.Text.Trim(); 
            string address = AddressTextBox.Text.Trim();

            _viewModel.SaveCustomer(name, idNumber, address);
        }
        catch (Exception error)
        {
            MessageBox.Show(error.ToString());
        }
    }


运行一下试试看,代码跑起来了选择一个客户更改名称点击保存数据保存没有问题。
接着试着添加一个新客户点击保存现在问题出现了!我们明明点击了保存但是客户列表并没有更新,而且也没有报错!这是怎么回事呢?那么我们的数据到底添加成功了吗?
关闭当前这个窗口重新再运行一次,这一次我们就可以看到客户列表中多了一条数据,证明数据已经成功被添加了,那么为什么客户列表并没有成功更新呢?

回到 MainViewModel 还记得我们刚刚使用过的 INotifyPropertyChanged 这个接口吗?

这个接口可以帮我们向 UI 发送视图模型更新的指令,那么是不是我们可以采用类似的方法来继续处理 UI 更新、继续处理用户列表的更新呢?
可以的,不过这个 INotifyPropertyChanged 只能处理“非列表型的数据”,对于列表 WPF 有另一种处理方式,这种处理方式就是 Observable 观察者模式,虽然观察者模式听起来好像很高大上,不过 WPF 已经帮我们做了最顶层的封装了,我们直接使用就可以了,甚至感觉不到观察者模式的存在。

代码修正非常简单找到 List<CustomerViewModel> Customers 这个客户列表,我们使用 ObservableCollection 来代替这个 List 。这个 ObservableCollection 来自 System.Collections.ObjectModel 命名空间。

    public ObservableCollection<CustomerViewModel> Customers { get; set; } = new();

11-14 【重构】显示预约列表

打开 AppointmentViewModel ,与客户视图类似这个预约视图的基本数据来自预约模型 Appointment 。创建一个私有预约对象并且在构造方法中传递数据接着声明两个属性 ID 与预约时间。

处理方式跟 CustomerViewModel 类似。

11-15 【重构】添加新预约

AddAppointment

MVVM重构后项目运行示例图


 

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

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

相关文章

ArkUI:鸿蒙应用响应式与组件化开发指南(一)

文章目录 引言1.ArkUI核心能力概览1.1状态驱动视图1.2组件化&#xff1a;构建可复用UI 2.状态管理&#xff1a;从单一组件到全局共享2.1 状态装饰器2.2 状态传递模式对比 引言 鸿蒙生态正催生应用开发的新范式。作为面向全场景的分布式操作系统&#xff0c;鸿蒙的北向应用开发…

List优雅分组

一、前言 最近小永哥发现&#xff0c;在开发过程中&#xff0c;经常会遇到需要对list进行分组&#xff0c;就是假如有一个RecordTest对象集合&#xff0c;RecordTest对象都有一个type的属性&#xff0c;需要将这个集合按type属性进行分组&#xff0c;转换为一个以type为key&…

AI与.NET技术实操系列(八):使用Catalyst进行自然语言处理

引言 自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能领域中最具活力和潜力的分支之一。从智能客服到机器翻译&#xff0c;再到语音识别&#xff0c;NLP技术正以其强大的功能改变着我们的生活方式和工作模式。 Catalyst的推出极大降低了NLP…

MySQL 8.0 OCP 1Z0-908 题目解析(13)

题目49 Choose the best answer. t is a non - empty InnoDB table. Examine these statements, which are executed in one session: BEGIN; SELECT * FROM t FOR UPDATE;Which is true? ○ A) mysqlcheck --analyze --all - databases will execute normally on all ta…

Docker 一键部署倒计时页面:Easy Countdown全设备通用

Easy Countdown 介绍 Easy countdown是一个易于设置的倒计时页面。可以设置为倒计时或计时器。可用于个人生活、工作管理、教育、活动策划等多个领域。 &#x1f6a2; 项目地址 Github&#xff1a;https://github.com/Yooooomi/easy-countdown &#x1f680;Easy Countdown …

Python训练打卡Day35

模型可视化与推理 知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 模型结构可视化 理解一个深度学习网络最重要的2点…

四、生活常识

一、效应定律 效应 1、沉没成本效应 投入的越多&#xff0c;退出的难度就越大&#xff0c;因为不甘心自己之前的所有付出都付之东流。 2、破窗效应 干净的环境下&#xff0c;没有人会第一个丢垃圾&#xff0c;但是当环境变得糟糕&#xff0c;人们就开始无所妒忌的丢垃圾。…

机器学习圣经PRML作者Bishop20年后新作中文版出版!

机器学习圣经PRML作者Bishop20年后新书《深度学习&#xff1a;基础与概念》出版。作者克里斯托弗M. 毕晓普&#xff08;Christopher M. Bishop&#xff09;微软公司技术研究员、微软研究 院 科学智 能 中 心&#xff08;Microsoft Research AI4Science&#xff09;负责人。剑桥…

Python应用嵌套猜数字小游戏

大家好!今天向大家分享的是有关“嵌套”的猜数字小游戏。希望能够帮助大家理解嵌套。 代码呈现: # 1. 构建一个随机的数字变量 import random num random.randint(1, 10)guess_num int(input("输入你要猜测的数字&#xff1a; "))# 2. 通过if判断语句进行数字的猜…

黑马k8s(十四)

1.Service-概述 service&#xff1a;用于四层路由的负载&#xff0c;Ingress七层路由的负载&#xff1b;&#xff0c;先学习service 开启ipvs 2.Service-资源清单文件介绍 修改每个显示的内容 ClusterIP类型的Service Endpoints&#xff1a;建立service与pod关联 亲和性测试…

Kotlin 中 Lambda 表达式的语法结构及简化推导

在 Kotlin 编程中&#xff0c;Lambda 表达式是一项非常实用且强大的功能。今天&#xff0c;我们就来深入探讨一下 Lambda 表达式的语法结构&#xff0c;以及它那些令人 “又爱又恨” 的简化写法。 一、Lambda 表达式完整语法结构 Lambda 表达式最完整的语法结构定义为{参数名…

Kafka Streams 和 Apache Flink 的无状态流处理与有状态流处理

Kafka Streams 和 Apache Flink 与数据库和数据湖相比的无状态和有状态流处理的概念和优势。 在数据驱动的应用中&#xff0c;流处理的兴起改变了我们处理和操作数据的方式。虽然传统数据库、数据湖和数据仓库对于许多基于批处理的用例来说非常有效&#xff0c;但在要求低延迟…

【后端高阶面经:缓存篇】34、高并发下缓存穿透、击穿、雪崩怎么解决

一、缓存三大核心问题:穿透、击穿、雪崩的本质区别 (一)概念对比表 问题类型核心特征典型场景危害等级缓存穿透数据在缓存和数据库中均不存在,请求直接穿透到数据库恶意攻击(伪造不存在的ID)、业务逻辑漏洞★★★★★缓存击穿热点数据在缓存中过期,大量并发请求同时击穿…

使用Rancher在CentOS 环境上部署和管理多Kubernetes集群

引言 随着容器技术的迅猛发展&#xff0c;Kubernetes已成为容器编排领域的事实标准。然而&#xff0c;随着企业应用规模的扩大&#xff0c;多集群管理逐渐成为企业IT架构中的重要需求。 Rancher作为一个开源的企业级多集群Kubernetes管理平台&#xff0c;以其友好的用户界面和…

【Mini-F5265-OB开发板试用测评】按键控制测试

本文介绍了如何使用按键控制 MCU 引脚的输出电平。 原理 由原理图可知 板载用户按键 K1 和 K2 分别与主控的 PB0 和 PB1 相连。 代码 #define _MAIN_C_#include "platform.h" #include "gpio_key_input.h" #include "main.h"int main(void) …

用C#最小二乘法拟合圆形,计算圆心和半径

用C#最小二乘法拟合圆形&#xff0c;计算圆心和半径 using System; using System.Collections.Generic;namespace ConsoleApp2 {internal class Program{static void Main(string[] args){List<Tuple<double, double>> points new List<Tuple<double, doubl…

四、web安全-行业术语

1. 肉鸡 所谓“肉鸡”是一种很形象的比喻&#xff0c;比喻那些可以随意被我们控制的电脑&#xff0c;对方可以是WINDOWS系统&#xff0c;也可以是UNIX/LINUX系统&#xff0c;可以是普通的个人电脑&#xff0c;也可以是大型的服务器&#xff0c;我们可以象操作自己的电脑那样来…

MYSQL丢失pid处理方式

1、停止服务器 systemctl stop mysqld 2、修改 /data/mysql/etc/my.cnf pid-file /tmp/mysql/mysql.pid 改为 pid-file /data/mysql/mysql.pid 3、创建 touch /data/mysql/mysql.pid ch…

《计算机组成原理》第 2 章 - 计算机的发展及应用​

计算机从诞生至今&#xff0c;经历了翻天覆地的变化&#xff0c;应用领域也在不断拓展。本文将结合 Java 代码实例&#xff0c;带你深入了解计算机的发展历程、应用场景及未来展望&#xff0c;让你在学习理论的同时&#xff0c;还能通过实践加深理解。​ 2.1 计算机的发展史​ …

Github 2025-05-26 开源项目周报Top15

根据Github Trendings的统计,本周(2025-05-26统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目3JavaScript项目3C++项目2Roff项目1Go项目1C#项目1Jupyter Notebook项目1Rust项目1CSS项目1Shell项目1Dockerfile项目…