(x => x);
+ }
+
+ public override void SetData(XtraReport report, string url) {
+ // Stores the specified report to a Report Storage using the specified URL.
+ // This method is called only after the IsValidUrl and CanSetData methods are called.
+ if(!IsWithinReportsFolder(url, ReportDirectory))
+ throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name.");
+ report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension));
+ }
+
+ public override string SetNewData(XtraReport report, string defaultUrl) {
+ // Stores the specified report using a new URL.
+ // The IsValidUrl and CanSetData methods are never called before this method.
+ // You can validate and correct the specified URL directly in the SetNewData method implementation
+ // and return the resulting URL used to save a report in your storage.
+ SetData(report, defaultUrl);
+ return defaultUrl;
+ }
+ }
+}
diff --git a/ServerApp/appsettings.Development.json b/ServerApp/appsettings.Development.json
new file mode 100644
index 0000000..cc7fe77
--- /dev/null
+++ b/ServerApp/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "System": "Information",
+ "Microsoft": "Information",
+ "DevExpress": "Information"
+ }
+ }
+}
diff --git a/ServerApp/appsettings.json b/ServerApp/appsettings.json
new file mode 100644
index 0000000..2f226ec
--- /dev/null
+++ b/ServerApp/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "DevExpress": "Warning"
+ }
+ },
+ "ConnectionStrings": {
+ "NWindConnectionString": "XpoProvider=SQLite;Data Source=|DataDirectory|Data/nwind.db",
+ "ReportsDataConnectionString": "Filename=Data/reportsData.db"
+ }
+}
diff --git a/dxSampleReactReportingPrintWithoutPreview/.gitignore b/dxSampleReactReportingPrintWithoutPreview/.gitignore
deleted file mode 100644
index 67e842d..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/.gitignore
+++ /dev/null
@@ -1,232 +0,0 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-
-# User-specific files
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-build/
-bld/
-bin/
-Bin/
-obj/
-Obj/
-
-# Visual Studio 2015 cache/options directory
-.vs/
-/wwwroot/dist/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUNIT
-*.VisualState.xml
-TestResult.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-*_i.c
-*_p.c
-*_i.h
-*.ilk
-*.meta
-*.obj
-*.pch
-*.pdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# JustCode is a .NET coding add-in
-.JustCode
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# NuGet Packages
-*.nupkg
-# The packages folder can be ignored because of Package Restore
-**/packages/*
-# except build/, which is used as an MSBuild target.
-!**/packages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/packages/repositories.config
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Microsoft Azure ApplicationInsights config file
-ApplicationInsights.config
-
-# Windows Store app package directory
-AppPackages/
-BundleArtifacts/
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-/node_modules
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-
-# SQL Server files
-*.mdf
-*.ldf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-
-# FAKE - F# Make
-.fake/
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore b/dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore
deleted file mode 100644
index 69e588b..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore
+++ /dev/null
@@ -1,23 +0,0 @@
-# See https://help.github.com/ignore-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-package-lock.json
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json b/dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json
deleted file mode 100644
index 811faad..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "name": "dxsamplereactreportingprintwithoutpreview",
- "version": "0.1.0",
- "private": true,
- "dependencies": {
- "@devexpress/analytics-core": "24.2-stable",
- "bootstrap": "^4.1.3",
- "devexpress-reporting": "24.2-stable",
- "devextreme": "24.2-stable",
- "devextreme-react": "24.2-stable",
- "file-saver": "^2.0.2",
- "jquery": "^3.4.1",
- "merge": "^2.1.1",
- "oidc-client": "^1.9.0",
- "@testing-library/jest-dom": "^5.16.5",
- "@testing-library/react": "^13.4.0",
- "@testing-library/user-event": "^13.5.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-scripts": "5.0.1",
- "react-router-bootstrap": "^0.26.2",
- "react-router-dom": "^6.11.2",
- "web-vitals": "^2.1.4"
- },
- "eslintConfig": {
- "extends": [
- "react-app",
- "react-app/jest"
- ]
- },
- "scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
- "eject": "react-scripts eject"
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- }
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/favicon.ico b/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/favicon.ico
deleted file mode 100644
index a3a7999..0000000
Binary files a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/favicon.ico and /dev/null differ
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/index.html b/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/index.html
deleted file mode 100644
index 64934fc..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/index.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- DevExpress Reporting Sample
-
-
-
-
- You need to enable JavaScript to run this app.
-
- DevExpress Reporting Sample: Print Reports Without Preview
-
-
-
-
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json b/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json
deleted file mode 100644
index b50a0b1..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "short_name": "dxSampleReactReportingPrintWithoutPreview",
- "name": "dxSampleReactReportingPrintWithoutPreview",
- "icons": [
- {
- "src": "favicon.ico",
- "sizes": "64x64 32x32 24x24 16x16",
- "type": "image/x-icon"
- }
- ],
- "start_url": "./index.html",
- "display": "standalone",
- "theme_color": "#000000",
- "background_color": "#ffffff"
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js
deleted file mode 100644
index 2d26145..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { MemoryRouter } from 'react-router-dom';
-import App from './App';
-
-it('renders without crashing', async () => {
- const div = document.createElement('div');
- ReactDOM.render(
-
-
- , div);
- await new Promise(resolve => setTimeout(resolve, 1000));
-});
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx
deleted file mode 100644
index d100eca..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import React from "react";
-import saveAs from "file-saver";
-
-
-class HomeComponent extends React.Component {
- constructor(props) {
- super(props);
-
- this.selectedFormat = 'pdf';
- this.printUrl = "";
-
- this.reportUrl = "TestReport";
-
- this.onChange = this.onChange.bind(this);
- this.downloadFile = this.downloadFile.bind(this);
- this.printInNewWindow = this.printInNewWindow.bind(this);
- this.printInIframe = this.printInIframe.bind(this);
- };
-
- onChange(event) {
- this.selectedFormat = event.target.value;
- }
-
- printInNewWindow() {
- var frameElement = window.open("api/Home/Print", "_blank");
- frameElement.addEventListener("load", function (e) {
- if (frameElement.document.contentType !== "text/html")
- frameElement.print();
- });
- }
-
- printInIframe() {
- var iframe = document.getElementById('printFrame');
- if (iframe.contentDocument.contentType !== "text/html")
- iframe.contentWindow.print();
- }
-
- downloadFile() {
- fetch("api/Home/Export?format=" + this.selectedFormat)
- .then(response => response.blob())
- .then(data => {
- saveAs(data, 'TestReport.' + this.selectedFormat.toLowerCase());
- });
-
- }
-
- render() {
-
- return (
-
-
- PDF
- DOCX
- XLS
- XLSX
- RTF
- MHT
- HTML
- TXT
- CSV
- PNG
-
-
- Export a report
-
-
- Print a report in a new tab
-
-
- Print a report with IFrame
-
-
-
- );
- }
- componentDidMount() {
-
- }
- componentWillUnmount() {
-
- }
-};
-
-export default HomeComponent;
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css
deleted file mode 100644
index 24a6ac4..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css
+++ /dev/null
@@ -1,14 +0,0 @@
-/* Provide sufficient contrast against white background */
-a {
- color: #0366d6;
-}
-
-code {
- color: #E01A76;
-}
-
-.btn-primary {
- color: #fff;
- background-color: #1b6ec2;
- border-color: #1861ac;
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js
deleted file mode 100644
index 3ed9e66..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import 'bootstrap/dist/css/bootstrap.css';
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { BrowserRouter } from 'react-router-dom';
-import App from './App';
-import registerServiceWorker from './registerServiceWorker';
-
-const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
-const rootElement = document.getElementById('root');
-
-ReactDOM.render(
-
-
- ,
- rootElement);
-
-registerServiceWorker();
-
diff --git a/dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs b/dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs
deleted file mode 100644
index 1b6e765..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using DevExpress.AspNetCore.Reporting.QueryBuilder;
-using DevExpress.AspNetCore.Reporting.QueryBuilder.Native.Services;
-using DevExpress.AspNetCore.Reporting.ReportDesigner;
-using DevExpress.AspNetCore.Reporting.ReportDesigner.Native.Services;
-using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
-using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services;
-
-namespace dxSampleReactReportingPrintWithoutPreview.Controllers {
- public class CustomWebDocumentViewerController : WebDocumentViewerController {
- public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService) {
- }
- }
-
- public class CustomReportDesignerController : ReportDesignerController {
- public CustomReportDesignerController(IReportDesignerMvcControllerService controllerService) : base(controllerService) {
- }
- }
-
- public class CustomQueryBuilderController : QueryBuilderController {
- public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService) : base(controllerService) {
- }
- }
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml b/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml
deleted file mode 100644
index 09da0d2..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml
+++ /dev/null
@@ -1,26 +0,0 @@
-@page
-@model ErrorModel
-@{
- ViewData["Title"] = "Error";
-}
-
-Error.
-An error occurred while processing your request.
-
-@if (Model.ShowRequestId)
-{
-
- Request ID: @Model.RequestId
-
-}
-
-Development Mode
-
- Swapping to the Development environment displays detailed information about the error that occurred.
-
-
- The Development environment shouldn't be enabled for deployed applications.
- It can result in displaying sensitive information from exceptions to end users.
- For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
- and restarting the app.
-
diff --git a/dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml b/dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml
deleted file mode 100644
index 3f481f3..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml
+++ /dev/null
@@ -1,3 +0,0 @@
-@using dxSampleReactReportingPrintWithoutPreview
-@namespace dxSampleReactReportingPrintWithoutPreview.Pages
-@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/dxSampleReactReportingPrintWithoutPreview/Program.cs b/dxSampleReactReportingPrintWithoutPreview/Program.cs
deleted file mode 100644
index 68e6070..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/Program.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-
-namespace dxSampleReactReportingPrintWithoutPreview
-{
- public class Program
- {
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
-
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup();
- });
- }
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/Startup.cs b/dxSampleReactReportingPrintWithoutPreview/Startup.cs
deleted file mode 100644
index 0afbe04..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/Startup.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.HttpsPolicy;
-using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using DevExpress.AspNetCore;
-using DevExpress.AspNetCore.Reporting;
-using Microsoft.AspNetCore.SpaServices.AngularCli;
-using DevExpress.XtraReports.Web.Extensions;
-using dxSampleReactReportingPrintWithoutPreview.Services;
-using DevExpress.Security.Resources;
-using System.IO;
-
-namespace dxSampleReactReportingPrintWithoutPreview
-{
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
-
- services.AddDevExpressControls();
- services.AddScoped();
- services
- .AddControllersWithViews()
- .AddNewtonsoftJson();
- services.ConfigureReportingServices(configurator => {
- configurator.ConfigureReportDesigner(designerConfigurator => {
- designerConfigurator.RegisterDataSourceWizardConfigFileConnectionStringsProvider();
- });
- configurator.ConfigureWebDocumentViewer(viewerConfigurator => {
- viewerConfigurator.UseCachedReportSourceBuilder();
- });
- });
-
- // In production, the React files will be served from this directory
- services.AddSpaStaticFiles(configuration =>
- {
- configuration.RootPath = "ClientApp/build";
- });
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- var contentDirectoryAllowRule = DirectoryAccessRule.Allow(new DirectoryInfo(Path.Combine(env.ContentRootPath, "..", "Content")).FullName);
- AccessSettings.ReportingSpecificResources.TrySetRules(contentDirectoryAllowRule, UrlAccessRule.Allow());
-
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- else
- {
- app.UseExceptionHandler("/Home/Error");
- // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
- app.UseHsts();
- }
-
- app.UseHttpsRedirection();
- app.UseStaticFiles();
- if (!env.IsDevelopment()) {
- app.UseSpaStaticFiles();
- }
-
- app.UseRouting();
-
- app.UseDevExpressControls();
-
- System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllerRoute(
- name: "default",
- pattern: "{controller}/{action=Index}/{id?}");
- });
-
- app.UseSpa(spa =>
- {
- spa.Options.SourcePath = "ClientApp";
-
- if (env.IsDevelopment())
- {
- spa.UseReactDevelopmentServer(npmScript: "start");
- }
- });
- }
- }
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json b/dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json
deleted file mode 100644
index dba68eb..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- }
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/appsettings.json b/dxSampleReactReportingPrintWithoutPreview/appsettings.json
deleted file mode 100644
index b07cc57..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/appsettings.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- },
- "ConnectionStrings": {
- "NWindConnectionString": "XpoProvider=SQLite;Data Source=Data/nwind.db"
- },
- "AllowedHosts": "*"
-}
diff --git a/dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj b/dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj
deleted file mode 100644
index 10dd1a6..0000000
--- a/dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
- net8.0
- true
- Latest
- false
- ClientApp\
- $(DefaultItemExcludes);$(SpaRoot)node_modules\**
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TestReport.repx
-
-
- TestReport.repx
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- %(DistFiles.Identity)
- PreserveNewest
- true
-
-
-
-
\ No newline at end of file
diff --git a/react-app/.gitignore b/react-app/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/react-app/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/react-app/README.md b/react-app/README.md
new file mode 100644
index 0000000..7059a96
--- /dev/null
+++ b/react-app/README.md
@@ -0,0 +1,12 @@
+# React + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
diff --git a/react-app/eslint.config.js b/react-app/eslint.config.js
new file mode 100644
index 0000000..cee1e2c
--- /dev/null
+++ b/react-app/eslint.config.js
@@ -0,0 +1,29 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{js,jsx}'],
+ extends: [
+ js.configs.recommended,
+ reactHooks.configs['recommended-latest'],
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ ecmaFeatures: { jsx: true },
+ sourceType: 'module',
+ },
+ },
+ rules: {
+ 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
+ },
+ },
+])
diff --git a/react-app/index.html b/react-app/index.html
new file mode 100644
index 0000000..0c589ec
--- /dev/null
+++ b/react-app/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite + React
+
+
+
+
+
+
diff --git a/react-app/package.json b/react-app/package.json
new file mode 100644
index 0000000..cc6580a
--- /dev/null
+++ b/react-app/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "react-app",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0",
+ "file-saver": "^2.0.2",
+ "devexpress-reporting-react": "24.2-stable"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.29.0",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
+ "@vitejs/plugin-react": "^4.5.2",
+ "eslint": "^9.29.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.20",
+ "globals": "^16.2.0",
+ "vite": "^7.0.0"
+ }
+}
diff --git a/react-app/public/vite.svg b/react-app/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/react-app/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-app/src/App.css b/react-app/src/App.css
new file mode 100644
index 0000000..b9d355d
--- /dev/null
+++ b/react-app/src/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js b/react-app/src/App.jsx
similarity index 64%
rename from dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js
rename to react-app/src/App.jsx
index 30f1b77..7328c09 100644
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js
+++ b/react-app/src/App.jsx
@@ -1,18 +1,19 @@
-import React, { Component } from 'react';
-import HomeComponent from "./components/HomeComponent";
-
-import './custom.css'
-
-export default class App extends Component {
- static displayName = App.name;
-
- render() {
- return (
-
- );
- }
-}
+import { useState, Component } from 'react'
+import reactLogo from './assets/react.svg'
+import viteLogo from '/vite.svg'
+import './App.css'
+import HomeComponent from "./components/HomeComponent";
+
+export default class App extends Component {
+ static displayName = App.name;
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/react-app/src/assets/react.svg b/react-app/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/react-app/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-app/src/components/HomeComponent.jsx b/react-app/src/components/HomeComponent.jsx
new file mode 100644
index 0000000..87b3d41
--- /dev/null
+++ b/react-app/src/components/HomeComponent.jsx
@@ -0,0 +1,69 @@
+import React, { useRef, useState } from 'react';
+import { saveAs } from 'file-saver';
+
+export default function HomeComponent() {
+ let selectedFormat = 'PDF';
+ const iframeRef = useRef();
+
+ const downloadFile = () => {
+ fetch(`/api/Home/Export?format=${selectedFormat}`)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('An error has occurred.');
+ }
+ return response.blob();})
+ .then(data => {
+ saveAs(data, 'TestReport.' + selectedFormat.toLowerCase());
+ })
+ .catch(error => {
+ console.error('An error has occurred:', error);
+ });
+ };
+
+ const printInNewTab = () => {
+ var frameElement = window.open("api/Home/Print", "_blank");
+ frameElement?.addEventListener("load", function (e) {
+ if (frameElement.document.contentType !== "text/html")
+ frameElement.print();
+ });
+ };
+
+ const printInIframe = () => {
+ const iframe = iframeRef.current;
+ if (!iframe) {
+ console.error('IFrame not found');
+ return;
+ }
+ try {
+ if (iframe.contentDocument?.contentType !== "text/html") {
+ iframe.contentWindow.print();
+ }
+ } catch (error) {
+ console.error('An error has occurred::', error);
+ }
+ };
+
+
+ return (
+
+
+ selectedFormat = (e.target.value)}>
+ PDF
+ HTML
+ TXT
+ DOCX
+ RTF
+ MHT
+ XLSX
+ XLS
+ CSV
+ PNG
+
+ Export the report
+ Print the report in new tab
+ Print via iFrame
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/react-app/src/index.css b/react-app/src/index.css
new file mode 100644
index 0000000..08a3ac9
--- /dev/null
+++ b/react-app/src/index.css
@@ -0,0 +1,68 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/react-app/src/main.jsx b/react-app/src/main.jsx
new file mode 100644
index 0000000..1cc6021
--- /dev/null
+++ b/react-app/src/main.jsx
@@ -0,0 +1,14 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.jsx'
+import registerServiceWorker from './registerServiceWorker'
+
+
+createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
+
+registerServiceWorker();
\ No newline at end of file
diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/registerServiceWorker.js b/react-app/src/registerServiceWorker.js
similarity index 97%
rename from dxSampleReactReportingPrintWithoutPreview/ClientApp/src/registerServiceWorker.js
rename to react-app/src/registerServiceWorker.js
index da7c120..10b0baf 100644
--- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/registerServiceWorker.js
+++ b/react-app/src/registerServiceWorker.js
@@ -1,108 +1,108 @@
-// In production, we register a service worker to serve assets from local cache.
-
-// This lets the app load faster on subsequent visits in production, and gives
-// it offline capabilities. However, it also means that developers (and users)
-// will only see deployed updates on the "N+1" visit to a page, since previously
-// cached resources are updated in the background.
-
-// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
-// This link also includes instructions on opting out of this behavior.
-
-const isLocalhost = Boolean(
- window.location.hostname === 'localhost' ||
- // [::1] is the IPv6 localhost address.
- window.location.hostname === '[::1]' ||
- // 127.0.0.1/8 is considered localhost for IPv4.
- window.location.hostname.match(
- /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
- )
-);
-
-export default function register () {
- if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
- // The URL constructor is available in all browsers that support SW.
- const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
- if (publicUrl.origin !== window.location.origin) {
- // Our service worker won't work if PUBLIC_URL is on a different origin
- // from what our page is served on. This might happen if a CDN is used to
- // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
- return;
- }
-
- window.addEventListener('load', () => {
- const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
-
- if (isLocalhost) {
- // This is running on localhost. Lets check if a service worker still exists or not.
- checkValidServiceWorker(swUrl);
- } else {
- // Is not local host. Just register service worker
- registerValidSW(swUrl);
- }
- });
- }
-}
-
-function registerValidSW (swUrl) {
- navigator.serviceWorker
- .register(swUrl)
- .then(registration => {
- registration.onupdatefound = () => {
- const installingWorker = registration.installing;
- installingWorker.onstatechange = () => {
- if (installingWorker.state === 'installed') {
- if (navigator.serviceWorker.controller) {
- // At this point, the old content will have been purged and
- // the fresh content will have been added to the cache.
- // It's the perfect time to display a "New content is
- // available; please refresh." message in your web app.
- console.log('New content is available; please refresh.');
- } else {
- // At this point, everything has been precached.
- // It's the perfect time to display a
- // "Content is cached for offline use." message.
- console.log('Content is cached for offline use.');
- }
- }
- };
- };
- })
- .catch(error => {
- console.error('Error during service worker registration:', error);
- });
-}
-
-function checkValidServiceWorker (swUrl) {
- // Check if the service worker can be found. If it can't reload the page.
- fetch(swUrl)
- .then(response => {
- // Ensure service worker exists, and that we really are getting a JS file.
- if (
- response.status === 404 ||
- response.headers.get('content-type').indexOf('javascript') === -1
- ) {
- // No service worker found. Probably a different app. Reload the page.
- navigator.serviceWorker.ready.then(registration => {
- registration.unregister().then(() => {
- window.location.reload();
- });
- });
- } else {
- // Service worker found. Proceed as normal.
- registerValidSW(swUrl);
- }
- })
- .catch(() => {
- console.log(
- 'No internet connection found. App is running in offline mode.'
- );
- });
-}
-
-export function unregister () {
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.ready.then(registration => {
- registration.unregister();
- });
- }
-}
+// In production, we register a service worker to serve assets from local cache.
+
+// This lets the app load faster on subsequent visits in production, and gives
+// it offline capabilities. However, it also means that developers (and users)
+// will only see deployed updates on the "N+1" visit to a page, since previously
+// cached resources are updated in the background.
+
+// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
+// This link also includes instructions on opting out of this behavior.
+
+const isLocalhost = Boolean(
+ window.location.hostname === 'localhost' ||
+ // [::1] is the IPv6 localhost address.
+ window.location.hostname === '[::1]' ||
+ // 127.0.0.1/8 is considered localhost for IPv4.
+ window.location.hostname.match(
+ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
+ )
+);
+
+export default function register () {
+ if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+ // The URL constructor is available in all browsers that support SW.
+ const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
+ if (publicUrl.origin !== window.location.origin) {
+ // Our service worker won't work if PUBLIC_URL is on a different origin
+ // from what our page is served on. This might happen if a CDN is used to
+ // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
+ return;
+ }
+
+ window.addEventListener('load', () => {
+ const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+
+ if (isLocalhost) {
+ // This is running on localhost. Lets check if a service worker still exists or not.
+ checkValidServiceWorker(swUrl);
+ } else {
+ // Is not local host. Just register service worker
+ registerValidSW(swUrl);
+ }
+ });
+ }
+}
+
+function registerValidSW (swUrl) {
+ navigator.serviceWorker
+ .register(swUrl)
+ .then(registration => {
+ registration.onupdatefound = () => {
+ const installingWorker = registration.installing;
+ installingWorker.onstatechange = () => {
+ if (installingWorker.state === 'installed') {
+ if (navigator.serviceWorker.controller) {
+ // At this point, the old content will have been purged and
+ // the fresh content will have been added to the cache.
+ // It's the perfect time to display a "New content is
+ // available; please refresh." message in your web app.
+ console.log('New content is available; please refresh.');
+ } else {
+ // At this point, everything has been precached.
+ // It's the perfect time to display a
+ // "Content is cached for offline use." message.
+ console.log('Content is cached for offline use.');
+ }
+ }
+ };
+ };
+ })
+ .catch(error => {
+ console.error('Error during service worker registration:', error);
+ });
+}
+
+function checkValidServiceWorker (swUrl) {
+ // Check if the service worker can be found. If it can't reload the page.
+ fetch(swUrl)
+ .then(response => {
+ // Ensure service worker exists, and that we really are getting a JS file.
+ if (
+ response.status === 404 ||
+ response.headers.get('content-type').indexOf('javascript') === -1
+ ) {
+ // No service worker found. Probably a different app. Reload the page.
+ navigator.serviceWorker.ready.then(registration => {
+ registration.unregister().then(() => {
+ window.location.reload();
+ });
+ });
+ } else {
+ // Service worker found. Proceed as normal.
+ registerValidSW(swUrl);
+ }
+ })
+ .catch(() => {
+ console.log(
+ 'No internet connection found. App is running in offline mode.'
+ );
+ });
+}
+
+export function unregister () {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then(registration => {
+ registration.unregister();
+ });
+ }
+}
diff --git a/react-app/vite.config.js b/react-app/vite.config.js
new file mode 100644
index 0000000..9d1433d
--- /dev/null
+++ b/react-app/vite.config.js
@@ -0,0 +1,18 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ https: false, // Vite dev server remains on HTTP
+ proxy: {
+ '/api': {
+ target: 'https://localhost:44335',
+ changeOrigin: true,
+ secure: false, // allow self-signed SSL cert
+ }
+ }
+ }
+})
\ No newline at end of file