抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

在WPF開發中(或其他Windows框架),若要實現MVVM架構的話,通常需要實作 INotifyPropertyChangedICommand,這兩個 interface 。自己先前的習慣是,從網路上找其他人寫好的實作,複製到自己的專案中使用,且每開一次新專案,就複製一份。

某天如往常地尋找資料時,發現了微軟在 Nuget 上的 CommunityToolkit.Mvvm 套件(也稱為MVVM工具包)。該套件不但實作了MVVM所需要的物件,還提供一些好用的 Attribute ,讓程式碼可以更精簡,從而讓開發人員更專注在重要的程式邏輯中。

關於 CommunityToolkit.Mvvm 套件使用方法,預計拆分成幾篇不同主題的文章,而本次將記錄,在未使用此套件之前的做法,接著套件的安裝方法、基本使用,以及如何使用其提供的 Attribute 功能。

開發環境,如下

  • IDE : Visual Studio 2022
  • .NET版本 : . NET 6.0

以往的開發狀況

有比較才有傷害,首先回顧尚未使用套件前,以往撰寫所用的類別與狀況。

View 的 XAML 部分如下,本篇文章的範例,View 都是用同一個,後面則是根據 ViewModel 的不同,替換 DataContext 中的 ViewModel 即可。

此 View 分為兩個部分,

第一部分為兩個 TextBox 搭配一個 Button 組成,預計按下 Button 後顯示 MessageBox。

第二部分為一個 TextBox 配上一個 Button ,不同之處在於預計點擊 Button 後,會傳送 CommandParameter,再顯示 MessageBox。

XAML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<Window.Resources>
<Thickness x:Key="Margin.Top">0,10,0,0</Thickness>
</Window.Resources>

<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>

<Grid>

<StackPanel Margin="20">

<!--Name-->
<TextBlock Text="Name:"/>
<TextBox Text="{Binding Path=Name,
UpdateSourceTrigger=PropertyChanged}"/>

<!--Phone-->
<TextBlock Margin="{StaticResource Margin.Top}"
Text="Phone:"/>
<TextBox Text="{Binding Path=Phone,
UpdateSourceTrigger=PropertyChanged}"/>

<Button Margin="{StaticResource Margin.Top}"
Content="Show Info"
Command="{Binding Path=ShowInfoCommand}"/>

<!--Note-->
<TextBlock Margin="{StaticResource Margin.Top}"
Text="Note:"/>
<TextBox x:Name="tbNote"/>

<Button Margin="{StaticResource Margin.Top}"
Content="Show Note"
Command="{Binding Path=ShowNoteCommand}"
CommandParameter="{Binding ElementName=tbNote, Path=Text}"/>

</StackPanel>

</Grid>

View 執行起來,如下圖。
範例的 WPF 程式執行結果

INotifyPropertyChanged 的實作 ViewModelBase 程式碼如下。

每個人的實作與使用上多少有點不同(廢話),這邊則是網路上找到的某個版本。

ViewModelBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace BindingLibrary
{
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;

protected virtual void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(
this, new PropertyChangedEventArgs(propertyName));
}

protected bool SetProperty<T>(
ref T field,
T value,
[CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}

field = value;
OnPropertyChanged(propertyName);

return true;
}
}
}

再來是 ICommand 的實作 RelayCommand 程式碼如下。

RelayCommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System.Windows.Input;

namespace BindingLibrary
{
public class RelayCommand : ICommand
{
private readonly Action<object?> _execute;
private readonly Predicate<object?>? _canExecute;

public RelayCommand
(Action<object?> execute,
Predicate<object?>? canExecute = null)
{
_execute = execute ??
throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}

public event EventHandler? CanExecuteChanged;

public bool CanExecute(object? parameter)
{
return _canExecute == null
|| _canExecute(parameter);
}

public void Execute(object? parameter)
{
_execute(parameter);
}

public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

最後是 ViewModel 的程式碼 MainWindowViewModel。

繼承部分,

  • 此類別繼承 ViewModelBase。

Property 部分,

  • Name,set 時使用 SetProperty 方法外,還會呼叫 RaiseCanExecuteChanged 進行對應的 CanExecute 方法。
  • Phone,單純使用 SetProperty 方法。

Command 部分,

  • ShowInfoCommand,CanExecute 部分呼叫 CanShowInfo() ,檢查 _name 變數是否為 null 或 empty ;Execute 部分呼叫 ShowInfo() ,將 Name 與 Phone 的內容顯示在 MessageBox 中。
  • ShowNoteCommand,Execute 部分呼叫 ShowNote(object? obj) ,將 View 傳進來的物件,傳成字串並顯示在 MessageBox 中。

MainWindowViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MainWindowViewModel : ViewModelBase
{
private string? _name;
public string? Name
{
get => _name;
set
{
SetProperty(ref _name, value);
ShowInfoCommand.RaiseCanExecuteChanged();
}
}

private string? _phone;
public string? Phone
{
get => _phone;
set => SetProperty(ref _phone, value);
}

private RelayCommand? _showInfoCommand;
public RelayCommand ShowInfoCommand
=> _showInfoCommand ??= new RelayCommand(
_ => ShowInfo(), _ => CanShowInfo());

private ICommand? _showNoteCommand;
public ICommand ShowNoteCommand
=> _showNoteCommand ??= new RelayCommand(ShowNote);

private bool CanShowInfo()
{
return !string.IsNullOrEmpty(_name);
}

private void ShowInfo()
{
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

private void ShowNote(object? obj)
{
string? str = (string?)obj;
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}
}

整體程式執行結果,如下,

在輸入 Name 前,Show Info 按鈕為 Disable 狀態,輸入文字後則變為 Enable 狀態,按下 Show Info 按鈕後,出現 MessageBox 並顯示所 Binding 的內容。
範例的 WPF 程式,View 與 ViewModel 執行結果1

輸入 Note 後,按下 Show Note 按鈕,View 會將內容文字經由 CommandParameter 傳到 ViewModel 中,最後由 MessageBox 顯示其內容。
範例的 WPF 程式,View 與 ViewModel 執行結果2

到這裡,示範了如何在 View 與 ViewModel DataBinding 時,實作基礎介面與使用方式,相信有寫過 C# MVVM 架構的人,是會感到很熟悉的。

那麼,接下來將會把上面的範例程式,改成使用套件的情況。

開始使用ToolKit.MVVM

開始使用前,需要進行套件的安裝。

打開專案後,在該專案下找到”相依性”,並在該項目上按右鍵,選擇選單中的 “管理NuGet套件” ,切換到”瀏覽”的頁籤中,在搜尋框中輸入 “ToolKit.Mvvm”。

在搜尋結果列表中,選擇 “CommunityToolkit.Mvvm” 進行安裝。

另外列表中會看到 Microsoft.Toolkit.Mvvm 的套件,該套件是較早期的版本而 CommunityToolkit.Mvvm 則是其最新的版本。

Nuget 搜尋結果
Nuget 中 Toolkit.Mvvm 的搜尋結果

套件安裝完成後,接下來使用套件內的類別來完成 ViewModel,這邊我建立新的 ViewModel,並且將前一個 ViewModel 複製過來做程式碼的修改。

using 下面這兩個 Namespace,當然也可以輸入對應的類別,在IDE上設定引用。

Namespace部分

1
2
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

ViewModel 的程式碼如下,來說明有修改的地方

繼承部分,

  • 繼承 ObservableObject,該類別實作了 INotifyPropertyChangedINotifyPropertyChanging

Property 部分,

  • 基本上是一模一樣,唯一不同的地方是,Name 屬性的 set 方法中,原本呼叫 CanExecute 方法,在該套件下方法名是 NotifyCanExecuteChanged()

Command 部分,

  • Command 在使用上,也是差不多的,類別也是叫 RelayCommand
  • 套件內有分 RelayCommandRelayCommand<T> ,以前自己用的是 RelayCommand 算是兩種的混合,固定參數都是 object 型態,不使用時用 lamda 捨棄,要使用時從 object 來轉型,那既然套件內有做區分,使用對應的類別即可。

MainWindowViewModel01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MainWindowViewModel01 : ObservableObject
{
private string? _name;
public string? Name
{
get => _name;
set
{
SetProperty(ref _name, value);
ShowInfoCommand.NotifyCanExecuteChanged();
}
}

private string? _phone;
public string? Phone
{
get => _phone;
set => SetProperty(ref _phone, value);
}

private RelayCommand? _showInfoCommand;
public RelayCommand ShowInfoCommand
=> _showInfoCommand ??= new RelayCommand(ShowInfo, CanShowInfo);

private ICommand? _showNoteCommand;
public ICommand ShowNoteCommand
=> _showNoteCommand ??= new RelayCommand<string>(ShowNote);

private bool CanShowInfo()
{
return !string.IsNullOrEmpty(_name);
}

private void ShowInfo()
{
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

private void ShowNote(string? str)
{
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}
}

替換成套件中的類別,程式碼不要說87%像,根本一模一樣啊?

這樣有什麼特別的嗎?

ToolKit.Mvvm 還提供的一些 Attribute 供使用,接下來看看此部分。

那麼,接下來就來看看,讓程式碼變短的魔法!

使用套件提供的Attribute

ViewModel 全部改用 Attribute 後的程式碼如下。

程式碼少將近一半,留下最主要的邏輯部分,並且還完全等同於上一段的程式碼,接下來將逐步解析程式碼,以便更好地理解其邏輯並正確的使用。

MainWindowViewModel02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public partial class MainWindowViewModel02 : ObservableObject
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ShowInfoCommand))]
private string? _name;

[ObservableProperty]
private string? _phone;

private bool CanShowInfo()
{
return !string.IsNullOrEmpty(_name);
}

[RelayCommand(CanExecute = nameof(CanShowInfo))]
private void ShowInfo()
{
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

[RelayCommand]
private void ShowNote(string? str)
{
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}
}

首先,要使用此套件的 Attribute 時,除了要繼承 ObservableObject 以外,類別前面還需要加上 partial 關鍵字,這樣加上對應的 Attribute 時,對應的程式碼便會自動產生。忘記加 partial 關鍵字,使用了 Attrubute 也沒關係,IDE會提示的。

※補充:partial 這個關鍵字允許開發者將一個類別、結構、介面或方法的定義分散在兩個或更多的程式碼檔案中。

不過有個先決條件,專案的 C# 版本需要是 C# 8.0以上,才能使用此套件的 Attribute 功能。如果版本不到,IDE便會提示 MVVMTK0008 的錯誤提示。
程式碼的 ViewModel 類別前面增加 partial 關鍵字的畫面

再來是 Attribute 的使用。

在撰寫 ViewModel 時,通常會需要綁定一些變數。在大多數情況下,會先撰寫私有變數,然後再寫相應的屬性。這些屬性不僅用於讀寫私有變數,還需要包含通知 View 變動的方法,如下圖中紅底部分。

然而,如果使用 ObservableProperty Attribute,只需要在私有變數上方加上 [ObservableProperty] 標註,就完成綁定屬性的撰寫,無需額外撰寫紅底部分的程式碼,如下圖中綠底部分所示。

紅底:標準使用
綠底:使用 Attribute
使用 ObservableProperty Attribute 程式碼前後差異比較

那麼為什麼加上 ObservableProperty Attribute 就能達成這個效果呢?

此部分可參考文件註解的詳細說明,如下圖。

大致的意思是,在需要的變數前加上 ObservableProperty Attribute 後,便會自動產生出公開屬性,包含 set 方法中使用 SetProperty。

而屬性的命名,則會自動根據使用的變數名稱來命名,支援小寫、底線、小m開頭。例如變數名稱為 phone、_phone、m_phone 時,自動產生的屬性則會叫做 Phone。
ObservableProperty Attribute 文件註解說明

如果想看自動產生的程式碼,該怎麼做呢?

其中一個方式為,在撰寫的 ViewModel 上,右鍵 > 移至定義 (或按F12)。
於 IDE 中,在類別名上按右鍵的畫面

如下圖,IDE會顯示所有定義該 ViewModel 的檔案,以圖中的狀況,前三個檔案都是自動產生的,這邊點選第一個檔案。
IDE 中顯示在整個解決方案中,該類別的定義

進入到該檔案後,如下圖,IDE會提示該檔案為自動產生的。

在內容中便可找到對應的 Property,同時能看到set方法中,還使用很多的方法,至於怎麼使用,後面的章節會再做說明。
使用 ObservableProperty Attribute 所自動產生的程式碼

如果在屬性中需要使用 CanExecute 通知方法,除了使用 ObservableObject 的同時,還需要加上 NotifyCanExecuteChangedFor Attribute。

如下圖所示,紅底的區域的程式碼可以用綠底的兩個 Attribute 取代,並且將要執行通知的 Command 填入 NotifyCanExecuteChangedFor Attribute 的參數中。

紅底:標準使用
綠底:使用 Attribute
使用 ObservableProperty 和 NotifyCanExecuteChangedFor Attribute 程式碼前後差異比較

除了 Property 可以使用 Attribute 外,Command 也有方便的 Attribute 可以使用。

使用方法也很簡單,只需要在 Command 呼叫時要執行的方法上面,加上 [RelayCommand] 標註即可,如下圖,紅底的區域的程式碼,由兩個綠底的中的Attribute取代。

若要使用 CanExecute 方法,則在 RelayCommand 的 Attribute 中設定 CanExecute 的參數指定要使用的方法。

紅底:標準使用
綠底:使用 Attribute
使用 RelayCommand Attribute 程式碼前後差異比較

套件內雖然有分 RelayCommandRelayCommand<T> 兩種類別,不過 Attribute 都是使用 RelayCommand,自動產生程式碼會根據方法的定義,自動使用對應的類別。

至於自動產生的 Command 命名規則,通常會是方法名加上 “Command” 。例如,在範例程式中 ShowInfo 方法上使用,自動產生的 Command 就會命名為 ShowInfoCommand。

上述的詳細資訊,可至自動產生的程式碼檔案中看到,如下圖所示。
使用 RelayCommand Attribute 所自動產生的程式碼

以上便是範例程式替換成套件內提供的 Attribute 的情況。該範例基本上涵蓋了寫 ViewModel 時較常用到的程式碼。

接下來,將進一步延伸使用 Attribute 的範例,來探索套件內其他功能。

屬性變更時的額外處理

當ViewModel程式中使用了 ObservableProperty
Attribute ,但同時又想在Property的set過程中執行一些額外的操作時,該怎麼辦呢?

這時候,可以試著使用自動產生的 partial 方法來處理看看。

當進入到自動產生的 Property 程式碼中,在 set 方法中可以看到中間會有以該屬性名的 OnXxxChanging()
OnXxxChanged()
方法。如下圖,像是 Name 屬性便有 OnNameChaging() 、OnNameChaged(),而 Phone 屬性則是有 OnPhoneChaging() 、OnPhoneChaged() 。

那這些方法是怎麼定義的呢?
ObservableProperty Attribute 自動產生的程式碼中使用的 Changing 與 Changed 方法

在自動產生的程式碼部分繼續往下拉,可以看到方法的定義。

這些 Changing 、 Changed 方法,都是宣告為 partial ,這樣只要挑選需要的方法,並在 ViewModel 中實作即可。
ObservableProperty Attribute 自動產生的程式碼中使用的 Changing 與 Changed 方法 partial 定義

例如,把下面的 partial void OnNameChanged 程式,寫到 ViewModel中。

這段程式,會在 _name = value 之後,檢查value是否為空,如果是的話,就把 Phone 屬性設為空字串。

基本上,在自動產生的程式碼中確認方法呼叫的時機,並將要實現的方法定義複製到 ViewModel 中,再撰寫需要的程式邏輯即可。

使用 partial 方法

1
2
3
4
5
6
7
8
9
partial void OnNameChanged(string? value)
{
if (string.IsNullOrEmpty(value))
{
Phone = "";
}

Console.WriteLine(value);
}

附上完整的程式碼。

MainWindowViewModel03

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public partial class MainWindowViewModel03 : ObservableObject
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ShowInfoCommand))]
private string? _name;

[ObservableProperty]
private string? _phone;

partial void OnNameChanged(string? value)
{
if (string.IsNullOrEmpty(value))
{
Phone = "";
}

Console.WriteLine(value);
}

private bool CanShowInfo()
{
return !string.IsNullOrEmpty(_name);
}

[RelayCommand(CanExecute = nameof(CanShowInfo))]
private void ShowInfo()
{
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

[RelayCommand]
private void ShowNote(string? str)
{
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}
}

使用異步Command

CommunityToolkit.Mvvm 套件, Command 的部分,除了提供了

  • RelayComman
  • RealyCommand<T>

除此之外,如果有異步程式的需要,也有提供

  • AsyncRelayCommand
  • AsyncRelayCommand<T>

這兩種 Command 類別供使用。

而在使用上,可以像一般情況下那樣宣告 AsyncRelayCommand 並使用 new 來實例化。

個人還是推薦使用 Attribute ,如下面的程式碼所示,跟之前的程式碼基本相同,方法上方加上 [RelayCommand] 標註即可,而差別則是回傳型別,從 void 改成 async Task 。自動產生程式碼就會自動去變更並使用 AsyncRelayCommand

使用AsyncRelayCommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[RelayCommand(CanExecute = nameof(CanShowInfo))]
private async Task ShowInfo()
{
await Task.Delay(500);
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

[RelayCommand]
private async Task ShowNote(string? str)
{
await Task.Delay(500);
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}

附上完整的程式碼。

MainWindowViewModel04

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public partial class MainWindowViewModel04 : ObservableObject
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ShowInfoCommand))]
private string? _name;

[ObservableProperty]
private string? _phone;

private bool CanShowInfo()
{
return !string.IsNullOrEmpty(_name);
}

[RelayCommand(CanExecute = nameof(CanShowInfo))]
private async Task ShowInfo()
{
await Task.Delay(500);
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

[RelayCommand]
private async Task ShowNote(string? str)
{
await Task.Delay(500);
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}
}

普通類別使用 Attribute

當使用的類別已經繼承了其他父類別,但由於 C# 不支援多重繼承,因此無法直接繼承 ObservableObject 來創建 ViewModel 類別,在這種情況下,該怎麼處理呢?

針對這樣的情況,套件中提供了 ObservableObject Attribute。

如下面的程式碼所示,ViewModel 已經繼承了其他類別,此時將該類別宣告為 partial ,並且在類別上方加上 [ObservableObject] 標註,這樣該類別就等同於繼承了 ObservableObject

MainWindowViewModel05

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 示範用父類別
public class SomeOtherClass
{
protected string someString = "Some string";
}

// 使用ObservableObject Attribute
[ObservableObject]
public partial class MainWindowViewModel05 : SomeOtherClass
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ShowInfoCommand))]
private string? _name;

[ObservableProperty]
private string? _phone;

private bool CanShowInfo()
{
return !string.IsNullOrEmpty(_name);
}

[RelayCommand(CanExecute = nameof(CanShowInfo))]
private void ShowInfo()
{
string info = $"Name: {Name}\nPhone: {Phone}";
MessageBox.Show(info, "MessageBox");
}

[RelayCommand]
private void ShowNote(string? str)
{
string info = $"Note: {str}";
MessageBox.Show(info, "MessageBox");
}
}

至於怎麼做到的呢?

有興趣的話可以到自動產生的程式碼中看看,便能看到類別多繼承了 INotifyPropertyChangedINotifyPropertyChanging 這兩個 interface 外,也能看到實作了很多ViewModel需要用的方法,基本上與 ObservableObject 類別所繼承和實作的方法是一樣的。
使用 ObservableObject Attribute 所自動產生的程式碼

總結

以上便是關於 CommunityToolkit.Mvvm 套件的系列文章的Part 1,本次介紹了在 WPF 中使用該套件中的類別來實現 ViewModel ,取代了自己 (網路複製來的) 實現 ViewModel 所需的相關類別。

以及套件所提供的 Attribute 來減少 ViewModel 中的程式碼,像是

類別方面,

  • [ObservableObject]
  • [INotifyPropertyChanged] (※未出現在文章範例中)

Property 方面,

  • [ObservableProperty]
  • [NotifyPropertyChangedFor] (※未出現在文章範例中)
  • [NotifyCanExecuteChangedFor]

Command 方面,

  • [RelayCommand] (※RelayCommand、RelayCommand、AsyncRelayCommand、AsyncRelayCommand 皆使用此 Attribute )

當然還有其他的 Attribute ,這邊僅列出個人認為基礎且較常會用到的部分。

至於能夠實現數據驗證的 ViewModel 功能,將在Part 2中來講解與示範。

另外,雖然本篇文章是以 WPF 來做範例,而該套件也同樣適用在其他的 XAML 平台 (應該吧,看到官網有 UWP 的範例,不過我沒用過)

自言自語543

真沒想到有這樣的套件,以前在建立新的 WPF 專案或撰寫時,很常到別的專案中複製 ViewModelBase ,或是複製 Property 中使用相關方法的程式碼,總是想著,明明是 MVVM 99% 會用到的東西,為什麼一直沒有內建實作的類別可以直接用呢?

然後隨著 ViewModel 龐大起來,Property 和 Command 的程式越來越大串,雖然有 Region 可以收納起來,但是展開後…嗯…還是收起來吧…

結果居然在 Nuget 上,已經有套件的存在了 (小丑竟是我自己!?)

不僅解決了,複製來複製去的程式碼,還把很多程式碼給變不見,有夠讚,有夠讚,有夠讚 (因為很重要所以說三次)

未來就只會看到,舒服乾淨的 ViewModel 程式碼。

評論




本站使用 Volantis 作為主題,總訪問量為