设置WPF窗口相对于非WPF窗口的位置
在前一個Post當中,指出了在WPF的WindowInteropHelper類中的一個BUG:通過WindowInteropHelper的Owner屬性不能實現把WPF窗口的Owner屬性設置為一個非WPF窗口的句柄。
在我的Post帖出后不到一天,在WPF SDK的Blog上,就針對這個BUG給出了一個非常完美的解決方案。既然不同通過設置WindowStartupLocation.CenterOwner來改變窗口的位置。那么我們就用WindowStartupLocation.Manual來手動計算設置窗口的位置。大致的代碼如下:
using System.Windows;
using System.Windows.Interop; // WindowInteropHelper
...
// Instantiate the owned WPF window
Window cw = new Window();
// Set the owned WPF window’s owner with the non-WPF owner window
IntPtr ownerWindowHandle = ...;
?
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;
// Manually calculate Top/Left to appear centered
int nonWPFOwnerLeft = ...; ?// Get non-WPF owner’s Left
int nonWPFOwnerWidth = ...; ?// Get non-WPF owner’s Width
int nonWPFOwnerTop = ...; ?// Get non-WPF owner’s Top
int nonWPFOwnerHeight = ...; ?// Get non-WPF owner’s Height
cw.WindowStartupLocation = WindowStartupLocation.Manual;
cw.Left = nonWPFOwnerLeft + (nonWPFOwnerWidth - cw.Width) / 2;
cw.Top = nonWPFOwnerTop + (nonWPFOwnerHeight - cw.Height) / 2;
// Show the owned WPF window
cw.Show();
這段代碼理論上沒有什么問題呢?但是WPF是支持設備獨立的。因此,在非WPF Owner窗口的某些情況下可能會因為DPI的而不能正常工作。解決這個問題,可以利用HwndSource類進行窗口位置的設備獨立計算:
using System.Windows; // Window, WindowStartupLocation, Point
using System.Windows.Interop; // WindowInteropHelper, HwndSource
using System.Windows.Media; // Matrix
...
// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();
// Get the handle to the non-WPF owner window
IntPtr ownerWindowHandle = ...; // Get hWnd for non-WPF window
?
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;
?
// Center window
// Note - Need to use HwndSource to get handle to WPF owned window,
//??????? and the handle only exists when SourceInitialized has been
//??????? raised
cw.SourceInitialized += delegate
{
??? // Get WPF size and location for non-WPF owner window
??? int nonWPFOwnerLeft = ...; // Get non-WPF owner’s Left
??? int nonWPFOwnerWidth = ...; // Get non-WPF owner’s Width
??? int nonWPFOwnerTop = ...; // Get non-WPF owner’s Top
??? int nonWPFOwnerHeight = ...; // Get non-WPF owner’s Height
??? // Get transform matrix to transform non-WPF owner window
??? // size and location units into device-independent WPF?
??? // size and location units
??? HwndSource source = HwndSource.FromHwnd(helper.Handle);
??? if (source == null) return;
??? Matrix matrix = source.CompositionTarget.TransformFromDevice;
??? Point ownerWPFSize = matrix.Transform(
????? new Point(nonWPFOwnerWidth, nonWPFOwnerHeight));
??? Point ownerWPFPosition = matrix.Transform(
????? new Point(nonWPFOwnerLeft, nonWPFOwnerTop));
??? // Center WPF window
??? cw.WindowStartupLocation = WindowStartupLocation.Manual;
??? cw.Left = ownerWPFPosition.X + (ownerWPFSize.X - cw.Width) / 2;
??? cw.Top = ownerWPFPosition.Y + (ownerWPFSize.Y - cw.Height) / 2;
};
// Show WPF owned window
cw.Show();
在上面的代碼中需要注意的是HwndSource的使用。這個類需要一個窗口句柄,因此它的代碼被放在一個SourceInitialized的事件委派函數中執行。
最后,除了上面這種方法,其實我們還可以用Win32 API函數來實現,在ATL的CWindow類中,就有這樣的一個函數,我直接把放在下面,有興趣的朋友參考其中的實現原理:?
?
BOOL?CenterWindow(HWND?hWndCenter?=?NULL)?throw(){
????ATLASSERT(::IsWindow(m_hWnd));
????//?determine?owner?window?to?center?against
????DWORD?dwStyle?=?GetStyle();
????if(hWndCenter?==?NULL)
????{
????????if(dwStyle?&?WS_CHILD)
????????????hWndCenter?=?::GetParent(m_hWnd);
????????else
????????????hWndCenter?=?::GetWindow(m_hWnd,?GW_OWNER);
????}
????//?get?coordinates?of?the?window?relative?to?its?parent
????RECT?rcDlg;
????::GetWindowRect(m_hWnd,?&rcDlg);
????RECT?rcArea;
????RECT?rcCenter;
????HWND?hWndParent;
????if(!(dwStyle?&?WS_CHILD))
????{
????????//?don't?center?against?invisible?or?minimized?windows
????????if(hWndCenter?!=?NULL)
????????{
????????????DWORD?dwStyleCenter?=?::GetWindowLong(hWndCenter,?GWL_STYLE);
????????????if(!(dwStyleCenter?&?WS_VISIBLE)?||?(dwStyleCenter?&?WS_MINIMIZE))
????????????????hWndCenter?=?NULL;
????????}
????????//?center?within?screen?coordinates
????????::SystemParametersInfo(SPI_GETWORKAREA,?NULL,?&rcArea,?NULL);
????????if(hWndCenter?==?NULL)
????????????rcCenter?=?rcArea;
????????else
????????????::GetWindowRect(hWndCenter,?&rcCenter);
????}
????else
????{
????????//?center?within?parent?client?coordinates
????????hWndParent?=?::GetParent(m_hWnd);
????????ATLASSERT(::IsWindow(hWndParent));
????????::GetClientRect(hWndParent,?&rcArea);
????????ATLASSERT(::IsWindow(hWndCenter));
????????::GetClientRect(hWndCenter,?&rcCenter);
????????::MapWindowPoints(hWndCenter,?hWndParent,?(POINT*)&rcCenter,?2);
????}
????int?DlgWidth?=?rcDlg.right?-?rcDlg.left;
????int?DlgHeight?=?rcDlg.bottom?-?rcDlg.top;
????//?find?dialog's?upper?left?based?on?rcCenter
????int?xLeft?=?(rcCenter.left?+?rcCenter.right)?/?2?-?DlgWidth?/?2;
????int?yTop?=?(rcCenter.top?+?rcCenter.bottom)?/?2?-?DlgHeight?/?2;
????//?if?the?dialog?is?outside?the?screen,?move?it?inside
????if(xLeft?<?rcArea.left)
????????xLeft?=?rcArea.left;
????else?if(xLeft?+?DlgWidth?>?rcArea.right)
????????xLeft?=?rcArea.right?-?DlgWidth;
????if(yTop?<?rcArea.top)
????????yTop?=?rcArea.top;
????else?if(yTop?+?DlgHeight?>?rcArea.bottom)
????????yTop?=?rcArea.bottom?-?DlgHeight;
????//?map?screen?coordinates?to?child?coordinates
????return?::SetWindowPos(m_hWnd,?NULL,?xLeft,?yTop,?-1,?-1,
????????SWP_NOSIZE?|?SWP_NOZORDER?|?SWP_NOACTIVATE);
}
本文轉自賴儀靈博客園博客,原文鏈接:http://www.cnblogs.com/YilingLai/archive/2007/05/15/746706.html,如需轉載請自行聯系原作者。
總結
以上是生活随笔為你收集整理的设置WPF窗口相对于非WPF窗口的位置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法踩坑6-二叉搜索树排序
- 下一篇: Linux LB 集群知识、如何用 LV