简体   繁体   中英

Show a message when user clicks outside a window thats launched as ShowDialog

I have Sample Wpf application where I am launching a Window from the Main Window that is launched in full screen. Whenever the user clicks outside of the child Window thats launched I want to show a messagebox. I actually referred to this post how to close a WPF Dialog Window when the user clicks outside it .

But the problem is the Deactivated in my case only gets raised when the child window is closed in this case. Please help how I can achieve this. Edit: Note I want to ShowDialog as I want the child window to be a blocking window.

private void Button_Click(object sender, RoutedEventArgs e)
        {
            Window w = new Window();
            w.Height = 990;
            w.Width = 1840;
            w.MaxHeight = 990;
            w.Width = 1840;
            w.WindowStartupLocation = WindowStartupLocation.Manual;
            w.Top = this.Height - w.Height;
            w.Left = this.Width - w.Width;
            w.Deactivated += W_Deactivated;
            w.ShowDialog();
        }

        private void W_Deactivated(object sender, EventArgs e)
        {
            MessageBox.Show("You clicked out");
        }

Here I'm using the using System.Windows.Input InputManager PreProcessInput event to preview a Mouse input event, filtered by e.StagingItem.Input , generated on a defined element ( Window ).

Then, calculate the position of a Mouse Left click with element.RestoreBounds.Contains(element.PointToScreen(e.GetPosition(element))) .
If the click is generated outside the element Bounds , the Window shows a message, then closes (or quits the app).

The Mouse capture is activated using UIElement.CaptureMouse() and released on a MouseDown event with UIElement.ReleaseMouseCapture .
It's more or less the same as using Mouse.Capture() .

Note that you can click on your Window controls, but if you drag the Window using its TitleBar , the MouseUp event is not rised (by default it's eaten).
If this is an issue, create a logic that detects whether the mouse event was generated in that area and restore the capture after the MouseDown has completed.

using System.Windows;
using System.Windows.Input;

public partial class YourWindow : Window
{
    MouseCapturePreview MouseCaptureHandler;
    //(...)

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        MouseCaptureHandler = new MouseCapturePreview(this);
        //(...)
    }

    public class MouseCapturePreview
    {
        public MouseCapturePreview(Window element)
        {
            InputManager.Current.PreProcessInput += (s, e) => {
                if (e.StagingItem.Input is MouseButtonEventArgs)
                    Handler(s, (MouseButtonEventArgs)e.StagingItem.Input);
            };

            void Handler(object sender, MouseButtonEventArgs e)
            {
                Console.WriteLine("Event Received");
                if (e.LeftButton == MouseButtonState.Pressed) {
                    Console.WriteLine("Mouse Released");
                    element.ReleaseMouseCapture();
                    Mouse.Capture(element);
                    if (!element.RestoreBounds.Contains(element.PointToScreen(e.GetPosition(element))))
                    {
                        Console.WriteLine("Clicked outside");
                        MessageBox.Show("You clicked outside, time to close.");
                        //Application.Current.Shutdown();
                        element.Close();
                    }
                } else {
                    Console.WriteLine("Mouse Captured");
                    element.CaptureMouse();
                }
            }
        }
    }

    private void Window_Activated(object sender, EventArgs e)
    {
        Console.WriteLine("Activated");
        this.CaptureMouse();
    }
}

I found another way with a few steps done. You could create an eventhandler of parent inside the child view.. and don't call ShowDialog() just call Dialog()

This is the parent window, where you shouldn't click once child window opens.

public partial class Parent : Window
{
    private void OpenDialog_Clicked(object sender, RoutedEventArgs e)
    {
          Child child = new Child(this);
          child.Show();
    }
}

This is child window, acts as a dialog and prevents user the click on parent.

public partial class Child : Window
{
    private Parent _parent;

    public Child(Parent parent)
    {
        InitializeComponent();
        _parent = parent;
        parent.PreviewMouseDown += new MouseButtonEventHandler(this.ParentWindow_PreviewMouseDown);       
        this.Closed += new System.EventHandler(this.Window_Closed);         
    }

    // If you click the parent, you get a message and child window will be top most again
    private void ParentWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        MessageBox.Show("na ah");
        this.Topmost = true;
    }

    // If you forget this, you will keep getting that "na ah" message even after closing child window.
    private void Window_Closed(object sender, EventArgs e)
    {
        _parent.PreviewMouseDown -= new MouseButtonEventHandler(ParentWindow_PreviewMouseDown);
    }
}

Use

w.Show();

Because earlier one is modal dialog ( Showdialog() ), you can not go further until you close it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM