1. 程式人生 > >WPF 窗口居中 & 變更觸發機制

WPF 窗口居中 & 變更觸發機制

ech return nsa 因此 false 附加 lba bsp param

原文:WPF 窗口居中 & 變更觸發機制

窗口居中 & 變更觸發機制

解決:

1。單實例窗口,窗口每次隱藏後再顯示時,位置居中顯示

2。多屏幕下單實例窗口,當父窗口移動到其它屏幕時,單實例窗口再次彈出時,位置才更新到父窗口屏幕。

3。子窗口每次喚醒時,都居中顯示。

窗口首次顯示的位置 - WindowStartupLocation

windows的啟動時位置顯示,WindowStartupLocation

  • CenterOwner --顯示在父窗口的中間(設置Owner)
  • CenterScreen --顯示在當前屏幕中間
  • Manual --默認位置

當第一次window.ShowDialog時,window顯示如上設置。

變更觸發機制

上面只涉及到了首次顯示位置,之後,窗口的位置會繼續保留

  • 如何設置窗口隱藏之後再次彈出時,顯示在中間(CenterOwner/CenterScreen)?
  • 如何設置窗口一直停留在顯示在中間?

我們先了解一下,有哪些觸發機制

  1. Activated 窗口激活
    窗口變更為前臺窗口時(即顯示在最前面),會觸發
  2. IsVisibleChanged 顯示變更
    當我們設置窗口隱藏Hide()時,IsVisibile=false.窗口再次ShowDialog時,IsVisibile=true;

利用如上倆種機制,下面就可以搞事情了。

首先定義幾個枚舉:

 1     /// <summary>
 2     /// 窗口顯示變更觸發時機
 3     /// </summary>
 4     public enum WindowLocationInvokeOccasion
 5     {
 6         /// <summary>
 7         /// 只要Activated就顯示在中間
 8         /// </summary>
 9         Activated = 0,
10 
11         /// <summary>
12         ///
只在第一次Activated時,顯示在中間一次,之後的變化就不修改 13 /// </summary> 14 FirstActivated, 15 16 /// <summary> 17 /// 窗口每次顯示時,窗口居中 18 /// <para>可以解決單實例窗口彈出不居中問題</para> 19 /// </summary> 20 Visibile, 21 22 /// <summary> 23 /// 窗口每次顯示時,如父窗口與當前窗口不在同一屏幕時,窗口居中 24 /// <para>可以解決單實例窗口彈出不居中問題</para> 25 /// </summary> 26 VisibileInDifferentScreen, 27 28 /// <summary> 29 /// 不觸發 30 /// </summary> 31 Defatult 32 }

如上枚舉包含了4種觸發機制。

我們再定義個附加屬性,通過附加屬性去設置窗口的額外功能-居中顯示觸發機制

1 /// <summary>
2 /// 窗口顯示居中觸發時機
3 /// <para>另:居中顯示設置,請使用<see cref="Window"/><see cref="WindowStartupLocation"/>屬性</para>
4 /// </summary>
5 public static readonly DependencyProperty InvokeOccasionProperty = DependencyProperty.RegisterAttached(
6     "InvokeOccasion", typeof(WindowLocationInvokeOccasion), typeof(WindowLocationOptions),
7     new PropertyMetadata(default(WindowLocationInvokeOccasion), InvokeOccasionProperty_ChangedCallback));

在屬性更改觸發事件中,根據不同的觸發條件,設置不同的居中顯示。

    • Activated --只要Activated就顯示在中間
      每次觸發,直接顯示窗口即可
    • 首次Activated
      通過設置window.Activated -= ShowInCenter_Activated;禁用下次觸發進入
    • Visibile
    • VisibileInDifferentScreen
      窗口顯示時,如父窗口與當前窗口不在同一屏幕時,窗口居中.
      怎麽判斷當前子窗口與父窗口是否在同一屏幕?
 1 var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);
 2 
 3 Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
 4 double dpiXRatio = currentGraphics.DpiX / 96;
 5 double dpiYRatio = currentGraphics.DpiY / 96;
 6 
 7 //當子窗口與父窗口所在屏幕相同時,不作處理
 8 var isSubWindowInSameScreen = subWindow.Left > screen.Bounds.Left / dpiXRatio &&
 9                                 subWindow.Left < screen.Bounds.Left / dpiXRatio + screen.Bounds.Width / dpiXRatio &&
10                                 subWindow.Top > screen.Bounds.Top / dpiYRatio &&
11                                 subWindow.Top < screen.Bounds.Top / dpiYRatio + screen.Bounds.Height / dpiYRatio;
12 return isSubWindowInSameScreen;

介紹完成觸發條件,下面說下窗口居中顯示。
居中顯示,分為當前屏幕內居中/主窗口內居中,直接上代碼

1.在主窗口中居中顯示-CenterOwner

設置窗口的依靠位置Location(Left,Top)(左上角)

  • 子窗口最大化時 --WindowState=“Maximized”最大化窗口,固定的彈出到主屏幕,因此需額外處理,根據屏幕Location設置位置;
  • 父窗口最大化時 --父窗口最大化時,父窗口的location,因窗口設置margin,有可能不準確,故取屏幕位置
  • CenterOwner窗口居中顯示 --直接取父窗口的位置/大小和子窗口的大小,進行計算即可

PS:窗口的位置Left/Top可能為負

 1 /// <summary>
 2 /// 在主窗口中居中顯示
 3 /// </summary>
 4 /// <param name="subWindow"></param>
 5 /// <param name="parentWindow"></param>
 6 private static void SetWindowInCenterOwner(Window subWindow, Window parentWindow)
 7 {
 8     //最大化窗口,固定的彈出到主屏幕,因此需額外處理
 9     if (subWindow.WindowState == WindowState.Maximized)
10     {
11         //子窗口最大化時,需要根據屏幕設置位置;
12         var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);
13 
14         Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
15         double dpiXRatio = currentGraphics.DpiX / 96;
16         double dpiYRatio = currentGraphics.DpiY / 96;
17 
18         subWindow.Left = screen.Bounds.Left / dpiXRatio;
19         subWindow.Top = screen.Bounds.Top / dpiYRatio;
20     }
21     if (parentWindow.WindowState == WindowState.Maximized)
22     {
23         //父窗口最大化時,父窗口的location,因窗口設置margin,有可能不準確,故取屏幕位置
24         var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);
25 
26         Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(parentWindow).Handle);
27         double dpiXRatio = currentGraphics.DpiX / 96;
28         double dpiYRatio = currentGraphics.DpiY / 96;
29 
30         //窗口居中顯示
31         subWindow.Left = screen.Bounds.Left / dpiXRatio +
32                             (screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / 2;
33         subWindow.Top = screen.Bounds.Top / dpiYRatio +
34                         (screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / 2;
35     }
36     else
37     {
38         //窗口居中顯示
39         subWindow.Left = parentWindow.Left + (parentWindow.ActualWidth - subWindow.ActualWidth) / 2;
40         subWindow.Top = parentWindow.Top + (parentWindow.ActualHeight - subWindow.ActualHeight) / 2;
41     }
42 }

2.當前屏幕內居中-CenterScreen

窗口位置設置和上面的一樣,值得註意的是DPI。

通過win的顯示設置,調整文本顯示比例,屏幕的位置轉換(X,Y),得考慮DPI的換算

 1 /// <summary>
 2 /// 在父窗口所在屏幕居中顯示
 3 /// </summary>
 4 /// <param name="subWindow"></param>
 5 /// <param name="parentWindow"></param>
 6 private static void SetWindowInCenterScreen(Window subWindow, Window parentWindow)
 7 {
 8     SetWindowLocationInScreen(subWindow, parentWindow, subWindow.WindowState);
 9 }
10 
11 private const int DpiPercent = 96;
12 
13 private static void SetWindowLocationInScreen(Window subWindow, Window parentWindow, WindowState windowState)
14 {
15     var intPtr = new WindowInteropHelper(parentWindow).Handle;
16     var screen = Screen.FromHandle(intPtr);
17 
18     using (Graphics currentGraphics = Graphics.FromHwnd(intPtr))
19     {
20         double dpiXRatio = currentGraphics.DpiX / DpiPercent;
21         double dpiYRatio = currentGraphics.DpiY / DpiPercent;
22 
23         if (windowState == WindowState.Maximized)
24         {
25             //設置全屏Location
26             subWindow.Left = screen.Bounds.Left / dpiXRatio;
27             subWindow.Top = screen.Bounds.Top / dpiYRatio;
28         }
29         else
30         {
31             //設置居中Location
32             subWindow.Left = screen.Bounds.Left / dpiXRatio +
33                                 (screen.Bounds.Width / dpiXRatio - subWindow.ActualWidth) / 2;
34             subWindow.Top = screen.Bounds.Top / dpiYRatio +
35                             (screen.Bounds.Height / dpiYRatio - subWindow.ActualHeight) / 2;
36         }
37     }
38 }

關鍵字:單實例窗口,窗口居中,CenterOwner,CenterScreen,當前屏幕DPI

WPF 窗口居中 & 變更觸發機制