[WPF自定义控件库]使用WindowChrome的问题

1.前言上一篇文章介绍了使用WindowChrome自定义Window,实际使用下来总有各种各样的问题,这些问题大部分都不影响使用,可能正是因为不影响使用所以一直没得到修复(也有可能别人根本不觉得这些是问题)。这篇文章我总结了一些实际遇到的问题及其解决方案。2.WindowChrome最大化的问题2.1影响Chrome尺寸的几个值上一篇文章提到有几个值用于计算Chrome的尺寸:属性值(像素)描述...

[WPF自定义控件库]使用WindowChrome的问题

1. 前言

上一篇文章介绍了使用WindowChrome自定义Window,实际使用下来总有各种各样的问题,这些问题大部分都不影响使用,可能正是因为不影响使用所以一直没得到修复(也有可能别人根本不觉得这些是问题)。

这篇文章我总结了一些实际遇到的问题及其解决方案。

2. WindowChrome最大化的问题

2.1 影响Chrome尺寸的几个值

上一篇文章提到有几个值用于计算Chrome的尺寸:

属性值(像素)描述
SM_CXFRAME/SM_CYFRAME4The thickness of the sizing border around the perimeter of a window that can be resized, in pixels. SM_CXSIZEFRAME is the width of the horizontal border, and SM_CYSIZEFRAME is the height of the vertical border.This value is the same as SM_CXFRAME.
SM_CXPADDEDBORDER4The amount of border padding for captioned windows, in pixels.Windows XP/2000: This value is not supported.
SM_CYCAPTION23The height of a caption area, in pixels.

在有标题的标准Window,chrome的顶部尺寸为SM_CYFRAMESM_CXPADDEDBORDERSM_CYCAPTION = 31,左右两边尺寸为SM_CXFRAMESM_CXPADDEDBORDER = 8,底部尺寸为SM_CYFRAMESM_CXPADDEDBORDER = 8。

具体的计算方式可以参考Firefox的源码:

  // mCaptionHeight is the default size of the NC area at  // the top of the window. If the window has a caption,  // the size is calculated as the sum of:  //SM_CYFRAME  - The thickness of the sizing border  //  around a resizable window  //SM_CXPADDEDBORDER - The amount of border padding  //  for captioned windows  //SM_CYCAPTION- The height of the caption area  //  // If the window does not have a caption, mCaptionHeight will be equal to  // `GetSystemMetrics(SM_CYFRAME)`  mCaptionHeight = GetSystemMetrics(SM_CYFRAME)   (hasCaption ? GetSystemMetrics(SM_CYCAPTION)   GetSystemMetrics(SM_CXPADDEDBORDER) : 0);  // mHorResizeMargin is the size of the default NC areas on the  // left and right sides of our window.  It is calculated as  // the sum of:  //SM_CXFRAME  - The thickness of the sizing border  //SM_CXPADDEDBORDER - The amount of border padding  //  for captioned windows  //  // If the window does not have a caption, mHorResizeMargin will be equal to  // `GetSystemMetrics(SM_CXFRAME)`  mHorResizeMargin = GetSystemMetrics(SM_CXFRAME)  (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);  // mVertResizeMargin is the size of the default NC area at the  // bottom of the window. It is calculated as the sum of:  //SM_CYFRAME  - The thickness of the sizing border  //SM_CXPADDEDBORDER - The amount of border padding  //  for captioned windows.  //  // If the window does not have a caption, mVertResizeMargin will be equal to  // `GetSystemMetrics(SM_CYFRAME)`  mVertResizeMargin = GetSystemMetrics(SM_CYFRAME)  (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);

在WPF中这几个值分别映射到SystemParameters的相关属性:

系统值SystemParameters属性
SM_CXFRAME/SM_CYFRAMEWindowResizeBorderThickness4,4,4,4
SM_CXPADDEDBORDER4
SM_CYCAPTIONWindowCaptionHeight23

另外还有WindowNonClientFrameThickness,相当于WindowResizeBorderThickness的基础上,Top =WindowCaptionHeight,值为 4,27,4,4。

SM_CXPADDEDBORDER在WPF里没有对应的值,我写了个WindowParameters的类,添加了这个属性:

/// <summary>/// returns the border thickness padding around captioned windows,in pixels. Windows XP/2000:  This value is not supported./// </summary>public static Thickness PaddedBorderThickness{ [SecurityCritical] get {  if (_paddedBorderThickness == null)  {var paddedBorder = NativeMethods.GetSystemMetrics(SM.CXPADDEDBORDER);var dpi = GetDpi();Size frameSize = new Size(paddedBorder, paddedBorder);Size frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, dpi / 96.0, dpi / 96.0);_paddedBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height);  }  return _paddedBorderThickness.Value; }}

2.2 WindowChrome的实际大小和普通Window不同

先说说我的环境,WIndows 10,1920 * 1080 分辨率,100% DPI。

<WindowChrome.WindowChrome> <WindowChrome /></WindowChrome.WindowChrome><Window.Style> <Style TargetType=“{x:Type Window}“>  <Setter Property=“Template“><Setter.Value> <ControlTemplate TargetType=“{x:Type Window}“>  <Border><Grid> <AdornerDecorator>  <ContentPresenter /> </AdornerDecorator> <ResizeGrip x:Name=“WindowResizeGrip“ HorizontalAlignment=“Right“ IsTabStop=“false“ Visibility=“Collapsed“ VerticalAlignment=“Bottom“ /></Grid>  </Border>  <ControlTemplate.Triggers><MultiTrigger> <MultiTrigger.Conditions>  <Condition Property=“ResizeMode“ Value=“CanResizeWithGrip“ />  <Condition Property=“WindowState“ Value=“Normal“ /> </MultiTrigger.Conditions> <Setter Property=“Visibility“TargetName=“WindowResizeGrip“Value=“Visible“ /></MultiTrigger>  </ControlTemplate.Triggers> </ControlTemplate></Setter.Value>  </Setter> </Style></Window.Style>

按上一篇文章介绍的方法打开一个使用WindowChrome的Window(大小为800 * 600),在VisualStudio的实时可视化树可以看到AdornerDecorator的实际大小和Window的实际大小都是800 * 600(毕竟边WindowChrome里的Border、Grid等都没设Margin或Padding)。然后用Inspect观察它的边框。可以看到Window实际上的范围没什么问题。但和标准Window的对比就可以看出有区别,我在之前的文章中介绍过标准Window的实际范围和用户看到的并不一样。

上面两张图分别是通过Inspect观察的标准Window(上图)和使用WindowChrome的Window(下图),可以看到标准Window左右下三个方向有些空白位置,和边框加起来是8个像素。WindowChrome则没有这个问题。

2.3 最大化状态下Margin和标题高度的问题

WindowChrome最大化时状态如上图所示,大小也变为1936 * 1066,这个大小没问题,有问题的是它不会计算好client-area的尺寸,只是简单地加大non-client的尺寸,导致client-area的尺寸也成了1936 * 1066。标准Window在最大化时non-client area的尺寸为1936 * 1066,client-area的尺寸为1920 * 1027。

2.4 最大化时chrome尺寸的问题

结合Window(窗体)的UI元素及行为这篇文章,WindowChrome最大化时的client-area的尺寸就是Window尺寸(1936 * 1066)减去WindowNonClientFrameThickness(4,27,4,4)再减去PaddedBorderThickness(4,4,4,4)。这样就准确地计算出client-area在最大化状态下的尺寸为1920 * 1027。

在自定义Window的ControlTempalte中我使用Trigger在最大化状态下将边框改为0,然后加上WindowResizeBorderThickness的Padding和PaddedBorderThickne

源文地址:https://www.guoxiongfei.cn/cntech/19216.html