【发布时间】:2020-10-30 14:07:44
【问题描述】:
我正在使用表单来显示通知(它显示在屏幕的右下方),但是当我显示此表单时,它会从主表单中窃取焦点。有没有办法在不窃取焦点的情况下显示这个“通知”表单?
【问题讨论】:
我正在使用表单来显示通知(它显示在屏幕的右下方),但是当我显示此表单时,它会从主表单中窃取焦点。有没有办法在不窃取焦点的情况下显示这个“通知”表单?
【问题讨论】:
嗯,简单地覆盖 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;
}
}
【讨论】:
form1.Enabled = false 以防止内部控件窃取焦点
WS_EX_NOACTIVATE和WS_EX_TOOLWINDOW的值分别为0x08000000和0x00000080。
从PInvoke.net的ShowWindow方法中窃取:
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 回答了这个问题,我只是通过直接粘贴代码来扩展它。有编辑权限的人可以将它复制到那里并删除它,我只关心它;))
【讨论】:
这对我有用。它提供 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。
这是从这里被盗、出错、借来的(点击解决方法):
【讨论】:
如果您愿意使用Win32P/Invoke,那么您可以使用ShowWindow 方法(第一个代码示例正是您想要的)。
【讨论】:
这样做似乎是一种 hack,但它似乎有效:
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
编辑:注意,这只会引发一个已经创建的表单而不会窃取焦点。
【讨论】:
Alex Lyman/TheSoftwareJedi 的答案中来自 pinvoke.net 的示例代码将使窗口成为“最顶层”窗口,这意味着在弹出窗口后您不能将其放在普通窗口后面。鉴于马蒂亚斯对他想用这个做什么的描述,这可能就是他想要的。但是,如果您希望用户能够在弹出窗口后将其置于其他窗口之后,只需在示例中使用 HWND_TOP (0) 而不是 HWND_TOPMOST (-1)。
【讨论】:
在 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"。
【讨论】:
我有类似的东西,我只是显示通知表然后做
this.Focus();
将焦点重新放在主窗体上。
【讨论】:
在单独的线程中创建并启动通知表单,并在表单打开后将焦点重置回主表单。让通知表单提供从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()) 以编程方式关闭它。
缺少一些细节,但希望这能让你朝着正确的方向前进。
【讨论】:
您可能需要考虑要显示哪种通知。
如果让用户知道某个事件是绝对关键的,那么推荐使用 Messagebox.Show 的方式,因为它的性质是阻止任何其他事件进入主窗口,直到用户确认为止。但请注意弹出式盲区。
如果不是很重要,您可能希望使用其他方式来显示通知,例如窗口底部的工具栏。您写道,您在屏幕的右下角显示通知 - 执行此操作的标准方法是使用 balloon tip 和 system tray 图标的组合。
【讨论】:
这很好用。
见:OpenIcon - MSDN 和 SetForegroundWindow - 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);
}
【讨论】:
您也可以仅通过逻辑来处理它,尽管我不得不承认,上面的建议是最优雅的建议,即您最终使用 BringToFront 方法而不实际窃取焦点。
无论如何,我遇到了这个问题,并通过使用 DateTime 属性解决了这个问题,如果最近已经进行了调用,则不允许进一步的 BringToFront 调用。
假设有一个核心类“Core”,它处理例如三个表单,“Form1、2 和 3”。每个表单都需要一个 DateTime 属性和一个调用 Core 将窗口置于前面的 Activate 事件:
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 文件,这只是一个建议。
【讨论】:
我不知道这是否被认为是 necro-posting,但这是我所做的,因为我无法让它与 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);
}
}
【讨论】:
我知道这听起来可能很愚蠢,但这很有效:
this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
【讨论】:
我需要对我的窗口 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.
【讨论】:
Form.ShowWithoutActivation Property
将此添加到您的子表单类中
protected override bool ShowWithoutActivation
{
get { return true; }
}
工作代码
Form2
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();
////-----------------
}
}
Form3
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;
}
}
【讨论】:
当您使用
创建新表单时Form f = new Form();
f.ShowDialog();
它会窃取焦点,因为您的代码在主窗体关闭之前无法继续在主窗体上执行。
例外是使用线程创建一个新表单,然后使用 Form.Show()。确保线程是全局可见的,因为如果你在函数中声明它,一旦你的函数退出,你的线程就会结束并且表单会消失。
【讨论】:
想通了:window.WindowState = WindowState.Minimized;。
【讨论】: