For some of our users, reports initially created within the DevExpress Visual Studio Report Designer require modifications at runtime prior to display in either our royalty-free End-User Report Designer or Document Viewer.
Specifically, runtime modifications (or configuration) is often required for a report’s data source. For example, your app might need to change default report settings or replace a report data source with another.
In this blog post, we would like to describe common runtime modification/configuration options for report data sources and also describe how to address these tasks using theDataSourceManager
class.
The DataSourceManager Class
This static class contains five methods that allow you to execute basic operations with report data sources at runtime:
- AddDataSources– adds the specified data sources to a report.
- GetDataSources– returns all report data sources.
- ReplaceDataSource– replaces a report data source with the specified data source.
- GetDataSourceAssignables– retrieves all report elements (subreports, controls, bands, parameters) to which a data source can be assigned.
- GetDataSourceAssignablesByDataSource– gets all report elements to which the specified data source is assigned.
By using these methods, you can address different runtime modification/configuration tasks for your report data source. Some of these tasks and their solutions are described in the first four sections of this blog post. The last section (Platform-Specific Code Samples) contains code snippets that demonstrate the use of DataSourceManager
class methods within reporting applications targeting WinForms, WPF, and ASP.NET Core platforms.
Add Data Sources to a Report
In certain situations, you may want to add data sources to the End-User Report Designer at runtime so that data sources are displayed in the Field List and can be used in a report immediately after the designer is invoked.
To solve this requirement, you can use our AddDataSources
method. Pass the report to which you want to add the data sources as the first argument, and then specify the data sources.
The following code sample adds two JSON data sources to a report:
var report = new XtraReport1();
var jsonDataSource1 = new JsonDataSource { /* ... */ };
var jsonDataSource2 = new JsonDataSource { /* ... */ };
DataSourceManager.AddDataSources(report, jsonDataSource1, jsonDataSource2);
Replace a Report Data Source
Another common requirement is to replace a report’s data source with another (for example, in order to switch from mock data to real data at runtime).
If your new data source is of the same type as the original report data source, you can simply update settings associated with the initial data source. However, if types (between your new and original data sources) are different, we recommend that you create a new data source and assign it to a report using the ReplaceDataSource
method.
The following code sample replaces a report’s original data source with a JSON data source:
var report = new XtraReport1();
var jsonDataSource = new JsonDataSource {/* ... */};
DataSourceManager.ReplaceDataSource(report, report.DataSource, jsonDataSource);
Make sure that the schema of the new data source matches the schema of the original report data source. Otherwise, bindings in your report will not work as expected, and report data will be displayed differently (not like that displayed in the original report/original data source.
Update All Report Data Source Settings
Assume that you want to bind your report and its elements (subreports, controls, bands, or parameters) to SQL databases and use different settings for these databases in your development and production environments. Here is an obvious question: how can you access these data sources at runtime and update settings?
The answer is to retrieve all SQL data sources associated with the report and update settings as needed (the same approach can be used for any other data source type).
The following code template uses the GetDataSources
method to retrieve all SQL data sources and update query parameters for each data source:
var report = new XtraReport1();
var sqlDataSources = DataSourceManager.GetDataSources<SqlDataSource>(
report: report,
includeSubReports: true
);
foreach (var sqlDataSource in sqlDataSources) {
foreach (var query in sqlDataSource.Queries) {
// Access and change query parameters here.
query.Parameters["paramName"].Value = 32;
}
}
Update Report and Subreport Data Source Settings
One more common task is to update a report data source and sub report data sources at runtime.
The solution for this requirement is much like the previous example. The difference is that you don’t need to access all report data sources but only the data sources for your subreports and the report itself. As you would expect, you cannot simply call the GetDataSources
method - since it can return a data source whose bound control type differs fromXtraReport
(for example, DetailReportBand
or XRCrossTab
).
The correct solution is to call the GetDataSourceAssignables<T>
method with T = XtraReport
to get the report itself alongside all its subreports. Once called, you can iterate through retrieved report objects, access data sources, and update settings as needed.
The following code sample shows how to retrieve a report and all its subreports (elements of the XtraReport
type) and update settings (the ConnectionName
property) of their data sources. In this example, we assume that the type of each data source is SqlDataSource
.
var report = new XtraReport1();
var dataBoundReports = DataSourceManager.GetDataSourceAssignables<XtraReport>(
report: report,
includeSubReports: true
);
foreach (var report in dataBoundReports) {
(dataBoundReport.DataSource as SqlDataSource).ConnectionName = "nwind";
}
Platform-Specific Code Samples (WinForms, WPF, ASP.NET Core)
Depending on your target platform, you might need to use different strategies to modify/configure data source settings at runtime.
If you wish to add data sources to your report so that they are displayed in the End-User Report Designer's Field List once the designer is invoked, simply call the AddDataSources
method before you invoke the designer.
If you want to change data source settings before displaying a report preview, you will need to complete a few more steps (advanced configuration of the End-User Report Designer). Please see the sections below for detailed examples.
Add Data Sources to a Report when Invoking the Report Designer
WinForms
private void Form1_Load(object sender, EventArgs e) {
var report = new XtraReport1();
var reportDesignTool = new ReportDesignTool(report);
var jsonDataSource1 = new JsonDataSource { /* ... */ };
var jsonDataSource2 = new JsonDataSource { /* ... */ };
DataSourceManager.AddDataSources(report, jsonDataSource1, jsonDataSource2);
reportDesignTool.ShowRibbonDesigner();
}
WPF
private void Window_Loaded(object sender, RoutedEventArgs e) {
var report = new XtraReport1();
var jsonDataSource1 = new JsonDataSource { /* ... */ };
var jsonDataSource2 = new JsonDataSource { /* ... */ };
DataSourceManager.AddDataSources(report, jsonDataSource1, jsonDataSource2);
reportDesigner.OpenDocument(report);
}
ASP.NET Core
In an ASP.NET Core reporting application, add data sources to a report in a service method that retrieves the report from storage and returns this report to the designer. The following code sample uses theGetData
method of the ReportStorageWebExtension
service:
public override byte[] GetData(string url) {
//...
if (ReportsFactory.Reports.ContainsKey(url)) {
using var ms = new MemoryStream();
using XtraReport report = ReportsFactory.Reports[url]();
var jsonDataSource1 = new JsonDataSource { /* ... */ };
var jsonDataSource2 = new JsonDataSource { /* ... */ };
DataSourceManager.AddDataSources(report, jsonDataSource1, jsonDataSource2);
report.SaveLayoutToXml(ms);
return ms.ToArray();
}
//...
}
Update Data Source Settings when Showing Report Preview in the End-User Report Designer
WinForms
private void Form1_Load(object sender, EventArgs e) {
// Create a ReportDesignTool instance and
// implement a DesignPanelLoaded event handler as
// shown below. After that, invoke the designer.
var reportDesignTool = new ReportDesignTool(new XtraReport1());
reportDesignTool.DesignRibbonForm.DesignMdiController.DesignPanelLoaded += DesignMdiController_DesignPanelLoaded;
reportDesignTool.ShowRibbonDesigner();
}
private void DesignMdiController_DesignPanelLoaded(object sender, DevExpress.XtraReports.UserDesigner.DesignerLoadedEventArgs e) {
ReportTabControl tabControl = ((XRDesignPanel) sender).GetService(typeof(ReportTabControl)) as ReportTabControl;
tabControl.PreviewReportCreated += TabControl_PreviewReportCreated;
}
private void TabControl_PreviewReportCreated(object sender, EventArgs e) {
var reportTabControl = sender as ReportTabControl;
var report = reportTabControl.PreviewReport;
// Update data source settings of the retrieved report here.
var jsonDataSource = new JsonDataSource { /* ... */ };
DataSourceManager.ReplaceDataSource(report, report.DataSource, jsonDataSource);
}
WPF
public MainWindow() {
InitializeComponent();
// Implement the designer's ActiveDocumentChanged event handler as shown below.
reportDesigner.ActiveDocumentChanged += reportDesigner_ActiveDocumentChanged;
}
private void reportDesigner_ActiveDocumentChanged(object sender, DependencyPropertyChangedEventArgs e) {
if (reportDesigner.ActiveDocument != null) {
reportDesigner.ActiveDocument.ReportCloned += ActiveDocument_ReportCloned;
}
}
private void ActiveDocument_ReportCloned(object? sender, DevExpress.Xpf.Reports.UserDesigner.ReportClonedEventArgs e) {
var report = e.Cloned;
// Update data source settings of the retrieved report here.
var jsonDataSource = new JsonDataSource { /* ... */ };
DataSourceManager.ReplaceDataSource(report, report.DataSource, jsonDataSource);
}
ASP.NET Core - Basic Approach
In an ASP.NETCore reporting application, inherit from the PreviewReportCustomizationService
class and override its CustomizeReport
method. In this method, update settings of report data sources as needed.
public class CustomPreviewReportCustomizationService : PreviewReportCustomizationService {
public override void CustomizeReport(XtraReport report) {
var jsonDataSource = new JsonDataSource {/* ... */};
DataSourceManager.ReplaceDataSource(report, report.DataSource, jsonDataSource);
}
}
Add this class to the services
collection in the ConfigureServices
method of the Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
//...
services.AddScoped<PreviewReportCustomizationService, CustomPreviewReportCustomizationService>();
//...
}
ASP.NET Core - Advanced Example
Assume that you wish to implement an ASP.NET Core reporting application that meets the following requirements:
You want to create and edit reports in the End-User Report Designer and display a preview.
- You can get report data only from a custom data provider - specifically, from a service method stored in the
ServiceContainer
collection.
To address these requirements, you should use the ObjectDataSource
component.
This component can generate a schema for your data and serialize data types. It allows you to create and edit reports in the designer without populating the component with actual data.
You can populate this component with data from a custom data provider when switching to the report preview.
To address the 2nd task, you should:
- Inherit from the
PreviewReportCustomizationService
class and override itsCustomizeReport
method. - In this method, get a data object of a required type from the
ServiceContainer
collection. - Assign the object to the component's
DataSource
property. To find the required component, call theDataSourceManager.GetDataSources
method.
public override void CustomizeReport(XtraReport report) {
var objectDataSources = DataSourceManager.GetDataSources<ObjectDataSource>(report, includeSubReports: true);
foreach (var ods in objectDataSources) {
if (ods.DataSource is Type dataSourceType) {
ods.DataSource = ServiceProvider.GetRequiredService(dataSourceType);
}
}
}
Refer to the following example for more information in this regard: Reporting for ASP.NET Core – Inject Data from the Entity Framework Core DbContext into a Report Using the Object Data Source.
Code samples are available in the following files:
- CustomPreviewReportCustomizationService.cs (example of
PreviewReportCustomizationService
class descendant implementation) - ObjectDataSourceInjector.cs (demonstrates the
DataSourceManager
class usage)
Your Feedback Counts
As always, we would love your feedback. Do you need to manage report data sources at runtime? If so, does the DataSourceManager
class address your requirements?
Clik here to view.