欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF 修改屏幕DPI,会触发控件重新加载Unload/Load

程序员文章站 2022-04-14 18:57:13
修改屏幕DPI,会触发控件的Unloaded/Loaded 现象/重现案例 这里简单介绍下,修改屏幕DPI,触发Unloaded/Loaded的神奇案例 1. 我们新建一个窗口,添加一个UserControl1,然后在UserControl1中添加UserControl2 1

修改屏幕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>

WPF 修改屏幕DPI,会触发控件重新加载Unload/Load

2. 显示窗口后,修改dpi比例

WPF 修改屏幕DPI,会触发控件重新加载Unload/Load

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事件不一定触发。如何必现呢?

将窗口靠近到任务栏上方,再修改文本比例。

WPF 修改屏幕DPI,会触发控件重新加载Unload/Load

 我们查看调用堆栈,貌似是系统给窗口发送消息然后调用broadcastunloadedevent事件,触发unload

WPF 修改屏幕DPI,会触发控件重新加载Unload/Load

 所以应该是修改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