我正在尝试创建一个带有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
这可以让我保留aero窗口边界并隐藏调整大小的光标,但它在窗口内部添加了一个~5像素的白色边界。
事实上,即使我在WndPrc
的顶部返回默认的windows过程结果,并且不做任何其他操作,边界仍然是。我需要在我的窗口上使用不同的背景色,所以这对我不起作用。
有什么想法吗?一如既往地提前感谢。
添加钩子时,应该只处理需要处理的消息,而忽略其他消息。我相信您正在处理某些消息两次,因为您调用了DefWindowProc,但从未将handled参数设置为true。
所以在你的情况下,你会使用:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == (uint)WM.NCHITTEST) {
handled = true;
var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
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);
}
return IntPtr.Zero;
}
此外,我可能会在OnSourceInitialized覆盖中添加钩子,如下所示:
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized(e);
IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc.AddHook(WndProc);
}
您可以在WPF应用程序的任何地方尝试
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
和:
// ******************************************************************
private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
{
if (!handled)
{
if (msg.message == WmHotKey)
{
HotKey hotKey;
if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey))
{
if (hotKey.Action != null)
{
hotKey.Action.Invoke(hotKey);
}
handled = true;
}
}
}
}