显示窗体而不窃取焦点?

我使用 Form 来显示通知(它出现在屏幕的右下角) ,但是当我显示这个表单时,它会从主 Form 窃取焦点。有没有一种方法可以显示这个“通知”表单而不会窃取焦点?

79984 次浏览

这样做看起来像是黑客行为,但似乎奏效了:

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 请求,那么可以使用 演出窗口方法(第一个代码示例完全满足您的需要)。

创建新窗体时,使用

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

它偷走了焦点,因为您的代码不能继续在主窗体上执行,直到这个窗体关闭。

例外的情况是使用线程创建一个新窗体,然后是 Form。秀()。但是要确保线程是全局可见的,因为如果在函数中声明它,那么一旦函数退出,线程就会结束,窗体也会消失。

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

如果使用 Messagebox 让用户知道某个事件是绝对重要的,则使用。显示将是推荐的方式,由于其性质阻止任何其他事件的主窗口,直到用户确认它。不过要注意弹出式失明。

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

在单独的线程中创建并启动通知窗体,并在窗体打开后将焦点重置回主窗体。让通知窗体提供从 Form.Shown事件激发的 OnFormOpen 事件。就像这样:

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演出窗口方法偷来的:

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/TheSoftwareedi 的回答中来自 pinvoke.net 的示例代码将使窗口成为“最顶端”窗口,这意味着在它弹出后你不能把它放到普通窗口后面。根据马蒂亚斯的描述他想用这个做什么这可能就是他想要的。但是如果希望用户能够在弹出窗口后将窗口放在其他窗口后面,只需在示例中使用 HWND _ TOP (0)而不是 HWND _ TOPMOST (- 1)。

嗯,不仅仅是覆盖表单。显示没有激活足够吗?

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;
}
}

效果不错。

参见: OpenIcon-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);
}

我知道这听起来可能很蠢,但这个方法奏效了:

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

我有一些类似的东西,我只是显示通知表格,然后做

this.Focus();

让焦点回到主要的形式上。

在 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”属性。

我算出来了: window.WindowState = WindowState.Minimized;

您也可以使用 可以单独的逻辑来处理它,尽管我不得不承认,上面的建议是最优雅的,它建议您最终使用 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;
}

然后在核心课程中创建作品:

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

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

inForm.WindowState = FormWindowState.Normal;

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

这对我来说很有效,它提供了 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;
}
}

请记住在 VisualStudio 设计器或其他地方省略设置 TopMost。

这是从这里偷来的,呃,借来的(点击工作区) :

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

我不知道这是否被认为是死亡发布,但这是我所做的,因为我不能让它与用户32的“显示窗口”和“ SetWindowPos”方法工作。并且不,覆盖“ ShowWithoutActivation”在这种情况下不起作用,因为新窗口应该始终在顶部。 无论如何,我创建了一个 helper 方法,它接受一个表单作为参数; 当调用它时,它会显示表单,将其放在前面,并使其成为 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);
}
}

我需要用我的 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

你没必要把事情搞得这么复杂。

a = new Assign_Stock();
a.MdiParent = this.ParentForm;
a.Visible = false;   //hide for a bit.
a.Show(); //show the form. Invisible form now at the top.
this.Focus(); //focus on this form. make old form come to the top.
a.Visible = true; //make other form visible now. Behind the main form.

enter image description here

Github 样本

属性

将其添加到子窗体类中

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

工作守则

表格2

   public partial class Form2 : Form
{
Form3 c;
public Form2()
{
InitializeComponent();
c = new Form3();
}


private void textchanged(object sender, EventArgs e)
{




c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();


//removethis
//if mdiparent 2 add this.focus() after show form


c.MdiParent = this.MdiParent;
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
this.Focus();
////-----------------




}




       

}

表格3

   public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
//ShowWithoutActivation = false;
}
protected override bool ShowWithoutActivation
{
get { return true; }
}




internal void ResetText(string toString)
{
label2.Text = toString;
}


     

}