WPF 修改屏幕DPI,会触发控件重新加载Unload/Load
修改屏幕DPI,会触发控件的Unloaded/Loaded
现象/重现案例
这里简单介绍下,修改屏幕DPI,触发Unloaded/Loaded的神奇案例
1. 我们新建一个窗口,添加一个UserControl1,然后在UserControl1中添加UserControl2

2. 显示窗口后,修改DPI比例
3. 设置完后,会触发Unloaded/Loaded重新加载
Unloaded的触发顺序是UserControl1-->UserControl2,Window并不会触发Unloaded事件!
是不是诡异?我们继续。。。
4. Window我们添加一个ControlTemplate模块
1 <Window.Template>2 <ControlTemplate TargetType="Window">3 <Border>4 <AdornerDecorator>5 <ContentPresenter />6 </AdornerDecorator>7 </Border>8 </ControlTemplate>9 </Window.Template>
再重复2、3步骤,Unloaded的触发顺序变了:
触发UserControl2的Unloaded,Window、UserControl1并不会触发Unloaded事件!
问题分析
第2步骤中修改DPI后,Unloaded事件不一定触发。如何必现呢?
将窗口靠近到任务栏上方,再修改文本比例。
我们查看调用堆栈,貌似是系统给窗口发送消息然后调用BroadcastUnloadedEvent事件,触发Unload
所以应该是修改DPI,窗口宽高超出了当前屏幕尺寸范围,系统对UserControl的视觉树进行重新加载布局。
至于窗口没有触发Unloaded、以及在窗口添加以上模块后下一级子控件也没有触发Unloaded事件的原因,暂不了解
而对WPF-Unloaded/Loaded的已知情况如下:
- FrameworkElement, 第一次加载显示时,会触发Loaded。元素被释放时,会触发Unloaded。窗口Show/Close时,视觉树变化都会触发加载事件
- MenuItem, 在FrameworkElement基础上,每次和隐藏MenuItem时,会额外触发Load/Unloaded
- TabControl,当你选中一个tabItem时会触发Loaded,当你取消选中一个tabItem时会触发Unloaded,所以切换Tab时必定有一个Loaded一个Unloaded。
- Expander,每次被Expanded扩展时会引发Loaded,但当隐藏时不会引发Unloaded。
以上问题的解决方案?暂时没有解决方案,只有规避措施,不要过于依赖于Unload/Loaded,而且使用了Unload/Loaded时也要添加注销机制,防止重入
我在github提了个issue:After Modified screen dpi,Unloaded/Loaded is trigged unexpectedly