当我的菜单打开时,首次单击最小化或最大化或关闭按钮不起作用。首先单击这些标题栏按钮关闭菜单并移动焦点,然后在第二次单击时最小化/最大化/关闭窗口工作。
我提到了这个article,它为Context菜单和Popups提出了类似的问题。 但该解决方案不适用于Menu类。
这就是我的尝试:
xmlns:controls="clr-namespace:TestProject.Controls"
MainWindow.xaml
<Menu Style="{StaticResource CustomMenuStyle}" controls:MenuMouseEnhance.Enabled="True">
<MenuItem Header="List of Items" >
<MenuItem Header="MenuOne" />
<MenuItem Header="MenuTwo" />
</MenuItem>
</Menu>
MenuMouseEnhance.cs
public static class MenuMouseEnhance
{
public static bool GetEnabled(UIElement element)
{
return (bool)element.GetValue(EnabledProperty);
}
public static void SetEnabled(UIElement element, bool value)
{
element.SetValue(EnabledProperty, value);
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.RegisterAttached(
"Enabled",
typeof(bool),
typeof(MenuMouseEnhance),
new PropertyMetadata(false, EnabledChanged));
private static void EnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem Menu = d as MenuItem;
if ((bool)e.NewValue)
{
Menu.SubmenuOpened += Menu_Opened;
}
else
{
Menu.SubmenuOpened -= Menu_Opened;
}
}
private static void Menu_Opened(object sender, EventArgs e)
{
MenuItem p = (MenuItem)sender;
// First, we determine the window we will monitor:
Window w = Window.GetWindow(p);
if (w != null)
{
// Then, we need a HwndSource instance of that window
// to be able to insert our custom Message Hook
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(w).Handle);
if (source != null)
{
//Enable the custom window helper!
WindowHelper.Enable(source, w, p);
}
}
}
/// <summary>
/// This is a custom helper class
/// This initialized the HwndSource and Window classes through constructor injection
/// </summary>
private class WindowHelper
{
private readonly HwndSource mHwndSource;
private readonly Window mWindow;
/// <summary>
/// Set the members of this class in constructor
/// </summary>
/// <param name="hwndSource"></param>
/// <param name="window"></param>
private WindowHelper(HwndSource hwndSource, Window window)
{
mHwndSource = hwndSource;
mWindow = window;
}
public static void Enable(HwndSource hwndSource, Window window, MenuItem menu)
{
WindowHelper helper = new WindowHelper(hwndSource, window);
hwndSource.AddHook(helper.WndProc);
menu.SubmenuClosed += helper.Menu_Closed;
}
private void Menu_Closed(object sender, EventArgs e)
{
// The ContextMenu is closed now - disable all!
MenuItem p = (MenuItem)sender;
p.SubmenuClosed -= Menu_Closed;
mHwndSource.RemoveHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// WM_SETCURSOR will be sent to our window when the user moves the mouse
// Cursor around and clicks the mouse buttons.
if (msg != NativeConstants.WM_SETCURSOR)
{
return IntPtr.Zero;
}
// Determine the necessary parameters.
//The low-order word of lParam specifies the hit-test code.
//The high-order word of lParam specifies the identifier of the mouse message.
var mouseMessage = ((int)lParam & 0xFFFF0000) >> 16;
var hitTest = (int)lParam & 0xFFFF;
switch (hitTest)
{
// Only continue if the mouse is over
// The 'minimize', 'maximize', 'close'
case NativeConstants.HTMINBUTTON:
case NativeConstants.HTMAXBUTTON:
case NativeConstants.HTCLOSE:
break;
default:
// Otherwise, do nothing.
return IntPtr.Zero;
}
// If the user clicks outside the Menu,
// a WM_MOUSEMOVE message will be transmitted via WM_SETCURSOR.
// So if we've received something other - ignore that.
if (mouseMessage != NativeConstants.WM_MOUSEMOVE)
{
return IntPtr.Zero;
}
// We need to perform these actions manually,
// because the window will not receive the corresponding messages
// on first mouse click (when the ContextMenu is still open).
switch (hitTest)
{
case NativeConstants.HTMINBUTTON:
mWindow.WindowState = WindowState.Minimized;
break;
case NativeConstants.HTMAXBUTTON:
if (mWindow.WindowState.ToString() == "Maximized")
{
//When Window is maximized
//Assign the Normal state to the window when Maximize is pressed
mWindow.WindowState = WindowState.Normal;
}
else
{
//When Window is in normal state
//Assign the maximized state to the window when Maximize is pressed
mWindow.WindowState = WindowState.Maximized;
}
break;
case NativeConstants.HTCLOSE:
mWindow.Close();
break;
}
// We always return 0, because we don't want any side-effects
// in the message processing.
return IntPtr.Zero;
}
}
private static class NativeConstants
{
public const int WM_SETCURSOR = 0x020;
public const int WM_MOUSEMOVE = 0x200;
public const int HTMINBUTTON = 8;
public const int HTMAXBUTTON = 9;
public const int HTCLOSE = 20;
}
}
答案 0 :(得分:1)
因为我在引用的问题中提出了解决方案,所以我可以很容易地回答这个问题。
正如我在那里的评论中提到的,你应该使用MenuItem.SubMenuOpened
路由事件。
因此,为了使此代码有效,您需要进行以下修改:
WindowHelper
课程以处理Menu
,而不是MenuItem
。我们要在Menu
上设置附加属性,请参阅您的XAML!private class WindowHelper
{
// Third parameter is a Menu, not a MenuItem
public static void Enable(HwndSource hwndSource, Window window, Menu menu)
{
WindowHelper helper = new WindowHelper(hwndSource, window);
hwndSource.AddHook(helper.WndProc);
// Subscribe to the routed event MenuItem.SubmenuClosed
menu.AddHandler(MenuItem.SubmenuClosedEvent, (RoutedEventHandler)helper.Menu_Closed);
}
// The method signature has to be changed - this is a routed event handler now
private void Menu_Closed(object sender, RoutedEventArgs e)
{
Menu menu = (Menu)sender;
MenuItem menuItem = (MenuItem)e.Source;
if (menuItem.Parent != menu)
{
// If it's not the first level menu, ignore it.
// We only disable our helper when the whole menu closes.
return;
}
// Unsubscribe from the routed event
menu.RemoveHandler(MenuItem.SubmenuClosedEvent, (RoutedEventHandler)Menu_Closed);
mHwndSource.RemoveHook(WndProc);
}
// Rest is unchanged
// ...
}
Menu_Opened
事件处理程序签名以对应RoutedEventHandler
委托。更新活动订阅代码。private static void Menu_Opened(object sender, RoutedEventArgs e)
{
Menu menu = (Menu)sender;
MenuItem menuItem = (MenuItem)e.Source;
if (menuItem.Parent != menu)
{
// We don't want to process any sub-menus in the deeper levels,
// because the helper will already be enabled when
// a first level menu opens
return;
}
Window w = Window.GetWindow(menu);
if (w != null)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(w).Handle);
if (source != null)
{
WindowHelper.Enable(source, w, menu);
}
}
}
private static void EnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Menu menu = (Menu)d;
if ((bool)e.NewValue)
{
menu.AddHandler(MenuItem.SubmenuOpenedEvent, (RoutedEventHandler)Menu_Opened);
}
else
{
menu.RemoveHandler(MenuItem.SubmenuOpenedEvent, (RoutedEventHandler)Menu_Opened);
}
}
现在这适用于菜单。
顺便说一句,而不是
w.WindowState.ToString() == "Maximized"
你应该使用
w.WindowState == WindowState.Maximized