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下显示:

  • 相关阅读:
    PAT (Advanced Level) Practice 1071 Speech Patterns (25分)
    PAT (Advanced Level) Practice 1070 Mooncake (25分)
    PAT (Advanced Level) Practice 1069 The Black Hole of Numbers (20分)
    PAT (Advanced Level) Practice 1074 Reversing Linked List (25分)
    PAT (Advanced Level) Practice 1073 Scientific Notation (20分)
    第一次冲刺个人总结01
    构建之法阅读笔记01
    人月神话阅读笔记01
    四则运算2
    学习进度条(软件工程概论1-8周)
  • 原文地址:https://www.cnblogs.com/kybs0/p/9834023.html
Copyright © 2011-2022 走看看