前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >dotnet 读 WPF 源代码笔记 为什么设置了SplashScreen会让Application.Current.Activated事件不触发

dotnet 读 WPF 源代码笔记 为什么设置了SplashScreen会让Application.Current.Activated事件不触发

作者头像
林德熙
发布于 2021-03-29 07:16:34
发布于 2021-03-29 07:16:34
1.1K00
代码可运行
举报
文章被收录于专栏:林德熙的博客林德熙的博客
运行总次数:0
代码可运行

在 WPF 应用中,可以非常方便将一张图片设置为 SplashScreen 启动界面欢迎图,但是如果有设置了启动界面欢迎界面,那么 Application.Current.Activated 事件就不会被触发。本文通过 WPF 框架开源的代码告诉大家这个原因

这是在 GitHub 上,一个小伙伴问的问题,详细请看 After adding a splashscreen Application.Current.Activated event is no longer fired · Issue #4316 · dotnet/wpf

设置某个图片作为 SplashScreen 启动图的方式很简单,只需要右击图片,设置属性,选择 SplashScreen 就可以。也可以在 csproj 中添加如下代码设置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  <ItemGroup>
    <SplashScreen Include="SplashScreen.png" />
  </ItemGroup>

尝试在 App 的构造函数里面添加如下代码用来监听 Activated 事件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public partial class App : Application
    {
        public App()
        {
            Current.Activated += Current_Activated;
        }

        private void Current_Activated(object sender, EventArgs e)
        {
        }
    }

原因是在设置 SplashScreen 启动界面,那么在生成的 App.g.cs 文件里面将会包含下面代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        [System.STAThreadAttribute()]
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "5.0.1.0")]
        public static void Main() 
        {
            SplashScreen splashScreen = new SplashScreen("SplashScreen.png");
            splashScreen.Show(true);
            App app = new App();
            app.InitializeComponent();
            app.Run();
        }

也就是说 SplashScreen 将会在 Main 函数里面最开始就执行,因此启动图显示的速度也比较快。在 SplashScreen 显示完成之后,再创建 App 出来,也就是说监听 Activated 事件是在启动图之后

那么 Activated 事件是由谁分发的?在 src\Microsoft.DotNet.Wpf\src\PresentationFramework\System\Windows\Application.cs 的 EnsureHwndSource 函数里面将是入口代码,而在 WmActivateApp 函数就是触发的逻辑,先看一下 WmActivateApp 的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        private bool WmActivateApp(Int32 wParam)
        {
            int temp = wParam;
            bool isActivated = (temp == 0? false : true);

            // Event handler exception continuality: if exception occurs in Activate/Deactivate event handlers, our state would not
            // be corrupted because no internal state are affected by Activate/Deactivate. Please check Event handler exception continuality
            // if a state depending on those events is added.
            if (isActivated == true)
            {
                OnActivated(EventArgs.Empty);
            }
            else
            {
                OnDeactivated(EventArgs.Empty);
            }
            return false;
        }

也就是说调用进入 WmActivateApp 的参数将决定是否调用 OnActivated 函数,在 OnActivated 函数里面就是事件触发

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        protected virtual void OnActivated(EventArgs e)
        {
            VerifyAccess();
            if (Activated != null)
            {
                Activated(this, e);
            }
        }

而 WmActivateApp 从函数名就可以看出,这是一个由 Win32 的 Windows 消息触发的方法,在 AppFilterMessage 函数里面将会调用到 WmActivateApp 方法。而 AppFilterMessage 函数的命名意思是 App 类的 FilterMessage 方法,也就是说这是一个处理应用级的 Windows 消息的函数,代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        private IntPtr AppFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            IntPtr retInt = IntPtr.Zero;
            switch ((WindowMessage)msg)
            {
                case WindowMessage.WM_ACTIVATEAPP:
                    handled = WmActivateApp(NativeMethods.IntPtrToInt32(wParam));
                    break;
                case WindowMessage.WM_QUERYENDSESSION :
                    handled = WmQueryEndSession(lParam, ref retInt);
                    break;
                default:
                    handled = false;
                    break;
            }
            return retInt;
        }

这个 AppFilterMessage 方法是在 EnsureHwndSource 函数里面注册消息的,请看代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        private void EnsureHwndSource()
        {
            if (_parkingHwnd == null)
            {
                // _appFilterHook needs to be member variable otherwise
                // it is GC'ed and we don't get messages from HwndWrapper
                // (HwndWrapper keeps a WeakReference to the hook)

                _appFilterHook = new HwndWrapperHook(AppFilterMessage);
                HwndWrapperHook[] wrapperHooks = {_appFilterHook};

                _parkingHwnd = new HwndWrapper(
                                0,
                                0,
                                0,
                                0,
                                0,
                                0,
                                0,
                                "",
                                IntPtr.Zero,
                                wrapperHooks);
            }
        }

也就是说 Activated 事件的触发就是依靠 WindowMessage.WM_ACTIVATEAPP 消息的,这个消息详细请看 WM_ACTIVATEAPP 官方文档

因为 SplashScreen 本身将会创建窗口,也因为 SplashScreen 的速度足够快,因此在 Application 的 EnsureHwndSource 函数调用之前,系统发送了 WM_ACTIVATEAPP 消息给到应用了

所以在 App 的构造函数监听 Activated 事件将不会收到触发

如果想要使用欢迎界面,也想收到系统的消息,可以在创建 Application 之后,手动创建 SplashScreen 类,或者可以使用 lsj 提供的 kkwpsv/SplashImage: Fast splash Image with GDI+ in C# 库,当然了,这个库代码量特别少,我推荐大家可以抄抄代码。这个库提供的是高性能的版本,可以在另一个线程中执行,换句话说,就是使用 kkwpsv/SplashImage 作为欢迎界面,是可以做到不占用 WPF 主线程时间的,性能比 WPF 提供的好

更多请看 dotnet 读 WPF 源代码笔记 启动欢迎界面 SplashScreen 的原理

当前的 WPF 在 https://github.com/dotnet/wpf 完全开源,使用友好的 MIT 协议,意味着允许任何人任何组织和企业任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。在仓库里面包含了完全的构建逻辑,只需要本地的网络足够好(因为需要下载一堆构建工具),即可进行本地构建


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%AE%BE%E7%BD%AE%E4%BA%86SplashScreen%E4%BC%9A%E8%AE%A9Application.Current.Activated%E4%BA%8B%E4%BB%B6%E4%B8%8D%E8%A7%A6%E5%8F%91.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
dotnet 读 WPF 源代码笔记 使用 Win32 方法修改窗口的坐标和大小对窗口依赖属性的影响
咱可以使用 Win32 的 SetWindowPos 修改窗口的坐标和大小,此时 WPF 的窗口的 Left 和 Top 和 Width 和 Height 依赖属性也会受到影响,本文将会告诉大家在啥时候会同步更改 WPF 依赖属性的值,而什么时候不会
林德熙
2021/01/12
8660
dotnet 读 WPF 源代码笔记 启动欢迎界面 SplashScreen 的原理
本文是我在读 WPF 源代码做的笔记。在 WPF 中的启动界面,为了能让 WPF 的启动界面显示足够快,需要在应用的 WPF 主机还没有启动完成之前就显示出启动图,此时的启动图需要自己解析图片同时也需要自己创建显示窗口
林德熙
2020/12/22
1.1K0
dotnet 读 WPF 源代码笔记 从 WM_POINTER 消息到 Touch 事件
本文记录我读 WPF 源代码的笔记,在 WPF 底层是如何从 Win32 的消息循环获取到的 WM_POINTER 消息处理转换作为 Touch 事件的参数
林德熙
2024/09/09
3760
dotnet 读 WPF 源代码 聊聊 DispatcherTimer 的实现
本文来告诉大家在 WPF 框架里面,是如何实现 DispatcherTimer 的功能。有小伙伴告诉我,读源代码系列的博客看不动,原因是太底层了。我尝试换一个方式切入逻辑,通过提问题和解决问题的方法,一步步告诉大家 WPF 是如何实现 DispatcherTimer 的功能
林德熙
2021/12/24
7430
WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹
本文将告诉大家如何在 WPF 里面,接收裸 Win 32 的 WM_Pointer 消息,从消息里面获取触摸点信息,使用触摸点信息绘制简单的笔迹
林德熙
2024/09/01
2200
WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹
Support Horizontal Scrolling of TouchPad in WPF Application
发布于 2017-11-23 14:09 更新于 2018-08-12 08:02
walterlv
2018/09/18
7500
Support Horizontal Scrolling of TouchPad in WPF Application
WPF 编写一个测试 WM_TOUCH 触摸消息延迟的应用
我听说在 Win10 到 Win11 的系统版本左右,微软加上了一大波触摸性能优化,准确来说是 HID 性能优化。我想测试一下在这些系统下,采用从 Windows 消息接收到 WM_TOUCH 触摸消息的延迟将会是多少。本文将告诉大家我编写的测试应
林德熙
2023/04/07
6370
WPF 渲染原理
在 WPF 最主要的就是渲染,因为 WPF 是一个界面框架。想用一篇博客就能告诉大家完整的 WPF 渲染原理是不可能的。本文告诉大家 WPF 从开发者告诉如何画图像到在屏幕显示的过程。本文是从一个很高的地方来看渲染的过程,在本文之后会添加很多博客来告诉大家渲染的细节。
林德熙
2018/09/19
3K0
WPF 渲染原理
wpf 单例
本文告诉大家如何做一个 wpf 单例程序。单例就是用户只能运行这个程序一次,也就是内存只有存在这个程序一个。
林德熙
2018/09/19
1.3K0
wpf 单例
dotnet 读 WPF 源代码 Popup 的 StaysOpen 为 false 将会吃掉其他窗口的首次激活
在 WPF 中,使用 Popup 控件,可以设置 StaysOpen 属性来控制是否在 Popup 失去焦点时,也就是点击界面空白处,自动收起 Popup 控件。但如果有两个窗口,在设置 Popup 控件的 StaysOpen 属性为 false 那么将会吃掉在点击其他窗口的第一次交互,如鼠标点击或触摸点击时将不会让本进程的其他窗口 Activate 激活
林德熙
2021/12/24
6800
dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息
在 WPF 触摸应用中,插入触摸设备,即可在应用里面使用上插入的触摸设备。在 WPF 使用触摸设备的触摸时,需要获取到触摸设备的信息,才能实现触摸
林德熙
2021/05/27
7030
dotnet 读 WPF 源代码笔记 渲染收集是如何触发
在 WPF 里面,渲染可以从架构上划分为两层。上层是 WPF 框架的 OnRender 之类的函数,作用是收集应用程序渲染的命令。上层将收集到的应用程序绘制渲染的命令传给下层,下层是 WPF 的 GFX 层,作用是根据收到的渲染的命令绘制出界面。本文所聊的是渲染上层部分,在 WPF 框架是如何做到界面刷新渲染,包括此调用的顺序以及框架逻辑
林德熙
2021/09/18
8600
dotnet 读 WPF 源代码笔记 渲染收集是如何触发
WPF 制作支持点击穿透的高性能的透明背景异形窗口
默认的 WPF 的支持点击穿透的透明背景窗口,是通过 AllowsTransparency 实现的,但是此方法的性能比较低。本文来告诉大家一个高性能的方法,通过此方法制作出来的 WPF 窗口可以获取很高的性能,设置透明和设置窗口不透明之间几乎没有性能差别
林德熙
2021/01/07
2.9K1
WPF 从触摸消息转触摸事件
在 WPF 程序可能因为一些坑让程序触摸失效,如果此时还可以收到系统的触摸消息,那么可以通过从触摸消息转触摸事件解决程序触摸失效但不适合所有触摸失效程序
林德熙
2020/07/07
1.3K0
WPF 使用 RawInput 接收裸数据
在 Windows 提供很底层的方法接收硬件设备的裸数据,通过接收裸数据可以做到性能更高的全局键盘,还能支持多个鼠标。但是用这个方法需要自己解析裸数据,同时会因为接受到很多消息降低性能
林德熙
2022/08/04
9870
dotnet DirectX 做一个简单绘制折线笔迹的 D2D 应用
本文将告诉大家如何从简单的控制台开始,使用 Vortice 辅助调用 Direct2D1 的功能,配合 WM_Pointer 消息,制作一个简单绘制触摸折线笔迹的 D2D 应用
林德熙
2024/10/16
1780
将 WPF 嵌入到 MFC 中,无法响应键盘输入
在 将 WPF 窗口嵌入到 MFC 窗口中 中提到,可以将 WPF 嵌入到 MFC 窗口中, 但遗留了一个没有发现的问题,WPF 界面,无法响应键盘的输入。
jgrass
2024/12/25
2680
WPF 开启Pointer消息存在的坑
启用了Pointer之后,调用Textbox.Focus(),起不来屏幕键盘,必须点在它之上才行,触摸在它之上才行
林德熙
2022/08/12
9070
WPF 开启Pointer消息存在的坑
WPF 插拔触摸设备触摸失效
最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效。通过分析 WPF 源代码可以找到 WPF 触摸失效的原因。
林德熙
2018/09/19
1.8K0
WPF 插拔触摸设备触摸失效
把WPF Dialog转成WinForm Dialog需要注意的问题续
上一篇讲到将WPF的窗口转为WinForm窗口需要注意的问题,这里给出了另一种解决方案,闲话不说,请看代码: //============================================================================== // File Name : WpfModalDialogFixer.cs // // Copyright (C) 2010 GrapeCity Inc. All rights reserved. // // Distribu
葡萄城控件
2018/01/10
1K0
推荐阅读
相关推荐
dotnet 读 WPF 源代码笔记 使用 Win32 方法修改窗口的坐标和大小对窗口依赖属性的影响
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验