簡體   English   中英

如何計算 WPF 中的非客戶端窗口大小?

[英]How do I compute the non-client window size in WPF?

WPF 具有公開大量系統指標的SystemParameters 在我的計算機上,我注意到一個普通窗口的標題高 30 像素,邊框寬 8 像素。 這是在啟用 Aero 主題的 Windows 7 上:

非客戶區 - Aero

但是, SystemParameters返回以下值:

SystemParameters.BorderWidth = 5
SystemParameters.CaptionHeight = 21

在這里,我禁用了 Aero 主題:

非客戶區 - 經典

現在, SystemParameters返回以下值:

SystemParameters.BorderWidth = 1
SystemParameters.CaptionHeight = 18

如何使用SystemParameters計算實際觀察值?

對於可調整大小的窗口,您需要使用一組不同的參數來計算大小:

var titleHeight = SystemParameters.WindowCaptionHeight
  + SystemParameters.ResizeFrameHorizontalBorderHeight;
var verticalBorderWidth = SystemParameters.ResizeFrameVerticalBorderWidth;

當您修改主題時,這些尺寸會發生變化。

我很確定GetSystemMetrics函數SystemParameters類使用適當的參數在內部調用)正在為您的系統返回正確的值,它只是在 Aero 主題是否被禁用的情況下返回正確的值。 通過打開 Aero,你可以獲得更強大的邊框和更高的窗口標題,所有這些都是多汁的圖形優點。

如果您想獲得這些窗口元素的正確大小,無論用戶當前的主題如何(請記住,您可以使用 Classic 主題、Aero Basic 主題或完整的 Aero 主題運行 Windows Vista 及更高版本,所有這些都將要擁有不同大小的 UI 元素),您需要使用 Vista 及更高版本中可用的不同方法。

您需要向窗口發送WM_GETTITLEBARINFOEX消息以請求擴展標題欄信息。 wParam未使用,應為零。 lParam包含一個指向TITLEBARINFOEX結構的指針,該結構將接收所有信息。 調用者負責為此結構分配內存並設置其cbSize成員。

要從 .NET 應用程序執行所有這些操作,您顯然需要執行一些 P/Invoke。 首先定義您需要的常量,以及TITLEBARINFOEX結構:

internal const int WM_GETTITLEBARINFOEX = 0x033F;
internal const int CCHILDREN_TITLEBAR = 5;

[StructLayout(LayoutKind.Sequential)]
internal struct TITLEBARINFOEX
{
    public int cbSize;
    public Rectangle rcTitleBar;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
    public int[] rgstate;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
    public Rectangle[] rgrect;
}

然后相應地定義SendMessage函數:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(
                                          IntPtr hWnd,
                                          int uMsg,
                                          IntPtr wParam,
                                          ref TITLEBARINFOEX lParam);

最后,您可以使用以下代碼調用所有這些混亂:

internal static TITLEBARINFOEX GetTitleBarInfoEx(IntPtr hWnd)
{
    // Create and initialize the structure
    TITLEBARINFOEX tbi = new TITLEBARINFOEX();
    tbi.cbSize = Marshal.SizeOf(typeof(TITLEBARINFOEX));

    // Send the WM_GETTITLEBARINFOEX message
    SendMessage(hWnd, WM_GETTITLEBARINFOEX, IntPtr.Zero, ref tbi);

    // Return the filled-in structure
    return tbi;
}

編輯:現在在我的運行 Windows 7 的筆記本上進行測試和工作。

請參閱以下內容:

http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2009/09/20/wpf-quick-tip-how-to-get-wpf-window-client-area-size.aspx

我想您正在嘗試計算必須使應用程序窗口的大小,以便提供適量的客戶區以完全顯示某些 WPF 內容?

如果是這樣,那么請記住 WPF 的像素為 96dpi,並且您的顯示器可能以不同的 dpi 運行……正如其他答案所述,主題會影響您必須調整主窗口的大小以獲得所需的大小客戶區。

或者,您可以在 Window 的子控件上使用 MinWidth/MinHeight。

這是一個 C++/CLI 答案,它沒有使用SystemParameters ,但我認為這是解決這個問題的更好方法,因為它應該適用於任何窗口。

事實上,其他答案僅對可調整大小的窗口有效,並且必須為每個可用的WindowStyle創建不同的案例。

由於這些計算所需的每個SystemParameters都有一個記錄在案的 SM_CX* 或 SM_CY* 值,我認為與其重新發明輪子, AdjustWindowRectEx簡單地使用 WinAPI AdjustWindowRectEx函數。

bool SetWindowClientArea(System::Windows::Window^ win, int width, int height) {
    System::Windows::Interop::WindowInteropHelper^ wi = gcnew System::Windows::Interop::WindowInteropHelper(win);
    wi->EnsureHandle();
    HWND win_HWND = (HWND)(wi->Handle.ToPointer());

    LONG winStyle = GetWindowLong(win_HWND, GWL_STYLE);
    LONG winExStyle = GetWindowLong(win_HWND, GWL_EXSTYLE);
    RECT r = { 0 };
    r.right = width;
    r.bottom = height;
    BOOL bres = AdjustWindowRectEx(&r, winStyle, FALSE, winExStyle);
    if (bres) {
        Double w = r.right - r.left;
        Double h = r.bottom - r.top;
        win->Width = w;
        win->Height = h;
    }

    return bres;
}

可以使用更多的DllImport輕松將上述代碼轉換為 C#,或者如果您的項目已經在使用,則可以將其放入 C++/CLI 程序集。

對於重新調整大小的窗口

NON_CLIENT_AREA_HEIGHT = SystemParameters.WindowNonClientFrameThickness.Top +
                SystemParameters.WindowNonClientFrameThickness.Bottom +
                SystemParameters.WindowResizeBorderThickness.Top +
                SystemParameters.WindowResizeBorderThickness.Bottom;

            NON_CLIENT_AREA_WIDTH = SystemParameters.WindowNonClientFrameThickness.Left +
                SystemParameters.WindowNonClientFrameThickness.Right +
                SystemParameters.WindowResizeBorderThickness.Left +
                SystemParameters.WindowResizeBorderThickness.Right;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM