前言

今天以Stylet.Samples.Hello这个demo为例,学习一下Stylet的启动机制。

平常我们新建一个WPF程序结构是这样的:



启动之后就是这样的:

为什么启动之后是这样的呢?



我们知道是因为在App.xaml中我们设置了StartupUri="MainWindow.xaml"

现在来看看Stylet.Samples.Hello的结构:

再来看看它的App.xaml:

<Application x:Class="Stylet.Samples.Hello.App"
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"
xmlns:local="clr-namespace:Stylet.Samples.Hello">
<Application.Resources>
<s:ApplicationLoader>
<s:ApplicationLoader.Bootstrapper>
<local:HelloBootstrapper/>
</s:ApplicationLoader.Bootstrapper>
</s:ApplicationLoader>
</Application.Resources>
</Application>

我们发现它删掉了StartupUri,然后多了一个<s:ApplicationLoader>

说明启动起来就显示ShellView的玄机就在其中!!

Stylet启动机制

Stylet的启动流程可以分为以下几个关键阶段:

  • WPF应用程序启动(Application.Startup)
  • ApplicationLoader初始化(XAML解析阶段)
  • Bootstrapper配置与启动
  • IoC容器初始化
  • 根视图模型创建
  • 视图定位与创建
  • 窗口显示

看似一个简单的启动过程,其实作者做了很多的处理!!

现在就让我们来看看吧!!

阶段1:WPF应用程序启动

<Application x:Class="Stylet.Samples.Hello.App"
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"
xmlns:local="clr-namespace:Stylet.Samples.Hello">
<Application.Resources>
<s:ApplicationLoader>
<s:ApplicationLoader.Bootstrapper>
<local:HelloBootstrapper/>
</s:ApplicationLoader.Bootstrapper>
</s:ApplicationLoader>
</Application.Resources>
</Application>

当WPF解析App.xaml时,会创建ApplicationLoader实例。ApplicationLoader是Stylet提供的特殊资源字典,继承自ResourceDictionary,ApplicationLoader的构造函数会立即执行,加载Stylet的基础资源。

来看下ApplicationLoader的源码:

public class ApplicationLoader : ResourceDictionary
{
private readonly ResourceDictionary styletResourceDictionary; public ApplicationLoader()
{
// 加载Stylet的基础样式资源
this.styletResourceDictionary = new ResourceDictionary() {
Source = new Uri("pack://application:,,,/Stylet;component/Xaml/StyletResourceDictionary.xaml", UriKind.Absolute)
};
this.LoadStyletResources = true;
} public IBootstrapper Bootstrapper
{
get => this._bootstrapper;
set
{
this._bootstrapper = value;
// 关键:立即调用Setup方法
this._bootstrapper.Setup(Application.Current);
}
}
}

当XAML解析器遇到<local:HelloBootstrapper/>时,会:

  • 创建HelloBootstrapper实例
  • 设置给ApplicationLoader.Bootstrapper属性
  • 触发Bootstrapper.Setup(Application.Current)调用

阶段2:Bootstrapper配置与启动

HelloBootstrapper:

public class HelloBootstrapper : Bootstrapper<ShellViewModel>
{
// 空实现!所有功能都在基类中
}

虽然HelloBootstrapper看起来很简单,但它继承的Bootstrapper提供了完整的启动逻辑。

现在来看看HelloBootstrapper的继承链:

  HelloBootstrapper

Bootstrapper<ShellViewModel>

StyletIoCBootstrapperBase

BootstrapperBase

刚刚在阶段1中看到的Setup方法在BootstrapperBase中,现在来看看:

public void Setup(Application application)
{
this.Application = application; // 设置UI线程调度器
Execute.Dispatcher = new ApplicationDispatcher(this.Application.Dispatcher); // 关键:注册Startup事件
this.Application.Startup += (o, e) => this.Start(e.Args); // 注册其他生命周期事件
this.Application.Exit += (o, e) =>
{
this.OnExit(e);
this.Dispose();
}; this.Application.DispatcherUnhandledException += (o, e) =>
{
LogManager.GetLogger(typeof(BootstrapperBase)).Error(e.Exception, "Unhandled exception");
this.OnUnhandledException(e);
};
}

Setup方法在XAML解析阶段就被调用,但实际的启动逻辑在Application.Startup事件触发时才执行,这确保了所有配置都在UI线程上完成。

阶段3:IoC容器初始化

现在重点看看这个:

  // 关键:注册Startup事件
this.Application.Startup += (o, e) => this.Start(e.Args);

我们看到作者为this.Application.Startup事件增加了事件处理函数/程序 (o, e) => this.Start(e.Args)

现在我们来看看这个事件处理函数/程序:

public virtual void Start(string[] args)
{
// 1. 保存命令行参数
this.Args = args;
this.OnStart(); // 2. 配置Bootstrapper(主要是IoC容器)
this.ConfigureBootstrapper(); // 3. 注册ViewManager到应用程序资源
this.Application?.Resources.Add(View.ViewManagerResourceKey, this.GetInstance(typeof(IViewManager))); // 4. 用户自定义配置
this.Configure(); // 5. 显示根视图
this.Launch(); // 6. 启动完成通知
this.OnLaunch();
}

先来看看StyletIoC容器配置:

protected sealed override void ConfigureBootstrapper()
{
var builder = new StyletIoCBuilder();
builder.Assemblies = new List<Assembly>(new List<Assembly>() { this.GetType().Assembly }); // 用户自定义IoC配置
this.ConfigureIoC(builder); // 默认配置
this.DefaultConfigureIoC(builder); // 构建容器
this.Container = builder.BuildContainer();
} protected virtual void DefaultConfigureIoC(StyletIoCBuilder builder)
{
// 配置ViewManager
var viewManagerConfig = new ViewManagerConfig()
{
ViewFactory = this.GetInstance,
ViewAssemblies = new List<Assembly>() { this.GetType().Assembly }
};
builder.Bind<ViewManagerConfig>().ToInstance(viewManagerConfig).AsWeakBinding(); // 注册核心服务
builder.Bind<IViewManager>().And<ViewManager>().To<ViewManager>().InSingletonScope();
builder.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
builder.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope(); // 自动绑定特性
builder.Autobind();
}

注册的默认服务:

  • IViewManager → ViewManager(视图管理器)
  • IWindowManager → WindowManager(窗口管理器)
  • IEventAggregator → EventAggregator(事件聚合器)

阶段4:根视图模型创建

现在来看看Bootstrapper<TRootViewModel>

public abstract class Bootstrapper<TRootViewModel> : StyletIoCBootstrapperBase
where TRootViewModel : class
{
private TRootViewModel _rootViewModel; // 延迟加载根视图模型
protected virtual TRootViewModel RootViewModel =>
this._rootViewModel ??= this.Container.Get<TRootViewModel>(); // 启动时显示根视图
protected override void Launch()
{
this.DisplayRootView(this.RootViewModel);
}
}

关键点:

  • 使用延迟加载模式,只有在需要时才创建TRootViewModel
  • 通过IoC容器解析ShellViewModel,支持构造函数注入
  • 在HelloBootstrapper中,TRootViewModel就是ShellViewModel

阶段5:视图定位与创建

现在来看看DisplayRootView方法:

protected virtual void DisplayRootView(object rootViewModel)
{
var windowManager = (IWindowManager)this.GetInstance(typeof(IWindowManager));
windowManager.ShowWindow(rootViewModel);
}

现在来看看ShowWindow方法:

public void ShowWindow(object viewModel)
{
this.CreateWindow(viewModel, false, null).Show();
}

现在来看看CreateWindow方法:

protected virtual Window CreateWindow(object viewModel, bool isDialog, IViewAware ownerViewModel)
{
// 1. 通过ViewManager创建视图
UIElement view = this.viewManager.CreateAndBindViewForModelIfNecessary(viewModel); // 2. 确保视图是Window类型
if (view is not Window window)
{
throw new StyletInvalidViewTypeException(...);
} // 3. 设置窗口属性
if (viewModel is IHaveDisplayName haveDisplayName)
{
window.SetBinding(Window.TitleProperty, new Binding("DisplayName"));
} // 4. 设置窗口位置
if (window.WindowStartupLocation == WindowStartupLocation.Manual)
{
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
} return window;
}

现在再来看看CreateAndBindViewForModelIfNecessary方法:

  public virtual UIElement CreateAndBindViewForModelIfNecessary(object model)
{
if (model is IViewAware modelAsViewAware && modelAsViewAware.View != null)
{
logger.Info("ViewModel {0} already has a View attached to it. Not attaching another", model);
return modelAsViewAware.View;
} return this.CreateAndBindViewForModel(model);
}

在这里得到了ViewModel所绑定的View。

阶段6:ShellView显示全过程

WPF Application

Application Startup

ApplicationLoader.Setup()

Bootstrapper.Setup()

注册Startup事件

Application.Startup触发

Bootstrapper.Start()

ConfigureBootstrapper()

IoC容器构建

Launch()调用

DisplayRootView(ShellViewModel)

WindowManager.ShowWindow()

ViewManager.CreateViewForModel()

定位ShellView类型

创建ShellView实例

绑定ShellView到ShellViewModel

显示ShellView窗口

看似一个简单的操作,其实作者做了很多工作。

从这个探索中我们可以了解什么?

  • 声明式配置:通过XAML配置启动器
  • 约定优于配置:自动的ViewModel-View映射
  • 依赖注入:自动的依赖解析
  • 生命周期管理:完整的应用程序生命周期钩子
  • 可扩展性:通过继承和重写实现自定义

Stylet的设计哲学是让简单的事情保持简单,让复杂的事情成为可能。通过理解这些基础机制,你可以构建出更加优雅和可维护的WPF应用程序。

Stylet启动机制详解:从Bootstrap到View显示的更多相关文章

  1. JVM类加载机制详解(二)类加载器与双亲委派模型

    在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...

  2. JVM类加载机制详解

    引言 如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 在加载阶段,虚拟机需要完成以下三件事情: 1)通过一个类的全限定名来获取定义此 ...

  3. java面试题之----JVM架构和GC垃圾回收机制详解

    JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...

  4. JVM的垃圾回收机制详解和调优

    JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...

  5. Android 核心分析 之八Android 启动过程详解

    Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...

  6. Linux 内存机制详解宝典

    Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...

  7. fabric网络环境启动过程详解

    这篇文章对fabric的网络环境启动过程进行讲解,也就是我们上节讲到的启动测试fabric网络环境时运行network_setup.sh这个文件的执行流程 fabric网络环境启动过程详解 上一节我们 ...

  8. Android应用AsyncTask处理机制详解及源码分析

    1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...

  9. 第14章 启动文件详解—零死角玩转STM32-F429系列

    第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ...

  10. Tomcat5的web应用启动顺序详解

    Tomcat5的web应用启动顺序详解 [收藏此页] [打印]   作者:佚名  2007-07-17 内容导航: 第1页   [IT168技术文档]摘要: 应用Tomcat对于我们来讲实在是司空见惯 ...

随机推荐

  1. Servlet中过滤器、监听器和拦截器的区别

    基本概念   过滤器(Filter):当你有一堆东西的时候,你只希望选择符合你要求的某一些东西.定义这些要求的工具,就是过滤器.就是对请求起到过滤的作用:在监听器之后servlet之前对请求进行过滤. ...

  2. Spring 注解之@Primary注解

      当一个接口有多个不同实现类时,使用注解@Autowired时会报 org.springframework.beans.factory.NoUniqueBeanDefinitionException ...

  3. vivo Pulsar万亿级消息处理实践(1)-数据发送原理解析和性能调优

    作者:vivo 互联网大数据团队- Quan Limin 本文是vivo互联网大数据团队<vivo Pulsar万亿级消息处理实践>系列文章第1篇. 文章以Pulsar client模块中 ...

  4. PicGo使用简明教程及踩坑记录

    PicGo使用简明教程及踩坑记录 PicGo使用 我现在用的博客的记录方式是Typora+PicGo+阿里云oss,这一套配置好后就非常方便了,可以快捷上传图片到云服务器,并且阿里云的速度也是我试过的 ...

  5. 总决赛定档!“天翼云息壤杯”高校AI大赛巅峰之战即将打响!

    近日,为梦想添翼,让AI发光--"天翼云息壤杯"高校AI大赛总决赛时间正式揭晓.总决赛将于2025年7月1日至7月17日在北京举办.届时,来自全国各地上百支成功晋级的优秀队伍和特邀 ...

  6. HyperWorks二维网格划分及拓扑改进

    Step 01:载入模型 Exercise_3a.hm. Step 02:2D 网格划分. (1) 进入 automesh 面板. 图 3-13 设置 automesh 面板网格控制参数 (2) 指定 ...

  7. SQLServer常用个技巧(一):根据某字符截取后面的字符串,String转int

    SELECT GOODS_CD AS goodsCd, 原字符串 reverse( GOODS_CD ) AS dCsdoog, 颠倒 LEFT ( reverse( GOODS_CD ), char ...

  8. AI领域又新增协议: AG-UI

    随着AI的快速发展正在重塑技术生态,协议的演进速度尤为迅猛.一个令人头疼的问题浮现了:不同的AI智能体和前端应用之间就像说着不同语言的人,无法顺畅交流.开发者们需要为每个智能体单独编写接口,维护成本高 ...

  9. PLY 模型文件简析

    PLY 模型文件简析 参考链接 wiki需要FFFQQQ TIPS 主要是一些英文的简析,但是一句话,网上讲的不清楚,特此说明 property list uchar int vertex_indic ...

  10. iPaaS 与 API 管理:企业数字化转型的双引擎

    一.企业集成的残酷现实 根据IDC 2024 年数字化转型报调研显示:大中型企业平均部署数十至数百个业务系统,涵盖 ERP.CRM.SaaS 应用及物联网平台等,但仅约 30% 的系统实现标准化集成. ...