views:

310

answers:

3

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

A: 

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

Adam Robinson
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.
Zach Johnson
+3  A: 

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);
        } 
    }
}
Zach Johnson
The intercept trick is really cool.
dbkk
A: 

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(); 
    }
}
dbkk
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.
Zach Johnson
@Zach. I agree timers could be fragile. Your intercept trick is great, didn't know that was possible.
dbkk
I was also trying to implement an autocomplete popup window similar to Intellisense for my launcher project Promptu (http://www.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:http://stackoverflow.com/questions/992352/overcome-os-imposed-windows-form-minimum-size-limithttp://stackoverflow.com/questions/813745/keep-window-inactive-in-appearance-even-when-activated
Zach Johnson