zoukankan      html  css  js  c++  java
  • 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下显示:

  • 相关阅读:
    查找表类算法//字母异位词分组
    查找表类算法//四数相加 II
    查找表类算法//四数相加 II
    第六章 类文件结构
    第六章 类文件结构
    查找表的算法//四数之和
    查找表的算法//四数之和
    第五章 调优案例分析与实战
    第五章 调优案例分析与实战
    C++_基础4-分支语句和逻辑运算符
  • 原文地址:https://www.cnblogs.com/kybs0/p/9834023.html
Copyright © 2011-2022 走看看