From 74438f9e6a2e0498e419804e697f2ba4239bb4d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:46:10 +0000 Subject: [PATCH 1/5] Initial plan From 34187c7272416d884d49780c782d13ed6729f0e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:37:02 +0000 Subject: [PATCH 2/5] Fix SaveAs to use runtime parameter values instead of defaults Modified GetParameters() method in RdlViewer.cs to retrieve current runtime parameter values from UserReportParameters instead of using the stale _Parameters dictionary. This ensures SaveAs to XLSX, HTML, CSV, XML, RTF, and MHTML uses the correct parameter values selected by the user at runtime. Co-authored-by: majorsilence <656288+majorsilence@users.noreply.github.com> --- RdlViewer/RdlViewer.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/RdlViewer/RdlViewer.cs b/RdlViewer/RdlViewer.cs index 05c00c07..1cf60e25 100644 --- a/RdlViewer/RdlViewer.cs +++ b/RdlViewer/RdlViewer.cs @@ -1481,6 +1481,32 @@ private async Task GetPages(Report report) private IDictionary GetParameters() { + // If we have a loaded report with user parameters, use those runtime values + // instead of the _Parameters dictionary which may be stale + if (_Report != null && _Report.UserReportParameters != null && _Report.UserReportParameters.Count > 0) + { + Dictionary runtimeParams = new Dictionary(); + foreach (UserReportParameter urp in _Report.UserReportParameters) + { + if (urp.Value != null) + { + runtimeParams[urp.Name] = urp.Value; + } + } + // Merge with any parameters from _Parameters that aren't in UserReportParameters + if (_Parameters != null) + { + foreach (DictionaryEntry kvp in _Parameters) + { + string key = kvp.Key.ToString(); + if (!runtimeParams.ContainsKey(key)) + { + runtimeParams[key] = kvp.Value; + } + } + } + return runtimeParams; + } return _Parameters; } From 86beb4f4629a3ea345996c4cee0e269295232a05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:40:36 +0000 Subject: [PATCH 3/5] Add comment explaining null parameter handling in GetParameters() Co-authored-by: majorsilence <656288+majorsilence@users.noreply.github.com> --- RdlViewer/RdlViewer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RdlViewer/RdlViewer.cs b/RdlViewer/RdlViewer.cs index 1cf60e25..9d484fd7 100644 --- a/RdlViewer/RdlViewer.cs +++ b/RdlViewer/RdlViewer.cs @@ -1488,6 +1488,8 @@ private IDictionary GetParameters() Dictionary runtimeParams = new Dictionary(); foreach (UserReportParameter urp in _Report.UserReportParameters) { + // Skip null values as they would use the parameter's default value anyway + // and we want to let the report engine handle defaults properly if (urp.Value != null) { runtimeParams[urp.Name] = urp.Value; From aabb3c9333520ed21fa54dce616cd32235b6f30f Mon Sep 17 00:00:00 2001 From: Peter Gill Date: Tue, 24 Feb 2026 20:06:55 -0330 Subject: [PATCH 4/5] fix: update Value getter to handle async runtime value retrieval --- RdlEngine/Definition/ReportParameter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RdlEngine/Definition/ReportParameter.cs b/RdlEngine/Definition/ReportParameter.cs index 8a25048c..e07bf4a8 100644 --- a/RdlEngine/Definition/ReportParameter.cs +++ b/RdlEngine/Definition/ReportParameter.cs @@ -498,7 +498,11 @@ private object GetDataValueFromDisplay(object dvalue) /// public object Value { - get { return _rp.GetRuntimeValue(this._rpt); } + get + { + // HACK: async + return Task.Run(async () => await _rp.GetRuntimeValue(this._rpt)).GetAwaiter().GetResult(); + } set { if (this.MultiValue && value is string) From 20c939b8ecaaf59a1b6d332aa1b3f27325304e7b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 23:50:09 +0000 Subject: [PATCH 5/5] fix: make GetParameters() async to avoid Task.Run blocking and fix null param edge case - Added GetValueAsync() to UserReportParameter for proper async access to runtime values - Updated GetParameters() to be async and use GetValueAsync() to avoid synchronous blocking - Updated both callers of GetParameters() (SaveAs and GetPages) to await the async call - Updated null handling: always include user parameters (even null values) so stale _Parameters values can never override user-visible parameters - Improved HACK comment in Value getter to explain the async trade-off Co-authored-by: majorsilence <656288+majorsilence@users.noreply.github.com> --- RdlEngine/Definition/ReportParameter.cs | 13 ++++++++++++- RdlViewer/RdlViewer.cs | 15 ++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/RdlEngine/Definition/ReportParameter.cs b/RdlEngine/Definition/ReportParameter.cs index e07bf4a8..41b869bf 100644 --- a/RdlEngine/Definition/ReportParameter.cs +++ b/RdlEngine/Definition/ReportParameter.cs @@ -495,12 +495,14 @@ private object GetDataValueFromDisplay(object dvalue) /// /// The runtime value of the parameter. + /// Prefer using GetValueAsync() to avoid synchronous blocking on an async call. /// public object Value { get { - // HACK: async + // HACK: async - synchronous wrapper needed for property getter; + // use GetValueAsync() when an async context is available. return Task.Run(async () => await _rp.GetRuntimeValue(this._rpt)).GetAwaiter().GetResult(); } set @@ -517,6 +519,15 @@ public object Value } } + /// + /// Asynchronously gets the runtime value of the parameter. + /// Prefer this over the synchronous Value getter when an async context is available. + /// + public async Task GetValueAsync() + { + return await _rp.GetRuntimeValue(this._rpt); + } + /// /// Take a string and parse it into multiple values /// diff --git a/RdlViewer/RdlViewer.cs b/RdlViewer/RdlViewer.cs index 9d484fd7..1885a7b6 100644 --- a/RdlViewer/RdlViewer.cs +++ b/RdlViewer/RdlViewer.cs @@ -900,7 +900,7 @@ public async Task SaveAs(string FileName, Majorsilence.Reporting.Rdl.OutputPrese if (!(type == OutputPresentationType.PDF || type == OutputPresentationType.PDFOldStyle || type == OutputPresentationType.TIF || type == OutputPresentationType.TIFBW)) { - var ld = GetParameters(); // split parms into dictionary + var ld = await GetParameters(); // split parms into dictionary await _Report.RunGetData(ld); // obtain the data (again) } try @@ -1445,7 +1445,7 @@ private async Task GetPages(Report report) { Pages pgs = null; - var ld = GetParameters(); // split parms into dictionary + var ld = await GetParameters(); // split parms into dictionary try { @@ -1479,7 +1479,7 @@ private async Task GetPages(Report report) return pgs; } - private IDictionary GetParameters() + private async Task GetParameters() { // If we have a loaded report with user parameters, use those runtime values // instead of the _Parameters dictionary which may be stale @@ -1488,12 +1488,9 @@ private IDictionary GetParameters() Dictionary runtimeParams = new Dictionary(); foreach (UserReportParameter urp in _Report.UserReportParameters) { - // Skip null values as they would use the parameter's default value anyway - // and we want to let the report engine handle defaults properly - if (urp.Value != null) - { - runtimeParams[urp.Name] = urp.Value; - } + // Always include user parameters, even when the value is null, so that + // stale values from _Parameters cannot override user-visible parameters. + runtimeParams[urp.Name] = await urp.GetValueAsync(); } // Merge with any parameters from _Parameters that aren't in UserReportParameters if (_Parameters != null)