Determine Where Activation Is Going When A Form Is Deactivated
Asked Answered
C

3

4

Does anyone know of a way to determine which window is going to receive focus when a form is deactivated?

Convulse answered 1/5, 2009 at 21:20 Comment(2)
What are you trying to do? it feels like you're asking the wrong questionHighfalutin
I am trying to determine whether focus is being transferred to one of my forms or to another window not from my application. (My application consists of two forms that work together. I need to hide them when focus is being transferred away from my application, but I do not want to hide my forms if the user is only interacting between them.Convulse
C
8

I found the answer. Instead of subscribing to the activated and deactivate events, handle the WM_ACTIVATE message (used for both activation and deactivation) in WndProc. Since it reports the handle of the window being activated, I can compare that handle to the handles of my forms and determine if focus is changing to any of them.

const int WM_ACTIVATE = 0x0006;
const int WA_INACTIVE = 0;
const int WA_ACTIVE = 1;  
const int WA_CLICKACTIVE = 2;  

protected override void WndProc(ref Message m)  
{  
    if (m.Msg == WM_ACTIVATE)  
    {  
         // When m.WParam is WA_INACTIVE, the window is being deactivated and
         // m.LParam is the handle of the window that will be activated.

         // When m.WParam is WA_ACTIVE or WA_CLICKACTIVE, the window is being 
         // activated and m.LParam is the handle of the window that has been 
         // deactivated.
    }  

    base.WndProc(ref m);  
} 

Edit: This method can be used outside the window it applies to (e.g. outside in a popup window).

You can use NativeWindow to attach to any window based on its handle and view its message loop. See code example below:

public class Popup : Form
{
    const int WM_ACTIVATE = 0x0006;
    const int WA_INACTIVE = 0;
    private ParentWindowIntercept parentWindowIntercept;

    public Popup(IntPtr hWndParent)
    {
        this.parentWindowIntercept = new ParentWindowIntercept(hWndParent);
    }

    private class ParentWindowIntercept : NativeWindow
    {
        public ParentWindowIntercept(IntPtr hWnd)
        {
            this.AssignHandle(hWnd);
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_ACTIVATE)
            {
                if ((int)m.WParam == WA_INACTIVE)
                {
                    IntPtr windowFocusGoingTo = m.LParam;
                    // Compare handles here
                }
            }

            base.WndProc(ref m);
        } 
    }
}
Convulse answered 1/5, 2009 at 23:36 Comment(0)
C
0

You should be able to determine the active form using the Form.ActiveForm static property.

Cephalonia answered 1/5, 2009 at 21:25 Comment(1)
Unfortunately, Form.ActiveForm is still the form being deactivated during the Form.Deactivated event, because the window that will be activated is not activated until the old form has been deactivated.Convulse
B
0

I'm having the same issue when implementing an autocomplete popup window (similar to Intellisense window in VS).

Problem with the WndProc approach is that code must be added to any form that happens to host the popup.

Alternative approach is to use a timer and check Form.ActiveControl after a brief interval. This way, code is encapsulated better, within the editor control or the popup form.

Form _txPopup;

// Subscribe whenever convenient
public void IntializeControlWithPopup(Form _hostForm)
{
    _hostForm.Deactivate + OnHostFormDeactivate;
}

Timer _deactivateTimer;
Form _hostForm;
void OnHostFormDeactivate(object sender, EventArgs e)
{
    if (_deactivateTimer == null) 
    {
        _mainForm = sender as Form;
        _deactivateTimer = new Timer();
        _deactivateTimer.Interval = 10;
        _deactivateTimer.Tick += DeactivateTimerTick;
    }
    _deactivateTimer.Start();
}

void  DeactivateTimerTick(object sender, EventArgs e)
{
    _deactivateTimer.Stop();
    Form activeForm = Form.ActiveForm;
    if (_txPopup != null && 
        activeForm != _txPopup && 
        activeForm != _mainForm) 
    { 
        _txPopup.Hide(); 
    }
}
Brott answered 1/11, 2009 at 14:13 Comment(3)
Yes, it is certainly possible to do it this way, but I would tend to avoid using timers if at all possible because they introduce race conditions and can cause subtle bugs. I have edited my answer to show how it is possible to encapsulate the code for the WndProc method within a popup.Convulse
@Zach. I agree timers could be fragile. Your intercept trick is great, didn't know that was possible.Brott
I was also trying to implement an autocomplete popup window similar to Intellisense for my launcher project Promptu (PromptuLauncher.com) when I asked this question. Here are two other related questions I asked dealing with specific implementation challenges that may help you in implementing yours: #992852 #814245Convulse

© 2022 - 2024 — McMap. All rights reserved.