輕量級MVVM框架Stylet介紹:(11) Screen和Conductors


ViewModel生命周期

一個好的起點是查看 ViewModel 生命周期。

想象一個選項卡式界面 - 類似於Visual Studio,它有一個shell(包含菜單,工具欄等)和一個包含編輯器選項卡的TabControl。在 Stylet 中,每個編輯器選項卡都將由其自己的 ViewModel 提供支持。

現在,其中一個 ViewModel 將通過實例化來開始其生命。接下來,將顯示它。之后,它可能會顯示或隱藏,具體取決於當前處於活動狀態的選項卡,然后最終關閉。在關閉之前,它有機會阻止關閉以提示您保存文件。

簡而言之,這是 ViewModel 的生命周期:它已創建,然后激活(顯示給用戶)。之后,它可以被停用(仍然活着但未顯示)並再次激活任意次數,然后最終被關閉(在被問及它是否准備好關閉之后)。

IDisposable

如果ViewModel實現了IDisposable,那么在其被父類關閉后將自動釋放(除非父類的DisposeChildren的屬性為false).

Conductors介紹

現在,ViewModel 不會神奇地知道它何時顯示、隱藏或關閉。必須告訴它。這是指揮的角色。

簡單地說,Conductors是一個視圖模型,它擁有另一個視圖模型,並且知道如何管理其生命周期。

在我們的Visual Studio示例中,Conductor將是ViewModel,它擁有TabControl,其中顯示了編輯器ViewModels,因此可能是Shell ViewModel。每當用戶選擇新的編輯器選項卡時,Conductor 都會停用舊選項卡,並激活新選項卡。當用戶關閉選項卡時,Conductor 將告訴該選項卡它已關閉,然后確定要顯示的下一個選項卡,並激活該選項卡。

ViewModels 有一個生命周期,它由擁有 ViewModel 的 Conductor 實現。

到目前為止,這已經非常抽象了 - 讓我們進入細節。

IScreen和Screen

正如我們在上面看到的,ViewModel 的生命周期由該 ViewModel 上的 Conductor 調用方法進行管理。這些方法在一組獨立的接口中定義 - 如果您實現了該接口,並且 ViewModel 的管理對象是 Conductor,則將調用該方法。如果需要,您可以選擇所需的接口。

有一個調用的總體接口IScreen來組成它們,還有一個名為Screen 的默認實現。這表現得非常好,你可能永遠不需要實現自己的。

  • IScreenState:用於激活、停用和關閉 ViewModel。具有Activate 、Deactivate 和 Close方法,以及用於跟蹤屏幕狀態更改的事件和屬性。
  • IGuardClose:用於詢問 ViewModel 是否可以關閉。有一個方法CanCloseAsync。
  • IViewAware:有時 ViewModel 需要了解其視圖(何時附加、它是什么等)。此接口通過屬性View和方法AttachView允許這樣做。
  • IHaveDisplayName:有一個DisplayName屬性。這個名稱被用作使用窗口管理器顯示的窗口和對話框的標題,對於像TabControls這樣的東西也很有用。
  • IChild:對於 ViewModel 來說,知道 Conductor 在管理它的是什么(例如,請求關閉它)可能是有利的。如果 ViewModel 實現了IChild ,它將被告知這一點。
    請注意,無法保證調用"激活"、"停用"和"關閉"的順序 - ViewModel 可以連續激活兩次,然后關閉而不停用。由 ViewModel 來注意這些事情,並做出相應的反應。Stylet's Screen就是這樣做的。

Screen有一些虛擬方法,如果你願意,我們鼓勵你覆蓋:

  • OnInitialActivate:第一次激活屏幕時調用,並且永遠不會再調用。對於設置您不想在構造函數中設置的內容非常有用。
  • OnActivate:在屏幕激活時調用。僅當屏幕尚未激活時才會被調用。
  • OnDeactivate:在屏幕停用時調用。僅當屏幕尚未停用時才會被調用。
  • OnClose:在屏幕關閉時調用。只會被調用一次。僅在屏幕停用時調用。
  • OnViewLoaded:在觸發 View 的Loaded事件時調用。
  • CanCloseAsync:當Conductor想知道Screen是否可以關閉時調用,默認情況下,返回Task.FromResult(this.CanClose).但您可以在此處添加自己的異步邏輯。
  • CanClose:默認情況下調用CanCloseAsync。如果要決定是否可以同步關閉,請覆蓋CanClose 。如果要異步決定,請覆蓋 CanCloseAsync。
  • RequestClose(bool? dialogResult = null):當您想向自己的Conductor請求關閉時,您可以調用此方法。如果需要在對話框中顯示,則使用 DialogResult 參數。

Screen 派生自PropertyChangedBase,因此很容易引發 PropertyChanged 通知。

您可能會發現所有 ViewModels 都是 Screen 的子類。這並不是說它們需要 - 您可以創建自己的實現,或者從上面選擇要實現的接口 IScreen- 但它既方便又強大。

有關Conductors的詳細描述

Conductors有各種風格,每種風格都有自己的用例。Conductors可以擁有單個 ViewModel(想想一次顯示一個頁面的導航),也可以擁有多個 ViewModel,但一次只能有一個活動模型(想想上面 Visual Studio 示例中的 TabControl),也可以是所有 ViewModels(想想具有大量獨立元素的網格)。Conductors還可以添加行為,例如保持一個顯示ViewModel的記錄(對於導航很有用)。

與Screen類一樣,Stylet 定義了許多Conductor感興趣的接口,以及許多實現(取決於所需的Conductor行為類型),盡管您當然可以實現自己的接口。

主接口是 IConductor ,它表示一個可供交互的Conductor。它具有以下方法

  • ActivateItem(T item):獲取給定的項目,然后將其激活。是否停用了先前的項目由Conductor決定。
  • DeactivateItem(T item):獲取給定的項目,然后將其停用。是否激活另一個項目由Conductor決定。
  • CloseItem(T item):取出給定的項目,然后將其關閉。這是否會導致另一個項目被激活以取代其位置是特定於Conductor的。

具有單個活動項(無論它們可能有多少個非活動項)的Conductor也實現IHaveActiveItem ,它具有單個屬性 ActiveItem。

所有內置Conductors都將各已經實現IChild接口的item的Parent屬性設置為其身。所有內置Conductors額外實現了IChildDelegate,這允許子項請求關閉(調用CloseItem)。默認Screen實現中,調用Screen.RequestClose將會導致Screen在其父類上調用CloseItem(前提是其父類實現IChildDelegate),這反過來又會導致其父級(如果存在)關閉它。

內置Conductor

Stylet內置了一些Conductor,它們以多種直觀的方式執行。

所有這些Condcutor都派生自Screen ,允許Conductor輕松擁有其他conductor。這意味着您可以以任何您想要的方式組成conductors和screens。

Conductor

這個非常簡單的Conductor擁有一個 ViewModel(類型T),該模型公開為 ActiveItem. ActivateItem方法用於將當前實例替換為新的 ViewModel 實例,並將激活新項並關閉舊項。每當 Conductor 激活 時,它就會激活其ActiveItem ;同樣,它分別在停用或關閉時停用和關閉ActiveItem。

當被問及是否可以關閉它(當CanCloseAsync被調用時)時,它會返回ActiveItem返回的任何內容,如果沒有ActiveItem, 則返回 true。

也可以直接設置ActiveItem,這與調用ActivateItem具有相同的效果。

Conductor的 ViewModel 如下所示 - 綁定了到Conductor的 ActiveItem 屬性的ContentControl:

<Window x:Class="MyNamespace.ConductorViewModel"
        xmlns:s="https://github.com/canton7/Stylet" ....>
   <ContentControl s:View.Model="{Binding ActiveItem}"/>
</Window>

Conductor .Collection.OneActive

該Conductor擁有許多items,但一次只能有一個item處於活動狀態。通過這種方式,它可以對 TabControl 的行為進行建模 - 許多選項卡可以同時存在,但一次只能顯示一個選項卡。

它擁有一個T類型的item的集合,其中一個被設置為ActiveItem。調用ActivateItem將添加的item傳遞到集合的Items,並且還會激活它並將其設置為ActiveItem ;如果之前設置了ActiveItem,則其舊值將被停用,並保留在集合中。

調用DeactivateItem或CloseItem某個項目將分別導致該項目被停用和關閉。由於它不再處於活動狀態,因此不能保持為ActiveItem - 而是選擇另一個項目作為 ActiveItem,並被激活並按此方式設置。默認情況下,新的ActiveItem位於集合Items中要停用/關閉的項前面。

如果需要,可以直接操作集合Items。也可以直接設置ActiveItem,這與調用ActivateItem具有相同的效果。

帶有使用此Conductor的 TabControl 的 ViewModel 可能如下所示(有關簡短版本,請參見下文):

<TabControl ItemsSource="{Binding Items}" SelectedItem="{Binding ActiveItem}" DisplayMemberPath="DisplayName">
   <TabControl.ContentTemplate>
      <DataTemplate>
         <ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False"/>
      </DataTemplate>
   </TabControl.ContentTemplate>
</TabControl>

除了以上風格以外,Stylet還提供了一種可以做同樣的事情的風格。這意味着您可以改為執行以下操作:

<TabControl Style="{StaticResource StyletConductorTabControl}"/>

Conductor .Collection.AllActive

這個Conductor與Conductor .Collection.OneActive非常相似,只是它沒有一個ActiveItem 。相反,它只有Item的集合。激活項目(使用 ActivateItem)后,該項目將添加到此集合中,關閉后,該項目將從此集合中刪除。

調用DeactivateItem將就地停用該項目,而不會將其從集合Items中刪除。

也可以直接操作集合Items。添加的任何項目都將被激活,任何已刪除的項目都將被關閉。

典型的用例可能是使用 ItemsControl,其中所有項同時可見。以這種方式使用 ItemsControl 的 ViewModel 可能如下所示(同樣,有關簡短版本,請參閱下面的內容):

<ItemsControl ItemsSource="{Binding Items}">
   <ItemsControl.ItemTemplate>
      <DataTemplate>
         <ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False"/>
      </DataTemplate>
   </ItemsControl.ItemTemplate>
</ItemsControl>

由於這非常冗長,Stylet 提供了一種樣式,用於設置以下屬性:

<ItemsControl Style="{StaticResource StyletConductorItemsControl}"/>

Conductor .StackNavigation

這個Conductor是 Conductor 和Conductor .Collection.OneActive 之間的混合體,它提供了一些額外的東西:基於堆棧的導航。

它有一個單一的ActiveItem,但也保留了過去活動的項目的(私有的)歷史記錄。激活新項目時,將停用前一個ActiveItem,並將其推送到歷史記錄堆棧。調用GoBack()將關閉當前ActiveItem ,並重新激活此歷史記錄堆棧中的頂部項目,並將其設置為新的 ActiveItem。

如果對當前ActiveItem調用CloseItem,則具有相同的效果。如果調用歷史記錄堆棧中存在的任何項目,則該項目將被關閉並從歷史記錄堆棧中刪除。調用Clear()將關閉並從歷史記錄堆棧中刪除所有項目。

WindowConductor

這個有點奇怪,因為它是內部的,你不需要直接與它互動,但為了引起興趣,我把它包括在這里。每當您使用WindowManager 顯示對話框或窗口時(這包括 Stylet 首次啟動應用程序時顯示的窗口)時,都會有一個新的WindowConductor來管理其生命周期。每當窗口或對話框最小化時,它都會被停用。每當它被最大化時,它就會被激活。如果您的 ViewModel 請求關閉它(見上文RequestClose),則WindowConductor會處理此問題。同樣,如果用戶自己關閉窗口,WindowConductor則會詢問您的 ViewModel 它是否已准備好關閉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM