我正在使用表单来显示通知(它出现在屏幕的右下角),但是当我显示此表单时,它会从主表单中窃取焦点。有没有办法在不偷窃焦点的情况下显示这种“通知”形式?
答案 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.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回答了这个问题,我只是通过直接粘贴代码来扩展它。拥有编辑权限的人可以将其复制到那里并删除它,我所关心的一切;)
答案 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。
这是偷来的,错误的,借来的,从这里(点击解决方法):
答案 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 - 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);
}
答案 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;
。