Random programming things I'd want to remember

Monday, October 15, 2012

Responsive UI while uploading stuff in the background Part 3


In this final post, I will make sure that the form can do work on the background, that it can inform the user about the progress, and that the user can cancel the job at any time. I will build on the code that I posted in parts 1 and 2. At part 2, I left the form fully responsive while it was doing work, but without an ability for the user to cancel work. In order to be able to cancel, I will add a Cancel property to my Uploader class like so:

        public bool Cancel { get; set; }

Then, I will modify my UploadFiles method to check whether the work has been cancelled and quit the upload if needed.

        public void UploadFiles()
        {
            int partTracker = 0;

            if (OnStartingUpload != null) //Here I need number of pieces that will be uploaded
                OnStartingUpload(this, new UploaderEventArgs 
                {  
                    DataToUpload_Length = this.DataToUpload_Length, 
                    NumberOfFilesToUpload = FileNames.Count,
                    PartsToUpload_TotalCount = this.DataToUpload_Length,
                    PartsToUpload_UploadedSoFar = 0
                });

            for (int i = 0; i < FileNames.Count; i++)
            {                
                if (Cancel) 
                    break;

                //Log.UploadingFile(FileNames[i]);
                if (OnStartingFileUpload != null) //here I need the number of pieces in the file that will be uploaded, and file name
                    //OnStartingFileUpload(this, new UploaderEventArgs {NumberOfFilesToUpload=FileNames.Count, FileName = FileNames[i], FileUpload = true, FilePartUpload = false, PartsToUpload = 0, PartsUploaded = 0 });
                    OnStartingFileUpload(this, new FileUploaderEventArgs { FileName = FileNames[i], Cancelled=this.Cancel/*, PartsInFile = FileNames[i].Length*/ });


                for (int k = 0; k < FileNames[i].Length; k++)
                {
                    if (Cancel) 
                        break;
                    //Log.UploadingPartAOfB(k+1, partsPerFile);
                    if (OnStartingFilePartUpload != null) //here I need the number of pieces in the file that were uploaded so far and total number of pieces to upload
                        //OnStartingFilePartUpload(this, new UploaderEventArgs { NumberOfFilesToUpload = FileNames.Count, FileName = FileNames[i], FileUpload = false, FilePartUpload = true, PartsToUpload = partsPerFile, PartsUploaded = k });
                        OnStartingFilePartUpload(this, new FilePartUploaderEventArgs { PartsUploadedSoFar = k, PartsToUpload = FileNames[i].Length, FileName = FileNames[i], Cancelled = this.Cancel });

                    Thread.Sleep(new TimeSpan(partUploadDelayNs*1000));
                    
                    partTracker += 1;

                    //Log.UploadedPartAOfB(k+1, partsPerFile);
                    if (OnFinishedFilePartUpload != null) //here I need the number of pieces in the file that were uploaded so far and total number of pieces to upload
                        //OnFinishedFilePartUpload(this, new UploaderEventArgs { NumberOfFilesToUpload = FileNames.Count, FileName = FileNames[i], FileUpload = false, FilePartUpload = true, PartsToUpload = partsPerFile, PartsUploaded = k });
                        OnFinishedFilePartUpload(this, new FilePartUploaderEventArgs { PartsUploadedSoFar = k + 1, PartsToUpload = FileNames[i].Length, FileName = FileNames[i], Cancelled = this.Cancel });
                }

                if (OnFinishedFileUpload != null) //Here I need the file name
                    //OnFinishedFileUpload(this, new UploaderEventArgs { NumberOfFilesToUpload = FileNames.Count, FileName = FileNames[i], FileUpload = true, FilePartUpload = false, PartsToUpload = 0, PartsUploaded = 0 });
                    OnFinishedFileUpload(this, new FileUploaderEventArgs { FileName = FileNames[i], Cancelled = this.Cancel/*, PartsInFile = FileNames[i].Length*/ });
            }

            if (OnFinishedUpload != null) //Here I need the number of files uploaded
                OnFinishedUpload(this, new UploaderEventArgs { NumberOfFilesToUpload = FileNames.Count, Cancelled = this.Cancel });
        }

Since I also notify about cancellation in the raised events, I need to update the EventArgs classes:

    public class FileUploaderEventArgs : EventArgs
    { 
        public string FileName { get; set; }
        public bool Cancelled { get; set; }
        //public int PartsInFile { get; set; }
    }

    public class FilePartUploaderEventArgs : EventArgs
    {
        //Number of parts uploaded so far, total number of parts to upload
        public int PartsUploadedSoFar { get; set; }
        public int PartsToUpload { get; set; }
        public string FileName { get; set; }
        public bool Cancelled { get; set; }
    }

    public class UploaderEventArgs : EventArgs
    {
        public int PartsToUpload_TotalCount { get; set; }
        public int PartsToUpload_UploadedSoFar { get; set; }

        public int DataToUpload_Length { get; set; }
        public int DataToUpload_Uploaded { get; set; }
        public int NumberOfFilesToUpload { get; set; }

        public string FileName { get; set; }
        public int PartsToUpload { get; set; }
        public int PartsUploaded { get; set; }

        public bool FileUpload { get; set; }
        public bool FilePartUpload { get; set; }
        public bool Cancelled { get; set; }
    }

Finally, in the stop button event handler, I need to set the Cancel property of the Uploader global variable to true:

        private void stopButton_Click(object sender, RoutedEventArgs e)
        {
            u.Cancel = true;

            if (bg1.IsBusy)
                bg1.CancelAsync();

            overallStatusLabel.Content = "Cancelling upload";
        }

And now I have a template for a work that is done on the background that can be cancelled at any time.

No comments: