WPF 窗口大小自适应

简介: 原文:WPF 窗口大小自适应在设置桌面不同分辨率以及较大DPI下,窗口如何显示的问题。 方案一 设置窗口最大值和最小值显示 通过对比当前屏幕的可显示区域,将窗口高宽最大值和最小值,设置为窗口的实际高宽(此例中仅设置高度) 界面设置 设置窗口内容自适应SizeToContent="WidthAndHeight" 添加ViewBox -- 设置默认不拉伸Stretch="None",当DPI超大时如超过1920*1080p的175%(即win10默认不支持的比例显示),开启ViewBox缩放 顶层布局容器RootGrid添加高宽最大值和最小值。
原文: WPF 窗口大小自适应

在设置桌面不同分辨率以及较大DPI下,窗口如何显示的问题。

方案一 设置窗口最大值和最小值显示

通过对比当前屏幕的可显示区域,将窗口高宽最大值和最小值,设置为窗口的实际高宽(此例中仅设置高度)

界面设置

  1. 设置窗口内容自适应SizeToContent="WidthAndHeight"
  2. 添加ViewBox -- 设置默认不拉伸Stretch="None",当DPI超大时如超过1920*1080p的175%(即win10默认不支持的比例显示),开启ViewBox缩放
  3. 顶层布局容器RootGrid添加高宽最大值和最小值。
 1 <Window x:Class="WindowHeightChangedForDpi.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:WindowHeightChangedForDpi"
 7         mc:Ignorable="d"
 8         Title="MainWindow" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen">
 9     <Viewbox x:Name="RootViewbox" Stretch="None">
10         <Grid x:Name="RootGrid" Width="1000" MaxHeight="680" MinHeight="520" ClipToBounds="True">
11 
12         </Grid>
13     </Viewbox>
14 </Window>

后台设置 - 窗口大小自适应设置

  1. 添加对Loaded事件的监听,并在之后注销。窗口只需要首次初始其高度即可。
  2. 获取屏幕的高度和任务栏的高度 -- 具体可以参考C# 获取当前屏幕的宽高和位置
  3. 比较当前可显示高度(屏幕高度-任务栏高度)与窗口的最大/最小高度,然后设置当前窗口的实际高度。
  4. 如果可显示高度比最小值还小,则开启ViewBox内容缩放。ViewBox的高度为当前可显示高度。
  5. 如果当前窗口有阴影,可设置阴影高度大小。保证窗口在可显示区域内正常显示。
 1     public partial class MainWindow : Window
 2     {
 3         public MainWindow()
 4         {
 5             InitializeComponent();
 6             Loaded += InitWindowActualHeight_OnLoaded;
 7         }
 8 
 9         #region 设置窗口对屏幕高度的自适应
10 
11         private void InitWindowActualHeight_OnLoaded(object sender, RoutedEventArgs e)
12         {
13             Loaded -= InitWindowActualHeight_OnLoaded;
14             InitWindowActualHeight();
15         }
16 
17         private const double WindowShadowHeight = 0;
18 
19         private void InitWindowActualHeight()
20         {
21             //获取窗体所在屏幕的高度
22             var visibleAreaHeight = GetScreenHeight();
23 
24             //可显示高度 > 窗口最大高度
25             if (visibleAreaHeight > RootGrid.MaxHeight + WindowShadowHeight)
26             {
27                 //设置高度等于最大高度
28                 RootGrid.Height = RootGrid.MaxHeight;
29             }
30             //可显示高度 < 窗口最小高度
31             else if (visibleAreaHeight < RootGrid.MinHeight + WindowShadowHeight)
32             {
33                 //设置Viewbox高度=可视高度-阴影高度(此处通过绽放显示窗口,所以不能通过设置窗口或者设置内容的高度来实现)
34                 RootViewbox.Height = visibleAreaHeight - WindowShadowHeight;
35                 //等比例缩小
36                 RootViewbox.Stretch = Stretch.Uniform;
37             }
38             else
39             {
40                 //设置高度等于最小高度
41                 RootGrid.Height = RootGrid.MinHeight;
42             }
43         }
44         const double DpiPercent = 96;
45         private double GetScreenHeight()
46         {
47             var intPtr = new WindowInteropHelper(this).Handle;//获取当前窗口的句柄
48             var screen = Screen.FromHandle(intPtr);//获取当前屏幕
49 
50             double height = 0;
51             using (Graphics currentGraphics = Graphics.FromHwnd(intPtr))
52             {
53                 double dpiXRatio = currentGraphics.DpiX / DpiPercent;
54                 double dpiYRatio = currentGraphics.DpiY / DpiPercent;
55                 height = screen.WorkingArea.Height / dpiYRatio;
56                 //var width = screen.WorkingArea.Width / dpiXRatio;
57                 //var left = screen.WorkingArea.Left / dpiXRatio;
58                 //var top = screen.WorkingArea.Top / dpiYRatio;
59             }
60             return height;
61         }
62         #endregion
63     }

注:获取的屏幕高度为屏幕像素,需要转换为WPF单位。

以上只是设置了高度的最大值最值,如果需要,可以对高度设置多个梯度,对应不同分辨率下的显示。

下载Demo

方案二 设置窗口为屏幕的百分比(如60%)显示

窗口设置为屏幕的百分比大小(如60%高宽)显示,在这基础上添加限制(最大值、最小值)。

如此,对多种分辨率、DPI比例,我们开发时就不需要考虑其它因素,简单明了且所有窗口大小能统一。

比如主窗口A设置为屏幕可显示区域的60%大小,二级子窗口设置为可显示区域的40%大小,三级子窗口设置为可显示区域的30%大小。

实现方案与案例

通过添加附加属性,设置当前窗口宽为可显示区域的80%大小,高为可显示区域高的75%大小。

 1 <Window x:Class="WindowSizeToScreenRatioDisplay.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:WindowSizeToScreenRatioDisplay"
 7         mc:Ignorable="d"
 8         Title="MainWindow" 
 9         local:WindowAdaptation.WidthByScreenRatio="0.8" MaxWidth="1200" MinWidth="800"
10         local:WindowAdaptation.HeightByScreenRatio="0.75" MaxHeight="800" MinHeight="520">
11     <Grid Background="CornflowerBlue">
12         
13     </Grid>
14 </Window>

添加附加属性 WidthByScreenRatio、HeightByScreenRatio。

控制窗口大小:

  1. 默认设置为当前屏幕工作区域的显示比例大小
  2. 如果超过窗口最大高度/宽高,则显示为窗口最大高度/宽高
  3. 如果小于窗口最小高度/宽高,则显示为当前可显示区域的最大高度/宽高
  1     /// <summary>
  2     /// 为窗口<see cref="Window"/>添加附加属性的辅助类
  3     /// </summary>
  4     public class WindowAdaptation
  5     {
  6         #region 窗口宽度比例
  7         /// <summary>
  8         /// 窗口宽度比例 单位:小数(0 - 1.0]
  9         /// <para>窗口实际宽度=使用屏幕可显示区域(屏幕高度-任务栏高度)* 窗口宽度比例</para>
 10         /// </summary>
 11         public static readonly DependencyProperty WidthByScreenRatioProperty = DependencyProperty.RegisterAttached(
 12             "WidthByScreenRatio", typeof(double), typeof(WindowAdaptation), new PropertyMetadata(1.0, OnWidthByScreenRatioPropertyChanged));
 13 
 14         private static void OnWidthByScreenRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 15         {
 16             if (d is Window window && e.NewValue is double widthByScreenRatio)
 17             {
 18                 if (widthByScreenRatio <= 0 || widthByScreenRatio > 1)
 19                 {
 20                     throw new ArgumentException($"屏幕比例不支持{widthByScreenRatio}");
 21                 }
 22 
 23                 var screenDisplayArea = GetScreenSize(window);
 24                 var screenRatioWidth = screenDisplayArea.Width * widthByScreenRatio;
 25 
 26                 if (!double.IsNaN(window.MaxWidth) && screenRatioWidth > window.MaxWidth)
 27                 {
 28                     window.Width = window.MaxWidth;
 29                 }
 30                 else if (!double.IsNaN(window.MinWidth) && screenRatioWidth < window.MinWidth)
 31                 {
 32                     window.Width = screenDisplayArea.Width;
 33                 }
 34                 else
 35                 {
 36                     window.Width = screenRatioWidth;
 37                 }
 38             }
 39         }
 40 
 41         public static void SetWidthByScreenRatio(DependencyObject element, double value)
 42         {
 43             element.SetValue(WidthByScreenRatioProperty, value);
 44         }
 45 
 46         public static double GetWidthByScreenRatio(DependencyObject element)
 47         {
 48             return (double)element.GetValue(WidthByScreenRatioProperty);
 49         }
 50         #endregion
 51 
 52         #region 窗口高度比例
 53         /// <summary>
 54         /// 窗口宽度比例 单位:小数(0 - 1.0]
 55         /// <para>窗口实际宽度=使用屏幕可显示区域(屏幕高度-任务栏高度)* 窗口宽度比例</para>
 56         /// </summary>
 57         public static readonly DependencyProperty HeightByScreenRatioProperty = DependencyProperty.RegisterAttached(
 58             "HeightByScreenRatio", typeof(double), typeof(WindowAdaptation), new PropertyMetadata(1.0, OnHeightByScreenRatioPropertyChanged));
 59 
 60         private static void OnHeightByScreenRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 61         {
 62             if (d is Window window && e.NewValue is double heightByScreenRatio)
 63             {
 64                 if (heightByScreenRatio <= 0 || heightByScreenRatio > 1)
 65                 {
 66                     throw new ArgumentException($"屏幕比例不支持{heightByScreenRatio}");
 67                 }
 68 
 69                 var screenDisplayArea = GetScreenSize(window);
 70                 var screenRatioHeight = screenDisplayArea.Height * heightByScreenRatio;
 71 
 72                 if (!double.IsNaN(window.MaxHeight) && screenRatioHeight > window.MaxHeight)
 73                 {
 74                     window.Height = window.MaxHeight;
 75                 }
 76                 else if (!double.IsNaN(window.MinHeight) && screenRatioHeight < window.MinHeight)
 77                 {
 78                     window.Height = screenDisplayArea.Height;
 79                 }
 80                 else
 81                 {
 82                     window.Height = screenRatioHeight;
 83                 }
 84             }
 85         }
 86 
 87         public static void SetHeightByScreenRatio(DependencyObject element, double value)
 88         {
 89             element.SetValue(HeightByScreenRatioProperty, value);
 90         }
 91 
 92         public static double GetHeightByScreenRatio(DependencyObject element)
 93         {
 94             return (double)element.GetValue(HeightByScreenRatioProperty);
 95         }
 96         #endregion
 97 
 98         const int DpiPercent = 96;
 99         private static dynamic GetScreenSize(Window window)
100         {
101             var intPtr = new WindowInteropHelper(window).Handle;//获取当前窗口的句柄
102             var screen = Screen.FromHandle(intPtr);//获取当前屏幕
103             using (Graphics currentGraphics = Graphics.FromHwnd(intPtr))
104             {
105                 //分别获取当前屏幕X/Y方向的DPI
106                 double dpiXRatio = currentGraphics.DpiX / DpiPercent;
107                 double dpiYRatio = currentGraphics.DpiY / DpiPercent;
108 
109                 var width = screen.WorkingArea.Width / dpiXRatio;
110                 var height = screen.WorkingArea.Height / dpiYRatio;
111 
112                 return new { Width = width, Height = height };
113             }
114         }
115     }

下载 Demo

下图为1920*1080p的175%DPI显示:

下图为1366*768的125%DPI下显示:

 

目录
相关文章
|
编解码 前端开发 C#
一起谈.NET技术,利用WPF建立自适应窗口大小布局的WinForm窗口
  编写WinForm程序时,都会碰到一个问题。就是WinForm窗口在不同分辨率下的大小问题。举例说明,你编写的WinForm窗口在1024×768下是合适、匀称的。不过,如果用户的计算机的分辨率为1400×900时,你的WinForm窗口就显得偏小,其中的字体和控件都显得偏小。
1778 0
|
编解码 C# Windows
WPFの获取屏幕分辨率并自适应
原文:WPFの获取屏幕分辨率并自适应 double x = SystemParameters.WorkArea.Width;//得到屏幕工作区域宽度 double y = SystemParameters.
1901 0
|
前端开发 C#
使用MVVM DataTriggers在WPF XAML视图之间切换/Window窗口自适应内容大小并居中
原文 使用MVVM DataTriggers在WPF XAML视图之间切换 相关文章: http://www.technical-recipes.com/2016/switching-between-wpf-xaml-views-using-mvvm-datatemplate/ 这篇文章解决了能够根据ViewModel类的属性在不同视图之间切换的问题。
1871 0
|
C# Windows
WPF无边框捕获消息改变窗口大小
原文:WPF无边框捕获消息改变窗口大小 文章大部分转载自http://blog.csdn.net/fwj380891124,如有问题,请联系删除  最近一直在学习 WPF,看着别人做的WPF程序那么漂亮,眼红啊~ 很多漂亮的程序都是无边框的。
1213 0
|
C# 数据可视化 容器
WPF自适应可关闭的TabControl 类似浏览器的标签页
原文:WPF自适应可关闭的TabControl 类似浏览器的标签页 效果如图:   虽然说是自适应可关闭的TabControl,但TabControl并不需要改动,不如叫自适应可关闭的TabItem.
2113 0
|
Shell C#
WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox
原文:WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox 一.前言   申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接。
2091 0
基于WPF的窗体控件自适应思考
    WinForm要实现窗体、控件的自适应还是比较困难的。不是说不行,单靠算比较因子和坐标的办法是解决不了根本问题的。     得转变思路,考虑用WPF实现这种功能。
963 0
|
编解码 前端开发 C#
利用WPF建立自适应窗口大小布局的WinForm窗口
  编写WinForm程序时,都会碰到一个问题。就是WinForm窗口在不同分辨率下的大小问题。举例说明,你编写的WinForm窗口在1024×768下是合适、匀称的。不过,如果用户的计算机的分辨率为1400×900时,你的WinForm窗口就显得偏小,其中的字体和控件都显得偏小。
1235 0
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件