Does anyone know of a way to determine which window is going to receive focus when a form is deactivated?
You should be able to determine the active form using the Form.ActiveForm
static property.
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);
}
}
}
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();
}
}