WPF 原生系結和命令功能使用指南
魏劉宏 2020 年 2 月 21 日
如今,當談到 WPF 時,我們言必稱 MVVM、框架(如 Prism)等,似乎已經忘了不用這些的話該怎么使用 WPF 了,當然,這里說的不用框架和 MVVM,并不是說像使用 Winform 那樣使用 WPF,而是追本溯源,重識 WPF 與生俱來的系結和命令的風采,
一、系結的使用
目標:前臺頁面通過系結獲取后臺屬性的值,
這個目標實際上分為兩部分,一是前臺獲取后臺的屬性值,二是屬性值變動后能夠及時體現出來,
要實作目標的第一部分,實際只需在表單后臺的建構式中添加一行代碼即可:
this.DataContext = this;
這行代碼很關鍵,MVVM 模式中頁面與 ViewModel 關聯也是通過指定頁面類的 DataContext 為相應的 ViewModel 物件來實作的,
下面再來說說如何實作目標的第二部分,也就是屬性變化后能及時體現出來,包括后臺屬性變化后前臺顯示自動變化,以及前臺修改了內容,后臺屬性的值跟著改變,眾所周知,這就是系結,而要實作這一功能,需要相關類實作一個屬性變動通知介面 —— InotifyPropertyChanged ,具體演變程序可參考網上的文章《 .NET 4.5 (C#):INotifyPropertyChanged 執行的演變:從運算式樹到呼叫方資訊的 BindableBase 型別 | Mgen》,這里直接給出最后的結果,
首先,實作 InotifyPropertyChanged 當然是必要的,如果是要系結其他類,則讓該類實作之,如果是直接在視窗后臺做相關功能,則最終視窗類看上去像這樣:
public partial class MainWindow : Window, INotifyPropertyChanged
然后添加一個事件和兩個方法:
public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { var eventHandler = this.PropertyChanged; eventHandler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) { if (Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; }
最后就是要提供系結的屬性了,可以像下面這樣寫:
private string _UserName = "wlh"; public string UserName { get => _UserName; set => SetProperty(ref _UserName, value); }
前臺系結就很簡單了:
<TextBox Text="{Binding UserName, Mode=TwoWay}"></TextBox>
二、命令 ICommand
WPF 和 Winform 的重大區別就是,用戶的互動、資料的變化等,在 Winform 中,都需要程式員一點一點仔細地手動處理,而在 WPF 中,資料是系結的,互動通過命令傳遞,所以很多事情其實 WPF 這個大框架就可以幫我們自動處理了,說了這么多,其實就是說 Winform 是事件驅動的,而 WPF 是資料驅動的,所以在 Winform 中常用的按鈕點擊事件等各種事件,在 WPF 中是不怎么用了,而是使用命令,
命令也是系結的,先來看看前臺的樣子:
<TextBox Text="{Binding UserName, Mode=TwoWay}"></TextBox>
至于后臺怎么寫,先不急,通過《[WPF] ICommand 最佳使用方法》一文,我們知道首先需要一個輔助類:
public class RelayCommand : ICommand { private readonly Predicate<object> _CanExecute; private readonly Action<object> _Execute; public RelayCommand(Predicate<object> canExecute, Action<object> execute) { this._CanExecute = canExecute; this._Execute = execute; } public event EventHandler CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } public bool CanExecute(object parameter) { return _CanExecute(parameter); } public void Execute(object parameter) { _Execute(parameter); } }
可見 ICommand 中主要有兩個方法,一個檢查命令是否可用的 CanExecute (),以及實際干活的 Execute () ,
然后在后臺添加一個 “DoSomething” 的命令,也就是上面新建的 RelayCommand 型別:
private ICommand _DoSomething; public ICommand DoSomethingCommand { get { return _DoSomething ??= new RelayCommand( o => _CanDoSomething(o), o => { _DoSomethingMethod(o); }); } } private readonly Predicate<object> _CanDoSomething = o => true; // 可在之后再賦值,避免方法體中訪問屬性等受阻; private readonly Action<object> _DoSomethingMethod = o => { // do something };
這些還可以進一步簡化為:
public ICommand DoSomethingCommand { get; set; } /// <summary> /// 命令方法賦值(在構造方法中呼叫) /// </summary> private void SetCommandMethod() { DoSomethingCommand ??= new RelayCommand(o => true, async o => { // do something }); }
最后來看看對應前臺”GetTokenCommand” 命令的實際業務代碼:
public ICommand GetTokenCommand { get; set; } /// <summary> /// 命令方法賦值 (在建構式中呼叫) /// </summary> private void SetCommandMethod() { GetTokenCommand ??= new RelayCommand(o => !(string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password)), async o => { var req = new ReqGetToken() { userName = UserName, password = Password, }; var res = await GetToken(req); if (res.Code) { Token = res.Token; } }); }
可以看到,在檢查命令是否可用的部分,沒有像樣板代碼那樣直接回傳 true ,而是按照實際情況判斷,這樣的效果就是,當條件不滿足時,前臺相關控制元件自動禁用:

最后,經過我們這樣寫,其實和 MVVM 模式已經很接近了,只要把后臺所有代碼都移到另一個類,然后將頁面的 DataContext 重新指定一下,就能實作頁面顯示和業務邏輯分離了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/65259.html
標籤:其他
上一篇:現在java
下一篇:vue下載檔案
