2

I have forms: Splashscreen, Search, and NewEntry

Program.cs starts Splashscreen which checks some parameters and upon success (check user name, permissions, etc) opens Search form. From there I can call the NewEntry form.

So since the Splashscreen is the primary form called by the Main() function, once it closes, the app closes (this is the expected behaviour).
So what I did is from within Splashscreen I start the Search form as a dialog and hide Splashscreen, so it waits for the Search to close. As soon as it closes, I also close the Splashscreen and it all seemed to make sense at the time. From the Search form I open the NewEntry form (from a button click) also as a dialog (I don't want users clicking back and forth and creating multiple NewEntry windows).

Today I had to add a feature that allows the user to select a file. As an obvious choice, I used an OpenFileDialog. But as soon as I call .ShowDialog() method, the whole app just freezes.

I've read Windows Forms GUI hangs when calling OpenFileDialog.ShowDialog() and OpenFileDialog.ShowDialog() freezing application c# and a bunch of other posts here and on other sites, and when I almost lost hope, I came across this http://wishmesh.com/2011/06/call-to-openfiledialog-or-savefiledialog-hangs-or-freezes/
I do have the [STAThread] attribute set on my Main() function

And one particular point of interest is: For OpenFileDialog the ShowHelp property must be explicitly set.
And also ... They don’t have to be set to true, they just have to be initialized to either true or false.

So when I set the ShowHelp to true, the dialog shows up (with the useless Help button).

A further research showed that when I execute (new OpenFileDialog()).ShowDialog(); within Program.cs or within Splashscreen, it works just fine; however, when called from a dialog, there is a hang (without the ShowHelp). Furthermore, a MessageBox shows up just fine from the Dialog...

So is there a way to make this work? Or should I manage my windows differently?
For example, have Search as the main startup window, then before the window shows, call Splashscreen as a dialog, and if it fails, just close the main window? But then, how would I handle the NewEntry to be able to show OpenFileDialog or FolderBrowserDialog?

Thanks.

Community
  • 1
  • 1
nurchi
  • 770
  • 11
  • 24

3 Answers3

3

The part that concerns me the most is when you say you have SplashScreen wait for Search to close. If you are doing this

if (search.ShowDialog() == DialogResult.OK)
{
    Show();
}

Then you have frozen the UI thread for the SplashScreen. Since it's the main form things are not likely to work as expected. I suggest changing the code to this.

Hide(); // Hides SplashScreen
Search search = new Search();
search.ParentForm = this;
search.Show(); // Show Search without freezing SplashScreen thread

ParentForm is a public variable I added to Search. This should be private and access should be through a getter/setter but this keeps the code example short.

public partial class Search: Form
{
    public SplashScreen ParentForm;
    ...
}

Next you will need to add a handler to handle the Search form closing. This will show SplashScreen.

private void Form2_FormClosed(object sender, FormClosedEventArgs e)
{
    ParentForm.Show();
}

After making these changes OpenFileDialog show work fine.

Skye MacMaster
  • 894
  • 1
  • 10
  • 19
  • Thanks for the answer. The first part although not identical, is not much different, I was doing: `this.Hide(); (new Search()).ShowDialog(); this.Close();`. The main difference is that I don't care much about the `DialogResult` of the `Search` form, I just want it to act like the "main window" of the app. I'll give a try to your suggestions and keep this post updated. Thanks again. – nurchi Jan 24 '14 at 18:11
2

If in your program.cs, you are calling this: Application.Run(new splashscreen());, if it were me, I would do this instead so that you can have multiple forms open after the splashscreen closes.

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        new splashscreen().ShowDialog(); 
        Application.Run(new Search());
    }
}

This will show the splashscreen, and then once everything's loaded, you can open the search form and close the splashscreen, and won't have to call ShowDialog(). (you'll have to call application.End() to end the application.)

davidsbro
  • 2,761
  • 4
  • 23
  • 33
  • I implemented this suggestion and the only part that seems incorrect is that even when the last window is closed, the application does not end. Windows Task manager shows that process as still running and the debugger does not return to "normal" mode, I have to click the "Stop" button. But this seems like an easy fix and an good alternative to what I had. Thanks for the suggestion. – nurchi Jan 24 '14 at 18:26
  • Sorry for the inline code, can't format much in the comment. This is what I have right now: `if ((new Splashscreen()).ShowDialog() == DialogResult.OK) { (new Search()).Show(); Application.Run(); }` I close the window bu pressing the "X" in the top right corner and I do not handle the `Close`, `Closing`, etc methods that could override and minimise instead of closing... – nurchi Jan 24 '14 at 18:40
  • I guess, if I change what I have into `Application.Run(new Search());`, this should work. I will be hiding the `Search` window and showing the `NewEntry` while setting the `ParentForm` property and handling all as per @Scott MacMaster's suggestion – nurchi Jan 24 '14 at 18:47
  • @nurchi, my bad, you'll actually have to call `Application.End()` in the `closing` or `closed` event handlers in the last form to close (I had looked at a multi-form project I just made, and didn't realize I was ending the application in my 'mainform' when it was closed). – davidsbro Jan 24 '14 at 18:57
  • @nurchi, I updated my answer so that the splashscreen will show, and once it closes, the application will run, with search as its mainform. – davidsbro Jan 24 '14 at 19:07
1

I really appreciate your help in this matter.

So to summarise, I created Form1 and Form2 to test some code and here is what I have (only relevant portions):

// Form1:
void Button1Click(object sender, EventArgs e)
{
    Form2 f2=new Form2();
    this.Hide();
    f2.Show(this);
}

// Form2:
void Form2Load(object sender, EventArgs e)
{
    if (this.Owner==null) throw new ArgumentException("This window must be called with the Owner property set!");
    // or just ignore, or show a MessageBox and close, or use your imagination...
}

void Form2FormClosed(object sender, FormClosedEventArgs e)
{
    // sanity check...
    if (this.Owner!=null && !this.Owner.Visible) this.Owner.Show();
}

So when I call Form2, I first hide the current form and use an overload of Show() that takes 1 argument and sets the Form2's Owner property. Alternately I could just call f2.Owner=this; and then f2.Show();

Then when/after Form2 is closed, I check if Owner was set and show that form.

Now back to the original question, here is what I have inside Program.cs:

if ((new Splashscreen()).ShowDialog() == DialogResult.OK)
    Application.Run(new Search());

So now Search only shows if Splashscreen returned with DialogResult.OK and when Search is closed, the application exits properly.

Thanks for all the help.
This saved me tons of time.

P.S.
I wish I could mark both answers as answers, going to toss a coin and randomly mark one.

nurchi
  • 770
  • 11
  • 24
  • @ScottMacMaster and nurchi, I know how that feels, nurchi, with two good answers, and since mine was the lucky one, I'm going to go ahead and upvote Scott's (but I'm mostly going to because I actually thought it seems like it was more relevant to the final solution). – davidsbro Jan 24 '14 at 20:02