Stylet框架

Stylet框架

编辑时间:2023/8/25

1.Stylet简介

Stylet是一个小巧但功能强大的MVVM框架,灵感来自Caliburn.Micro。其目的是进一步降低复杂性和魔力(译者注:Caliburn.Micro有很多让人抓狂的约定,看起来像魔法,这对新手而言一点都不友好),让不熟悉任何MVVM框架的人(同事)更快地跟上速度。

它还提供了Caliburn.Micro中不可用的功能,包括自己的IoC容器,简单的ViewModel验证,甚至是与MVVM兼容的MessageBox。

低LOC数量和非常全面的测试套件使其成为使用和验证/验证SOUP具有高开销的项目的一个有吸引力的选择,其模块化工具包架构意味着它很容易使用你喜欢的部分,或者替换你不喜欢的部分。

2.Prism与Stylet对比

Prism 优势:

  1. 模块化架构: Prism 鼓励使用模块化架构,可以将应用程序拆分为独立的模块,每个模块有自己的视图、视图模型和服务。这有助于分离关注点,提高应用程序的可维护性。
  2. 事件聚合器: Prism 提供了事件聚合器,允许模块之间进行松耦合的通信。模块可以发布和订阅事件,从而实现解耦的通信方式。
  3. 导航支持: Prism 提供了强大的导航支持,可以管理复杂的导航流程和导航参数。
  4. 依赖注入: Prism 集成了依赖注入容器,可以帮助管理应用程序中的依赖关系,使代码更加可测试和可扩展。
  5. 文档和社区支持: Prism 有较多的文档和社区支持,有大量的教程、示例和解答。

Prism 劣势:

  1. 学习曲线: 由于 Prism 提供了丰富的功能,初学者可能需要一些时间来掌握其概念和用法。
  2. 较大的库: 由于 Prism 提供了许多功能,它的库大小相对较大,这可能会在一些资源受限的项目中造成一些影响。

Stylet 优势:

  1. 轻量级: Stylet 是一个相对轻量级的框架,专注于提供基本的 MVVM 支持,适用于小到中型的应用程序。
  2. 简单直观: Stylet 的设计使其非常简单直观,对于那些不需要复杂功能的项目来说,它可以更容易上手。
  3. 性能: 由于 Stylet 较为精简,可能在某些情况下具有更好的性能表现。

Stylet 劣势:

  1. 功能有限: Stylet 的功能相对较少,特别是在涉及复杂的模块化、导航和事件通信时可能会有限制。
  2. 缺乏广泛的社区支持: 相比于 Prism,Stylet 的社区规模可能较小,因此找到解决方案和资源可能会更具挑战性。

3.创建stylet程序

3.1创建一个WPF程序,添加stylet引用

Stylet框架_第1张图片

3.2创建一个Bootstrapper.cs的类并编辑

using Stylet;
using System;
using System.Windows.Threading;
using System.Windows;
using System.Data;
using WPF_Style.ViewModel;

namespace WPF_Style
{
    public class Bootstrapper : Bootstrapper<ViewModel.ViewModels.MainViewModel>
    {

    }
}

3.3更改App.xaml文件

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

3.4创建Views文件与ViewModels进行自动绑定使用

Stylet框架_第2张图片

3.5编写xaml代码与C#代码

<Window
    x:Class="WPF_Style.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WPF_Style.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:s="https://github.com/canton7/Stylet"
    Title="MainView"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Grid>
        <TextBox
            Width="165"
            Height="39"
            Margin="291,110,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Text="{Binding UserName}"
            TextWrapping="Wrap" />
        <Button
            Width="109"
            Height="39"
            Margin="319,178,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Click="{s:Action btnClick}"
            Content="刷新" />
    Grid>
Window>
using Stylet;
using System;

namespace WPF_Style.ViewModels
{
    public class MainViewModel : Screen
    {
        public MainViewModel()
        {
        }
        #region 属性
        private string _userName = "2222";

        public string UserName
        {
            get { return _userName; }
            set { SetAndNotify(ref _userName, value); }
        }
        #endregion

        #region 事件
        public void btnClick(object sender, EventArgs e)
        {
            UserName = "123456";
        }
        #endregion
    }
}

4.增强用法

4.1IOC注入

public class Bootstrapper : Bootstrapper<MainViewModel>
{
    protected override void OnStart()
    {
        // This is called just after the application is started, but before the IoC container is set up.
        // Set up things like logging, etc
    }

    protected override void ConfigureIoC(IStyletIoCBuilder builder)
    {
        // Bind your own types. Concrete types are automatically self-bound.
        //builder.Bind<ITestService>().To<TestService>().InSingletonScope();
        //builder.AddModule<FeatureSetModule>()
    }

    protected override void Configure()
    {
        // This is called after Stylet has created the IoC container, so this.Container exists, but before the
        // Root ViewModel is launched.
        // Configure your services, etc, in here
    }

    protected override void OnLaunch()
    {
        // This is called just after the root ViewModel has been launched
        // Something like a version check that displays a dialog might be launched from here
    }

    protected override void OnExit(ExitEventArgs e)
    {
        // Called on Application.Exit
    }

    protected override void OnUnhandledException(DispatcherUnhandledExceptionEventArgs e)
    {
        // Called on Application.DispatcherUnhandledException
    }
}
private readonly ITestService _test;
private readonly IModule _module;
public MainViewModel(ITestService test,IModule module)
{
    _test = test;
		_module = module;
		_module.Initialize();
}

4.2命令绑定

<Button
     Width="109"
     Height="39"
     Command="{s:Action btnClick}"
     Content="按钮1" />
 <Button
     Width="109"
     Height="39"
     Click="{s:Action btnClick}"
     Content="按钮2" />
 <Button
     Width="109"
     Height="39"
     s:View.ActionTarget="{Binding MainViewModel}"
     Command="{s:Action btnClick}"
     Content="按钮3" />

4.3订阅-发布模式

实现订阅-发布模式通常是通过使用内置的事件聚合器(Event Aggregator)来实现的。事件聚合器允许不同的部分(例如视图模型)在没有直接依赖的情况下进行通信,从而实现松耦合。

定义事件:

public class MyEvent
{
    public string Message { get; set; }
}

订阅者视图模型:

using Stylet;

public class SubscriberViewModel : Screen, IHandle<MyEvent>
{
    private readonly IEventAggregator _eventAggregator;
    public string ReceivedMessage { get; set; }

    public SubscriberViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this); // 订阅事件
    }

    // 实现 IHandle<MyEvent> 接口的方法,处理事件
    public void Handle(MyEvent message)
    {
        ReceivedMessage = message.Message;
    }
}

发布者视图模型:

using Stylet;

public class PublisherViewModel : Screen
{
    private readonly IEventAggregator _eventAggregator;

    public PublisherViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }

    public void PublishEvent()
    {
        var myEvent = new MyEvent
        {
            Message = "Hello from the publisher!"
        };

        _eventAggregator.Publish(myEvent); // 发布事件
    }
}

4.4IScreen和Screen

IScreen是一个接口,用于标识一个类作为视图模型。

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

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 通知。

4.5Conductor

  1. Conductor
    • Conductor 是最基本的 Conductors 类型,它实现了 IScreen 接口,并提供了管理子视图模型的基本方法。
    • 您可以使用 ActivateItem 方法来激活一个子视图模型。通过激活子视图模型,它将被显示在 UI 中,并且 OnActivate 方法将会被调用。
    • 使用 DeactivateItem 方法可以停用子视图模型,并将其从 UI 中移除。OnDeactivate 方法将会被调用。
  2. Conductor.OneActive
    • Conductor.OneActiveConductor 的一种特殊类型。它专门用于管理多个子视图模型,但只允许一个活动的子视图模型显示。
    • 当激活一个新的子视图模型时,之前的子视图模型将被停用并从 UI 中移除。
  3. Conductor.Collection.OneActive
    • Conductor.Collection.OneActive 是用于管理一组子视图模型的 Conductors 类型。
    • 它允许您在一个 UI 容器中显示多个子视图模型,但只允许一个子视图模型是活动的。
    • 您可以使用 Items 属性来管理子视图模型的集合。

代码示例:

using Stylet;

public class ParentViewModel : Conductor<IScreen>.Collection.OneActive
{
    public void ShowChildViewModel()
    {
        var childViewModel = new ChildViewModel();
        ActivateItem(childViewModel); // 激活子视图模型
    }
}

public class ChildViewModel : Screen
{
    // 子视图模型的属性和逻辑
}

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

4.6BindableCollection

BindableCollection 继承自**System.Collections.ObjectModel.ObservableCollection**,因此它继承了 ObservableCollection 的功能,如自动通知界面更改。

• 新增方法:AddRange,RemoveRange,Refresh

• 线程安全

代码示例:

using Stylet;
using System.Collections.ObjectModel;

public class MyViewModel : Screen
{
    private readonly BindableCollection<string> _items = new BindableCollection<string>();

    public BindableCollection<string> Items
    {
        get { return _items; }
    }

    public MyViewModel()
    {
        _items.Add("Item 1");
        _items.Add("Item 2");
        _items.Add("Item 3");
    }

    public void AddNewItem()
    {
        _items.Add("New Item");
    }

    public void RemoveItem(string item)
    {
        _items.Remove(item);
    }
}

4.7ValidationException

ValidationException 是一个异常类,用于表示数据验证失败时的异常。当使用数据绑定或验证时,如果数据不符合预期的规则或条件,就可以引发 ValidationException 来表示出现了验证错误。

代码示例:

using Stylet;
using System;

public class MyViewModel : Screen
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ValidationException("Name cannot be empty.");
            }

            _name = value;
        }
    }

    public void Save()
    {
        try
        {
            // 在保存数据之前,进行数据验证
            Name = "";
            
            // 保存数据逻辑
            Console.WriteLine("Data saved successfully.");
        }
        catch (ValidationException ex)
        {
            Console.WriteLine($"Validation error: {ex.Message}");
        }
    }
}

4.8StyletIoC

StyletIoC 是 “Stylet” 框架中的一个轻量级的依赖注入容器。它允许您管理应用程序中的依赖关系,并在需要时自动解析和注入这些依赖关系。使用 StyletIoC,您可以实现松散耦合、可测试和可维护的应用程序架构。

  1. 注册服务:
    • 您可以使用 StyletIoCBuilder 对象来注册服务和它们的实现。
    • 通常,在应用程序的启动代码中,您会初始化 StyletIoC 容器,并使用 .Bind().To() 或其他方法来注册您的服务。
  2. 解析依赖:
    • 当您需要使用某个服务时,您可以通过 StyletIoC 来解析它。只需从容器中请求所需的服务类型即可。
  3. 生命周期管理:
    • StyletIoC 支持不同的生命周期管理选项,如瞬态(Transient)、单例(Singleton)等。
    • 您可以使用 .InSingletonScope().InTransientScope() 方法来设置服务的生命周期。
  4. 构造函数注入:
    • StyletIoC 支持通过构造函数进行依赖注入。当您创建视图模型或服务实例时,构造函数中声明的依赖将会自动被解析和注入。

代码示例:

using Stylet;

public class MyService
{
    public string GetMessage()
    {
        return "Hello from MyService!";
    }
}

public class MyViewModel : Screen
{
    private readonly MyService _myService;

    public MyViewModel(MyService myService)
    {
        _myService = myService;
    }

    public string DisplayMessage => _myService.GetMessage();
}

public class Bootstrapper : Bootstrapper<ShellViewModel>
{
    protected override void ConfigureIoC(IStyletIoCBuilder builder)
    {
        builder.Bind<MyService>().ToSelf().InSingletonScope();
    }
}

你可能感兴趣的:(WPF,c#,wpf)