Progress Reporting and Cancellation

With GemBox.Spreadsheet you can track the progress of long-running operations like loading and saving large Excel files, and you have the option to cancel them. Currently, the supported operations are loading and saving XLSX files by handling the XlsxLoadOptions.ProgressChanged and XlsxSaveOptions.ProgressChanged events.

The following example is a simple console application that shows the save progress of a large file.

The progress reported with GemBox.Spreadsheet
Screenshot of the progress reported with GemBox.Spreadsheet
using System;
using GemBox.Spreadsheet;

class Program
{
    static void Main()
    {
        // If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
        // Use Trial Mode
        SpreadsheetInfo.FreeLimitReached += (eventSender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;

        Console.WriteLine("Creating file");

        // Create large workbook
        var workbook = new ExcelFile();
        var worksheet = workbook.Worksheets.Add("sheet");
        for (int i = 0; i < 1000000; i++)
            worksheet.Cells[i, 0].Value = i;

        // Create save options
        var saveOptions = new XlsxSaveOptions();
        saveOptions.ProgressChanged += (eventSender, args) =>
        {
            Console.WriteLine($"Progress changed - {args.ProgressPercentage}%");
        };

        // Save file
        workbook.Save("file.xlsx", saveOptions);
    }
}
Imports GemBox.Spreadsheet

Module Program

    Sub Main()

        ' If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY")
        ' Use Trial Mode
        AddHandler SpreadsheetInfo.FreeLimitReached,
            Sub(eventSender, args)
                args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial
            End Sub

        Console.WriteLine("Creating file")

        ' Create large workbook
        Dim workbook = New ExcelFile()
        Dim worksheet = workbook.Worksheets.Add("sheet")
        For i As Integer = 0 To 1000000
            worksheet.Cells(i, 0).Value = i
        Next

        ' Create save options
        Dim saveOptions = New XlsxSaveOptions()
        AddHandler saveOptions.ProgressChanged,
            Sub(eventSender, args)
                Console.WriteLine($"Progress changed - {args.ProgressPercentage}%")
            End Sub

        ' Save file
        workbook.Save("file.xlsx", saveOptions)
    End Sub
End Module

Progress reporting in WPF

The ProgressChanged event is fired on the same thread that started the operation. Therefore, in a WPF application, if the operation is started in the UI thread, the thread is blocked and the changes made as a response to the fired event will not be shown.

The following example shows how to use Tasks to run the load operation in a new thread and how to use SynchronizationContext to make changes to the progress bar on the UI thread.

The progress reported in WPF with GemBox.Spreadsheet
Screenshot of the progress reported in WPF with GemBox.Spreadsheet
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="Progress reporting in WPF" Height="150" Width="300">
    <Grid>
        <ProgressBar HorizontalAlignment="Left" Height="32" Margin="27,73,0,0" VerticalAlignment="Top" Width="200" Name="progressBar"/>
        <Button x:Name="button" Content="Load" HorizontalAlignment="Left" Margin="27,19,0,0" VerticalAlignment="Top" Width="236" Height="36" Click="loadButton_Click"/>
        <Label x:Name="percentageLabel" Content="" HorizontalAlignment="Left" Margin="239,77,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.867,0.59"/>
    </Grid>
</Window>
using System.Windows;
using System.Threading;
using System.Threading.Tasks;
using GemBox.Spreadsheet;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        // If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
        // Use Trial Mode
        SpreadsheetInfo.FreeLimitReached += (eventSender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;
        InitializeComponent();
    }

    private async void loadButton_Click(object sender, RoutedEventArgs e)
    {
        // Capture the current context on the UI thread
        var context = SynchronizationContext.Current;

        // Create load options
        var loadOptions = new XlsxLoadOptions();
        loadOptions.ProgressChanged += (eventSender, args) =>
        {
            var percentage = args.ProgressPercentage;
            // Invoke on the UI thread
            context.Post(progressPercentage =>
            {
                // Update UI
                this.progressBar.Value = (int)progressPercentage;
                this.percentageLabel.Content = progressPercentage.ToString() + "%";
            }, percentage);
        };

        this.percentageLabel.Content = "0%";
        // Use tasks to run the load operation in a new thread.
        var file = await Task.Run(() => ExcelFile.Load("LargeFile.xlsx", loadOptions));
    }
}
Imports System.Threading
Imports GemBox.Spreadsheet

Class MainWindow

    Public Sub New()
        ' If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY")
        ' Use Trial Mode
        AddHandler SpreadsheetInfo.FreeLimitReached,
            Sub(eventSender, args)
                args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial
            End Sub
        InitializeComponent()
    End Sub

    Private Async Sub loadButton_Click(sender As Object, e As RoutedEventArgs)
        ' Capture the current context on the UI thread
        Dim context = SynchronizationContext.Current

        ' Create load options
        Dim loadOptions = New XlsxLoadOptions()
        AddHandler loadOptions.ProgressChanged,
            Sub(eventSender, args)
                Dim percentage = args.ProgressPercentage
                ' Invoke on the UI thread
                context.Post(
                    Sub(progressPercentage)
                        ' Update UI
                        Me.progressBar.Value = CType(progressPercentage, Integer)
                        Me.percentageLabel.Content = progressPercentage.ToString() + "%"
                    End Sub, percentage)
            End Sub

        Me.percentageLabel.Content = "0%"
        ' Use tasks to run the load operation in a new thread.
        Await Task.Run(
            Sub()
                ExcelFile.Load("LargeFile.xlsx", loadOptions)
            End Sub)
    End Sub
End Class

Progress reporting in Windows Forms

Similarly to WPF, to show the progress of an operation in the UI, it is necessary to run the operation in a separate thread and report the changes on the UI thread.

The following example shows how to display the progress of a load operation in the progress bar.

The progress reported in Windows Forms with GemBox.Spreadsheet
Screenshot of the progress reported in Windows Forms with GemBox.Spreadsheet
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using GemBox.Spreadsheet;

public partial class MainForm : Form
{
    public MainForm()
    {
        // If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
        // Use Trial Mode
        SpreadsheetInfo.FreeLimitReached += (eventSender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;
        InitializeComponent();
    }

    private async void loadButton_Click(object sender, EventArgs e)
    {
        // Capture the current context on the UI thread
        var context = SynchronizationContext.Current;

        // Create load options
        var loadOptions = new XlsxLoadOptions();
        loadOptions.ProgressChanged += (eventSender, args) =>
        {
            var percentage = args.ProgressPercentage;
            // Invoke on the UI thread
            context.Post(progressPercentage =>
            {
                // Update UI
                this.progressBar.Value = (int)progressPercentage;
                this.percentageLabel.Text = progressPercentage.ToString() + "%";
            }, percentage);
        };

        this.percentageLabel.Text = "0%";
        // Use tasks to run the load operation in a new thread.
        var file = await Task.Run(() => ExcelFile.Load("LargeFile.xlsx", loadOptions));
    }
}
Imports System.Threading
Imports GemBox.Spreadsheet

Public Class MainForm
    Public Sub New()
        ' If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY")
        ' Use Trial Mode
        AddHandler SpreadsheetInfo.FreeLimitReached,
            Sub(eventSender, args)
                args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial
            End Sub
        InitializeComponent()
    End Sub

    Private Async Sub LoadButton_Click(sender As Object, e As EventArgs) Handles LoadButton.Click
        ' Capture the current context on the UI thread
        Dim context = SynchronizationContext.Current

        ' Create load options
        Dim loadOptions = New XlsxLoadOptions()
        AddHandler loadOptions.ProgressChanged,
            Sub(eventSender, args)
                Dim percentage = args.ProgressPercentage
                ' Invoke on the UI thread
                context.Post(
                    Sub(progressPercentage)
                        ' Update UI
                        Me.ProgressBar.Value = CType(progressPercentage, Integer)
                        Me.PercentageLabel.Text = progressPercentage.ToString() + "%"
                    End Sub, percentage)
            End Sub

        Me.PercentageLabel.Text = "0%"
        ' Use tasks to run the load operation in a new thread.
        Await Task.Run(
            Sub()
                ExcelFile.Load("LargeFile.xlsx", loadOptions)
            End Sub)
    End Sub
End Class

Cancellation

By handling the ProgressChanged event in the save or load options, GemBox.Spreadsheet gives you the ability to cancel the given operation.

The following example shows how to cancel the saving of a file in a console application after a certain time.

using System;
using System.Diagnostics;
using GemBox.Spreadsheet;

class Program
{
    static void Main()
    {
        // If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
        // Use Trial Mode
        SpreadsheetInfo.FreeLimitReached += (eventSender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;

        // Create workbook
        var workbook = new ExcelFile();
        var worksheet = workbook.Worksheets.Add("sheet");
        for (int i = 0; i < 1000000; i++)
            worksheet.Cells[i, 0].Value = i;

        var stopwatch = new Stopwatch();
        stopwatch.Start();

        // Create save options
        var saveOptions = new XlsxSaveOptions();
        saveOptions.ProgressChanged += (sender, args) =>
        {
            // Cancel operation after five seconds
            if (stopwatch.Elapsed.Seconds >= 5)
                args.CancelOperation();
        };

        try
        {
            workbook.Save("Cancellation.xlsx", saveOptions);
            Console.WriteLine("Operation fully finished");
        }
        catch(OperationCanceledException)
        {
            Console.WriteLine("Operation was cancelled");
        }
    }
}
Imports System
Imports System.Diagnostics
Imports GemBox.Spreadsheet

Module Program

    Sub Main()

        ' If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY")
        ' Use Trial Mode
        AddHandler SpreadsheetInfo.FreeLimitReached,
            Sub(eventSender, args)
                args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial
            End Sub

        ' Create workbook
        Dim workbook = New ExcelFile()
        Dim worksheet = workbook.Worksheets.Add("sheet")
        For i As Integer = 0 To 1000000
            worksheet.Cells(i, 0).Value = i
        Next

        Dim stopwatch = New Stopwatch()
        stopwatch.Start()

        ' Create save options
        Dim saveOptions = New XlsxSaveOptions()
        AddHandler saveOptions.ProgressChanged,
            Sub(eventSender, args)
                ' Cancel operation after five seconds
                If stopwatch.Elapsed.Seconds >= 5 Then
                    args.CancelOperation()
                End If
            End Sub

        Try
            workbook.Save("Cancellation.xlsx", saveOptions)
            Console.WriteLine("Operation fully finished")
        Catch ex As OperationCanceledException
            Console.WriteLine("Operation was cancelled")
        End Try
    End Sub
End Module

Cancellation in WPF

The following example shows how to implement a button in WPF that cancels the load operation.

Cancellation in WPF with GemBox.Spreadsheet
Screenshot of cancelled operation in WPF with GemBox.Spreadsheet
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CancellationWpfCs"
        mc:Ignorable="d"
        Title="Cancellation in WPF" Height="200" Width="350">
    <Grid>
        <Button x:Name="loadButton" Content="Start Loading" HorizontalAlignment="Left" Margin="57,31,0,0" VerticalAlignment="Top" Width="228" Height="31" />
        <Button x:Name="cancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="57,111,0,0" VerticalAlignment="Top" Width="228" Height="32" />
        <ProgressBar HorizontalAlignment="Left" Height="25" Margin="57,72,0,0" VerticalAlignment="Top" Width="228" Name="progressBar" />
    </Grid>
</Window>
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using GemBox.Spreadsheet;

public partial class MainWindow : Window
{
    private volatile bool cancellationRequested;

    public MainWindow()
    {
        // If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
        // Use Trial Mode
        SpreadsheetInfo.FreeLimitReached += (eventSender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;
        InitializeComponent();
    }

    private async void loadButton_Click(object sender, RoutedEventArgs e)
    {
        // Capture the current context on the UI thread
        var context = SynchronizationContext.Current;

        var loadOptions = new XlsxLoadOptions();
        loadOptions.ProgressChanged += (eventSender, args) =>
        {
            // Show progress
            context.Post(progressPercentage => this.progressBar.Value = (int)progressPercentage, args.ProgressPercentage);

            // Cancel if requested
            if (this.cancellationRequested)
                args.CancelOperation();
        };

        try
        {
            var file = await Task.Run(() => ExcelFile.Load("LargeFile.xlsx", loadOptions));
        }
        catch (OperationCanceledException)
        {
            // Operation cancelled
        }
    }

    private void cancelButton_Click(object sender, RoutedEventArgs e)
    {
        this.cancellationRequested = true;
    }
}
Imports System.Threading
Imports GemBox.Spreadsheet

Class MainWindow

    Private Property cancellationRequested As Boolean

    Public Sub New()
        ' If using Professional version, put your serial key below.
        SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY")
        ' Use Trial Mode
        AddHandler SpreadsheetInfo.FreeLimitReached,
            Sub(eventSender, args)
                args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial
            End Sub

        InitializeComponent()
    End Sub

    Private Async Sub loadButton_Click(sender As Object, e As RoutedEventArgs) Handles loadButton.Click
        ' Capture the current context on the UI thread
        Dim context = SynchronizationContext.Current

        ' Create load options
        Dim loadOptions = New XlsxLoadOptions()
        AddHandler loadOptions.ProgressChanged,
            Sub(eventSender, args)
                ' Show progress
                context.Post(
                    Sub(progressPercentage)
                        Me.progressBar.Value = CType(progressPercentage, Integer)
                    End Sub, args.ProgressPercentage)

                ' Cancel if requested
                If Me.cancellationRequested Then
                    args.CancelOperation()
                End If
            End Sub

        Try
            Dim file = Await Threading.Tasks.Task.Run(
                Function() As ExcelFile
                    Return ExcelFile.Load("LargeFile.xlsx", loadOptions)
                End Function)
        Catch ex As OperationCanceledException
            ' Operation cancelled
        End Try
    End Sub

    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs) Handles cancelButton.Click
        Me.cancellationRequested = True
    End Sub
End Class

Want more?

Next example GitHub

Check the next example or select an example from the menu. You can also download our examples from the GitHub.


Like it?

Download Buy

If you want to try the GemBox.Spreadsheet yourself, you can download the free version. It delivers the same performance and set of features as the professional version, but with some operations limited. To remove the limitation, you need to purchase a license.