views:

2690

answers:

3

I am trying to display a please wait dialog for a long running operation. The problem is since this is single threaded even though I tell the WaitScreen to display it never does. Is there a way I can change the visibility of that screen and make it display immediately? I included the Cursor call as an example. Right after I call this.Cursor, the cursor is updated immediately. This is exactly the behavior I want.

private void Button_Click(object sender, RoutedEventArgs e)
{
  this.Cursor = System.Windows.Input.Cursors.Pen;
  WaitScreen.Visibility = Visibility.Visible;

  // Do something long here
  for (Int32 i = 0; i < 100000000; i++)
  {
    String s = i.ToString();
  }

  WaitScreen.Visibility = Visibility.Collapsed;
  this.Cursor = System.Windows.Input.Cursors.Arrow; 
}

WaitScreen is just a Grid with a Z-index of 99 that I hide and show.

update: I really don't want to use a background worker unless I have to. There are a number of places in the code where this start and stop will occur.

+8  A: 

Doing it single threaded really is going to be a pain, and it'll never work as you'd like. The window will eventually go black in WPF, and the program will change to "Not Responding".

I would recommending using a BackgroundWorker to do your long running task.

It's not that complicated. Something like this would work.

private void DoWork(object sender, DoWorkEventArgs e)
{
    //Do the long running process
}

private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //Hide your wait dialog
}

private void StartWork()
{
   //Show your wait dialog
   BackgroundWorker worker = new BackgroundWorker();
   worker.DoWork += DoWork;
   worker.RunWorkerCompleted += WorkerCompleted;
   worker.RunWorkerAsync();
}

You can then look at the ProgressChanged event to display a progress if you like (remember to set WorkerReportsProgress to true). You can also pass a parameter to RunWorkerAsync if your DoWork methods needs an object (available in e.Argument).

This really is the simplest way, rather than trying to do it singled threaded.

Ray
Thanks for the answer. I found a way to do it on the UI thread (See answer). In a perfect world BackgroundWorker is definitely the way to go. I just don't have the luxury of changing that much code at this time.
Shaun Bowe
+6  A: 

I found a way! Thanks to this thread.

public static void ForceUIToUpdate()
{
  DispatcherFrame frame = new DispatcherFrame();

  Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
  {
    frame.Continue = false;
    return null;
  }), null);

  Dispatcher.PushFrame(frame);
}

That function needs to be called right before the long running operation. That will then Force the UI thread to update.

Shaun Bowe
+1  A: 

Another option is to write your long-running routine as a function that returns IEnumerable<double> to indicate progress, and just say:

yield return 30;

That would indicate 30% of the way through, for example. You can then use a WPF timer to execute it in the "background" as a co-operative coroutine.

It's described in some detail here, with sample code.

Daniel Earwicker