Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

WPF has the SystemParameters class that exposes a great number of system metrics. On my computer I have noticed that a normal window has a title that is 30 pixels high and a border that is 8 pixels wide. This is on Windows 7 with the Aero theme enabled:

Non-client area - Aero

However, SystemParameters return the following values:

SystemParameters.BorderWidth = 5 SystemParameters.CaptionHeight = 21 

Here I have disabled the Aero theme:

Non-client area - classic

Now, SystemParameters return the following values:

SystemParameters.BorderWidth = 1 SystemParameters.CaptionHeight = 18 

How do I compute the actual observed values by using SystemParameters?

like image 895
Martin Liversage Avatar asked May 17 '11 14:05

Martin Liversage


People also ask

What is the difference between page and window in WPF?

Window is the root control that must be used to hold/host other controls (e.g. Button) as container. Page is a control which can be hosted in other container controls like NavigationWindow or Frame. Page control has its own goal to serve like other controls (e.g. Button). Page is to create browser like applications.

How do I display a WPF window?

When a Window is created at run-time using the Window object, it is not visible by default. To make it visible, we can use Show or ShowDialog method. Show method of Window class is responsible for displaying a window.


2 Answers

For a resizable window you need to use a different set of parameters to compute the size:

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

These sizes will change when you modify the theme.

like image 178
Tim Avatar answered Sep 21 '22 15:09

Tim


I'm pretty sure that the GetSystemMetrics function (which the SystemParameters class calls internally with the appropriate arguments) is returning the correct values for your system, it's just returning the correct values in the case whether the Aero theme is disabled. By turning on Aero, you get beefier borders and taller window captions, all the name of juicy graphical goodness.

If you want to get the correct size of these window elements, regardless of the user's current theme (remember, you can run Windows Vista and beyond with the Classic theme, the Aero Basic theme, or the full Aero theme, all of which are going to have different-sized UI elements), you need to use a different method available in Vista and later.

You need to send the window a WM_GETTITLEBARINFOEX message in order to request extended title bar information. The wParam is unused and should be zero. The lParam contains a pointer to a TITLEBARINFOEX structure that will receive all of the information. The caller is responsible for allocating memory for this structure and setting its cbSize member.

To do all of this from a .NET application, you'll obviously need to do some P/Invoke. Start by defining the constants you need, as well as the TITLEBARINFOEX structure:

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; } 

Then define the SendMessage function accordingly:

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

And finally, you can call all of that mess using something like the following code:

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; } 

EDIT: Now tested and working on my notebook running Windows 7.

like image 45
Cody Gray Avatar answered Sep 22 '22 15:09

Cody Gray