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
繁體地址,請注明出處:http://hk.pswp.cn/news/907206.shtml
英文地址,請注明出處:http://en.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項目…