Random programming things I'd want to remember

Thursday, October 11, 2012

Responsive UI while uploading stuff in the background Part 2


In my previous post, I showed a program that is supposed to be doing stuff on the background, even though it was not updating the UI. In this post, I will show how to make it so that we can see the changes in the UI while the program is doing its magic.

Most of the program stayed the same, the only difference is in the MainWindow.xaml.cs. The secret weapon here is System.ComponentModel.BackgroundWorker. To successfully launch a background worker, put the time-consuming operation into the code for the worker's DoWork event. I need the interface updates here, and since my Uploader class is doing time-consuming work on another thread, I wrapped up the calls for the UI update into the Dispatcher.BeginInvoke(... code blocks. The only thing that is not working here is the event for the Stop button. It calls the background worker's CancelAsync() method, but the Uploader class cannot interrupt its actions. I will show a way to fix that in the next post.

    public partial class MainWindow : Window
    {
        Stopwatch s = new Stopwatch();
        Uploader u = new Uploader();
        BackgroundWorker bg1 = new BackgroundWorker();

        public MainWindow()
        {
            InitializeComponent();
            InitializeUploaderEvents();
            InitializeBackgroundWorker();
        }

        public void InitializeUploaderEvents()
        {
            u.OnStartingUpload += new StartingUpload(u_OnStartingUpload);
            u.OnFinishedUpload += new FinishedUpload(u_OnFinishedUpload);
            u.OnStartingFileUpload += new StartingFileUpload(u_OnStartingFileUpload);
            u.OnFinishedFileUpload += new FinishedFileUpload(u_OnFinishedFileUpload);
            u.OnStartingFilePartUpload += new StartingFilePartUpload(u_OnStartingFilePartUpload);
            u.OnFinishedFilePartUpload += new FinishedFilePartUpload(u_OnFinishedFilePartUpload);
        }

        private void InitializeBackgroundWorker()
        {
            bg1.DoWork += new DoWorkEventHandler(bg1_DoWork);
            bg1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg1_RunWorkerCompleted);
            bg1.WorkerReportsProgress = true;
            bg1.WorkerSupportsCancellation = true;
        }

        void bg1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
                MessageBox.Show(e.Error.Message);
            else if (e.Cancelled)
                overallStatusLabel.Content = "Upload cancelled";
            else
            {
                overallStatusLabel.Content = String.Format("Upload finished successfully, took {0} ", s.Elapsed);
            }
        }

        void bg1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker b = sender as BackgroundWorker;
            Uploader u1 = e.Argument as Uploader; //I am not using this variable yet

            u.UploadFiles();
        }
        
        void u_OnStartingFilePartUpload(object source, FilePartUploaderEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
            {
                currentFileStatusLabel.Content = String.Format("Uploading file {0}, {1}/{2} parts done.", e.FileName, e.PartsUploadedSoFar, e.PartsToUpload);
                fileUploadProgressBar.Maximum = e.PartsToUpload;
                fileUploadProgressBar.Value = e.PartsUploadedSoFar;
            });
        }

        void u_OnFinishedFilePartUpload(object source, FilePartUploaderEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
             {
                 currentFileStatusLabel.Content = String.Format("Uploading file {0}, {1}/{2} parts done.", e.FileName, e.PartsUploadedSoFar, e.PartsToUpload);
                 progressBar1.Value += 1;
                 fileUploadProgressBar.Maximum = e.PartsToUpload;
                 fileUploadProgressBar.Value = e.PartsUploadedSoFar;
             });
        }

        void u_OnStartingFileUpload(object source, FileUploaderEventArgs e)
        {
           Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
            { 
                currentFileStatusLabel.Content = String.Format("Starting to upload file {0}", e.FileName); 
            });
        }

        void u_OnFinishedFileUpload(object source, FileUploaderEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
            {
                currentFileStatusLabel.Content = String.Format("Finished uploading file {0}", e.FileName); 
            });
        }

        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            progressBar1.Maximum = u.DataToUpload_Length;
            progressBar1.Value = 0;
            bg1.RunWorkerAsync();
        }

        void u_OnFinishedUpload(object source, UploaderEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
               {
                   s.Stop();
                   overallStatusLabel.Content = String.Format("Finished uploading {0} files, elapsed time: {1}", e.NumberOfFilesToUpload, s.Elapsed);
               });
        }

        void u_OnStartingUpload(object source, UploaderEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
            {
                overallStatusLabel.Content = String.Format("Started uploading {0} files", e.NumberOfFilesToUpload);
                progressBar1.Maximum = e.PartsToUpload_TotalCount;
                progressBar1.Value = e.PartsToUpload_UploadedSoFar; //0
                s.Start();
            });
        }

        private void stopButton_Click(object sender, RoutedEventArgs e)
        {
            if (bg1.IsBusy)
                bg1.CancelAsync();

            overallStatusLabel.Content = "Cancelling upload";
        }
    }


No comments: