打开一个TopMost属性为true的WinForms窗体,但不让它抢占焦点?

20

我有一个表单会弹出在用户的屏幕上,并设置了 TopMost=true,但它会窃取焦点。如何让它在首次出现时窃取焦点?


2
+1:我讨厌那些窃取焦点的Windows! - user180326
如果有可能的话,我敢打赌Raymond Chen已经写过这个问题了:http://blogs.msdn.com/b/oldnewthing/ - user180326
我很惊讶这不是WinForms中内置的东西... - sooprise
7个回答

17

这是我使用的方法。它可以提供最高层级窗口但不会抢占焦点。

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

    private const int WS_EX_TOPMOST = 0x00000008;
    protected override CreateParams CreateParams
    {
       get
       {
          CreateParams createParams = base.CreateParams;
          createParams.ExStyle |= WS_EX_TOPMOST;
          return createParams;
       }
    }

记得在Visual Studio设计器或其他地方省略设置TopMost。

这段内容是从这里(点击Workarounds)偷来的:

https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost


这个链接也失效了。 - Factor Mystic
@FactorMystic 嗯,有点晚了,但是Archive.org链接 - Jed Burke

6

将这段代码粘贴到您的表单中:

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

当TopMost设置为True时,它在VS2008中无法工作。我现在会在VS2010上尝试一下。 - Nope
仍然无法在VS 2010中工作。只有当弹出窗口的TopMost设置为false时才能正常工作。 - Nope
9
啊,真糟糕,TopMost会捣乱。在VS2010中也行不通。你需要使用P/Invoke调用SetWindowPos()函数,并传入HWND_TOPMOST和SWP_NOACTIVATE。使用pinvoke.net获取函数声明。 - Hans Passant
@Soo:不确定你是如何让它工作的,因为这是一个众所周知的错误。 @Hans:赞同评论中提到使用P/Invoke来解决这个问题。 - Nope

2
你可以设置:
this.TopMost = True;

在该表单的“加载”事件中。

我没问题!


不要忘记先在IDE上禁用.topmost - 这样做起来非常顺畅。 - Stavm

2
你可以这样做:
    private const int SW_SHOWNOACTIVATE = 4;
    private const int HWND_TOPMOST = -1;
    private const uint SWP_NOACTIVATE = 0x0010;

    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    private 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

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow);

    public static void ShowInactiveTopmost(System.Windows.Forms.Form frm)
    {
        try
        {
            ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
            SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
            frm.Left, frm.Top, frm.Width, frm.Height,
            SWP_NOACTIVATE);
        }
        catch (System.Exception ex)
        {
            // error handling
        }
    }

1

我使用form1上的计时器测试了下面的代码,以实例化并显示具有form1作为所有者的form2。

在form2的Shown事件中,我将焦点设置为所有者,即当前活动的窗体。

我在form1上有一个文本框,并且能够在此过程中持续写入文本框而不失去焦点。

我的form1计时器代码:

private void timer1_Tick(object sender, EventArgs e)
{
    Form2 popup = new Form2();
    popup.TopMost = true;
    popup.Show(this);
    timer1.Enabled = false;
}

我的代码在Form2的Shown事件中:

private void Form2_Shown(object sender, EventArgs e)
{
    this.Owner.Focus();
}

您可以这样做,或者简单地将 TopMost 设置为 false 并使用 ShowWithoutActivation 的覆盖,如 Hans Passant 所述。
编辑:(或者使用 p/invoke,如 Hans Passant 的其他评论中所述,我在撰写此文时错过了)

1
我遇到了同样的问题。我不是使用C#而是C++。我认为这可能仍然有用:
使用windows.h:
BOOL WINAPI SetWindowPos(
  __in      HWND hWnd,
  __in_opt  HWND hWndInsertAfter,
  __in      int X,
  __in      int Y,
  __in      int cx,
  __in      int cy,
  __in      UINT uFlags
);

将标志SWP_NOACTIVATE传递给uFlags参数对我很有用。

0

不要在_activated事件中编写.setfocus(),而是将其编写到窗体的.shown事件中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接