本文章是由機器翻譯。

MVVM

狀態機器模式下 WPF 的命令執行

Tarquin Vaughan-Scott

Windows Presentation Foundation(WPF) 有一個強大的指揮框架使您可以分離使用者介面和命令邏輯。當您使用模型-視圖-模型 (MVVM) 設計模式時,命令被暴露在 ViewModel 作為實現 ICommand 介面的屬性。在視圖上的控制項綁定到這些屬性。當使用者與控制項進行交互時,執行指定的命令。

一如往常,魔鬼是在細節中。真正的挑戰不是在執行命令,但當 ViewModel 處於有效狀態,這一行動的時候確保它執行。通常情況下,"可執行"驗證執行條件運算式使用本地變數,如 IsLoading,CanDoXYZ,SomeObject! = null,等等。每個新的國家需要需要評價,所以這種策略會很快變得過於複雜的附加條件。

一個狀態機解決了的"可執行"的問題,因為它限制所允許的特定狀態的操作。將命令綁定到狀態機直接提供這個棘手的問題的好辦法。

這篇文章將說明如何設計和實現一個狀態機,通過分析您的應用程式。它還會告訴你如何將命令綁定到狀態機。也有一些額外的好處,例如防止可重入性的非同步方法,以及顯示和隱藏控制項為特定的國家。

狀態機

狀態機來用不同的味道,但它們本質上是一種設計模式,它表示從一個狀態轉移到另一個過程。使用者操作 (也稱為觸發器) 導致狀態機狀態之間轉換。規則限制每個國家所允許的操作。

有限狀態機只允許一個國家在一段時間。分層狀態機允許各州和子狀態。分層狀態機往往更有用,因為子狀態繼承其超國家的所有屬性。這可以減少所需的配置。

例如,請考慮使用者按一下搜尋按鈕:應用程式轉換到搜索狀態。在此狀態下,使用者應阻止執行其他操作 (與取消操作可能例外)。搜索狀態會因此有一個規則,阻止或忽略除取消操作的所有操作。一些狀態機實現還允許進入和退出動作。你可以有這種邏輯執行時的狀態是進入和退出。

命令結構

像瓶中的精靈,命令有做他們主人的命令,但命令是什麼?命令在命令列介面,要求使用者鍵入一個指令,然後由應用程式被解釋他們的起源。

這一概念已經在現代介面抽象使用者的預期的行為。例如,如果使用者想要複製某些文本,該命令是複製的文本。使用者可以通過按一下某個功能表項目,按右鍵滑鼠按鍵或甚至使用鍵盤控制 (CTRL + C) 達到這個。如何執行該命令取決於基礎應用程式和賴以建立的框架。

WPF 實現指揮概念通過 ICommand 介面。這是 Microsoft.NET 框架的一部分。此介面有兩種方法和事件:

  • 無效執行 (物件參數) — — 這執行代碼,當調用該命令。
  • bool CanExecute (物件參數) — — 這就決定了是否可以調用的命令。
  • 事件 EventHandler CanExecuteChanged — — 這通知影響 CanExecute 法的條件已經發生變化的框架。這通常是由 WPF 框架處理。

基本上,例如按鈕或功能表項目的命令源實現 ICommandSource 介面。此介面具有一個名為類型 ICommand 的命令屬性。通過將此屬性綁定到 ViewModel ICommand 實現,該控制項將調用 Execute 方法。您還可以啟用和禁用基於 CanExecute 方法的結果。如果作為命令源的控制項沒有 ICommand 屬性 (即,不幸的是,相當普遍),然後您可以使用命令技術的事件。

ICommand 內置於.NET 框架的實現是 RoutedCommand 和 RoutedUICommand。這些視覺化樹的事件路由,而且並不適合使用 MVVM 模式。約什-Smith和棱鏡框架的一部分是常用的 DelegateCommand RelayCommand 用於 MVVM 模式的實現。

本文附帶的原始程式碼中包含演示狀態機的原理以及如何使用它來管理命令的Visual Studio2013年解決方案。應用程式範例允許使用者搜尋的雇員清單,選擇員工,然後顯示對話方塊,用於編輯員工詳細資訊 (請參閱圖 1)。

員工載入和準備好的清單進行編輯
圖 1 員工載入和準備好的清單進行編輯

狀態機設計

在設計過程中的第一步是定義使用流程圖或狀態圖的應用程式的部分。圖 2 顯示員工管理器螢幕流程關係圖。它還包括表明潛在的長期運行的流程,例如搜索的塊。您需要處理這些在一種特殊方式,防止使用者重新運行操作,而是忙著。創建這些仲介的"忙"塊是.NET Framework 中,新的非同步功能控制返回給使用者,他可能可以試著和運行相同的操作,再次導致折返顯得尤其重要。

流程圖顯示過程的員工管理器螢幕
圖 2 流程圖顯示過程的員工管理器螢幕

在設計過程中的第二步是定義允許使用者與應用程式交互的任何命令。在員工管理器螢幕中,使用者可以搜索、 編輯員工,然後結束編輯。使用者也能推動選擇和取消選擇員工的過程,然而,這些並不作為命令進行管理。他們是作為在 DataGrid 控制項的資料繫結處理。這些命令現在映射到工作流箭頭 (或控制流) 在相關點,如中所示圖 2

實現狀態機

你不必從零開始,創建一個狀態機框架有很多自由可用的庫。在.NET 框架內唯一的選項是工作流的基礎 (WF) 狀態機活動。這是過於複雜,我想在這裡,解決這個問題,但是它非常適合長時間運行持久性的工作流。經過一些初步的研究,我解決無國籍狀態機庫,可用作 NuGet 套裝程式 bit.ly/ZL58MG

現在,我可以使用我在設計階段創建觸發器 (流程圖中的每個箭頭) 以及各國 (流程圖中的每個塊) 清單中創建的流程圖。無狀態使用泛型型別對於國家和觸發器,所以我要往前走,兩者使用的枚舉:

public enum States
{
  Start, Searching, SearchComplete, Selected, NoSelection, Editing
}
public enum Triggers
{
  Search, SearchFailed, SearchSucceeded, Select, DeSelect, Edit, EndEdit
}

一旦定義了枚舉,我需要配置每個狀態在使用流暢的介面。重要的選項是:

  • SubstateOf (TState 狀態) — — 表明一個國家具有超級-­狀態和將繼承它的配置。
  • 許可證 TState targetState TTrigger 觸發器) — — 允許過渡到目標狀態通過觸發器狀態。
  • 忽略 (TTrigger 觸發器) — — 如果忽略該觸發器的狀態會導致其被解雇。
  • OnEntry (行動 entryAction) — — 導致進入狀態時要執行的操作。
  • 請將 OnExit (行動 exitAction) — — 導致操作時的狀態被退出,要執行的。

將狀態機的異常,如果在一個國家沒有有效的配置觸發的觸發器。這是搜索狀態的配置:

Configure(States.Searching)
  .OnEntry(searchAction)
  .Permit(Triggers.SearchSucceeded, States.SearchComplete)
  .Permit(Triggers.SearchFailed, States.Start)
  .Ignore(Triggers.Select)
  .Ignore(Triggers.DeSelect);

OnEntry 行動執行搜索過程和許可證允許有關觸發器的觸發。忽略防止發射選擇視圖,然後取消選擇觸發器,當 DataGrid 控制項綁定到基礎資料來源 (與 WPF 中的大多數清單控制項發生煩惱)。

狀態機也暴露出兩種重要方法:

  • 無效火 (TTrigger) — — 這過渡狀態機使用以前的配置。
  • bool CanFire (觸發器觸發器) — — 此方法返回 true,如果目前狀態允許觸發器將被解雇。

這些都是創建命令所需的關鍵方法。他們執行的執行,並且可以執行邏輯。

將命令綁定到狀態機

MVVM 模式公開在 ViewModel 上實現 ICommand 介面的屬性。創建此命令屬性現在是一個簡單的將其執行和 CanExecute 的方法分別綁定到狀態機火和 CanFire 的方法,問題。創建一個擴充方法來集中這種邏輯:

public static ICommand CreateCommand<TState, TTrigger>(
  this StateMachine<TState, TTrigger> stateMachine, TTrigger trigger)
    {
      return new RelayCommand
        (
          () => stateMachine.Fire(trigger),
          () => stateMachine.CanFire(trigger)
        );
    }

一旦此擴充方法是在的地方,在 ViewModel 上創建 ICommand 屬性 (回指中的流圖圖 2 在分析階段確定的命令):

SearchCommand = StateMachine.CreateCommand(Triggers.Search);
EditCommand = StateMachine.CreateCommand(Triggers.Edit);
EndEditCommand = StateMachine.CreateCommand(Triggers.EndEdit);

將視圖綁定到視圖命令

對於作為命令源的控制,你可以有其命令屬性綁定到視圖模型的 ICommand 屬性。應用程式範例有兩個按鈕和功能表項目綁定到的命令屬性在 ViewModel 上:

<Button ToolTip="Search" VerticalAlignment="Center" 
  Style="{StaticResource ButtonStyle}"
  Command="{Binding SearchCommand}">
  <Image Source="Images\Search.png"></Image>
</Button>

現在該命令是直接綁定到狀態機。它將觸發配置的觸發器執行時,但更重要的是,它將被禁用如果該觸發器不允許目前狀態。這種呈現的棘手的護理可以執行邏輯,並在狀態機中都整齊地配置。圖 3 顯示員工管理器螢幕,而是忙著尋找。注意搜索命令被禁用作為搜索觸發器所不允許的搜索狀態。

忙動畫在搜索對話方塊
圖 3 忙動畫在搜索對話方塊

其他好處

一旦你已經實現了狀態機,也可以綁定到其狀態的其他視覺元素。當搜索功能正忙於執行 (並保持銘記,這可能是一個長時間運行的操作,在非同步方法中),是可取的以向使用者顯示一個繁忙的指標。圖 3 顯示動畫的圖像,僅當狀態機是在搜索狀態。做到這一點通過創建一個狀態機狀態轉換為可見度值的自訂轉換器:

public class StateMachineVisibilityConverter : IValueConverter
  {
    public object Convert(object value, Type targetType,
      object parameter, CultureInfo culture)
    {
      string state = value != null ? 
        value.ToString() : String.Empty;
      string targetState = parameter.ToString();
      return state == targetState ? 
        Visibility.Visible : Visibility.Collapsed;
    }
 }

動畫的圖像然後綁定到使用自訂轉換器的狀態機狀態:

<local:AnimatedGIFControl Visibility="{Binding StateMachine.State,
                          Converter={StaticResource StateMachineConverter},
                          ConverterParameter=Searching}"/>

您可以對多在員工管理器螢幕中所示的編輯對話方塊應用同樣的原則圖 4。當狀態機處於編輯狀態時,對話方塊中將變為可見,使用者可以編輯所選的員工的詳細資訊。

編輯對話方塊狀態
圖 4 編輯對話方塊狀態

心態

狀態機模式不僅解決了與命令邏輯相關的問題,而且還會創建一種允許更好地應用分析的心理狀態。很容易想到的命令作為執行某種形式的邏輯的控制項。什麼往往被忽視,這確定何時允許要執行的命令。命令狀態機模式解決了這些問題早就在設計過程中。他們自然狀態機配置的一部分。國家機器已經簡化了我的 ViewModel 代碼,並能做為您的應用程式相同的。

其他參考資料

  • 電腦狀態: 這是出奇地難以找到精心撰寫的簡介狀態機,但遊戲開發商Bob奈斯特龍寫了一個非常好 (bit.ly/1uGxVv6)。
  • WPF 使用 MVVM: WPF 使用 MVVM 約什-Smith的"父親"提供了 ICommand 介面,RelayCommand,在 2009 年 2 月執行 MSDN 雜誌 條 (msdn.microsoft.com/magazine/dd419663)。
  • 命令: WPF 指揮官方 MSDN 文檔討論了路由的命令和事件是.NET 框架的一部分 (bit.ly/1mRCOTv)。
  • 路由的命令: Brian諾伊斯涵蓋路由的命令在 2008 年 9 月 MSDN 雜誌條。它是一個很好的起點,瞭解路由的命令和 MVVM 命令模式之間的區別 (bit.ly/1CihBVZ)。
  • 命令、 RelayCommands 和 EventToCommand: Laurent Bugnion2013 年 5 月文章介紹如何將事件轉換為命令,這對於不實現 ICommandSource 介面的控制項很有用 (msdn.microsoft.com/magazine/dn237302)。
  • 棱鏡框架: 棱鏡框架的官方文檔討論 MVVM 模式和 DelegateCommand (bit.ly/1k2Q6sY)。
  • NuGet 無國籍的包裝: NuGet Web 網站上的無國籍的套裝軟體都帶有下載說明 (bit.ly/1sXBQl2)。

Tarquin Vaughan-Scott 是在 InfoVest 的開發組長 (infovest.co.za),在開普敦,南非,造成金融業的資料管理解決方案的基礎。他主要的興趣是資料庫的設計和體系結構 — —­尤其是事務性之間的差異和資料倉儲系統。聯繫到他在 tarquin@infovest.co.za

感謝以下的微軟技術專家對本文的審閱:NicholasBlumhardt
NicholasBlumhardt (無狀態) 是微軟的前雇員,作為 MEF 團隊的一部分