C# 在不窃取焦点的情况下显示表单?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/156046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:02:26  来源:igfitidea点击:

Show a Form without stealing focus?

提问by Matías

I'm using a Form to show notifications (it appears at the bottom right of the screen), but when I show this form it steals the focus from the main Form. Is there a way to show this "notification" form without stealing focus?

我正在使用一个表单来显示通知(它出现在屏幕的右下角),但是当我显示这个表单时,它从主表单中窃取了焦点。有没有办法在不窃取焦点的情况下显示这种“通知”表单?

采纳答案by Martin Plante

Hmmm, isn't simply overriding Form.ShowWithoutActivation enough?

嗯,是不是简单地覆盖 Form.ShowWithoutActivation 就足够了?

protected override bool ShowWithoutActivation
{
  get { return true; }
}

And if you don't want the user to click this notification window either, you can override CreateParams:

如果您也不希望用户单击此通知窗口,您可以覆盖 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;
  }
}

回答by Matthew Scharley

Doing this seems like a hack, but it seems to work:

这样做似乎是一种黑客行为,但似乎有效:

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

Edit: Note, this merely raises an already created form without stealing focus.

编辑:注意,这只是在不窃取焦点的情况下引发一个已经创建的表单。

回答by Alex Lyman

If you're willing to use Win32P/Invoke, then you can use the ShowWindowmethod (the first code sample does exactly what you want).

如果您愿意使用Win32 P/Invoke,那么您可以使用ShowWindow方法(第一个代码示例完全符合您的要求)。

回答by Fry

When you create a new form using

当您使用创建新表单时

Form f = new Form();
f.ShowDialog();

it steals focus because your code can't continue executing on the main form until this form is closed.

它会窃取焦点,因为您的代码在主窗体关闭之前无法继续在主窗体上执行。

The exception is by using threading to create a new form then Form.Show(). Make sure the thread is globally visible though, because if you declare it within a function, as soon as your function exits, your thread will end and the form will disappear.

例外情况是使用线程创建一个新表单,然后使用 Form.Show()。确保线程是全局可见的,因为如果您在函数中声明它,一旦您的函数退出,您的线程将结束并且表单将消失。

回答by Silver Dragon

You might want to consider what kind of notification you would like to display.

您可能需要考虑要显示哪种通知。

If it's absolutely critical to let the user know about some event, using Messagebox.Show would be the recommended way, due to its nature to block any other events to the main window, until the user confirms it. Be aware of pop-up blindness, though.

如果让用户知道某个事件是绝对重要的,那么使用 Messagebox.Show 将是推荐的方式,因为它的性质会阻止任何其他事件到主窗口,直到用户确认为止。但是,请注意弹出式失明。

If it's less than critical, you might want to use an alternative way to display notifications, such as a toolbar on the bottom of the window. You wrote, that you display notifications on the bottom-right of the screen - the standard way to do this would be using a balloon tipwith the combination of a system trayicon.

如果不太重要,您可能需要使用其他方式来显示通知,例如窗口底部的工具栏。您写道,您在屏幕右下角显示通知 - 执行此操作的标准方法是使用气球提示系统托盘图标的组合。

回答by Bob Nadler

Create and start the notification Form in a separate thread and reset the focus back to your main form after the Form opens. Have the notification Form provide an OnFormOpened event that is fired from the Form.Shownevent. Something like this:

在单独的线程中创建并启动通知表单,并在表单打开后将焦点重置回主表单。让通知表单提供从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
}

You can also keep a handle to your NotifcationForm object around so that it can be programmatically closed by the main Form (frm.Close()).

您还可以保留 NotifcationForm 对象的句柄,以便它可以由主 Form ( frm.Close())以编程方式关闭。

Some details are missing, but hopefully this will get you going in the right direction.

缺少一些细节,但希望这能让您朝着正确的方向前进。

回答by TheSoftwareJedi

Stolen from PInvoke.net's ShowWindowmethod:

PInvoke.netShowWindow方法中窃取

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 answered this, I'm just expanding it by directly pasting the code. Someone with edit rights can copy it over there and delete this for all I care ;) )

(Alex Lyman 回答了这个问题,我只是通过直接粘贴代码来扩展它。具有编辑权限的人可以将它复制到那里并删除它,我只关心它;))

回答by Micah

The sample code from pinvoke.net in Alex Lyman/TheSoftwareJedi's answers will make the window a "topmost" window, meaning that you can't put it behind normal windows after it's popped up. Given Matias's description of what he wants to use this for, that could be what he wants. But if you want the user to be able to put your window behind other windows after you've popped it up, just use HWND_TOP (0) instead of HWND_TOPMOST (-1) in the sample.

在 Alex Lyman/TheSoftwareJedi 的回答中来自 pinvoke.net 的示例代码将使窗口成为“最顶层”窗口,这意味着在它弹出后你不能把它放在普通窗口的后面。鉴于马蒂亚斯对他想用它做什么的描述,这可能就是他想要的。但是,如果您希望用户能够在您弹出窗口后将其置于其他窗口的后面,只需在示例中使用 HWND_TOP (0) 而不是 HWND_TOPMOST (-1)。

回答by Martin Plante

This works well.

这很好用。

See: OpenIcon - MSDNand SetForegroundWindow - MSDN

请参阅:OpenIcon - MSDNSetForegroundWindow - MSDN

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ActivateInstance()
{
    IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

    // Restore the program.
    bool result = OpenIcon(hWnd); 
    // Activate the application.
    result = SetForegroundWindow(hWnd);

    // End the current instance of the application.
    //System.Environment.Exit(0);    
}

回答by Martin Plante

I know it may sound stupid, but this worked:

我知道这听起来可能很愚蠢,但这有效:

this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();