WPF progress window in separate thread

Many times I faced with the situations where my program load huge amount of data. Obviously, the best way is to use BackgroundWorker class to separate main application thread and data loading.

But sometimes it’s necessary to perform some operations in main thread. In this situation we have to inform user that our applicaton perform some operation and it can take some time. Simply, it’s possible to show progress window and information string. But if we show progress window in the same thread that the main program, our progress window will be busy.

The solution of this proglem is to show progress window in the separate thread.

1. Let’s create new WPF Applocation project in VS.

2. Right click on the project and choose “Add” -> “New item”. Select “Windows (WPF)” element, set name “ProgressWindow” and click “Add” button.

3. Drop progress bar control on the ProgressWindow form.

<Window x:Class="WPFProgressWindow.ProgressWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="ProgressWindow" Width="300" SizeToContent="Height" >

    <StackPanel Margin="10">
        <ProgressBar Name="Progress" Width="200" Height="20" Minimum="0" Maximum="1" Margin="10" Orientation="Horizontal" />
        <TextBlock HorizontalAlignment="Center" Name="StatusText" Margin="10" Height="50" Foreground="Black" Text="Loading..."/>
    </StackPanel>
</Window>

You can use your own progress instead of standard control. Follow by this link to find out how create custom progress in WPF and Silverlight.
3. Go to code in ProgressWindow and create new class:

public class ProgressManager
{
   private Thread thread;
   private bool canAbortThread = false;
   private ProgressWindow window;

   public void BeginWaiting()
   {
       this.thread = new Thread(this.RunThread);
       this.thread.IsBackground = true;
       this.thread.SetApartmentState(ApartmentState.STA);
       this.thread.Start();
   }
   public void EndWaiting()
   {
       if (this.window != null)
       {
           this.window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
           { this.window.Close(); }));
           while (!this.canAbortThread) { };
       }
       this.thread.Abort();
   }

   public void RunThread()
   {
       this.window = new ProgressWindow();
       this.window.Closed += new EventHandler(waitingWindow_Closed);
       this.window.ShowDialog();
   }
   public void ChangeStatus(string text)
   {
       if (this.window != null)
       {
            this.window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            { this.window.tbStatus.Text = text; }));
       }
   }
   public void ChangeProgress(double Value)
   {
       if (this.window != null)
       {
           this.window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
           { this.window.pProgress.Value = Value; }));
       }
   }
   public void SetProgressMaxValue(double MaxValue)
   {
       Thread.Sleep(100);
       if (this.window != null)
       {
           this.window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
           {
                this.window.pProgress.Minimum = 0;
                this.window.pProgress.Maximum = MaxValue;
           }));
       }
   }
   void waitingWindow_Closed(object sender, EventArgs e)
   {
       Dispatcher.CurrentDispatcher.InvokeShutdown();
       this.canAbortThread = true;
   }
}

BeginWaiting method only create new thread and run RunThread() method inside. EndWaiting method try to close opened progress window and stop thread. Using SetProgressMaxValue method we can set minimum and maximun values of progress. Both ChangeStatus and ChangeProgress methods is used for changing progress value and information string text.
I call Thread.Sleep(100) in SetProgressMaxValue because window is created in separate thread so, if we call SetProgressMaxValue after BeginWaiting method possibly window has not been created at this time.

4. Finally, drop button in the main window and add click event hander for it.

private void button1_Click(object sender, RoutedEventArgs e)
{
      ProgressManager pm = new ProgressManager();
      pm.BeginWaiting();
      pm.SetProgressMaxValue(10);

      for (int i = 0; i < 10; i++)
      {
             pm.ChangeStatus("Loading " + i.ToString() + " from 10");
             pm.ChangeProgress(i);
             Thread.Sleep(1000);
      }

      pm.EndWaiting();
}

Well, now our progress window work fine in separate thread.

Advertisements