WPF 4.0 Progress Bar Control using Dispatcher Thread

Posted by: Mahesh Sabnis , on 9/12/2010, in Category WPF
Views: 108277
Abstract: In this article, we will see how to make use of the Progress Bar control provided in WPF.
In this article, we will see how to make use of the Progress Bar control provided in WPF. This control is especially used for show progress on automated operations e.g. File Copy, Installation etc. In this short article, I will explain how to make use of the Progress Bar control for simulating file copy operation from source to the destination path.
Step 1: Open VS2010 and create a WPF application. Name it as ‘WPF40_ProgressBar’.
Step 2: Open MainPage.Xaml and write the following Xaml code:
<Window x:Class="WPF40_ProgressBar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="483" Width="719"
         Loaded="Window_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="82*" />
            <RowDefinition Height="229*" />
        </Grid.RowDefinitions>
        <TextBlock Height="26" HorizontalAlignment="Left" Margin="18,10,0,0"
                   Name="txtStatus" Text="Copying....." VerticalAlignment="Top" Width="211" />
        <ProgressBar Height="30" HorizontalAlignment="Left" Margin="14,36,0,0" Name="downloadProgress" VerticalAlignment="Top" Width="672" />
        <Button Content="Copy Files" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="278,254,0,0" Name="btnCopy" VerticalAlignment="Top" Width="75" Click="btnCopy_Click" />
        <ListBox Grid.Row="1" Height="202" HorizontalAlignment="Left" Margin="26,90,0,0" Name="lstSourceFiles" VerticalAlignment="Top" Width="180" />
       <ListBox Height="202" HorizontalAlignment="Left" Margin="496,90,0,0" Name="lstTgrFiles" VerticalAlignment="Top" Width="180" Grid.Row="1" />
        <TextBlock Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="66,51,0,0" Name="textBlock1" Text="Source Path" VerticalAlignment="Top" Width="89" />
        <TextBlock Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="558,51,0,0" Name="textBlock2" Text="Target Path" VerticalAlignment="Top" Width="78" />
        <Button Content="Source Path" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="278,15,0,0" Name="btnSourcePath" VerticalAlignment="Top" Width="75" Click="btnSourcePath_Click" />
        <TextBlock Height="35" HorizontalAlignment="Left" Margin="14,72,0,0" Name="txtFileCopied" Text="" VerticalAlignment="Top" Width="224" />
 
    </Grid>
</Window>
 
 
Note: We will be talking about the events on the controls when we come to the code. The following UI will be the generated.
image_3
The above UI defines ProgressBar(in Gray shade), ListBoxes represents Source Files to copy to Destination file path. The Button ‘Source Path’ displays all the files to copy in the ListBox below ‘Source Path’ TextBlock. When the user clicks on ‘Copy Files’ button, all the files will be copied in the ListBox below ‘Target Path’ TextBlock. This copy operations are simulated by the ‘Progress Bar’.
 
Step 3: Open MainWindow.Xaml.cs and write the following code:
Declare following variables at class leve (Change the drive name and folder as per your requirements):
C#
string SrcPath = @"H:\SrcFiles";
string TgtPath = @"H:\TgtFiles";
string[] Files;
string strFileName = "";
 
 
VB.NET (Converted Code)
Dim SrcPath As String = "H:\SrcFiles"
Dim TgtPath As String = "H:\TgtFiles"
Dim Files() As String
Dim strFileName As String = ""
Write the following code on the Loaded Event of the window:
C#
private void Window_Loaded(object sender, RoutedEventArgs e)
{
      txtStatus.Visibility = System.Windows.Visibility.Hidden;
}
 
VB.NET (Converted Code)
Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
      txtStatus.Visibility = System.Windows.Visibility.Hidden
End Sub
 
Code on ‘Source Path’ button:
C#
private void btnSourcePath_Click(object sender, RoutedEventArgs e)
{
     string[] Files = Directory.GetFiles(SrcPath);
 
     foreach (string s in Files)
     {
        lstSourceFiles.Items.Add(s);
     }
     downloadProgress.Maximum = Files.Length;
 
}
 
VB.NET (Converted Code)
Private Sub btnSourcePath_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
      Dim Files() As String = Directory.GetFiles(SrcPath)
 
      For Each s As String In Files
            lstSourceFiles.Items.Add(s)
      Next s
            downloadProgress.Maximum = Files.Length
 
End Sub
 
Write the following code on the ‘Copy Files’ button:
C#
private void btnCopy_Click(object sender, RoutedEventArgs e)
{
Files = Directory.GetFiles(SrcPath);
txtStatus.Visibility = System.Windows.Visibility.Visible;
DispatcherTimer timer = new DispatcherTimer();
foreach (string s in Files)
{
 
    Thread t = new Thread(
        new ThreadStart(
            delegate()
            {
                DispatcherOperation dispOp =
                    downloadProgress.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                    new Action(
                        delegate()
                        {
                            strFileName = s;
                            string fileName = System.IO.Path.GetFileName(strFileName);
                            string destFile = System.IO.Path.Combine(TgtPath, fileName);
                            System.IO.File.Copy(strFileName, destFile, true);
                            lstTgrFiles.Items.Add(destFile);
                            downloadProgress.Value = lstTgrFiles.Items.Count;
                            txtFileCopied.Text = lstTgrFiles.Items.Count + " File(s) Copied.";
                                    
                            Thread.Sleep(100);
                        }
                        ));
                dispOp.Completed += new EventHandler(dispOp_Completed);
            }
            ));
    t.Start();
               
}
}
 
VB.NET (Converted Code)
Private Sub btnCopy_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Files = Directory.GetFiles(SrcPath)
txtStatus.Visibility = System.Windows.Visibility.Visible
Dim timer As New DispatcherTimer()
For Each s As String In Files
 
      Dim t As New Thread(New ThreadStart(Sub()
            Dim dispOp As DispatcherOperation = downloadProgress.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, New Action(Sub()
                  strFileName = s
                  Dim fileName As String = System.IO.Path.GetFileName(strFileName)
                  Dim destFile As String = System.IO.Path.Combine(TgtPath, fileName)
                  System.IO.File.Copy(strFileName, destFile, True)
                  lstTgrFiles.Items.Add(destFile)
                  downloadProgress.Value = lstTgrFiles.Items.Count
                  txtFileCopied.Text = lstTgrFiles.Items.Count & " File(s) Copied."
                  Thread.Sleep(100)
            End Sub))
            AddHandler dispOp.Completed, AddressOf dispOp_Completed
      End Sub))
      t.Start()
 
Next s
End Sub
The above code also shows an excellent feature of the WPF Threading mechanism. In WPF, we have been provided with the Dispatcher mechanism. This allows us to update WPF UI elements on the thread, which does not own the control to it. This facility was not provided in .NET 2.0. In the above code, I am using Dispatcher thread for ProgressBar control and updating ListBox control by adding files in it. In the TextBlock ‘txtFileCopied’, I am displaying the number of files being copied. The reason I am doing this is to provide user experience to the end user in a way that when files are getting copied in the target listbox, the ProgressBar will show the progress and the TextBlock ‘txtFileCopied’ will show files copied.
Write the following code in the Completed event of the ‘Dispatcher’:
C#
void dispOp_Completed(object sender, EventArgs e){
     txtStatus.Visibility = System.Windows.Visibility.Collapsed;
     btnCopy.IsEnabled = false;
}
 
VB.NET
Private Sub dispOp_Completed(ByVal sender As Object, ByVal e As EventArgs)
      txtStatus.Visibility = System.Windows.Visibility.Collapsed
      btnCopy.IsEnabled = False
End Sub
 
Step 4: Run the application.
Click on ‘Source Path’ button and the following result will be displayed:
image_1
Click on the copy files button and the following result will be displayed:
image_2
The entire source code of this article can be downloaded over here
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Mahesh is having 10 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by JohanStern on Thursday, February 3, 2011 12:04 PM
thank you for the information, please what is this for:
DispatcherTimer timer = new DispatcherTimer();
Comment posted by Joost Breed on Saturday, May 28, 2011 9:59 AM
Hi Mahesh,

Wouldn't it be better to move the next lines outside the dispatcher delegate:

strFileName = s;
string fileName = System.IO.Path.GetFileName(strFileName);
string destFile = System.IO.Path.Combine(TgtPath, fileName);
System.IO.File.Copy(strFileName, destFile, true);

Now the long running methods like Copy() are also executed on the dispatcher thread.

Might this work better?:


private void btnCopy_Click(object sender, RoutedEventArgs e)
{
   Files = Directory.GetFiles(SrcPath);
   txtStatus.Visibility = System.Windows.Visibility.Visible;
   DispatcherTimer timer = new DispatcherTimer();
   foreach (string s in Files)
   {
      Thread t = new Thread(
        new ThreadStart(
          delegate()
          {
             strFileName = s;
             string fileName = System.IO.Path.GetFileName(strFileName);
             string destFile = System.IO.Path.Combine(TgtPath, fileName);  
             System.IO.File.Copy(strFileName, destFile, true);
             DispatcherOperation dispOp =
                 downloadProgress.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                    new Action<string, int>( SetControls ), destFile, lstTgrFiles.Items.Count)
                 );
             dispOp.Completed += new EventHandler(dispOp_Completed);
         }
      ));
      t.Start();
   }

   private static void SetControls(string fileName, int fileCount)
   {   
   lstTgrFiles.Items.Add( fileName);
   downloadProgress.Value = fileCount;
   txtFileCopied.Text = fileCount + " File(s) Copied.";
        Thread.Sleep(100);
   }
}


Comment posted by JB on Saturday, June 18, 2011 8:22 AM
Great article!

Here you find another nice article about how to implement a WPF progress dialog:
http://www.parago.de/2011/04/how-to-implement-a-modern-progress-dialog-for-wpf-applications/
Comment posted by Johny on Friday, July 8, 2011 9:37 PM
Hello Mahesh,

The code copies the same file x times...
Comment posted by Piyush on Tuesday, July 26, 2011 4:55 AM
Hi Mahesh, nice post, just one thing. foreach loop should be inside the delegate
() section. then it will work.

Thanks.
Comment posted by Jared Barneck on Friday, August 26, 2011 9:57 PM
I like using a BackgroundWorker and MVVM for this.

Here is an example of what I do:

<a href="http://www.rhyous.com/2010/12/29/a-progress-bar-using-wpfs-progress-bar-control-backgroundworker-and-mvvm/">A Progress Bar using WPF’s ProgressBar Control, BackgroundWorker, and MVVM</a>
Comment posted by Sergei on Wednesday, September 5, 2012 10:01 AM
Great job!!! Very simple and works just great.
Comment posted by MAhesh Sabnis on Thursday, September 6, 2012 3:03 AM
Hi Sergi,
Thanks a lot.
Regards
Mahesh Sabnis
Comment posted by Handley on Sunday, September 9, 2012 7:36 PM
Please don't post your code on this horrible SkyDrive site.  I can never find a thing there.  
Comment posted by Julio on Monday, September 9, 2013 10:21 AM
Hi Mahesh. I have a method that creates a word file, but I can not implement a thread to fill the progress bar while the file is created. Which way do I go? The method is called by a button.
Comment posted by ganesh on Wednesday, August 6, 2014 8:48 AM
Hi,
It just copies only the last image. please help me with the latest code.
Comment posted by sanjeev on Monday, August 25, 2014 11:53 PM
this is testing mail

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel