Determine Where Activation Is Going When A Form Is Deactivated

帅比萌擦擦* 提交于 2019-12-05 13:35:50

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);
        } 
    }
}

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

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