詳解WPF中MVVM架構(gòu)中的多種數(shù)據(jù)綁定方式
WPF基礎(chǔ)框架
通過繼承接口INotifyPropertyChanged和ICommand 來實(shí)現(xiàn)。
數(shù)據(jù)綁定部分
INotifyPropertyChanged原代碼如下,WPF框架會(huì)自動(dòng)注冊(cè)DataContext對(duì)象中的PropertyChanged事件(若事件存在)。然后根據(jù)該事件修改前端屬性。
namespace System.ComponentModel
{
// Notifies clients that a property value has changed.
public interface INotifyPropertyChanged
{
// Occurs when a property value changes.
event PropertyChangedEventHandler? PropertyChanged;
}
}
注意:INotifyPropertyChanged接口并非是強(qiáng)制要求的,當(dāng)不需要通過設(shè)置屬性自動(dòng)修改頁(yè)面數(shù)據(jù)時(shí),也就是不需要執(zhí)行set方法時(shí),不需要繼承該接口。若使用了通知集合ObservableCollection,也是不需要專門通過事件通知頁(yè)面的。
public class NativeViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<StackPanel>
<TextBox Text="{Binding Name}" />
</StackPanel>
數(shù)據(jù)綁定中的命令綁定
ICommand 原代碼如下,可以看到其中有按鈕操作常用的幾種屬性:是否可用、可用性變化、觸發(fā)事件。與前端通過Click屬性指定事件相比:一個(gè)是前端指定要執(zhí)行的邏輯,一個(gè)是由vm來確定最終的執(zhí)行邏輯,相當(dāng)于是反轉(zhuǎn)了控制方??梢愿鶕?jù)需要使用、并非強(qiáng)制要求。
#nullable enable
using System.ComponentModel;
using System.Windows.Markup;
namespace System.Windows.Input
{
//
// 摘要:
// Defines a command.
[TypeConverter("System.Windows.Input.CommandConverter, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
[ValueSerializer("System.Windows.Input.CommandValueSerializer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
public interface ICommand
{
//
// 摘要:
// Occurs when changes occur that affect whether or not the command should execute.
event EventHandler? CanExecuteChanged;
//
// 摘要:
// Defines the method that determines whether the command can execute in its current
// state.
//
// 參數(shù):
// parameter:
// Data used by the command. If the command does not require data to be passed,
// this object can be set to null.
//
// 返回結(jié)果:
// true if this command can be executed; otherwise, false.
bool CanExecute(object? parameter);
//
// 摘要:
// Defines the method to be called when the command is invoked.
//
// 參數(shù):
// parameter:
// Data used by the command. If the command does not require data to be passed,
// this object can be set to null.
void Execute(object? parameter);
}
}
對(duì)于ICommand屬性,推薦使用方式是不要讓其觸發(fā)PropertyChanged,不應(yīng)該被動(dòng)態(tài)設(shè)置,而應(yīng)該初始定好。不過如果使用場(chǎng)景確實(shí)需要,應(yīng)該也是能生效的。
public class NativeViewModel
{
public ICommand GreetCommand { get; }
public NativeViewModel()
{
// 使用自定義的 RelayCommand 需要自己實(shí)現(xiàn)
GreetCommand = new RelayCommand();
}
}
// 需要實(shí)現(xiàn)的簡(jiǎn)單命令類
public class RelayCommand : ICommand
{
// 略
}
<StackPanel>
<Button Content="Say Hello" Command="{Binding GreetCommand}" />
</StackPanel>
CommunityToolkit.Mvvm 方式
CommunityToolkit.Mvvm 利用 C# 的源碼生成器,在編譯時(shí)自動(dòng)生成INotifyPropertyChanged和ICommand的樣板代碼。需要nuget包CommunityToolkit.Mvvm。
其僅在vm上有區(qū)別,在實(shí)際綁定方式上沒有區(qū)別。
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
// 3. 使用 [ObservableObject] 特性或繼承 ObservableObject
[ObservableObject]
public partial class ToolkitViewModel
{
// 4. 使用 [ObservableProperty] 標(biāo)記字段,自動(dòng)生成名為 "Name" 的屬性
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(GreetCommand))] // 當(dāng) Name 改變時(shí),通知 GreetCommand 重新驗(yàn)證
private string _name;
[ObservableProperty]
private string _greeting;
// 5. 使用 [RelayCommand] 自動(dòng)生成名為 GreetCommand 的 ICommand 屬性
[RelayCommand(CanExecute = nameof(CanGreet))]
private void Greet()
{
Greeting = $"Hello from Toolkit, {Name}!";
}
private bool CanGreet() => !string.IsNullOrWhiteSpace(Name);
}
ReactiveUI
ReactiveUI 將響應(yīng)式編程理念引入 MVVM,核心是使用ReactiveObject和WhenAnyValue等來聲明數(shù)據(jù)流和反應(yīng)關(guān)系。需要nuget包ReactiveUI.WPF。
其僅在vm上有區(qū)別,在實(shí)際綁定方式上沒有區(qū)別。
using ReactiveUI;
using System.Reactive.Linq;
// 6. 繼承 ReactiveObject
public class ReactiveViewModel : ReactiveObject
{
// 7. 使用 [Reactive] 特性或 WhenAnyValue
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
private readonly ObservableAsPropertyHelper<string> _greeting;
public string Greeting => _greeting.Value;
// 8. 使用 ReactiveCommand 創(chuàng)建命令
public ReactiveCommand<Unit, Unit> GreetCommand { get; }
public ReactiveViewModel()
{
// 判斷命令何時(shí)可執(zhí)行:當(dāng) Name 不為空時(shí)
var canGreet = this.WhenAnyValue(x => x.Name, name => !string.IsNullOrWhiteSpace(name));
// 創(chuàng)建命令
GreetCommand = ReactiveCommand.CreateFromTask(
execute: async () => { /* 可以執(zhí)行異步操作 */ return $"Hello from ReactiveUI, {Name}!"; },
canExecute: canGreet // 綁定可執(zhí)行條件
);
// 9. 將命令的執(zhí)行結(jié)果(一個(gè)IObservable<string>)訂閱到 Greeting 屬性
_greeting = GreetCommand.ToProperty(this, x => x.Greeting, initialValue: "Waiting...");
// 另一種更直接的寫法(不通過命令結(jié)果):
// GreetCommand = ReactiveCommand.Create(() => { Greeting = $"Hello from ReactiveUI, {Name}!"; }, canGreet);
// 但上面那種方式展示了將 IObservable 流轉(zhuǎn)換為屬性的強(qiáng)大能力。
}
}
到此這篇關(guān)于詳解WPF中MVVM架構(gòu)中的多種數(shù)據(jù)綁定方式的文章就介紹到這了,更多相關(guān)WPF MVVM架構(gòu)的數(shù)據(jù)綁定內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#零基礎(chǔ)開發(fā)中最重要的概念總結(jié)
這篇文章主要為大家詳細(xì)介紹了C#零基礎(chǔ)開發(fā)中最重要的一些概念,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,需要的可以參考一下2023-02-02
C#從foreach語(yǔ)句中枚舉元素看數(shù)組詳解
這篇文章主要給大家介紹了關(guān)于C#從foreach語(yǔ)句中枚舉元素看數(shù)組的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-05-05
c#斐波那契數(shù)列(Fibonacci)(遞歸,非遞歸)實(shí)現(xiàn)代碼
c#斐波那契數(shù)列(Fibonacci)(遞歸,非遞歸)實(shí)現(xiàn)代碼,需要的朋友可以參考一下2013-05-05

