計算機界的頂極大牛們,站在金字塔尖的專家們,發明了模式,並大力推廣模式,其目的就是想要達到高內聚低耦合。在WPF開發中,經典的程式設計模式是MVVM,是為WPF量身定做的模式,該模式充分利用了WPF的資料繫結機制,最大限度地降低了Xmal檔案和CS檔案的耦合度,也就是UI顯示和邏輯程式碼的耦合度,如需要更換介面時,邏輯程式碼修改很少,甚至不用修改。與WinForm開發相比,我們一般在後臺程式碼中會使用控制元件的名字來操作控制元件的各種屬性,進行UI更新,而在WPF中通常是通過資料繫結來更新UI;在響應使用者操作上,WinForm是通過控制元件的事件來處理,而WPF可以使用命令繫結的方式來處理,耦合度將降低。
MVVM是Model、View、ViewModel的簡寫,MVVM的根本思想就是介面和業務功能進行分離,View的職責就是負責如何顯示資料及傳送命令,ViewModel的功能就是如何提供資料和執行命令。各司其職,互不影響。
在實際的業務場景中我們經常會遇到客戶對介面提出建議要求修改,使用MVVM模式開發,當設計的介面不滿足客戶時,我們僅僅只需要對View作修改,不會影響到ViewModel中的功能程式碼,減少了犯錯的機會。隨著功能地增加,系統會越來越複雜,程式會不停的增加View和ViewModel檔案,這樣一來會將複雜的介面分離成區域性的View,區域性的View對應區域性的ViewModel,各個不同的功能點可能散落在不同的ViewModel中,每個ViewModel只專注自己職能之內的事情。
理想情況下介面和邏輯是完全分離的,單方面更改介面時不需要對邏輯程式碼改動,同樣的邏輯程式碼更改時也不需要更改介面。同一個ViewModel可以使用完全不用的View進行展示,同一個View也可以使用不同的ViewModel以提供不同的操作。
使用MVVM架構具有以下優勢
1、易維護
2、靈活擴充套件
3、易測試
4、使用者介面設計師與程式開發者能更好的合作
本篇文章我們來實現按鈕的Click方法也採用繫結的形式,將業務邏輯程式碼寫到業務邏輯類中,而不是寫在View的後臺cs檔案中,這就需要使用Command指令。
在WPF中使用Command指令的步驟如下:
1)建立命令
2)繫結命令
3)設定命令源
4)設定命令目標
WPF中Command指令的核心是繼承System.Windows.Input.ICommand介面,所有Command指令物件都實現了此介面。當建立自己的Command指令時,不能直接實現ICommand介面,而是要使用System.Windows.Input.RouteCommand類,該類已經實現了ICommand介面,所有WPF中的Command指令都是RouteCommand類的範例。在程式中處理的大部分Command指令不是RoutedCommand物件,而是RoutedUICommand類的範例,它繼承自RouteCommand類。
WPF提供了一個很好的方式來解決事件繫結的問題--ICommand。很多控制元件都有Command屬性,如果沒有,我們可以將Command指令繫結到觸發器上。接下來我們來先實現一個ICommand介面。ICommand需要使用者定義兩個方法bool CanExecute和void Execute。第一個方法可以讓我們來判斷是否可以執行這個命令,第二個方法就是我們具體的命令。
1. 在Visual Studio 2022的「解決方案資源管理器」中,使用滑鼠右鍵單擊「WpfGridDemo.NET7」專案,在彈出選單中選擇「新增-->新建資料夾」。 並將「新資料夾」改名為 「Command」。
2. 在Visual Studio 2022的解決方案資源管理器中,使用滑鼠右鍵單擊「Command」資料夾,在彈出選單中選擇「新增--> 類」,在彈出的「新增新項」對話方塊中,選擇新增 「SaveCommand」類,這是一個我們要實現的儲存操作指令,然後選擇「新增」。
3.要實現在按鈕的Command上繫結方法,代替Click事件,就需要SaveCommand實現ICommand介面,需要我們自己建立型別去實現介面的CanExecute、Execute、CanExecuteChanged,下面就是實現介面的程式碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfGridDemo.NET7.Command
{
public class SaveCommand
{
/// <summary>
/// 命令能否執行
/// </summary>
readonly Func<bool> _canExecute;
/// <summary>
/// 命令執行的方法
/// </summary>
readonly Action _execute;
/// <summary>
/// 命令的建構函式
/// </summary>
/// <param name="action">命令需執行的方法</param>
/// <param name="canExecute">命令是否可以執行的方法</param>
public SaveCommand(Action action, Func<bool> canExecute)
{
_execute = action;
_canExecute = canExecute;
}
/// <summary>
/// 判斷命令是否可以執行
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(Object parameter)
{
if (_canExecute == null)
return true;
return _canExecute();
}
/// <summary>
/// 執行命令
/// </summary>
/// <param name="parameter"></param>
public void Execute(Object parameter)
{
_execute();
}
/// <summary>
/// 事件追加、移除
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
}
}
4.SaveCommand類就是為了在使用命令的時候, 建立一條命令出來用於繫結,這個型別接收兩個引數,一個是命令執行的方法,另一個是有返回值的方法, 這個返回值bool用來確定,該條命令是否可以執行,如果命令不能被執行,則按鈕的IsEnabled就被會設定成不可點選。
1. 在Visual Studio 2022的「解決方案資源管理器」中,使用滑鼠右鍵單擊「WpfGridDemo.NET7」專案,在彈出選單中選擇「新增-->新建資料夾」。 並將「新資料夾」改名為 「ViewModel」。
2. 在Visual Studio 2022的解決方案資源管理器中,使用滑鼠右鍵單擊「ViewModel」資料夾,在彈出選單中選擇「新增--> 類」,在彈出的「新增新項」對話方塊中,選擇新增 「MainWindowVM」類,這是一個ViewModel,然後選擇「新增」。
3.之前我們已經建立了SaveCommand類,並實現了ICommand介面,下面在MainWindowVM中使用SaveCommand類,建立ClickSaveAction方法。
4.由於此次我們使用的是MVVM模組,無法直接使用介面中的控制元件DataGrid的屬性,我們要將介面中所做的修改的資料儲存到資料,則要在MainWindowVM新增一個繫結屬性AreaVM,用於接收介面中傳遞過來的資料。具體如下程式碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using WpfGridDemo.NET7.Entitys;
namespace WpfGridDemo.NET7.ViewModel
{
public class MainWindowVM
{
private Area m_Area;
/// <summary>
/// 員工資料
/// </summary>
public Area AreaVM
{
get { return m_Area; }
set { m_Area = value; }
}
/// <summary>
/// 命令要執行的方法
/// </summary>
void SaveExecute()
{
try
{
GridDbContext db = new GridDbContext();
var list=db.Area.AsTracking().ToList();
Area modifyArea = list.Where(x=>x.Id==AreaVM.Id).FirstOrDefault();
if (modifyArea != null)
{
modifyArea.Name = AreaVM.Name;
modifyArea.Updated = DateTime.Now;
db.SaveChanges();
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 命令是否可以執行
/// </summary>
/// <returns></returns>
bool CanSaveExecute()
{
return true;
}
/// <summary>
/// 建立新命令
/// </summary>
public ICommand ClickSaveAction
{
get
{
return new Command.SaveCommand(SaveExecute, CanSaveExecute);
}
}
}
}
注意,建立這個新的命令的名字需要和我們介面按鈕Command中繫結的名字一致,叫ClickSaveAction。