Application.OpenForms.Count = 0 always
Solution 1:
There's a bug in Windows Forms that makes a form disappear from the Application.OpenForms collection. This will happen when you assign the ShowInTaskbar, FormBorderStyle, ControlBox, Min/MaximizedBox, RightToLeftLayout, HelpButton, Opacity, TransparencyKey, ShowIcon or MdiParent property after the window was created. These properties are special in that they are specified as style flags in the native CreateWindowEx() call. This sample form demonstrates the bug:
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 Forms must call CreateWindowEx() again to make the changed property effective, passing different style flags. Destroying the original window first has side effects beyond the very noticeable flicker, one of them is that the Application class loses track of the form since it sees the window disappear. With the bug that it doesn't add it back when the new window is created. Avoid the bug by setting the property only in the constructor, code that runs before CreateWindowEx() is called, not in any event handlers.
In general, avoid relying on OpenForms due to this bug. Give the class that needs to display the message box a reference to the form instance through its constructor. MessageBox usually figures out a parent window by itself correctly btw, it will pick the active window and that's correct 99% of the time. If you need it to call BeginInvoke() from a worker thread then be sure to copy SynchronizationContext.Current in your constructor and call its Post() method later. Ensures your library will also work with other GUI class libraries.
Solution 2:
I got the issue when I used ShowInTaskBar = true. I solved it by using windows API instead of the .Net properties. Application.OpenForms remained intact.
I do not know if it works as a general workaround using SetWindowLong to change the properties but it works for 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);
}
}