我使用WPF Shell集成库(http://archive.msdn.microsoft.com/WPFShell)实现了自定义窗口镶边。
在我将ReSizeMode设置为NoResize之前,Chrome一直有效。然后,如果将鼠标悬停在关闭按钮上,我会注意到基础系统关闭按钮显示在自定义铬关闭按钮下。
预期的行为是底层关闭按钮永远不会出现。如果我移动窗口或在桌面上选择另一个窗口并返回到此窗口,我会注意到系统关闭按钮再次被隐藏。 所以可能基本上是(1)应用程序首次启动时和(2)当ResizeMode = NoResize时。
我首先怀疑我们如何处理自定义Chrome中的WM.NCHITTEST。如果我修改此函数以返回HTCLient,则此问题已解决。但是,我失去了拖放功能以及右键单击标题栏。
以下是来自WindowsChromeWorker Class的WM.NCHITTEST消息处理程序的代码。
private IntPtr _HandleNCHitTest( WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled ) {
IntPtr lRet = IntPtr.Zero;
handled = false;
// Give DWM a chance at this first.
if ( Utility.IsOSVistaOrNewer && _chromeInfo.GlassFrameThickness != default( Thickness ) && _isGlassEnabled ) {
// If we're on Vista, give the DWM a chance to handle the message first.
handled = NativeMethods.DwmDefWindowProc( _hwnd, uMsg, wParam, lParam, out lRet );
}
// Handle letting the system know if we consider the mouse to be in our effective non-client area.
// If DWM already handled this by way of DwmDefWindowProc, then respect their call.
if ( IntPtr.Zero == lRet ) {
var mousePosScreen = new Point( Utility.GET_X_LPARAM( lParam ), Utility.GET_Y_LPARAM( lParam ) );
Rect windowPosition = _GetWindowRect();
HT ht = _HitTestNca(
DpiHelper.DeviceRectToLogical( windowPosition ),
DpiHelper.DevicePixelsToLogical( mousePosScreen ) );
// Don't blindly respect HTCAPTION.
// We want UIElements in the caption area to be actionable so run through a hittest first.
if ( ht != HT.CLIENT) {
Point mousePosWindow = mousePosScreen;
mousePosWindow.Offset( -windowPosition.X, -windowPosition.Y );
mousePosWindow = DpiHelper.DevicePixelsToLogical( mousePosWindow );
IInputElement inputElement = _window.InputHitTest( mousePosWindow );
if ( inputElement != null && WindowChrome.GetIsHitTestVisibleInChrome( inputElement ) ) {
ht = HT.CLIENT;
}
}
//handled = false;
handled = true;
lRet = new IntPtr((int)ht);
}
return lRet;
}
private static readonly HT[,] _HitTestBorders = new[,]
{
{ HT.TOPLEFT, HT.TOP, HT.TOPRIGHT },
{ HT.LEFT, HT.CLIENT, HT.RIGHT },
{ HT.BOTTOMLEFT, HT.BOTTOM, HT.BOTTOMRIGHT },
};
private HT _HitTestNca( Rect windowPosition, Point mousePosition ) {
// Determine if hit test is for resizing, default middle (1,1).
int uRow = 1;
int uCol = 1;
bool onResizeBorder = false;
//if (_window.ResizeMode == ResizeMode.NoResize)
// _chromeInfo.ResizeBorderThickness = new Thickness(0);
// Determine if the point is at the top or bottom of the window.
if ( mousePosition.Y >= windowPosition.Top && mousePosition.Y < windowPosition.Top + _chromeInfo.ResizeBorderThickness.Top + _chromeInfo.CaptionHeight ) {
onResizeBorder = ( mousePosition.Y < ( windowPosition.Top + _chromeInfo.ResizeBorderThickness.Top ) );
uRow = 0; // top (caption or resize border)
} else if ( mousePosition.Y < windowPosition.Bottom && mousePosition.Y >= windowPosition.Bottom - (int)_chromeInfo.ResizeBorderThickness.Bottom ) {
uRow = 2; // bottom
}
// Determine if the point is at the left or right of the window.
if ( mousePosition.X >= windowPosition.Left && mousePosition.X < windowPosition.Left + (int)_chromeInfo.ResizeBorderThickness.Left ) {
uCol = 0; // left side
} else if ( mousePosition.X < windowPosition.Right && mousePosition.X >= windowPosition.Right - _chromeInfo.ResizeBorderThickness.Right ) {
uCol = 2; // right side
}
// If the cursor is in one of the top edges by the caption bar, but below the top resize border,
// then resize left-right rather than diagonally.
if ( uRow == 0 && uCol != 1 && !onResizeBorder ) {
uRow = 1;
}
HT ht = _HitTestBorders[uRow, uCol];
if ( ht == HT.TOP && !onResizeBorder) {
ht = HT.CAPTION;
}
return ht;
}
任何想法如何解决这个问题?
非常感谢, 阿琼
答案 0 :(得分:3)
所有的仪式......我确实想出了这个问题。
首先,我最初的怀疑是错误的。 WM.NCHITTEST消息的处理不是不正确。这真的是一个窗口样式的问题。
最佳解决方案是隐藏系统关闭按钮,让Chrome关闭按钮完成其工作。但是你在网上找到的解决方案,即窗口样式http://winsharp93.wordpress.com/2009/07/21/wpf-hide-the-window-buttons-minimize-restore-and-close-and-the-icon-of-a-window/中的切换SYSMENU位标志在我的情况下不起作用。
关闭按钮是隐藏的,但是尽管ResizeMode设置为NoResize,我注意到Resize Cursor和Resize Menu {Max \ Min \ Restore}都已启用。
经过几个小时的试验和错误后,我想出了这段代码:
//This property descriptor is used to hook-onto the resizemode change notification
private void Window_Loaded( object sender, System.Windows.RoutedEventArgs e)
{
// When the default handling of ResizeMode = NoResize causes problems - this is why custom handling is required.
System.ComponentModel.DependencyPropertyDescriptor _resizeModePropertyDescriptor;
_resizeModePropertyDescriptor = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Window.ResizeModeProperty,
typeof(Window));
_resizeModePropertyDescriptor.AddValueChanged(this._window, new EventHandler(this._Window_ResizeModePropertyChanged));
}
/// <summary>
/// This property change handler only reacts when the ReSizeMode is set to NoResize.
/// In the default window style when Resize = NoResize, causes the underlying system close button to show up under the Chrome Close Button.
/// This is a fix to handle that problem. [ Refer to defect #5134 for further details.
/// </summary>
/// <param name="sender">ChromeWorker object</param>
/// <param name="e">Event Args - Not really used</param>
private void _Window_ResizeModePropertyChanged(object sender, EventArgs e)
{
if (_window.ResizeMode == ResizeMode.NoResize)
{
//Got these styles merely by trial and error.
_ModifyStyle(
WS.SYSMENU | WS.DLGFRAME | WS.BORDER | WS.CLIPCHILDREN | WS.CLIPSIBLINGS, //Styles to Remove
WS.POPUPWINDOW); //Style to Add
}
}
/// <summary>Add and remove a native WindowStyle from the HWND.</summary>
/// <param name="removeStyle">The styles to be removed. These can be bitwise combined.</param>
/// <param name="addStyle">The styles to be added. These can be bitwise combined.</param>
/// <returns>Whether the styles of the HWND were modified as a result of this call.</returns>
private bool _ModifyStyle( WS removeStyle, WS addStyle ) {
Assert.IsNotDefault( _hwnd );
var dwStyle = (WS)NativeMethods.GetWindowLongPtr( _hwnd, GWL.STYLE ).ToInt32();
var dwNewStyle = ( dwStyle & ~removeStyle ) | addStyle;
if ( dwStyle == dwNewStyle ) {
return false;
}
NativeMethods.SetWindowLongPtr( _hwnd, GWL.STYLE, new IntPtr( (int)dwNewStyle ) );
return true;
}
你也可以使用SourceInitialized事件进行连线,但我还没有真正测试过。 而已!这抓住了对Window Chromes的一周激烈探索!
我不得不承认我有点好奇 - 在WPF上工作这么长时间并没有让我意识到win32的整个世界都更加强大(而且令人沮丧!)