【发布时间】:2008-10-12 15:15:24
【问题描述】:
MessageBox.Show 有 MessageBox.Show(ownerWindow, .... ) 之类的形式。
分配所有者窗口有什么好处?
【问题讨论】:
MessageBox.Show 有 MessageBox.Show(ownerWindow, .... ) 之类的形式。
分配所有者窗口有什么好处?
【问题讨论】:
消息框是一种模态窗体,这意味着它的父窗口在消息框被关闭之前处于禁用状态。
如果调用不带所有者标识符的 Show() 重载,则通常会自动选择父窗体。我在文档中找不到任何描述如何选择该表单的内容,但我的经验是,如果消息框显示在 GUI 线程(即主线程或消息泵线程)内,那么用于该线程被选为父线程。
其他线程可能会创建没有父窗体的消息框。这可能是一个糟糕的情况,因为它可能位于活动窗口的后面,用户在关闭程序之前甚至都不知道它在那里。为避免这种情况,您可以将应用程序主窗口的句柄传递给 Show 方法,这将在消息框期间禁用该窗口。
添加:我一直在考虑这个问题,现在我不太确定。您在下面给出的反射器的 sn-p 让我认为线程可能无关紧要。 (我确实说过我在文档中找不到任何东西!)
我必须回去查看代码以确保,但我认为我以前在主窗体后面丢失的消息框实际上可能是自定义消息框窗体,在这种情况下我的经验是错误的,并且您永远不必提供父表单参数。
很抱歉给您带来了困惑。
我现在的建议是永远不要提供这个参数,除非你需要除了活动窗口之外的东西作为主窗体。
【讨论】:
MessageBox 是模态的,请确保它是从Form 上的Invoke() 调用的,它需要是模态的。如果你发现你确实需要使用Invoke(),请务必同时传入所有者。 (因此,例如,在按钮 Click 事件处理程序中调用 MessageBox.Show() 时,无需指定 owner 或 Invoke()。
MessageBoxes 从其他线程中产生确实仍然需要将表单作为第一个参数传递。事实上,这正是为什么众多MessageBox.Show() 重载中的第一个参数是即将生成的MessageBox 的owner 的原因。正如@binki 指出的那样,我们可以通过调用所需的所有者表单让自动所有者自动选择正确的表单,但是在主要(并且只有最佳实践)目的是容纳抽象代码逻辑的类中,调用表单似乎更少比通过引用传递它更可取。加上一个参数更简洁。
owner 表单的情况下,您的用户仍将能够与 no-longer-owner 表单交互,而不是被MessageBox 中的Form.ShowDialog() 时尚(相对于 Form.Show())。如果这是您的应用程序所需的功能,则省略将表单传递给 owner 参数。
设置所有者会导致在消息框打开时禁用所有者。
如果您不设置所有者,则用户可以在消息框打开时单击其他内容甚至关闭所有者,然后当消息框关闭并且调用 MessageBox.Show 后的代码运行您的程序时可以处于未知状态 - 或者如果所有者已关闭,您现在正在一个不再存在的窗口内运行代码,并且对 WinForms 或 WPF(或就此而言,WinAPI 和任何其他框架)的方法的任何调用都可能导致崩溃。
【讨论】:
根据测试和this other answer,.net 将自动选择与您的MessageBox.Show() 调用相同的线程中的当前焦点窗口。要获得正确的行为,您必须确保从与此窗口相同的线程中显示MessageBox并将MessageBox逻辑关联的窗口指定为它的owner。 MessageBox 仅模态阻止输入到启动它的线程上的其他 Forms。这可能与MessageBox 如何使自己成为模态有关(也许它会拦截针对当前线程的消息,并且只允许某些消息通过其自身以外的窗口?)。模态阻塞输入的效果是用户将无法聚焦这些窗口或其中的任何控件,并且尝试这样做会产生“叮”的声音。此外,如果没有指定所有者,MessageBox 将自动选择当前线程上的活动窗口。如果当前活动的窗口来自不同的线程(或不同的应用程序!),MessageBox 将没有所有者。这意味着它有自己的任务栏条目:它表现为自己独特的窗口。用户可以在您的应用程序中启动其他窗口(即使用户无法与它们交互)。您可能会遇到程序停止响应用户输入而用户感到困惑的情况,因为用户在显示对话框后以某种方式打开了应用程序的主窗口。但是,如果所有者设置正确,尝试提升主窗口将导致 MessageBox 被提升并闪烁。
为了使这一切与子窗口一起工作,请确保每个子窗口都有其Owner 属性集。如果您调用 ShowDialog()(这会使您的自定义 Form 模态(并且 do 在将 owner 传递给 MessageBox.Show() 的相同情况下传递其 owner 参数),这将自动发生))。
现在,大多数 winforms 编码包括将代码直接写入 Form 子类中的事件处理程序。在编写 winforms 事件处理程序时,您可以假设很多事情。首先,您永远不应该调用this.Invoke() 作为GUI 事件处理程序中的顶级语句,因为此类处理程序总是在创建Form 子类的同一线程上运行。其次,对于许多(但不是全部)这些处理程序,您可以假设Form 具有焦点(因此会被MessageBox.Show() 自动选择为其所有者)。例如,在按钮的 Click 事件处理程序(可能称为 button1_Click)中编写代码时,您可以放心地假定表单已获得焦点(因为在另一个表单的 Button 上调用 PerformClick() 是不好的做法)。但是,即使您的表单没有聚焦,Timer.Tick 也可能发生。因此,在Timer.Tick 的情况下,有no need to Invoke() (just like any other winforms event handler),但需要指定owner。后一种情况更难引起注意,因为开发人员必须让他们的应用程序不聚焦才能注意到在这种情况下显示的对话框的行为与他们的应用程序聚焦时的行为略有不同。因此,在您需要使用Invoke() 时指定owner 或如果可能在窗口未聚焦时触发事件。此指南适用于以下情况打电话给MessageBox.Show() 和Form.ShowDialog()。
我在这里讨论的大部分内容是messing around 在阅读其他答案时的结果。
【讨论】:
事实证明,窗口所有权是不可传递的。如果你有表单,生成表单,生成 MessageBox,MessageBox.Show 需要使用 ownerWindow 参数。原窗体应设置为MessageBox的所有者窗口。
【讨论】:
Show() 或 ShowDialog()?我猜Show() 因为当我尝试提升通过ShowDialog() 启动的嵌套表单的父级,它拥有MessageBox,Windows 会自动提升父级、嵌套表单和MessageBox 并闪烁@987654327 @ 适合我。
调用 MessageBox.Show(frmMain,"a message","a title") 会添加表单 TextDialog
到应用程序的 Application.OpenForms() 表单集合,以及 frmMain 主表单本身。关闭消息框后它仍然存在。
当我调用btnOK_Click() 并设置断点时,我刚刚发现了其中的 14 个。
当我调用frmMain.Close() 关闭主窗体时,什么也没发生。在所有 14 个 Application.OpenForms 都关闭之前,frmMain 不会关闭,否则您必须调用 Application.Exit()。
【讨论】:
如果我没记错的话,这会阻止所有者窗口到 Focus(),直到消息框关闭。
【讨论】:
MessageBox“所有者”的窗口。
使用Net Reflector,我刚刚在Messagebox.Show中找到了这段代码:
else if (owner == IntPtr.Zero)
owner = UnsafeNativeMethods.GetActiveWindow();
因此,如果您没有嵌套所有权(window--(owns)-->window--(owns)->messageBox),则省略 ownerWindow 会设置您通常选择的所有者。
【讨论】:
文档似乎暗示 owner 参数的唯一用途是,如果您指定 MB_HELP,则消息框知道应该将 WM_HELP 消息发送到哪个窗口。
http://msdn.microsoft.com/en-us/library/ms645505%28VS.85%29.aspx
哦,刚刚意识到 OP 是关于 .net 的——我给出了一个关于 winapi 的答案——对不起!
【讨论】: