none
Form.Deactivate event - How to find out who stole my focus? RRS feed

  • Question

  • Hello everyone.

    I have been trying to make a little pop-up panel for a project I'm working on.

    My first approach was to make a UserControl consisting of a panel with the controls I wanted on it, and simply displaying that over the main form. It works, but if the panel is bigger than the form, you obviously get some problems.

    So I stepped over to making a new form, generated at the current mouse position. It works flawlessly apart from one thing; I want it to stay in place when the user swaps to a different application. (the pop-up displays a textbox, which will cause the user to swap to a different application for copy-pasting)

    My next step was trying to find out which event caused my form to lose focus, but I seem to have hit a dead end; I cannot find any way to find that out.

    So now I turn to you, mighty codewarriors of the internet!

    Too long; didn't read: How can I find out what's causing my form to lose focus? The form.Deactivate event does not seem to give any information. Specifically, I want to know if it was another application.

    I was thinking something along these lines:

    private void PopUpBox_Deactivate(object sender, EventArgs e)
            {
                if (!(sender is MainWindow))
                {
                    this.CloseWindow();
                }
            }
    
    public void CloseWindow()
            {
                if (this.OnClose != null)
                {
                    this.OnClose(this, null);
                }
                this.Close();
            }

    If anyone would be able to help me with this, I would be most grateful.

    Thanks in advance.

    Aart Dijkstra


    Tuesday, October 23, 2012 10:45 PM

Answers

  • Aaaaand I guess I solved it myself.

    The eventual solution I came to is this:

    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_ACTIVATE) // control activated by clicking or key
                {
                    // wparam -> 0 = inactive, 1 = active, 2 = active by click
                    // if wparam = inactive, then lparam is a handle to the window being activated
                    if (m.WParam.ToInt32() == 0)
                    {
                        // we're being deactivated
                        int windowid = m.LParam.ToInt32();
                        bool ours = false;
                        foreach (Form win in Application.OpenForms)
                        {
                            if (win.Handle == m.LParam)
                            {
                                ours = true;
                            }
                        }
                        // if the window is being deactivated to another application, remove the topmost property to make it disappear
                        // we'll simply have to set that back to true when this application comes back
                        // if it's being deactivated to another window in this application, make it go!
                        if (ours)
                        {
                            // shoo window, shoo!
                            Logger.Log(LogType.Full, "We're being closed by our application.");
                            this.CloseWindow();
                        }
                        else
                        {
                            // keep window, but to background
                            Logger.Log(LogType.Full, "We're being closed by another application, ({0}) keep window.", m.LParam.ToInt32());
                            this.TopMost = false;
                            Settings.Current.CurrentBox = this;
                            // now we need to parse WndProc on the main window to see when we're being activated, and pipe the message through here
                        }
                    }
                    else // wParam is 1 or 2, thus activated
                    {
                        // set this.topmost to true to make zee window reappear
                        Logger.Log(LogType.Full, "We're being activated.");
                        this.TopMost = true;
                    }
                }
                base.WndProc(ref m);
            }

    And in the main window of the application:

    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
            protected override void WndProc(ref Message m)
            {
                // for WM_ACTIVATE:
                // wparam -> 0 = inactive, 1 = active, 2 = active by click
                // if wparam = inactive, then lparam is a handle to the window being activated
                // for WM_ACTIVATEAPP
                // wparam -> false = inactive, true = active
                if (m.Msg == WM_ACTIVATE || m.Msg == WM_ACTIVATEAPP) // application activated
                {
                    if (m.WParam.ToInt32() != 0) // we're being activated
                    {
                        // do we have to set a window to topmost?
                        if (Settings.Current.CurrentBox != null)
                        {
                            Settings.Current.CurrentBox.TopMost = true;
                        }
                    }
                }
                base.WndProc(ref m);
            }

    So, does anyone have any suggestions/remarks to this solution?

    An easier one would still be nice.

    Wednesday, October 24, 2012 9:19 AM

All replies

  • Turns out the Windows API does actually have that functionality, it's just not in .Net.

    WM_ACTIVATE is being sent every time the window (or the application) loses or gains focus.

    By comparing the receiving window handles to the handles in the application, you can find out whether the focus is being moved to another application or another window in the same application.

    Here's what I got so far, as I am now officially too tired to finish.

    if (m.Msg == WM_ACTIVATE) // control activated by clicking or key
                {
                    // wparam -> 0 = inactive, 1 = active, 2 = active by click
                    // if wparam = inactive, then lparam is a handle to the window being activated
                    if (m.WParam.ToInt32() == 0)
                    {
                        // we're being deactivated
                        int windowid = m.LParam.ToInt32();
                        bool ours = false;
                        foreach (Form win in Application.OpenForms)
                        {
                            if (win.Handle == m.LParam)
                            {
                                ours = true;
                            }
                        }
                        string str = "";
                    }
                }
    The string is only there as a debugging point.


    Wednesday, October 24, 2012 12:39 AM
  • Aaaaand I guess I solved it myself.

    The eventual solution I came to is this:

    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_ACTIVATE) // control activated by clicking or key
                {
                    // wparam -> 0 = inactive, 1 = active, 2 = active by click
                    // if wparam = inactive, then lparam is a handle to the window being activated
                    if (m.WParam.ToInt32() == 0)
                    {
                        // we're being deactivated
                        int windowid = m.LParam.ToInt32();
                        bool ours = false;
                        foreach (Form win in Application.OpenForms)
                        {
                            if (win.Handle == m.LParam)
                            {
                                ours = true;
                            }
                        }
                        // if the window is being deactivated to another application, remove the topmost property to make it disappear
                        // we'll simply have to set that back to true when this application comes back
                        // if it's being deactivated to another window in this application, make it go!
                        if (ours)
                        {
                            // shoo window, shoo!
                            Logger.Log(LogType.Full, "We're being closed by our application.");
                            this.CloseWindow();
                        }
                        else
                        {
                            // keep window, but to background
                            Logger.Log(LogType.Full, "We're being closed by another application, ({0}) keep window.", m.LParam.ToInt32());
                            this.TopMost = false;
                            Settings.Current.CurrentBox = this;
                            // now we need to parse WndProc on the main window to see when we're being activated, and pipe the message through here
                        }
                    }
                    else // wParam is 1 or 2, thus activated
                    {
                        // set this.topmost to true to make zee window reappear
                        Logger.Log(LogType.Full, "We're being activated.");
                        this.TopMost = true;
                    }
                }
                base.WndProc(ref m);
            }

    And in the main window of the application:

    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
            protected override void WndProc(ref Message m)
            {
                // for WM_ACTIVATE:
                // wparam -> 0 = inactive, 1 = active, 2 = active by click
                // if wparam = inactive, then lparam is a handle to the window being activated
                // for WM_ACTIVATEAPP
                // wparam -> false = inactive, true = active
                if (m.Msg == WM_ACTIVATE || m.Msg == WM_ACTIVATEAPP) // application activated
                {
                    if (m.WParam.ToInt32() != 0) // we're being activated
                    {
                        // do we have to set a window to topmost?
                        if (Settings.Current.CurrentBox != null)
                        {
                            Settings.Current.CurrentBox.TopMost = true;
                        }
                    }
                }
                base.WndProc(ref m);
            }

    So, does anyone have any suggestions/remarks to this solution?

    An easier one would still be nice.

    Wednesday, October 24, 2012 9:19 AM
  • Hi Aart,

    Thank you for sharing your solution here.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, October 24, 2012 10:49 AM
    Moderator