Skip to content

Instantly share code, notes, and snippets.

@unseensenpai
Created April 7, 2026 07:20
Show Gist options
  • Select an option

  • Save unseensenpai/cd9e1e1e7d220f7c394cd230c0107170 to your computer and use it in GitHub Desktop.

Select an option

Save unseensenpai/cd9e1e1e7d220f7c394cd230c0107170 to your computer and use it in GitHub Desktop.
DevExpress XtraReport (WinForm) Parameters Request Submit Event Race Condition Fix For Async Operations

DevExpress XtraReports: Async Parameter Submission Fix

The Problem

The ParametersRequestSubmit event in DevExpress XtraReports is a void method. When you need to fetch data asynchronously (e.g., from a Web API or a heavy DB query), the UI thread does not wait for your task to complete. This results in:

  1. Stale Data: The report displays the results from the previous submission.
  2. Empty Reports: The document is created before the DataSource is populated.

The Solution

Instead of relying on the internal report generation trigger, we manually handle the report lifecycle:

  1. Cancel any pending operations.
  2. Fetch data asynchronously.
  3. Manually trigger CreateDocumentAsync.
  4. Re-bind the DocumentViewer.PrintingSystem only after the document is ready.

Implementation Highlights

  • Uses CancellationTokenSource to prevent overlapping requests.
  • Leverages ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext) to safely return to the UI thread.
  • Manually disposes and updates the PrintingSystem to force a UI refresh.
using DevExpress.XtraReports.UI;
using DevExpress.XtraPrinting;
using DevExpress.XtraEditors;
public partial class MyReportViewerForm : XtraForm
{
private XtraReport? _currentReport;
private CancellationTokenSource? _cts;
// Triggered by the Report's "Submit" button in the Parameters pane
private async void OnReport_ParametersRequestSubmit(object? sender, DevExpress.XtraReports.Parameters.ParametersRequestEventArgs e)
{
// 1. Thread Safety: Cancel any ongoing report generation
_cts?.Cancel();
_cts = new CancellationTokenSource();
try
{
// 2. Async Data Fetch (Generic Example)
var reportData = await FetchReportDataAsync(e.Parameters, _cts.Token)
.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext);
// 3. Re-initialize and Bind
await RefreshReportDisplayAsync(reportData, _cts.Token);
}
catch (OperationCanceledException) { /* Ignored */ }
catch (Exception ex)
{
XtraMessageBox.Show($"Error loading report: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private async Task RefreshReportDisplayAsync(object? dataSource, CancellationToken ct)
{
// Prevent memory leaks: Unsubscribe and Dispose old report instance
if (_currentReport != null)
{
_currentReport.ParametersRequestSubmit -= OnReport_ParametersRequestSubmit;
_currentReport.Dispose();
}
// Initialize your specific XtraReport class
_currentReport = new MyXtraReport();
_currentReport.ParametersRequestSubmit += OnReport_ParametersRequestSubmit;
// Assign fetched data
_currentReport.DataSource = dataSource;
// 4. Generate the document in the background
await _currentReport.CreateDocumentAsync(ct).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext);
// 5. Swap the PrintingSystem to refresh the DocumentViewer
UpdateDocumentViewer();
}
private void UpdateDocumentViewer()
{
if (_currentReport == null) return;
// We use ReportPrintTool to safely extract the PrintingSystem
using ReportPrintTool tool = new(_currentReport);
documentViewer1.PrintingSystem?.Dispose();
documentViewer1.PrintingSystem = tool.PrintingSystem;
}
private async Task<object?> FetchReportDataAsync(parameterCollection params, CancellationToken token)
{
// Your DB logic here
return await Task.FromResult<object?>(null);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment