WPF的MVVM框架Stylet开发文档 11. Screens and Conductors

11. Screens and Conductors

Screens and Conductors是一个简单的主题,但需要一些思维飞跃,并且需要您在理解它们之前涵盖它们的所有部分。相信我,您花时间阅读这篇文章是非常值得的 - 它们非常强大,非常值得投入时间。

ViewModel 生命周期

一个好的起点是查看 ViewModel 生命周期。

想象一个选项卡式界面——类似于 Visual Studio,它有一个(非常简单的)外壳(包含菜单、工具栏等)和一个包含编辑器选项卡的 TabControl。在 Stylet 中,每个编辑器选项卡都将由其自己的 ViewModel 提供支持。

现在,其中一个 ViewModel 将通过实例化开始其生命。接下来,它将被显示。之后,它可能会根据当前处于活动状态的选项卡显示或隐藏,然后最终关闭。就在它关闭之前,它有机会阻止关闭以提示您保存文件。

简而言之,这就是 ViewModel 的生命周期:它被创建,然后被激活(显示给用户)。之后,它可以被停用(仍然存在但未显示)并再次激活任意次数,然后最终被关闭(在被询问是否准备好关闭之后)。

ID一次性

值得注意的是,如果 ViewModel 实现了IDisposable,那么它将在其父级关闭后被释放(除非父级的DisposeChildren属性为 false)。

介绍Conductor

现在,ViewModel 不会神奇地知道它何时显示、隐藏或关闭。必须告诉它。这就是指挥的角色。

简单来说,Conductor 是一个 ViewModel,它拥有另一个 ViewModel,并且知道如何管理它的生命周期。

在我们的 Visual Studio 示例中,Conductor 将是拥有显示编辑器 ViewModel 的 TabControl 的 ViewModel,因此可能是 Shell ViewModel。每当用户选择一个新的编辑器选项卡时,Conductor 将停用旧选项卡并激活新选项卡。当用户关闭一个选项卡时,Conductor 会告诉该选项卡它已关闭,然后决定下一个要显示的选项卡,并激活它。

就是这样,真的。ViewModel 有一个生命周期,它由拥有 ViewModel 的 Conductor 实现。

到目前为止,这是相当抽象的——让我们进入细节。

IScreen and Screen

正如我们在上面看到的,ViewModel 的生命周期由 Conductor 调用该 ViewModel 上的方法来管理。这些方法是在一组独立的接口中定义的——如果您实现接口,并且 ViewModel 的管理是一个 Conductor,该方法将被调用。如果需要,您可以选择所需的界面。

有一个名为的总体接口IScreen将它们组合在一起,还有一个名为的默认实现Screen。这表现得非常好,您可能永远不需要实现自己的 - 但如果您愿意,您可以。

  • IScreenState:用于激活、停用和关闭 ViewModel。具有ActivateDeactivateClosemethods 方法,以及用于跟踪屏幕状态变化的事件和属性。
  • IGuardClose: 用于询问 ViewModel 是否可以关闭。有CanCloseAsync方法。
  • IViewAware:有时 ViewModel 需要知道它的视图(何时附加,它是什么,等等)。这个接口允许通过一个View属性和一个AttachView方法。
  • IHaveDisplayName: 有财产DisplayName。此名称用作使用WindowManager显示的窗口和对话框的标题,并且对于 TabControls 之类的东西也很有用。
  • IChild:对于 ViewModel 来说,知道什么 Conductor 正在管理它(例如请求关闭它)可能是有利的。如果 ViewModel 实现了IChild,它会被告知。

请注意,无法保证调用 Activate、Deactivate 和 Close 的顺序 - ViewModel 可以连续激活两次,然后在不被停用的情况下关闭。ViewModel 会注意到这些事情,并做出相应的反应。Stylet 就是Screen这样做的。

Screen如果您愿意,我们鼓励您重写一些虚拟方法:

  • OnInitialActivate: 第一次激活屏幕时调用,以后不会再调用。用于设置您不想在构造函数中设置的内容。
  • OnActivate:屏幕激活时调用。仅当 Screen 尚未激活时才会被调用。
  • OnDeactivate:当屏幕停用时调用。仅当 Screen 尚未停用时才会调用。
  • OnClose:屏幕关闭时调用。只会被调用一次。只会在屏幕停用时调用。
  • OnViewLoaded:在视图的Loaded事件被触发时调用。
  • CanCloseAsync: 当 Conductor 想知道 Screen 是否可以关闭时调用。默认情况下, retuns Task.FromResult(this.CanClose),但您可以在此处添加自己的异步逻辑。
  • CanClose``CanCloseAsync:默认调用。这只是一种方便。如果您想决定是否可以同步关闭,请覆盖CanClose. 如果您想异步决定,请覆盖CanCloseAsync.
  • RequestClose(bool? dialogResult = null): 当你想请求你自己的 Conductor 关闭时调用它。如果您在对话框中显示,则使用 DialogResult 参数。

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

您可能会发现所有 ViewModel 都是 Screen 的子类。这并不是说它们必须是 - 您可以创建自己的 实现IScreen,或者从上面挑选您想要实现的接口 - 但它既方便又强大。

Conductors详解

导体有多种风格,每种都有自己的用例。conductor 可以拥有一个 ViewModel(想想一次显示一个页面的导航),或多个 ViewModel。具有多个 ViewModel 的视图模型可以一次只有一个处于活动状态(想想上面 Visual Studio 示例中的 TabControl),或者所有的视图模型(想想具有许多独立元素的网格)。Conductor 还可以添加行为,例如记录他们显示了哪些 ViewModel(对导航有用)。

Screen类一样,Stylet 定义了一些导体感兴趣的接口,以及一些实现(取决于你想要的导体行为的种类),当然你可以实现你自己的。

主要接口是IConductor<T>,它代表您将与之交互的导体。它有以下方法

  • ActivateItem(T item):拿取给定的项目,并激活它。这是否会停用先前的项目由指挥决定。
  • DeactivateItem(T item):拿取给定的项目,并将其停用。这是否激活另一个项目由指挥决定。
  • CloseItem(T item):拿走给定的项目,然后关闭它。这是否会导致激活另一个项目来取代它的位置是导体特定的。

具有单个活动项目的指挥(无论他们可能有多少非活动项目)也实现IHaveActiveItem<T>,它具有单一属性ActiveItem

如果项目实现ParentIChild. 所有内置的导体都额外实现了IChildDelegate,它允许孩子请求关闭它(通过调用CloseItem)。在默认Screen实现中,调用Screen.RequestClose将导致屏幕调用CloseItem其父级(提供其父级 implement IChildDelegate),这反过来又导致其父级(如果存在)关闭它。

内置Conductors

Stylet 带有一些内置的conductors ,它们以多种直观的方式执行。

所有这些导体都派生自Screen,允许导体轻松拥有其他导体。这意味着您可以以任何您想要的方式组成您的导体和屏幕。

Conductor<T>

这个非常简单的 conductor 拥有一个 ViewModel(类型T),它公开为ActiveItem. 该ActivateItem方法用于用ActiveItem新的 ViewModel 实例替换当前的,并将激活新项目并关闭旧项目。每当Condcutor<T>被激活时,它就会激活它的ActiveItem; 同样,它ActiveItem分别在停用或关闭时停用和关闭。

当询问它是否可以关闭时(CanCloseAsync调用时),它返回ActiveItem返回的任何内容,如果没有则返回 true ActiveItem

ActiveItem可以直接设置,和调用它的效果一样ActivateItem

这个 Conductor 的 ViewModel 看起来像这样——一个 ContentControl 绑定到 conductor 的 ActiveItem:

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

Conductor<T>.Collection.OneActive

该指挥拥有许多物品,但一次只能激活一个。通过这种方式,它模拟了 TabControl 的行为 - 许多选项卡可以同时存在,但一次只能显示一个。

它拥有一个T名为的集合Items,其中之一更幸运地成为了ActiveItem。调用ActivateItem会将传递的项目添加到Items集合中,并将激活它并将其设置为ActiveItem;如果ActiveItem之前已设置,则其旧值将被停用,并保留在Items集合中。

在项目上调用DeactivateItemCloseItem将分别导致该项目停用和关闭。由于它不再处于活动状态,因此它不能保持为ActiveItem- 相反,另一个项目被选择为ActiveItem,并被激活并设置为这样。默认情况下,新的ActiveItem是存在于Items集合中的项目之前被停用/关闭的那个。

Items如果需要,可以直接操作集合。也ActiveItem可以直接设置,与调用传递那个item效果一样ActivateItem

使用此导体的带有 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 为您提供了一种可以做同样事情的样式。这意味着您可以改为:

Conductor<T>.Collection.AllActive

该导体与 非常相似Conductor<T>.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<T>.StackNavigation

Conductor<T>这个 conductor 是和之间的混合体Conductor<T>.Collection.OneActive,它提供了一些额外的东西:基于堆栈的导航。

它有一个单一的ActiveItem,但也保留了过去活跃项目的(私人)历史。当您激活一个新项目时,前一个项目ActiveItem将被停用,并被推送到历史堆栈中。调用GoBack()将关闭当前的ActiveItem,并重新激活此历史堆栈中的顶部项目,并将其设置为新的ActiveItem

如果您调用CloseItemcurrent ActiveItem,则效果相同。如果您调用CloseItem历史堆栈中存在的任何项目,该项目将被关闭并从历史堆栈中删除。调用Clear()将关闭并从历史堆栈中删除所有项目。

WindowConductor

这个有点古怪,因为它是内部的,你不会直接与它交互,但我把它包含在这里是为了感兴趣。每当您使用显示对话框或窗口时WindowManager(这包括 Stylet 在您首次启动应用程序时显示的窗口),一个新的WindowConductor管理它的生命周期。每当您的窗口或对话框最小化时,它就会被停用。每当它最大化时,它就会被激活。如果您的 ViewModel 请求关闭它(见RequestClose上文),则WindowConductor处理它。同样,如果用户自己关闭了您的窗口,则会WindowConductor询问您的 ViewModel 是否已准备好关闭。

项目原地址:https://github.com/canton7/Stylet
当前文档原地址:https://github.com/canton7/Stylet/wiki/Screens-and-Conductors

上一篇:WPF的MVVM框架Stylet开发文档 10. 执行:调度到 UI 线程
下一篇:WPF的MVVM框架Stylet开发文档 12.可绑定集合BindableCollection

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值