在 WPF 窗口中挂钩 Windows 消息循环会在内部添加白色边框
我正在尝试创建一个无法调整大小的 WindowStyle="None"
(用于自定义按钮且没有标题)的 WPF 窗口。将 ResizeMode
设置为 NoResize
会删除我想保留的航空边框。
我可以设置最小/最大尺寸属性并完成它,除了:
- 调整大小光标仍然可见,并且
- 窗口显示以响应用户操作并适合其内容。它显示图像,因此大小会发生变化。
所以,我有一个简单的计划,可以让我完成 99% 的任务:
public class BorderedWindowNoResize : Window
{
[DllImport( "DwmApi.dll" )]
public static extern int DwmExtendFrameIntoClientArea(
IntPtr hwnd,
ref MARGINS pMarInset );
[DllImport( "user32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr DefWindowProc(
IntPtr hWnd,
int msg,
IntPtr wParam,
IntPtr lParam );
public BorderedWindowNoResize()
{
Loaded += BorderedWindowNoResize_Loaded;
}
private void BorderedWindowNoResize_Loaded( object sender, RoutedEventArgs e )
{
IntPtr mainWindowPtr = new WindowInteropHelper( this ).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd( mainWindowPtr );
mainWindowSrc.AddHook( WndProc );
}
private IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
{
var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32();
if( msg == (uint)WM.NCHITTEST )
{
handled = true;
switch( htLocation )
{
case (int)HitTestResult.HTBOTTOM:
case (int)HitTestResult.HTBOTTOMLEFT:
case (int)HitTestResult.HTBOTTOMRIGHT:
case (int)HitTestResult.HTLEFT:
case (int)HitTestResult.HTRIGHT:
case (int)HitTestResult.HTTOP:
case (int)HitTestResult.HTTOPLEFT:
case (int)HitTestResult.HTTOPRIGHT:
htLocation = (int)HitTestResult.HTBORDER;
break;
}
}
return new IntPtr( htLocation );
}
}
基本上;
- 覆盖窗口过程。
- 调用默认的窗口过程。
- 如果消息是
WM_NCHITTEST
,请检查边界结果。 - 如果是边框,则返回常规的
HTBORDER
。
这可以让我保留航空窗口边框并隐藏调整大小光标,但它会在窗口内部添加约 5 像素的白色边框。
事实上,即使我在 WndPrc
顶部返回默认的 Windows 过程结果并且不执行任何其他操作,边框仍然在那里。我的窗口需要不同的背景颜色,所以这对我不起作用。
有什么想法吗?一如既往地提前致谢。
I am trying to create a WPF window with WindowStyle="None"
(for custom buttons and no title) that cannot be resized. Setting ResizeMode
to NoResize
removes the aero border, which I want to keep.
I could set the min/max size properties and be done with it, except that:
- The resize cursors are still visible, and
- The window is displayed in response to a user action and fits to its contents. It displays an image, so the size changes.
So, I have a simple scheme that gets me 99% of the way there:
public class BorderedWindowNoResize : Window
{
[DllImport( "DwmApi.dll" )]
public static extern int DwmExtendFrameIntoClientArea(
IntPtr hwnd,
ref MARGINS pMarInset );
[DllImport( "user32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr DefWindowProc(
IntPtr hWnd,
int msg,
IntPtr wParam,
IntPtr lParam );
public BorderedWindowNoResize()
{
Loaded += BorderedWindowNoResize_Loaded;
}
private void BorderedWindowNoResize_Loaded( object sender, RoutedEventArgs e )
{
IntPtr mainWindowPtr = new WindowInteropHelper( this ).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd( mainWindowPtr );
mainWindowSrc.AddHook( WndProc );
}
private IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
{
var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32();
if( msg == (uint)WM.NCHITTEST )
{
handled = true;
switch( htLocation )
{
case (int)HitTestResult.HTBOTTOM:
case (int)HitTestResult.HTBOTTOMLEFT:
case (int)HitTestResult.HTBOTTOMRIGHT:
case (int)HitTestResult.HTLEFT:
case (int)HitTestResult.HTRIGHT:
case (int)HitTestResult.HTTOP:
case (int)HitTestResult.HTTOPLEFT:
case (int)HitTestResult.HTTOPRIGHT:
htLocation = (int)HitTestResult.HTBORDER;
break;
}
}
return new IntPtr( htLocation );
}
}
Basically;
- Override the window procedure.
- Call the default window procedure.
- If the message it is
WM_NCHITTEST
, check for the border results. - If it is a border, return the regular
HTBORDER
.
This works as far as allowing me to keep the aero window border and hiding the resize cursor(s), but it adds a ~5 pixel white border to the inside of my window.
In fact, even if I return the default windows procedure result at the top of WndPrc
and do nothing else the border is still there. I need a different background color on my window, so this won't work for me.
Any ideas? Thanks in advance as always.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
添加钩子时,您应该只处理您需要的消息,而忽略其他消息。我相信您正在处理某些消息两次,因为您调用了 DefWindowProc,但从未将已处理参数设置为 true。
因此,在您的情况下,您可以使用:
另外,我可能会在 OnSourceInitialized 覆盖,如下所示:
When you add your hook, you should only handle the messages you need to, and ignore the others. I believe you are handling certain messages twice, since you call DefWindowProc, but never set the handled parameter to true.
So in your case, you'd use:
Also, I'd probably add the hook in an OnSourceInitialized override, like so:
您可以在 WPF 应用程序中的任何位置进行尝试
,并且:
You can try from anywhere in a WPF App
and: