views:

1027

answers:

5

Hello all,

I'm having a form that takes a few seconds to finally display. This form is called through:

using (ResultsForm frm = new ResultsForm())
{
    this.Visible = false;
    frm.ShowDialog();
    this.Visible = true;
}

It's useful that I get the default cursor to Cursors.WaitCursor while waiting for the form to finally display. Currently I can only seem to be able to do this successfully by using the static 'Current' property:

using (ResultsForm frm = new ResultsForm())
{
    //this.Visible = false;
    Cursor.Current = Cursors.WaitCursor;
    frm.ShowDialog();
    //this.Visible = true;
}

But this has two problems:

  • It forces me to disable the MainForm hiding feature which I would like to retain.
  • It increases coupling since Cursor.Current = Cursor.Default; needs to be called within the ResultsForm Shown event.

How can I change the Cursor while the form loads without changing the first code snippet and while avoiding coupling?

UPDATE: Now the question was answered, video presentation was removed so I don't go over my ISP bandwidth limitations.

+1  A: 

Why do you have to remove the this.Visible = false? You should still be able to do it while setting the cursor.

Would it be an acceptable solution to have the ResultsForm set the cursor instead of the parent form? Get it to set the cursor before it starts the code that takes all the time, then set it back at the end.

Glenn Condron
>> Why do you have to remove the this.Visible = false? << That's the million dollar question to which I have no answer. I shouldn't have to. I updated my question with a narrated video showing the current behavior.
Krugar
http://stackoverflow.com/questions/302663/cursor-current-vs-this-cursor-in-net-c trying out the code for the answer in that question might be the way to go.
Glenn Condron
You last comment solved it. Thanks for the finding. I did my own searching before posting but don't seem to came across that question.
Krugar
+1  A: 

It forces me to disable the MainForm hiding feature which I would like to retain.

You should be able to do both, without issues.

It increases coupling since Cursor.Current = Cursor.Default; needs to be called within the ResultsForm Shown event

Have you tried putting your cursor logic entirely into the ResultsForm dialog code? You should be able to set Cursor.Current = Cursors.WaitCursor; inside of the ResultsForm's constructor, and then have Cursor.Current = Cursor.Default; set inside of the Shown event.

This would keep the logic entirely in the dialog, and out of the main window. You could also then keep the visibility toggling in the main window.

Reed Copsey
Well, that was my initial thought. But it didn't work. Neither my preferred solution of moving the logic to ResultsForm constructor and Shown event. The cursor remains unchanged under those situations.
Krugar
The property UseWaitCursor is boolean, and I believe sets the cursor for the children controls also.
Philip Wallace
@Krugar : Have you tried calling Application.DoEvents() after you set the cursor?
Philip Wallace
Application.DoEvents() Seems to not do it either (which is weird). I am going to produce a small video of the code and application behavior and upload it so this can be better discussed. All the remaining answers do not address the problem either and I'm starting to suspect something else is at play.
Krugar
A video presentation is now available. I go through the suggested changes and you can see how for some strange reason thinbgs aren't working as expected.
Krugar
+1  A: 

If you always want to display a wait cursor when loading this form (regardless of whether it's shown from), then, as Reed suggests, you should just do it in the form itself.

However, if you only need the cursor in this one particular case of displaying a form, then you can use a lambda (or anonymous delegate, if you're dealing with C# 2.0) for an event handler:

using (ResultsForm frm = new ResultsForm())
{
    this.Visible = false;

    var oldCursor = Cursor.Current;
    Action<object, EventArgs> restoreCursor = 
        delegate
        {
            Cursor.Current = oldCursor;
            frm.Shown -= restoreCursor;
        };
    frm.Shown += restoreCursor;

    Cursor.Current  = Cursor.WaitCursor
    frm.ShowDialog();
}
Pavel Minaev
Interesting option. Saved just for the beauty of it :)In any case I do need the cursor to be firmly set within the form since it is going to be called from different places in the application. I updated my question with a narrated video showing the current behavior.
Krugar
A: 

You could make the main form the owner of the second form and make it modal:

frm.ShowDialog(this);

Then in frm, add "Cursor.Current = Cursors.WaitCursor;" in the Constructor and add "Cursor.Current = Cursor.Default;" in the Shown event.

I've tried this with a button and it works. The wait cursor shows and then turns back to default once the form loads:

Form1:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WaitCursorTwoForms
{
   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();
      }

      private void ButtonClick(object sender, EventArgs e)
      {
         using(Form2 form2 = new Form2())
         {
            form2.ShowDialog(this);
         }
      }
   }
}

Form2:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WaitCursorTwoForms
{
   public partial class Form2 : Form
   {
      public Form2()
      {
         Cursor.Current = Cursors.WaitCursor;
         InitializeComponent();
         for (int i = 0; i < 10; i++)
         {
            Thread.Sleep(1000);
         }
      }

      private void OnShown(object sender, EventArgs e)
      {         
         Cursor.Current = Cursors.Default;
      }
   }
}
SwDevMan81
I would hope this worked too. It was similar to my initial code. But for some reason it didn't. I updated my question with a narrated video showing the application behavior.
Krugar
+1  A: 

The cursor will change to WaitCursor but that will survive only a fraction of a second. The problem is that hiding your main form makes the cursor overlap the window that's behind your main form. Or the desktop. The revealed window gets a WM_SETCURSOR message from Windows and it will change the cursor to its preferred shape.

But there's a bigger problem with your approach. After the dialog closes, there's a fraction of a second where no window in your app is visible. Windows is forced to find another window to give the focus to, it won't be a window in your app. When your main form again becomes visible, it may well end up behind the window that got the focus. This behavior tends to be a bit flaky, you might not yet have found it.

You can solve both problems by hiding the window a different way:

  this.Opacity = 0.01;
Hans Passant
You don't have a real coupling issue. Just subscribe to the dialog's Shown event, that's why it is there.
Hans Passant
Whoa nobugz. I think you may have hit it. Indeed this seems like a reasonable explanation. One I can work with. However it does not solve the coupling issue. I've removed entirely the form invisibility feature and set the cursor in the form constructor. But it won't change on this case until the form fully loads. Even with DoEvents().
Krugar