如何将 Windows Forms 应用程序置于前台?

41

我正在使用C#编写一个Windows窗体应用程序,我需要将其置于前台。经过一些谷歌搜索和尝试,我有了一个看起来很鬼畜的可行解决方案。

如果有一种优雅的方法,我想知道如何做到这一点。无论应用程序是最小化的,还是未最小化但在后台,我都需要它恢复并置于前台。

当前的代码看起来像这样:

WindowState = FormWindowState.Minimized;
WindowState = FormWindowState.Normal;
BringToFront();
Focus();

3
带有艾米利欧·拉加西的音频录音!嘭!(BAM!) - Ben M
1
好的,在经过一些谷歌搜索后,我觉得这很有趣。 - D'Arcy Rittich
3
通常我讨厌那些说这种话的人,但是请认真考虑,除非你无法决定,否则请不要让你的应用程序做出这样的举动。窃取焦点的应用程序应被视为有害行为。 - Coxy
1
@coxymla:我听到你的话,可能也会说同样的话。在这种情况下,我完全可以决定:这是我自己专属使用的,而且我发现这是一个非常简单的方法,在事件x发生时引起我的注意并提供输入。 - D'Arcy Rittich
我更新了我的答案;我认为form.Activate加上设置窗口状态可以得到你所要求的内容。 - bobbymcr
请查看我的解决方案这里。它适用于Show()ShowDialog() - bluish
12个回答

61

你尝试过 Form.Activate 吗?

以下代码似乎可以实现你想要的效果,如果窗体被最小化了,它会将其恢复到正常大小,并激活它以设置焦点:

if (this.WindowState == FormWindowState.Minimized)
{
    this.WindowState = FormWindowState.Normal;
}

this.Activate();

警告:这很烦人!但是,如果这只是您个人使用的应用程序,就像您所说的那样,也许您可以忍受它。:)


1
我刚试了一下。如果窗体是MDI子窗体并且用户正在使用另一个应用程序,则Activate()将无法工作。 - Jacob Seleznev
5
如果你将窗口最小化到系统托盘,需要在单击时激活并将其置于前台,即使窗口已处于正常状态,此功能也很有用。 - Robb Sadler
1
我也发现这个对于将窗体恢复到其先前的状态(正常或最大化)很有用。 - Giles
1
我发现在一些机器上,Activate 只是让任务栏图标闪烁。 - Robin Bennett

14

注意。下面我复制了在一个链接的问题中被关闭为重复的最受欢迎的答案。那个答案是我找到的唯一解决这个问题的纯C#答案。

this.WindowState = FormWindowState.Minimized;
this.Show();
this.WindowState = FormWindowState.Normal;

它总是把所需的窗口置于所有其他窗口之前。


1
也许它不够美观,但在我的(非常特殊的)情况下,这确实是正确的方法。非常感谢您提出这个建议 :-) - Xan-Kun Clark-Davis
2
有趣的是,在一台机器上我可以只使用 this.Activate()。但在另一台机器上却无法运行(理论上是相同的Windows 7版本)。这个答案解决了问题。 - Daniel Möller
1
@Daniel,同意!对我来说,其他答案都没有这种方法好用。 - John
1
谢谢你。真是太烦人了,我不得不采取这种方法。非常感谢你的帮助 :) - Missy
问题在于窗口被最小化,然后重新显示。由于我无法可靠地检测到窗体是否可见于屏幕上,在窗体已经可见于屏幕上的情况下,这会变得混乱不堪。 - Appleoddity
@Appleoddity,在我们的GUI中,我没有注意到这种方法会出现闪烁。然而,如果你遇到了闪烁问题,那真的很糟糕,我认为值得提一个新问题,并提供能够重现该问题的代码。 - John

9
private static class User32
{
    [DllImport("User32.dll")]
    internal static extern IntPtr SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    internal static readonly IntPtr InvalidHandleValue = IntPtr.Zero;
    internal const int SW_MAXIMIZE = 3;
}
public void Activate()
{
    Process currentProcess = Process.GetCurrentProcess();
    IntPtr hWnd = currentProcess.MainWindowHandle;
    if (hWnd != User32.InvalidHandleValue)
    {
        User32.SetForegroundWindow(hWnd);
        User32.ShowWindow(hWnd, User32.SW_MAXIMIZE);
    }
}

嗯,这并不算优雅的解决方案,难道没有更好的本地 .Net 方式吗? - D'Arcy Rittich
从MSDN: “SetForegroundWindow函数将创建指定窗口的线程置于前台并激活该窗口”。使用此功能,您的应用程序可以在用户使用另一个应用程序时将窗口强制置于前台。 - Jacob Seleznev
这对我有效,所有其他解决方案都没有起作用。 - CodingBarfield

7
你可以将.TopMost设置为true,调用DoEvents(),然后将.TopMost设置回false。虽然这仍然是一种hackish方法,但如果Activate和Show无法正常工作,它比最小化/重新显示要好。

这对我不起作用,至少在Visual Studio主机调试环境中不起作用。 - D'Arcy Rittich
2
DoEvents 可能会导致各种问题 - 我强烈建议不要使用它。 - Jon Cage
2
@JonCage 绝对同意:https://dev59.com/EGgu5IYBdhLWcg3wZmQm#11352575 - Joel Coehoorn
1
如果DoEvents如此邪恶,那么微软应该确保Show或BringToFront实际上起作用,这样我们就不必依赖DoEvents了。我遇到了同样的问题,将窗口发送到托盘时,当回来时它总是在后台,除了这个方法什么都不起作用。 - BloodyRain2k
我发现即使将 TopMost 设置回 false,窗口在用户点击其他地方后仍然会保持在顶部(除非你点击菜单),直到它至少接收到一次点击。 - Robin Bennett

3

尝试了几次后,我找到了可行的组合:

form.Show();
form.WindowState = FormWindowState.Normal;
form.Activate();

太棒了!!最简单和最有效的方法(至少在我的情况下)。 - Yet Another Code Maker

2
在经历了许多试错之后,我得到了这段代码。这是经过测试的。在焦点传递给窗体后,在其上调用 BringToFront 方法。这会使窗体弹出并显示在最前面。
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static bool BringToFrontCustom(Form f)
{
    bool toReturn = true;
    try
    {
        //This is the same as the name of the executable without the .exe at the end            
        Process[] processes = Process.GetProcessesByName("MyFormName");

        SetForegroundWindow(processes[0].MainWindowHandle);
        f.BringToFront();
    }
    catch(Exception e)
    {
         toReturn = false;
         MessageBox.Show("Something went wrong, Please bring the window to front manually");
    }
    return toReturn;
}

1

实际上,只需要在 Shown 事件中调用 Activate()

在你的 Example.Designer.cs 文件中:

this.Shown += new System.EventHandler(this.Example_Shown);

在你的 Example.cs 文件中:
private void Example_Shown(object sender, EventArgs e)
{
    this.Activate();
}

即使您的表单由另一个进程启动(例如:在不同线程中运行的闪屏表单),此方法仍然有效。


0

如前面的回答中已经提到的,一种(当然不太优雅)的方法是使用user32.dll并调用本地方法,如果我们正在使用一些未链接的进程或线程来自窗口调用者,这可能是有效的,该进程或线程在主调用者中被设置为后台窗口(例如:对于我们想要置顶的窗口始终保持在最上层)。

这部分内容只是为了方便而部分复制:

public enum WindowPos : int
{
    HWND_NOTOPMOST=-2,
    HWND_TOPMOST=-1,
    HWND_TOP=0,
    HWND_BOTTOM=1
}
public enum WindowFlags : uint
{
    SWP_NOSIZE=0x0001,
    SWP_NOMOVE=0x0002,
    SWP_NOZORDER=0x0004,
    SWP_NOREDRAW=0x0008,
    SWP_NOACTIVATE=0x0010,
    SWP_FRAMECHANGED=0x0020,  /* The frame changed: send WM_NCCALCSIZE */
    SWP_SHOWWINDOW=0x0040,
    SWP_HIDEWINDOW=0x0080,
    SWP_NOCOPYBITS=0x0100,
    SWP_NOOWNERZORDER=0x0200,  /* Don't do owner Z ordering */
    SWP_NOSENDCHANGING=0x0400  /* Don't send WM_WINDOWPOSCHANGING */
}
public enum ShowWindowCommands : int
{
    SW_HIDE=0,
    SW_SHOWNORMAL=1,
    SW_NORMAL=1,
    SW_SHOWMINIMIZED=2,
    SW_SHOWMAXIMIZED=3,
    SW_MAXIMIZE=3,
    SW_SHOWNOACTIVATE=4,
    SW_SHOW=5,
    SW_MINIMIZE=6,
    SW_SHOWMINNOACTIVE=7,
    SW_SHOWNA=8,
    SW_RESTORE=9,
    SW_SHOWDEFAULT=10,
    SW_FORCEMINIMIZE=11,
    SW_MAX=11
}

private static class User32
{
    [DllImport("user32.dll")]
    internal static unsafe extern IntPtr SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    internal static unsafe extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    internal static unsafe extern bool SetWindowPos(IntPtr hWnd, int hWndPutAfter, int x, int y, int cx, int cy, uint flags);

    [DllImport("user32.dll")]
    internal static unsafe extern IntPtr SetFocus( IntPtr hWnd );
}
public void Activate()
{
    Process currentProcess = Process.GetCurrentProcess();
    IntPtr hWnd = currentProcess.MainWindowHandle;
    if (hWnd != IntPtr.Zero)
    {
        User32.SetWindowPos(hWnd, (int)WindowPos.Top, 0, 0, 0, 0, (uint)(WindowFlags.SWP_NOMOVE | WindowFlags.SWP_NOSIZE));
        User32.ShowWindow(hWnd, (int)ShowWindowCommands.SW_SHOW);
        User32.SetForegroundWindow(hWnd);
        User32.SetFocus( hWnd );
    }
}

为了完整性,我添加了大部分可在Windows SDK中使用的常量引用。

0

使用TopMost已经被弃用!永远不要使用它。你的用户会讨厌你的。 - Elmue

0

设置.TopMost = true

然后使用

ShowDialog()

请给你的回答添加一些解释。SO 不鼓励只有代码的答案。 - Imran Ali Khan
调用form.ShowDialog()而不是form.Show()会将窗体置于前台。 - cortexm_0
如果主窗体设置了TopMost为true,最好在调用form.ShowDialog()之前将form.TopMost设置为true。 - cortexm_0

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