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 XLSX files and saving XLSX, PDF, and image files by handling the XlsxLoadOptions.ProgressChanged
, XlsxSaveOptions.ProgressChanged
, PdfSaveOptions.ProgressChanged
, and ImageSaveOptions.ProgressChanged
events.
The following example is a simple console application that shows the save progress of a large file.

using System;
using GemBox.Spreadsheet;
class Program
{
static void Main()
{
// If using the 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 the 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.

<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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 the 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 the 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.

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 the 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 the 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 the 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 the 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.

<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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" Click="loadButton_Click" />
<Button x:Name="cancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="57,111,0,0" VerticalAlignment="Top" Width="228" Height="32" Click="cancelButton_Click" />
<ProgressBar HorizontalAlignment="Left" Height="25" Margin="57,73,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 the 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 the 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