【问题标题】:Application.OpenForms.Count = 0 alwaysApplication.OpenForms.Count = 0 总是
【发布时间】:2010-09-20 12:40:57
【问题描述】:

我有这种情况。 Application.OpenForms 不返回正确的结果。即Application.OpenForms.Count = 0总是..

获取表单的目的是获取表单的所有者,以便我可以将所有者作为MessageBox.Show()函数的参数传递。

【问题讨论】:

  • 请提供更多信息。
  • 如果我在表单加载中检查 Application.OpenForms.Count,它会显示 1。您的应用程序有些奇怪,您需要提供有关如何重现问题的更多详细信息。
  • 我的应用程序有一个主表单。即使我在执行此代码时打开了表单,它也会返回 0。感谢 Albin,SLaks
  • 您使用的是Windows应用程序还是WPF应用程序,请问?
  • 是 Windows 应用程序感谢 Siva

标签: c# .net winforms


【解决方案1】:

Windows 窗体中存在一个错误,导致窗体从 Application.OpenForms 集合中消失。当您在创建窗口之后分配 ShowInTaskbar、FormBorderStyle、ControlBox、Min/MaximizedBox、RightToLeftLayout、HelpButton、Opacity、TransparencyKey、ShowIcon 或 MdiParent 属性时,就会发生这种情况。这些属性的特殊之处在于它们在本机 CreateWindowEx() 调用中被指定为样式标志。此示例表单演示了该错误:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
        button1.Click += button1_Click;
    }
    private void button1_Click(object sender, EventArgs e) {
        Console.WriteLine(Application.OpenForms.Count);
        this.ShowInTaskbar = !this.ShowInTaskbar;
        Console.WriteLine(Application.OpenForms.Count);
    }
}

Windows 窗体必须再次调用 CreateWindowEx() 以使更改的属性生效,并传递不同的样式标志。销毁原始窗口首先会产生除了非常明显的闪烁之外的副作用,其中之一是 Application 类因为看到窗口消失而失去了对窗体的跟踪。在创建新窗口时它不会将其添加回来的错误。通过仅在构造函数中设置属性来避免该错误,在调用 CreateWindowEx() 之前运行的代码,而不是在任何事件处理程序中。

一般来说,由于这个错误,请避免依赖 OpenForms。通过其构造函数为需要显示消息框的类提供对表单实例的引用。顺便说一句,MessageBox 通常会自行正确计算出一个父窗口,它会选择活动窗口,而且 99% 的时间都是正确的。如果您需要它从工作线程调用 BeginInvoke(),请确保在构造函数中复制 SynchronizationContext.Current 并稍后调用其 Post() 方法。确保您的库也可以与其他 GUI 类库一起使用。

【讨论】:

  • MessageBox 使用 Win32 API 方法 GetActiveWindow,它可以返回一个不属于您的应用程序的窗口。最好不要依赖这个,总是自己指定所有者窗口。
  • @Tergiver:GetActiveWindow 只能返回在同一线程上创建的窗口的窗口句柄。 msdn.microsoft.com/en-us/library/ms646292%28VS.85%29.aspx
  • 你是对的。抱歉,我似乎把我的 API 方法弄糊涂了。
  • 我在迁移到 VB.NET 的 VB6 应用程序中遇到了这个问题。在我的情况下,它没有分配给任何指定的属性,但是由于从 VB6 代码进行的 API 调用,它导致在窗体生命周期的早期创建窗口句柄。将 API 调用延迟到在正常事件过程中创建窗口句柄之后可以避免该问题。
  • @HansPassant 你能给我一些建议吗?这个问题已经表现出来,因为我必须允许用户更改应用程序的 MDI 表单的布局 - 当这种情况发生时,上述表现本身。我可以捕捉到集合为零的事实,并且只显示没有父母的按摩框 - 但是还有其他方法可以获得活动表单句柄吗?
【解决方案2】:

我在使用 ShowInTaskBar = true 时遇到了问题。我通过使用 Windows API 而不是 .Net 属性解决了这个问题。 Application.OpenForms 保持不变。

我不知道它是否可以作为使用 SetWindowLong 更改属性的一般解决方法,但它适用于 ShowInTaskBar = true。

 public static class ShowInTaskBar {

    [DllImport("User32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
    [DllImport("User32.dll")]
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int SW_HIDE = 0x00;
    private const int SW_SHOW = 0x05;

    private const int WS_EX_APPWINDOW = 0x40000;
    private const int GWL_EXSTYLE = -0x14;

    public static void ShowWindowInTaskbar(IntPtr pMainWindow) {
        SetWindowLong(pMainWindow, GWL_EXSTYLE, GetWindowLong(pMainWindow, GWL_EXSTYLE) | WS_EX_APPWINDOW);

        ShowWindow(pMainWindow, SW_HIDE);
        ShowWindow(pMainWindow, SW_SHOW);
    }

    public static void HideWindowFromTaskbar(IntPtr pMainWindow) {
        SetWindowLong(pMainWindow, GWL_EXSTYLE, GetWindowLong(pMainWindow, GWL_EXSTYLE) & ~WS_EX_APPWINDOW);

        ShowWindow(pMainWindow, SW_HIDE);
        ShowWindow(pMainWindow, SW_SHOW);
    }
}

【讨论】:

  • 它确实使表单显示并隐藏在任务栏中,但它也使我的表单窗口不可见,我无法再次使其可见。
猜你喜欢
  • 2012-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-04
  • 2016-08-28
  • 1970-01-01
相关资源
最近更新 更多