修改屏幕dpi,会触发控件的unloaded/loaded

现象/重现案例

这里简单介绍下,修改屏幕dpi,触发unloaded/loaded的神奇案例

1. 我们新建一个窗口,添加一个usercontrol1,然后在usercontrol1中添加usercontrol2

 1 <window x:class="wpfunloadedtriggertest.mainwindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:wpfunloadedtriggertest"
 7         mc:ignorable="d"
 8         title="mainwindow" height="450" width="800">
 9     <local:usercontrol1></local:usercontrol1>
10 </window>
11 ------------------------------我是分隔线-----------------------------------
12 <usercontrol x:class="wpfunloadedtriggertest.usercontrol1"
13              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
14              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
15              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
16              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
17              xmlns:local="clr-namespace:wpfunloadedtriggertest"
18              mc:ignorable="d" 
19              d:designheight="450" d:designwidth="800">
20     <local:usercontrol2></local:usercontrol2>
21 </usercontrol>

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