65.9K
CodeProject 正在变化。 阅读更多。
Home

Stylet:基础入门

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2020年7月21日

CPOL

4分钟阅读

viewsIcon

10885

Stylet MVVM 框架概述。

引言

Stylet 是一个小型 MVVM 框架,支持模块化 WPF 应用程序的开发。它旨在通过减少实现某些 MVVM 功能所需的代码量来简化创建 WPF-MVVM 应用程序的过程。在本文中,我将简要介绍其中一些功能,但请注意,本文不是 WPF 或 MVVM 的介绍。如果您想轻松跟上,应该了解这两者。

背景

我之前写过关于 Prism MvvmCross 的文章,使用的是一个显示代表虚构员工的卡片的示例应用程序。本文遵循相同的模式:示例应用程序中有带有部分员工详细信息的卡片,点击卡片会导航到显示特定员工更多详细信息的页面。

示例应用程序包含三个项目:一个 .NET Core WPF 应用程序项目和两个 .NET Core 类库项目;一个包含共享代码,另一个包含 Stylet 模块。

您可以从 GitHub 克隆或下载示例项目。

Stylet

根视图模型与导体

要在 WPF 应用程序项目中使用 Stylet,您必须引用 Stylet NuGet 包。然后,您必须创建根视图和根视图模型,它们将根据命名约定关联起来。根视图必须是 Window,它充当您应用程序的外壳。在示例项目中,ShellViewModel 是根视图模型。

using Stylet;

namespace StaffStuff.ViewModels
{
    public class ShellViewModel : Conductor<IScreen>.StackNavigation
    {        
        public ShellViewModel(StaffViewModel staffViewModel)
        {
            this.DisplayName = string.Empty;
            this.ActivateItem(staffViewModel);
        }
    }
}

ShellViewModel 继承自 Stylet 的 Conductor<T>.StackNavigation。导体负责管理其拥有的视图模型或视图模型的生命周期。它决定视图模型是激活、停用还是关闭。Conductor<T>.StackNavigation 是一个提供基于堆栈导航的导体。在示例应用程序中,导体将支持从卡片视图导航到详细信息视图。

ShellViewModel 的构造函数中,一个视图模型作为依赖项传入并设置为活动项。由于 ShellViewModel 是一个导体,它现在拥有 StaffViewModel 并将管理其生命周期。在 ShellView 中,活动项(或者说与活动项关联的视图)将放置在 ContentControl 中。

<Window x:Class="StaffStuff.Views.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="https://github.com/canton7/Stylet"
    Background="#FF0D2738" 
    Height="480" Width="800"         
    WindowStartupLocation="CenterScreen">
    <Grid>
        <ContentControl s:View.Model="{Binding ActiveItem}"/>
    </Grid>
</Window>

ActiveItem 是导体的一个属性,通过调用导体的 ActivateItem() 方法来设置。

Bootstrapper

在放置好根视图模型后,Stylet 要求您创建一个引导程序,您可以在其中指定根视图模型。

namespace StaffStuff
{
    public class Bootstrapper : Bootstrapper<ShellViewModel>
    {
        protected override void ConfigureIoC(IStyletIoCBuilder builder)
        {
            builder.AddModule(new ServicesModule());
        }
    }
}

然后,您将 Stylet 的 ApplicationLoader 添加为应用程序资源,并设置引导程序以加载。

<Application x:Class="StaffStuff.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:StaffStuff"
         xmlns:s="https://github.com/canton7/Stylet">
    <Application.Resources>
        <s:ApplicationLoader>
            <s:ApplicationLoader.Bootstrapper>
                <local:Bootstrapper/>
            </s:ApplicationLoader.Bootstrapper>
        </s:ApplicationLoader>
    </Application.Resources>
</Application>

除了设置根视图模型外,引导程序也是您可以将类型注册到 Stylet 的 IoC 容器的地方。这在 ConfigureIoC() 中使用 StyleIoCBuilder 完成,因此您可以执行诸如 builder.Bind<IStaffData>().To<StaffData>() 之类的操作。在示例项目的引导程序中,我向构建器添加了一个模块:builder.AddModule(new ServicesModule())

模块

如引言中所述,Stylet 支持模块化 WPF-MVVM 应用程序的开发。要创建 Stylet 模块,您需要创建一个 .NET Core 类库项目,该项目引用 Stylet NuGet 包,并在项目根目录添加一个派生自 StyletIoCModule 的类。然后,可以在模块的 Load() 方法中将类型注册到 IoC 容器。

using StaffStuff.Common.Interfaces;
using StaffStuff.Services.Services;
using StyletIoC;

namespace StaffStuff.Services
{
    public class ServicesModule : StyletIoCModule
    {
        protected override void Load()
        {
            Bind<IStaffData>().To<StaffData>().InSingletonScope();
        }
    }
}

屏幕截图

Screen 为派生自它的视图模型提供验证、属性更改通知和活动状态监视。它还包含一个 Parent 属性,该属性使视图模型能够知道哪个 Conductor 正在管理它,并允许它请求导体关闭它或激活另一个视图模型。在示例项目中,StaffViewModel(由 ShellViewModel 激活的第一个视图模型)派生自 Screen 类。

using StaffStuff.Common.Interfaces;
using StaffStuff.Common.Models;
using Stylet;
using System.Collections.Generic;

namespace StaffStuff.ViewModels
{
    public class StaffViewModel : Screen
    {
        private readonly IStaffData _staffData;

        public StaffViewModel(IStaffData staffData)
        {
            _staffData = staffData;
        }

        private List<Employee> _employees;
        public List<Employee> Employees
        {
            get => _employees;
            set => SetAndNotify(ref _employees, value);
        }

        protected override void OnInitialActivate()
        {
            Employees = _staffData.GetEmployees();
        }

        public void StaffDetails(Employee employee)
        {
            var staffDetailsVM = new StaffDetailsViewModel { Employee = employee };
            ((ShellViewModel)this.Parent).ActivateItem(staffDetailsVM);
        }
    }
}

OnInitialActivate() 仅在视图模型首次激活时被调用一次。StaffViewModelStaffDetails() 方法调用导体的 ActivateItem() 方法,请求它激活 StaffDetailsViewModel,从而触发导航到其关联视图。

using StaffStuff.Common.Models;
using Stylet;

namespace StaffStuff.ViewModels
{
    public class StaffDetailsViewModel : Screen
    {
        public StaffDetailsViewModel() { }

        private Employee _employee;
        public Employee Employee
        {
            get => _employee;
            set => SetAndNotify(ref _employee, value);
        }

        public void GoBack()
        {
            this.RequestClose();
        }
    }
}

GoBack() 调用 ScreenRequestClose() 方法,该方法会触发导体关闭视图模型。这会通过重新激活导体的先前活动项来触发导航回上一个视图——由于导体是 Conductor<T>.StackNavigation 类型,通过调用导体的 GoBack() 方法也可以触发导航回上一个活动项。

public void GoBack()
{
    ((ShellViewModel)this.Parent).GoBack();
}

Actions

您可能已经注意到,StaffViewModelStaffDetailsViewModel 都没有任何 ICommand 属性。这是因为 Stylet 省去了这些属性,而是允许您视图模型中的一个方法被设置为 ButtonCommand 属性的值。Stylet 通过 Action 来实现这一点。

<Button Command="{s:Action GoBack}">
  ...
</Button>

<DataTemplate x:Key="StaffDataTemplate" DataType="{x:Type models:Employee}">
    <Border Margin="10" Cursor="Hand" BorderThickness="1"
        Background="#FF16394F" BorderBrush="#FF3F5666"
        CornerRadius="8" Width="200" Height="240">
        ...
        <behaviors:Interaction.Triggers>
            <behaviors:EventTrigger EventName="MouseLeftButtonUp">
                <behaviors:InvokeCommandAction Command="{s:Action StaffDetails}"
                                               CommandParameter="{Binding}"/>
            </behaviors:EventTrigger>
        </behaviors:Interaction.Triggers>
    </Border>
</DataTemplate>

请注意,如果您的方法需要传递参数,您也可以将 CommandParameter 的值传递给您的方法。

结论

就是这样!这就是运行示例应用程序所需的所有内容。我认为 Stylet 非常棒,文档也非常全面,尽管如果它为应用程序和模块项目设置提供 Visual Studio 模板,那就更好了——目前没有像 Prism 那样的 Visual Studio 扩展来支持这一点,但希望将来会有。如果您有兴趣进一步了解该框架,我鼓励您阅读 文档 并查看 Stylet 仓库中的 示例 项目。

历史

  • 2020 年 7 月 21 日:初稿
© . All rights reserved.