Minimize, Maximize and close buttons of Windows title bar does not work on single click when pop up window is open

白昼怎懂夜的黑 提交于 2019-12-11 09:07:12

问题


While my popup window is open, The first single click on minimize or maximize or close button does not work. The first Click on these title bar buttons closes the popup menu and shifts the focus, And then on the second click the minimize/maximize/close of window works.

Is there any way - where we can activate these title bar buttons on the first click itself ?

MainWindow.xaml

<Button Height="54" Width="50" Margin="100,0,0,0" x:Name="btnNotification"  FontFamily="Segoe UI Symbol" FontSize="20" Content="&#x1f514;" Command="{Binding LoadNotification}" Click="btnNotification_Click"/>

<Popup Name="NotificationPopup" IsOpen="False" Closed="PopupClosed" StaysOpen="False" PlacementTarget="{Binding ElementName=btnNotification}" Placement="Bottom" VerticalOffset="20">
    <Grid x:Name="PopUpGrid" Height="560" Width="360" Background="White">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Right">
            <Button  BorderBrush="Transparent" BorderThickness="0" Background="White" >
                <StackPanel Width="{Binding ActualWidth, ElementName=PopUpGrid}" Orientation="Vertical">
                    <WrapPanel>
                        <Rectangle Width="20"/>
                        <TextBlock  Text="Notifications" Width="300" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="24" FontWeight="Light" />
                        <Button Click="btnNotification_Click" >
                            <StackPanel>
                                <TextBlock Text="&#x2715;" Foreground="Black" FontWeight="ExtraLight"/>
                            </StackPanel>
                        </Button>
                    </WrapPanel>
                    <Grid>
                            <!--Datagrid-->
                    </Grid>
                </StackPanel>
            </Button>
        </StackPanel>
    </Grid>
</Popup>

MainWindow.xaml.cs

public void PopupClosed(object sender, EventArgs e)
{
    NotificationPopup.IsOpen = false;               
}

回答1:


What you are trying to achieve, violates the 'normal' Windows behavior. Actually, this is managed by the Operating System.

But if you still want to override the default Operating System's behavior, you need to do some low-level Windows' messages magic.

I have a solution for you, this is a simple attached property that you can use for all your Popups. Just like that:

<Popup local:PopupMouseEnhance.Enabled="True">
    <!-- your content here -->
</Popup>

However, there's something going on behind the scenes, so I'll try to explain it. Here is the complete source of the attached property I created for you. Read the comments to understand what's going on.

static class PopupMouseEnhance
{
    // This is the usual attached property stuff...
    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(PopupMouseEnhance),
             new PropertyMetadata(false, EnabledChanged));

    // This method is called when you set the attached property value
    private static void EnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Popup popup = d as Popup;
        if (popup == null)
        {
            // We don't support anything but Popups
            throw new InvalidOperationException("This attached property can only be set on a Popup object.");
        }

        if ((bool)e.NewValue)
        {
            // if the attached property value is 'true', then enable our trick...
            popup.Opened += Popup_Opened;

            // This is to prevent memory leaks when Popups get removed from the visual tree
            popup.Unloaded += Popup_Unloaded;
        }
        else
        {
            // ... otherwise, disable
            popup.Unloaded -= Popup_Unloaded;
            popup.Opened -= Popup_Opened;
        }      
    }

    private static void Popup_Unloaded(object sender, RoutedEventArgs e)
    {
        // When a Popup is completely unloaded, unsubscribe from everything!
        // This event won't be raised on app's shutdown, but in that case
        // we don't bother with memory leaks anyway.
        Popup p = (Popup)sender;
        p.Unloaded -= Popup_Unloaded;
        p.Opened -= Popup_Opened;      
    }

    private static void Popup_Opened(object sender, EventArgs e)
    {
        Popup p = (Popup)sender;

        // Okay, the Popup is shown. Enable our tricks!
        // 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)
            {
                // All set, enable our custom window helper!
                WindowHelper.Enable(source, w, p);
            }
        }
    }

    // Our custom helper class. The magic occurs here.
    private class WindowHelper
    {
        private readonly HwndSource mHwndSource;
        private readonly Window mWindow;

        private WindowHelper(HwndSource hwndSource, Window window)
        {
            mHwndSource = hwndSource;
            mWindow = window;
        }

        public static void Enable(HwndSource hwndSource, Window window, Popup popup)
        {
            // Create an instance of our helper class...
            WindowHelper helper = new WindowHelper(hwndSource, window);

            // ... and set a Message Hook to our custom method.
            hwndSource.AddHook(helper.WndProc);

            // Don't forget to disable the magic, when the popup is closed.
            popup.Closed += helper.Popup_Closed;
        }

        private void Popup_Closed(object sender, EventArgs e)
        {
            // The Popup is closed now - disable all!
            Popup p = (Popup)sender;
            p.Closed -= Popup_Closed;
            mHwndSource.RemoveHook(WndProc);
        }

        // This is our custom Windows messages hook.
        // This method will be called whenever our window receives a message.
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // We're only interested in the WM_SETCURSOR message.
            // It 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.
            // See MSDN topic on WM_SETCURSOR for details.
            var mouseMessage = ((int)lParam & 0xFFFF0000) >> 16;
            var hitTest = (int)lParam & 0xFFFF;

            switch (hitTest)
            {
                // Only continue if the mouse is over
                // '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 Popup,
            // 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 Popup is still open).
            switch (hitTest)
            {
                case NativeConstants.HTMINBUTTON:
                    mWindow.WindowState = WindowState.Minimized;
                    break;

                case NativeConstants.HTMAXBUTTON:
                    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;
    }
}

Hope this helps!



来源:https://stackoverflow.com/questions/50037048/minimize-maximize-and-close-buttons-of-windows-title-bar-does-not-work-on-singl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!