本文經原作者授權以原創方式二次分享,歡迎轉載、分享。
原文作者:唐宋元明清
原文地址:https://www.cnblogs.com/kybs0/p/7420767.html
窗口居中 & 變更觸發機制
解決:
1)單實例窗口,窗口每次隱藏后再顯示時,位置居中顯示
2)多屏幕下單實例窗口,當父窗口移動到其它屏幕時,單實例窗口再次彈出時,位置才更新到父窗口屏幕。
3)子窗口每次喚醒時,都居中顯示。
窗口首次顯示的位置 - WindowStartupLocation
Windows
的啟動時位置顯示,WindowStartupLocation
CenterOwner
--顯示在父窗口的中間(設置Owner
)CenterScreen
--顯示在當前屏幕中間Manual
--默認位置 當第一次Window.ShowDialog
時,window
顯示如上設置。
變更觸發機制 上面只涉及到了首次顯示位置,之后,窗口的位置會繼續保留
如何設置窗口隱藏之后再次彈出時,顯示在中間(CenterOwner/CenterScreen
)?如何設置窗口一直停留在顯示在中間?
我們先了解一下,有哪些觸發機制
Activated 窗口激活 窗口變更為前臺窗口時(即顯示在最前面),會觸發
IsVisibleChanged 顯示變更 當我們設置窗口隱藏
Hide(
)時,IsVisibile=false
.窗口再次ShowDialog
時,IsVisibile=true
; 利用如上倆種機制,下面就可以搞事情了。
首先定義幾個枚舉:
///?<summary>///?窗口顯示變更觸發時機///?</summary>public?enum?WindowLocationInvokeOccasion{///?<summary>///?只要Activated就顯示在中間///?</summary>Activated?=?0,///?<summary>///?只在第一次Activated時,顯示在中間一次,之后的變化就不修改///?</summary>FirstActivated,///?<summary>///?窗口每次顯示時,窗口居中///?<para>可以解決單實例窗口彈出不居中問題</para>///?</summary>Visibile,///?<summary>///?窗口每次顯示時,如父窗口與當前窗口不在同一屏幕時,窗口居中///?<para>可以解決單實例窗口彈出不居中問題</para>///?</summary>VisibileInDifferentScreen,///?<summary>///?不觸發///?</summary>Defatult}
如上枚舉包含了4種觸發機制。
我們再定義個附加屬性,通過附加屬性去設置窗口的額外功能-居中顯示觸發機制
///?<summary>
///?窗口顯示居中觸發時機
///?<para>另:居中顯示設置,請使用<see?cref="Window"/>的<see?cref="WindowStartupLocation"/>屬性</para>
///?</summary>
public?static?readonly?DependencyProperty?InvokeOccasionProperty?=?DependencyProperty.RegisterAttached("InvokeOccasion",?typeof(WindowLocationInvokeOccasion),?typeof(WindowLocationOptions),new?PropertyMetadata(default(WindowLocationInvokeOccasion),?InvokeOccasionProperty_ChangedCallback));
在屬性更改觸發事件中,根據不同的觸發條件,設置不同的居中顯示。
Activated
--只要Activated
就顯示在中間 每次觸發,直接顯示窗口即可;首次
Activated
通過設置Window.Activated -= ShowInCenter_Activated
;禁用下次觸發進入Visibile
VisibileInDifferentScreen
窗口顯示時,如父窗口與當前窗口不在同一屏幕時,窗口居中. 怎么判斷當前子窗口與父窗口是否在同一屏幕?
var?screen?=?Screen.FromHandle(new?WindowInteropHelper(parentWindow).Handle);Graphics?currentGraphics?=?Graphics.FromHwnd(new?WindowInteropHelper(parentWindow).Handle);
double?dpiXRatio?=?currentGraphics.DpiX?/?96;
double?dpiYRatio?=?currentGraphics.DpiY?/?96;//當子窗口與父窗口所在屏幕相同時,不作處理
var?isSubWindowInSameScreen?=?subWindow.Left?>?screen.Bounds.Left?/?dpiXRatio?&&subWindow.Left?<?screen.Bounds.Left?/?dpiXRatio?+?screen.Bounds.Width?/?dpiXRatio?&&subWindow.Top?>?screen.Bounds.Top?/?dpiYRatio?&&subWindow.Top?<?screen.Bounds.Top?/?dpiYRatio?+?screen.Bounds.Height?/?dpiYRatio;
return?isSubWindowInSameScreen;
紹完成觸發條件,下面說下窗口居中顯示。居中顯示,分為當前屏幕內居中/主窗口內居中,直接上代碼
1)在主窗口中居中顯示-CenterOwner 設置窗口的依靠位置Location(Left,Top)(左上角)
子窗口最大化時 --
WindowState=“Maximized”
最大化窗口,固定的彈出到主屏幕,因此需額外處理,根據屏幕Location
設置位置;父窗口最大化時 --父窗口最大化時,父窗口的
location
,因窗口設置margin
,有可能不準確,故取屏幕位置CenterOwner窗口居中顯示 --直接取父窗口的位置/大小和子窗口的大小,進行計算即可;
PS:窗口的位置Left/Top
可能為負
///?<summary>
///?在主窗口中居中顯示
///?</summary>
///?<param?name="subWindow"></param>
///?<param?name="parentWindow"></param>
private?static?void?SetWindowInCenterOwner(Window?subWindow,?Window?parentWindow)
{//最大化窗口,固定的彈出到主屏幕,因此需額外處理if?(subWindow.WindowState?==?WindowState.Maximized){//子窗口最大化時,需要根據屏幕設置位置;var?screen?=?Screen.FromHandle(new?WindowInteropHelper(parentWindow).Handle);Graphics?currentGraphics?=?Graphics.FromHwnd(new?WindowInteropHelper(parentWindow).Handle);double?dpiXRatio?=?currentGraphics.DpiX?/?96;double?dpiYRatio?=?currentGraphics.DpiY?/?96;subWindow.Left?=?screen.Bounds.Left?/?dpiXRatio;subWindow.Top?=?screen.Bounds.Top?/?dpiYRatio;}if?(parentWindow.WindowState?==?WindowState.Maximized){//父窗口最大化時,父窗口的location,因窗口設置margin,有可能不準確,故取屏幕位置var?screen?=?Screen.FromHandle(new?WindowInteropHelper(parentWindow).Handle);Graphics?currentGraphics?=?Graphics.FromHwnd(new?WindowInteropHelper(parentWindow).Handle);double?dpiXRatio?=?currentGraphics.DpiX?/?96;double?dpiYRatio?=?currentGraphics.DpiY?/?96;//窗口居中顯示subWindow.Left?=?screen.Bounds.Left?/?dpiXRatio?+(screen.Bounds.Width?/?dpiXRatio?-?subWindow.ActualWidth)?/?2;subWindow.Top?=?screen.Bounds.Top?/?dpiYRatio?+(screen.Bounds.Height?/?dpiYRatio?-?subWindow.ActualHeight)?/?2;}else{//窗口居中顯示subWindow.Left?=?parentWindow.Left?+?(parentWindow.ActualWidth?-?subWindow.ActualWidth)?/?2;subWindow.Top?=?parentWindow.Top?+?(parentWindow.ActualHeight?-?subWindow.ActualHeight)?/?2;}
}
2)當前屏幕內居中-CenterScreen
;
窗口位置設置和上面的一樣,值得注意的是
DPI
。通過
win
的顯示設置,調整文本顯示比例,屏幕的位置轉換(X,Y)``,得考慮
DPI`的換算;
///?<summary>
///?在父窗口所在屏幕居中顯示
///?</summary>
///?<param?name="subWindow"></param>
///?<param?name="parentWindow"></param>
private?static?void?SetWindowInCenterScreen(Window?subWindow,?Window?parentWindow)
{SetWindowLocationInScreen(subWindow,?parentWindow,?subWindow.WindowState);
}private?const?int?DpiPercent?=?96;private?static?void?SetWindowLocationInScreen(Window?subWindow,?Window?parentWindow,?WindowState?windowState)
{var?intPtr?=?new?WindowInteropHelper(parentWindow).Handle;var?screen?=?Screen.FromHandle(intPtr);using?(Graphics?currentGraphics?=?Graphics.FromHwnd(intPtr)){double?dpiXRatio?=?currentGraphics.DpiX?/?DpiPercent;double?dpiYRatio?=?currentGraphics.DpiY?/?DpiPercent;if?(windowState?==?WindowState.Maximized){//設置全屏LocationsubWindow.Left?=?screen.Bounds.Left?/?dpiXRatio;subWindow.Top?=?screen.Bounds.Top?/?dpiYRatio;}else{//設置居中LocationsubWindow.Left?=?screen.Bounds.Left?/?dpiXRatio?+(screen.Bounds.Width?/?dpiXRatio?-?subWindow.ActualWidth)?/?2;subWindow.Top?=?screen.Bounds.Top?/?dpiYRatio?+(screen.Bounds.Height?/?dpiYRatio?-?subWindow.ActualHeight)?/?2;}}
}