WPF 窗口大小自適應

在設置桌面不一樣分辨率以及較大DPI下,窗口如何顯示的問題。html

方案一 設置窗口最大值和最小值顯示

經過對比當前屏幕的可顯示區域,將窗口高寬最大值和最小值,設置爲窗口的實際高寬(此例中僅設置高度)express

界面設置

  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單位。佈局

以上只是設置了高度的最大值最值,若是須要,能夠對高度設置多個梯度,對應不一樣分辨率下的顯示。this

下載Demospa

方案二 設置窗口爲屏幕的百分比(如60%)顯示

窗口設置爲屏幕的百分比大小(如60%高寬)顯示,在這基礎上添加限制(最大值、最小值)。code

如此,對多種分辨率、DPI比例,咱們開發時就不須要考慮其它因素,簡單明瞭且全部窗口大小能統一。orm

好比主窗口A設置爲屏幕可顯示區域的60%大小,二級子窗口設置爲可顯示區域的40%大小,三級子窗口設置爲可顯示區域的30%大小。xml

實現方案與案例

經過添加附加屬性,設置當前窗口寬爲可顯示區域的80%大小,高爲可顯示區域高的75%大小。htm

 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。blog

控制窗口大小:

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

相關文章
相關標籤/搜索