在 XAML 應用的開發過程中,使用 MVVM 框架能夠極大地提高軟體的可測試性、可維護性。MVVM 的核心思想是關註點分離,使得業務邏輯從 View 中分離出來到 ViewModel 以及 Model 中,從邏輯上來講,這也是業務邏輯應該處的位置。 具體來說,藉助於數據綁定 (Data Bindi ...
在 XAML 應用的開發過程中,使用 MVVM 框架能夠極大地提高軟體的可測試性、可維護性。MVVM 的核心思想是關註點分離,使得業務邏輯從 View 中分離出來到 ViewModel 以及 Model 中,從邏輯上來講,這也是業務邏輯應該處的位置。
具體來說,藉助於數據綁定 (Data Binding) 以及命令 (Command) 等這些XAML 平臺自身所提供的機制,使得 MVVM 在 XAML 平臺上很容易實現。但是,如果你對 XAML 應用開發以及 MVVM 的使用有一定的經驗,你會發現,單純 MVVM 本身並不能解決所有問題,比如頁面導航、彈出對話框、視窗操作等,這時就需要結合一些與它相關的技術,如消息、行為、服務以及依賴註入等。這個原因是 MVVM 主要針對的是 View 的內容,而不是 View 本身的操作或其它別的東西,所以像彈出視窗或者頁面導航之類的操作就需要上述技術的配合。
更具體一點,在 WPF 應用的開發過程中,很多時候我們需要打開和關閉視窗。要以“MVVM方式”來實現,就不容易。當然,我們可以使用消息來解決這個問題。不過,如果過多地使用消息,也會使代碼難以維護並且增加調試的難度。
本文主要通過分享一個類庫來解決這一問題:WpfWindowToolkit,就如名稱所示,它是針對視窗的一些操作類的集合。
一、基本介紹
從介紹里,我們可以看出,它可以解決視窗的打開與關閉,以及在視窗間傳遞參數和返回值等類的問題,而實現這一切都不需要我們在 View 的 CodeBehind 中寫任何代碼,也不需要藉助於消息,所需要作的修改僅在 XAML 代碼和 ViewModel 中,從而遵循了 MVVM 的原則並達到了其目的。
從 Github (http://github.com/imnbwd/WpfWindowToolkit)下載到源碼後,其中有 Demo,通過這個 Demo 我們可以瞭解它是如何使用的。在以下的內容里,我們先簡單對它有些瞭解,更為具體的內容則可以看它的源碼和 Demo。
二、安裝
要在項目中使用它,可以從 Nuget 上下載,或者直接使用命令:
InstallPackage WpfWindowToolkit
三、如何使用
1. 打開視窗
有兩種方式:使用附加屬性或行為。
在使用時,需要先添加命名空間:
xmlns:behaviors="clr-namespace:PraiseHim.Rejoice.WpfWindowToolkit.Behaviors;assembly=WpfWindowToolkit"
xmlns:helpers="clr-namespace:PraiseHim.Rejoice.WpfWindowToolkit.Helpers;assembly=WpfWindowToolk
附加屬性:
<Button x:Name="btn1" helpers:WindowHelper.OpenWindowType="{x:Type local:Window1}" Content="Open window using Window Helper" />
行為:
<Button x:Name="btn5" Margin="0,5,0,0" Content="Open window with parameter using action"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <behaviors:OpenWindowAction Parameter="WPF (action)" WindowType="{x:Type local:Window1}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button>
可以看出,它們都是通過將要打開的視窗的類型(Type) 指定給 WindowType 屬性。
2. 打開視窗時傳遞參數
要在打開視窗的同時,向其 ViewModel 傳遞參數,也很簡單。同上面一樣,只需要再為 Parameter 屬性賦值即可,這個 Parameter 可以綁定到當前視窗的 ViewModel 的成員上,因此,可以將任何類型的數據傳遞給要打開的視窗。
接下來,則是需要註意的,要對被打開的視窗的 ViewModel 作一些修改:使它繼承 ViewModelBaseData<T>,這裡的 T 就是所傳遞參數的類型。然後,通過 InternalData 屬性即可得到傳遞過來的參數。
<behaviors:OpenWindowAction Parameter="WPF (action)" WindowType="{x:Type local:Window1}" />
public class Window1ViewModel : ViewModelBaseData<string> { protected override string InternalData { get; set; } ... }
在打開視窗時,還有一種複雜的方式,同時也會給使用者更靈活的控制。方法是,當前視窗繼承 ViewModelBaseEx 類,然後調用它的 ShowWindow 方法。在此,再不詳述,具體可以參考 Github 上的說明文檔。
3. 從被打開的視窗中返回值
當關閉打開的視窗,要實現這個目的,也需要兩步,首先,第一個視窗(要打開其它視窗的那個視窗)的 ViewModel 需要繼承 ViewModelBaseEx,或 ViewModelBaseEx<T> 類,然後使用 ShowWindow(OpenWindowInfo, Action<TReturnValue>) 方法來打開視窗,其中第二個參數是一個帶參數的 Action,這裡的參數就是返回值,在這個 Action 中,我們就可以處理返回值 。
public class ReturnValueMainWindowViewModel : ViewModelBaseEx<Friend> { public void ShowFriendSelectionWindow() { this.ShowWindow(new OpenWindowInfo { WindowType = typeof(ReturnValueTestWindow) }, friend => { if (friend != null) { MessageBox.Show($"You have selected this friend: {friend.Name}"); } else { MessageBox.Show("No friend has been selected"); } }); } }
第二步,被打開的視窗的 ViewModel,需要實現 IWindowReturnValue<T> 介面,這個介面包含 ReturnValue 屬性,我們只要為它賦值就可以了。
public class ReturnValueTestWindowViewModel : BindableBase, IWindowReturnValue<Friend> { ... public void SetReturnValue() { ReturnValue = SelectedFriend; } ... }
4. 關閉視窗
在 View 中關閉
要關閉視窗,可以使用 CloseWindowAction,如下:
<Button Content="Close the current window with confirmation"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <behaviors:CloseWindowAction ClosingCheckFunc="{Binding CheckBeforeCloseWindow}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button>
它提供一個屬性 ClosingCheckFunc,可以綁定到一個返回布爾值的函數,如果此函數返回 true,則關閉,否則,則不關閉。
處理 Window.Closing 事件
除此以外,在視窗被關閉時,它也提供對 Window 的 Closing 事件的支持,通常我們可能會在這個事件處理中來詢問是否關閉視窗。要這麼做,只要為當前視窗添加一個行為即可,如下:
<i:Interaction.Behaviors> <behaviors:ClosingWindowBehavior ClosingCheckFunc="{Binding CheckBeforeCloseWindow}" /> </i:Interaction.Behaviors>
而其中的 ClosingCheckFunc 屬性所指向的方法則是我們在 ViewModel 中添加的判斷邏輯。
在 ViewModel 中關閉視窗
此外,它也支持在 ViewModel 的中來關閉當前視窗,首先,為視窗添加行為:
<Window> <i:Interaction.Behaviors> <behavior:EnableWindowCloseBehavior /> </i:Interaction.Behaviors> ... </Window>
然後,ViewModel 需要實現 IClosable 介面,它包含一個 Action,名為CloseAction 併在合適的位置調用 CloseAction 即可。
public class CloseTestViewModel : BindableBase, IClosable { public Action CloseWindow { get; set; } ... }
CloseWindow?.Invoke(); // or just CloseWindow();
總結
本文主要介紹了 WPF 視窗操作庫——WpfWindowToolkit 的使用,它完全支持 MVVM,能夠解決視窗的打開、關閉、視窗間的傳值與返回值等問題。如果你正在開發 WPF 應用,並且採用了 MVVM 模式,不妨試一個這個庫。要想多瞭解,可以到 Github 上下載其源碼。
如果什麼問題或建議,歡迎隨時交流。