显示表格而不会失去焦点?
我正在使用一个表单来显示通知(它显示在屏幕的右下角),但是当我显示此表单时,它会从主表单中夺走焦点。有没有一种方法可以显示这种"通知"形式而不窃取焦点?
解决方案
这样做似乎很简单,但似乎可行:
this.TopMost = true; // as a result the form gets thrown to the front this.TopMost = false; // but we don't actually want our form to always be on top
编辑:请注意,这仅引发了一个已经创建的表单,而没有窃取焦点。
如果我们愿意使用Win32 P / Invoke,则可以使用ShowWindow方法(第一个代码示例正是我们想要的)。
当我们使用创建新表单时
Form f = new Form(); f.ShowDialog();
它会失去焦点,因为在关闭该表单之前,代码无法在主表单上继续执行。
例外是使用线程创建新表单,然后使用Form.Show()。但是请确保该线程是全局可见的,因为如果我们在函数中声明该线程,则一旦函数退出,线程将结束并且表单将消失。
我们可能要考虑要显示的通知类型。
如果绝对重要的是让用户知道某个事件,则使用Messagebox.Show是推荐的方法,因为它具有阻止其他事件进入主窗口的性质,直到用户确认为止。但是要注意弹出窗口的盲目性。
如果不是很关键,则可能需要使用其他方式来显示通知,例如窗口底部的工具栏。我们写道,在屏幕的右下角显示通知,标准的方法是将气球提示与系统任务栏图标结合使用。
在单独的线程中创建并启动通知表单,并在表单打开后将焦点重设回主表单。让通知表单提供一个从Form.Shown事件触发的OnFormOpened事件。像这样的东西:
private void StartNotfication() { Thread th = new Thread(new ThreadStart(delegate { NotificationForm frm = new NotificationForm(); frm.OnFormOpen += NotificationOpened; frm.ShowDialog(); })); th.Name = "NotificationForm"; th.Start(); } private void NotificationOpened() { this.Focus(); // Put focus back on the original calling Form }
我们还可以保留NotifcationForm对象的句柄,以便可以通过主Form(frm.Close()
)以编程方式将其关闭。
缺少一些细节,但希望这能使我们朝正确的方向前进。
从PInvoke.net的ShowWindow方法窃取:
private const int SW_SHOWNOACTIVATE = 4; private const int HWND_TOPMOST = -1; private const uint SWP_NOACTIVATE = 0x0010; [DllImport("user32.dll", EntryPoint = "SetWindowPos")] static extern bool SetWindowPos( int hWnd, // Window handle int hWndInsertAfter, // Placement-order handle int X, // Horizontal position int Y, // Vertical position int cx, // Width int cy, // Height uint uFlags); // Window positioning flags [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); static void ShowInactiveTopmost(Form frm) { ShowWindow(frm.Handle, SW_SHOWNOACTIVATE); SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST, frm.Left, frm.Top, frm.Width, frm.Height, SWP_NOACTIVATE); }
(亚历克斯·莱曼(Alex Lyman)回答了这个问题,我只是通过直接粘贴代码来扩展它。拥有编辑权限的人可以在那儿复制它,并删除所有我关心的东西;))
Alex Lyman / TheSoftwareJedi的答案来自pinvoke.net的示例代码将使该窗口成为"最高"窗口,这意味着在弹出窗口后,我们不能将其置于普通窗口的后面。考虑到Matias对他要使用此功能的描述,这可能就是他想要的。但是,如果希望用户在弹出窗口后将其放置在其他窗口之后,只需在示例中使用HWND_TOP(0)而不是HWND_TOPMOST(-1)。
嗯,仅仅覆盖Form.ShowWithoutActivation还不够吗?
protected override bool ShowWithoutActivation { get { return true; } }
而且,如果我们也不希望用户单击此通知窗口,则可以覆盖CreateParams:
protected override CreateParams CreateParams { get { CreateParams baseParams = base.CreateParams; const int WS_EX_NOACTIVATE = 0x08000000; const int WS_EX_TOOLWINDOW = 0x00000080; baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW ); return baseParams; } }
这很好用:
[System.Runtime.InteropServices.DllImport("user32")] public static extern long OpenIcon(long hwnd); [System.Runtime.InteropServices.DllImport("user32")] public static extern long SetForegroundWindow(long hwnd); public static void ActivateInstance() { long MyHndl = 0; long result = 0; Process objProcess = Process.GetCurrentProcess(); MyHndl = objProcess.MainWindowHandle.ToInt32(); result = OpenIcon(MyHndl); // Restore the program. result = SetForegroundWindow(MyHndl); // Activate the application. //System.Environment.Exit(0); // End the current instance of the application. }
我知道这听起来可能很愚蠢,但这可行:
this.TopMost = true; this.TopMost = false; this.TopMost = true; this.SendToBack();