I'm trying to make a custom window frame for my form using DWM. The platform is C# WinForms, Pinvoking DWM.
Following the MSDN article on making custom window frame with DWM, the main steps are next:
- Remove standard frame (non-client area), returning 0 in answer to WM_NCCALCSIZE message
- Extend the frame into client area using DwmExtendFrameIntoClientArea function
I handle WM_NCCALCSIZE message in the next way:
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCCALCSIZE:
if (isDwmWindowFramePaintEnabled() && m.WParam != IntPtr.Zero)
{
m.Result = IntPtr.Zero;
}
else
{
base.WndProc(ref m);
}
return;
}
}
According to MSDN documentation on WM_NCCALCSIZE,
When wParam is TRUE, simply returning 0 without processing the NCCALCSIZE_PARAMS rectangles will cause the client area to resize to the size of the window, including the window frame. This will remove the window frame and caption items from your window, leaving only the client area displayed.
Everything is fine and works for me except one issue. When I maximize/restore window, it's always growing a little bit when it gets restored. I think, the issue is something like this:
- When window gets restored, it contains client area only
- Windows tries to give some non-client area to the window
- In WM_NCCALCSIZE client area grows to contain non-client area
So, like this window grows a little bit each time I maximize/restore it. I need to remove non-client area to paint custom form frame with DWM. I can't simply set window border style to none as then DWM will not paint the window caption and borders.
Please, help to solve the issue and happily have a custom window frame.
This is actually a bug in Windows Forms and there's a workaround. In function
Form.SizeFromClientSize(int, int)
theAdjustWindowRectEx
function is used to translate the size and it always uses the default measurements and can't be overridden. This function is called from two places:RestoreWindowBoundsIfNecessary
in WM_WINDOWPOSCHANGED window message handlerSetClientSizeCore
The workaround is the following:
Override CreateParams in the Form:
Override WndProc and insert the following code to handle WM_WINDOWPOSCHANGED:
Override SetClientSizeCore:
It may also be good idea to override
SizeFromClientSize(Size)
to return the correct measurements, but it is not strictly necessary.