在没有窃取焦点的情况下显示表单?

时间:2008-10-01 02:49:26

标签: c# .net winforms

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

17 个答案:

答案 0 :(得分:157)

嗯,不是简单地覆盖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;
  }
}

答案 1 :(得分:66)

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回答了这个问题,我只是通过直接粘贴代码来扩展它。拥有编辑权限的人可以将其复制到那里并删除它,我所关心的一切;)

答案 2 :(得分:14)

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

答案 3 :(得分:11)

这对我有用。它提供TopMost但没有焦点窃取。

    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。

这是偷来的,错误的,借来的,从这里(点击解决方法):

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

答案 4 :(得分:9)

这样做似乎是一种黑客攻击,但似乎有效:

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

编辑:注意,这只会提高已经创建的表单而不会窃取焦点。

答案 5 :(得分:8)

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

答案 6 :(得分:6)

在WPF中你可以像这样解决它:

在窗口中输入以下属性:

<Window
    x:Class="myApplication.winNotification"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Notification Popup" Width="300" SizeToContent="Height"
  WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>

最后一个属性是你需要ShowActivated =“False”。

答案 7 :(得分:3)

您可能需要考虑要显示的通知类型。

如果让用户了解某些事件绝对至关重要,那么使用Messagebox.Show将是推荐的方式,因为它可以阻止任何其他事件进入主窗口,直到用户确认它为止。但要注意弹出失明。

如果它不是很关键,您可能希望使用另一种方式来显示通知,例如窗口底部的工具栏。您写道,您在屏幕右下角显示通知 - 执行此操作的标准方法是使用balloon tip并结合system tray图标。

答案 8 :(得分:3)

在单独的主题中创建并启动通知表单,并在表单打开后将焦点重置回主表单。让通知表单提供从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对象的句柄,以便可以通过主窗体(frm.Close())以编程方式关闭它。

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

答案 9 :(得分:3)

我有类似的东西,我只是显示通知表单,然后执行

this.Focus();

将重点放在主表格上。

答案 10 :(得分:2)

这很有效。

请参阅: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);    
}

答案 11 :(得分:2)

也可以单独通过逻辑来处理它,虽然我不得不承认上面提到你最终使用BringToFront方法而不实际窃取焦点的建议是最优雅的。

无论如何,我遇到了这个并通过使用DateTime属性来解决它,如果最近已经进行了调用,则不允许进一步的BringToFront调用。

假设一个核心类'Core',它处理三种形式,'Form1,2和3'。每个表单都需要一个DateTime属性和一个Activate事件,该事件调用Core以将窗口置于前面:

internal static DateTime LastBringToFrontTime { get; set; }

private void Form1_Activated(object sender, EventArgs e)
{
    var eventTime = DateTime.Now;
    if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
        Core.BringAllToFront(this);
    LastBringToFrontTime = eventTime;
}

然后在Core Class中创建工作:

internal static void BringAllToFront(Form inForm)
{
    Form1.BringToFront();
    Form2.BringToFront();
    Form3.BringToFront();
    inForm.Focus();
}

另外,如果您想将最小化窗口恢复到原始状态(未最大化),请使用:

inForm.WindowState = FormWindowState.Normal;

同样,我知道这只是缺少BringToFrontWithoutFocus的补丁解决方案。如果你想避免使用DLL文件,这是一个建议。

答案 12 :(得分:1)

我不知道这是否被视为死讯,但这是我所做的,因为我不能使用user32的“ShowWindow”和“SetWindowPos”方法。不,覆盖“ShowWithoutActivation”在这种情况下不起作用,因为新窗口应该始终在顶部。 无论如何,我创建了一个以表单作为参数的辅助方法;在调用时,它显示表单,将其带到前面并使其成为TopMost而不会窃取当前窗口的焦点(显然它确实如此,但用户不会注意到)。

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

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

    public static void ShowTopmostNoFocus(Form f)
    {
        IntPtr activeWin = GetForegroundWindow();

        f.Show();
        f.BringToFront();
        f.TopMost = true;

        if (activeWin.ToInt32() > 0)
        {
            SetForegroundWindow(activeWin);
        }
    }

答案 13 :(得分:0)

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

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

答案 14 :(得分:0)

我需要在我的窗口TopMost中执行此操作。我实现了上面的PInvoke方法,但发现我的Load事件并没有像上面的Talha一样被调用。我终于成功了。也许这会对某人有所帮助。这是我的解决方案:

        form.Visible = false;
        form.TopMost = false;
        ShowWindow(form.Handle, ShowNoActivate);
        SetWindowPos(form.Handle, HWND_TOPMOST,
            form.Left, form.Top, form.Width, form.Height,
            NoActivate);
        form.Visible = true;    //So that Load event happens

答案 15 :(得分:-4)

使用

创建新表单时
Form f = new Form();
f.ShowDialog();

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

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

答案 16 :(得分:-5)

想出来:window.WindowState = WindowState.Minimized;