Display "Wait" screen in WPF


Question

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.

1
7
3/5/2009 9:15:46 PM

Accepted Answer

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.

16
3/5/2009 9:22:08 PM

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.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon