Dynamic Reportların sütun genişlikleri ve Windows Emoji

This commit is contained in:
Sedat Öztürk 2026-07-04 21:13:41 +03:00
parent 9129bc17db
commit 0e1e83fc00
7 changed files with 413 additions and 161 deletions

View file

@ -307,10 +307,17 @@ public class DynamicFormReport : XtraReport
if (TryGetFormattedValue(field, row, out var text))
{
label.Text = text;
label.Font = new DXFont(
DynamicReportFontHelper.GetFontFamilyForText(text),
DynamicReportFontHelper.DataFontSize);
}
else
{
label.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{field.BindingName}]"));
label.ExpressionBindings.Add(new ExpressionBinding(
"BeforePrint",
"Font.Name",
DynamicReportImageHelper.CreateFontFamilyExpression($"[{field.BindingName}]")));
}
return label;

View file

@ -115,6 +115,9 @@ public class DynamicGridReport : XtraReport
var visibleFields = DynamicReportGridStateHelper.GetVisibleFields(
availableFields,
gridState?.CustomizationData);
var stateWidths = DynamicReportGridStateHelper.GetColumnWidths(
gridState?.CustomizationData,
GetParameter(parameters, "columnWidths"));
if (visibleFields.Count == 0)
{
@ -174,12 +177,12 @@ public class DynamicGridReport : XtraReport
localizer);
reportColumns = await ApplyImageCountsAsync(reportColumns, dynamicDataRepository, selectQuery, connectionString, imageBaseUrl);
ConfigurePage(reportColumns, isSubReport);
ConfigurePage(reportColumns, localizer, stateWidths, isSubReport);
ConfigureDataSource(
selectQuery,
connectionString,
dataSourceType);
BuildLayout(listForm, reportColumns, groupColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
BuildLayout(listForm, reportColumns, groupColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport, stateWidths);
RequestParameters = false;
}
@ -207,12 +210,18 @@ public class DynamicGridReport : XtraReport
}
#pragma warning disable CA1416
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields, bool isSubReport)
private void ConfigurePage(
IReadOnlyCollection<ReportColumn> visibleFields,
IStringLocalizer<PlatformResource> localizer,
IReadOnlyDictionary<string, float> stateWidths,
bool isSubReport)
{
Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
var requestedContentWidth = visibleFields.Sum(x => x.IsImage
? GetImageColumnWidth(x)
: DynamicReportGridStateHelper.GetPreferredColumnWidth(x.Field, localizer, stateWidths));
Landscape = !isSubReport &&
(visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth);
Font = DynamicReportFontHelper.CreateDataFont();
@ -294,7 +303,8 @@ public class DynamicGridReport : XtraReport
string imageBaseUrl,
string currentUserName,
string criteriaText,
bool isSubReport)
bool isSubReport,
IReadOnlyDictionary<string, float> stateWidths)
{
Bands.Clear();
@ -352,8 +362,7 @@ public class DynamicGridReport : XtraReport
var detailIndent = GetGroupDetailIndent(groupFields.Count, pageWidth);
var tableWidth = pageWidth - detailIndent;
var detailWidths = CalculateWidths(fields, tableWidth);
var headerWidths = CreateIndentedHeaderWidths(detailWidths, detailIndent);
var detailWidths = DynamicReportGridStateHelper.CalculateWidths(fields, tableWidth, localizer, stateWidths);
var headerTable = CreateTable(pageWidth, 28);
var headerRow = new XRTableRow { HeightF = 28 };
var detailTable = CreateTable(tableWidth, detailHeight, detailIndent);
@ -362,10 +371,15 @@ public class DynamicGridReport : XtraReport
var footerTable = CreateTable(tableWidth, 24, detailIndent);
var footerRow = new XRTableRow { HeightF = 24 };
if (detailIndent > 0)
{
headerRow.Cells.Add(CreateIndentHeaderCell(detailIndent));
}
for (var i = 0; i < fields.Count; i++)
{
var field = fields[i];
headerRow.Cells.Add(CreateHeaderCell(field, headerWidths[i], localizer));
headerRow.Cells.Add(CreateHeaderCell(field, detailWidths[i], localizer));
detailRow.Cells.Add(CreateDetailCell(field, detailWidths[i], imageBaseUrl, detailHeight));
footerRow.Cells.Add(CreateFooterCell(field, detailWidths[i]));
}
@ -457,19 +471,6 @@ public class DynamicGridReport : XtraReport
return Math.Min(groupCount * GroupIndentWidth, pageWidth * 0.25F);
}
private static float[] CreateIndentedHeaderWidths(float[] detailWidths, float detailIndent)
{
if (detailWidths.Length == 0 || detailIndent <= 0)
{
return detailWidths;
}
var headerWidths = detailWidths.ToArray();
headerWidths[0] += detailIndent;
return headerWidths;
}
private static XRTable CreateTable(float width, float height, float left = 0)
{
return new XRTable
@ -481,6 +482,17 @@ public class DynamicGridReport : XtraReport
};
}
private static XRTableCell CreateIndentHeaderCell(float width)
{
return new XRTableCell
{
WidthF = width,
Weight = width,
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
Padding = new PaddingInfo(0, 0, 0, 0)
};
}
private static XRTableCell CreateHeaderCell(
ReportColumn column,
float width,
@ -532,10 +544,15 @@ public class DynamicGridReport : XtraReport
Multiline = true,
CanGrow = true
};
var valueExpression = DynamicReportImageHelper.CreateFormatValueExpression($"[{column.BindingName}]", field, column.EditorType);
cell.ExpressionBindings.Add(new ExpressionBinding(
"BeforePrint",
"Text",
DynamicReportImageHelper.CreateFormatValueExpression($"[{column.BindingName}]", field, column.EditorType)));
valueExpression));
cell.ExpressionBindings.Add(new ExpressionBinding(
"BeforePrint",
"Font.Name",
DynamicReportImageHelper.CreateFontFamilyExpression(valueExpression)));
return cell;
}
@ -614,66 +631,6 @@ public class DynamicGridReport : XtraReport
};
}
private static float[] CalculateWidths(IReadOnlyList<ReportColumn> fields, float pageWidth)
{
var raw = fields.Select(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field)).ToArray();
var total = raw.Sum();
if (total <= 0)
{
return fields.Select(_ => pageWidth / fields.Count).ToArray();
}
return raw.Select(x => pageWidth * x / total).ToArray();
}
private static float GetPreferredColumnWidth(ListFormField field)
{
if (field.Width > 0)
{
return Math.Max(field.Width.Value, 60);
}
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
var name = field.FieldName ?? string.Empty;
var text = $"{caption} {name}".ToLowerInvariant();
if (field.SourceDbType == DbType.Boolean)
{
return 75;
}
if (DynamicReportImageHelper.IsNumeric(field.SourceDbType))
{
return 70;
}
if (field.SourceDbType is DbType.Date or DbType.DateTime or DbType.DateTime2 or DbType.DateTimeOffset)
{
return 115;
}
if (text.Contains("description") ||
text.Contains("explanation") ||
text.Contains("note") ||
text.Contains("comment") ||
text.Contains("address") ||
text.Contains("content") ||
text.Contains("html") ||
text.Contains("text"))
{
return 220;
}
if (text.Contains("name") ||
text.Contains("title") ||
text.Contains("subject"))
{
return 160;
}
return Math.Clamp((caption?.Length ?? 0) * 9F + 36F, 85F, 180F);
}
private static bool HasAnySummary(IEnumerable<ReportColumn> fields)
{
return fields.Any(x => GetSummaryType(x.Field.TotalSummaryJson) != null);
@ -737,7 +694,10 @@ public class DynamicGridReport : XtraReport
bool IsImage,
bool IsHtml,
string EditorType,
int ImageCount = 1) : DynamicReportSqlBuilder.IReportLookupColumn;
int ImageCount = 1) : DynamicReportSqlBuilder.IReportLookupColumn, DynamicReportGridStateHelper.IReportColumnWidth
{
public float ImageWidth => GetImageColumnWidth(this);
}
private static SummaryFunc? GetSummaryType(string totalSummaryJson)
{

View file

@ -9,6 +9,7 @@ internal static class DynamicReportFontHelper
public const float DataFontSize = 8.5F;
private const string WindowsFontFamily = "Arial";
private const string WindowsEmojiFontFamily = "Segoe UI Emoji";
private const string LinuxFontFamily = "Liberation Sans";
public static DXFont CreateHeaderFont(DXFontStyle style = DXFontStyle.Bold) =>
@ -19,10 +20,48 @@ internal static class DynamicReportFontHelper
public static DXFont CreateValueFont(float size, DXFontStyle style = DXFontStyle.Regular)
{
var fontFamily = OperatingSystem.IsLinux()
return new DXFont(GetDefaultFontFamily(), size, style);
}
public static string GetFontFamilyForText(string text)
{
return OperatingSystem.IsWindows() && ContainsEmojiOrSymbol(text)
? WindowsEmojiFontFamily
: GetDefaultFontFamily();
}
private static bool ContainsEmojiOrSymbol(string text)
{
if (string.IsNullOrEmpty(text))
{
return false;
}
for (var i = 0; i < text.Length; i++)
{
int codePoint;
if (char.IsHighSurrogate(text[i]) && i + 1 < text.Length && char.IsLowSurrogate(text[i + 1]))
{
codePoint = char.ConvertToUtf32(text, i);
i++;
}
else
{
codePoint = text[i];
}
if (codePoint is >= 0x1F000 and <= 0x1FAFF ||
codePoint is >= 0x2600 and <= 0x27BF)
{
return true;
}
}
return false;
}
private static string GetDefaultFontFamily() =>
OperatingSystem.IsLinux()
? LinuxFontFamily
: WindowsFontFamily;
return new DXFont(fontFamily, size, style);
}
}

View file

@ -1,15 +1,25 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using Sozsoft.Platform;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.ListForms;
using Sozsoft.Platform.Localization;
namespace Sozsoft.Reports.PredefinedReports;
internal static class DynamicReportGridStateHelper
{
public interface IReportColumnWidth
{
ListFormField Field { get; }
bool IsImage { get; }
float ImageWidth { get; }
}
public static string GetFilterName(ListForm listForm, string prefix) =>
$"{prefix}-{GetStateStorageKey(listForm)}";
@ -79,6 +89,74 @@ internal static class DynamicReportGridStateHelper
}
}
public static Dictionary<string, float> GetColumnWidths(string gridStateJson, string columnWidthsJson = null)
{
var widths = TryGetColumnStates(gridStateJson, out var statesByField)
? statesByField
.Where(x => x.Value.Width.HasValue && x.Value.Width.Value > 0)
.ToDictionary(x => x.Key, x => x.Value.Width.Value, StringComparer.OrdinalIgnoreCase)
: new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
foreach (var item in ParseColumnWidths(columnWidthsJson))
{
widths[item.Key] = item.Value;
}
return widths;
}
public static float[] CalculateWidths<TColumn>(
IReadOnlyList<TColumn> columns,
float pageWidth,
IStringLocalizer<PlatformResource> localizer,
IReadOnlyDictionary<string, float> stateWidths = null)
where TColumn : IReportColumnWidth
{
if (columns.Count == 0)
{
return [];
}
var ideal = columns
.Select(column => column.IsImage
? column.ImageWidth
: GetPreferredColumnWidth(column.Field, localizer, stateWidths))
.ToArray();
var total = ideal.Sum();
if (total <= 0)
{
return columns.Select(_ => pageWidth / columns.Count).ToArray();
}
return ideal.Select(width => pageWidth * width / total).ToArray();
}
public static float GetPreferredColumnWidth(
ListFormField field,
IStringLocalizer<PlatformResource> localizer = null,
IReadOnlyDictionary<string, float> stateWidths = null)
{
var caption = localizer == null
? (string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName)
: DynamicReportImageHelper.GetLocalizedFieldCaption(field, localizer);
var name = field.FieldName ?? string.Empty;
var calculatedWidth = CalculateTextColumnWidth(field, caption, name);
if (!string.IsNullOrWhiteSpace(field.FieldName) &&
stateWidths?.TryGetValue(field.FieldName, out var stateWidth) == true &&
stateWidth > 0)
{
return Math.Max(stateWidth, 40F);
}
if (field.Width > 0)
{
return Math.Max(field.Width.Value, 40F);
}
return calculatedWidth;
}
private static string GetStateStorageKey(ListForm listForm)
{
if (!string.IsNullOrWhiteSpace(listForm.StateStoringJson))
@ -151,10 +229,14 @@ internal static class DynamicReportGridStateHelper
groupIndexProperty.TryGetInt32(out var groupValue)
? groupValue
: (int?)null;
var width = TryGetProperty(column, "width", out var widthProperty) &&
TryGetFloat(widthProperty, out var widthValue)
? widthValue
: (float?)null;
if (!string.IsNullOrWhiteSpace(fieldName))
{
states.Add(new GridColumnState(fieldName, visible, visibleIndex, groupIndex));
states.Add(new GridColumnState(fieldName, visible, visibleIndex, groupIndex, width));
}
if (TryGetProperty(column, "columns", out var childColumns) &&
@ -182,6 +264,114 @@ internal static class DynamicReportGridStateHelper
return null;
}
private static bool TryGetFloat(JsonElement element, out float value)
{
if (element.ValueKind == JsonValueKind.Number && element.TryGetSingle(out value))
{
return true;
}
if (element.ValueKind == JsonValueKind.String &&
float.TryParse(element.GetString(), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out value))
{
return true;
}
value = 0;
return false;
}
private static float CalculateTextColumnWidth(ListFormField field, string caption, string name)
{
if (field.SourceDbType == DbType.Boolean)
{
return Math.Max(75F, MeasureTextWidth(caption, 7.2F, 30F, 68F, 100F));
}
if (DynamicReportImageHelper.IsNumeric(field.SourceDbType))
{
return Math.Max(70F, MeasureTextWidth(caption, 7.2F, 30F, 68F, 120F));
}
if (field.SourceDbType is DbType.Date or DbType.DateTime or DbType.DateTime2 or DbType.DateTimeOffset)
{
return Math.Max(115F, MeasureTextWidth(caption, 7.2F, 30F, 90F, 145F));
}
return Math.Max(
MeasureTextWidth(caption, 7.2F, 30F, 85F, 220F),
MeasureTextWidth(name, 5.6F, 28F, 85F, 180F));
}
private static float MeasureTextWidth(
string value,
float characterWidth,
float padding,
float minimum,
float maximum)
{
if (string.IsNullOrWhiteSpace(value))
{
return 0;
}
return Math.Clamp(value.Trim().Length * characterWidth + padding, minimum, maximum);
}
private static Dictionary<string, float> ParseColumnWidths(string columnWidthsJson)
{
var widths = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(columnWidthsJson))
{
return widths;
}
try
{
using var document = JsonDocument.Parse(columnWidthsJson);
if (document.RootElement.ValueKind == JsonValueKind.Object)
{
foreach (var property in document.RootElement.EnumerateObject())
{
if (TryGetFloat(property.Value, out var width) && width > 0)
{
widths[property.Name] = width;
}
}
}
else if (document.RootElement.ValueKind == JsonValueKind.Array)
{
AddColumnWidths(document.RootElement, widths);
}
}
catch (JsonException)
{
}
return widths;
}
private static void AddColumnWidths(JsonElement columns, IDictionary<string, float> widths)
{
foreach (var column in columns.EnumerateArray())
{
var fieldName = GetColumnFieldName(column);
if (!string.IsNullOrWhiteSpace(fieldName) &&
TryGetProperty(column, "width", out var widthProperty) &&
TryGetFloat(widthProperty, out var width) &&
width > 0)
{
widths[fieldName] = width;
}
if (TryGetProperty(column, "columns", out var childColumns) &&
childColumns.ValueKind == JsonValueKind.Array)
{
AddColumnWidths(childColumns, widths);
}
}
}
private static List<ListFormField> GetGroupFieldsFromParameter(
IReadOnlyList<ListFormField> availableFields,
string groupParameter)
@ -249,5 +439,5 @@ internal static class DynamicReportGridStateHelper
return false;
}
private sealed record GridColumnState(string FieldName, bool Visible, int VisibleIndex, int? GroupIndex);
private sealed record GridColumnState(string FieldName, bool Visible, int VisibleIndex, int? GroupIndex, float? Width);
}

View file

@ -35,6 +35,7 @@ internal static class DynamicReportImageHelper
private const string ImageUrlFunctionName = "SozReportImageUrl";
private const string ImageSourceFunctionName = "SozReportImageSource";
private const string FormatValueFunctionName = "SozReportFormatValue";
private const string FontFamilyFunctionName = "SozReportFontFamily";
private static readonly Regex ReportPagingRegex = new(
@"\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?\s*$",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
@ -55,6 +56,7 @@ internal static class DynamicReportImageHelper
RegisterImageFunction(new ReportImageUrlFunction());
RegisterImageFunction(new ReportImageSourceFunction());
RegisterImageFunction(new ReportFormatValueFunction());
RegisterImageFunction(new ReportFontFamilyFunction());
}
public static Dictionary<string, SelectField> CreateSelectFieldMap(IEnumerable<SelectField> selectFields)
@ -487,13 +489,13 @@ internal static class DynamicReportImageHelper
{
WidthF = width,
Weight = width,
Padding = new PaddingInfo(3, 3, 3, 3),
Padding = new PaddingInfo(0, 0, 0, 0),
CanGrow = false
};
for (var i = 0; i < safeImageCount; i++)
{
var pictureBox = CreatePictureBox(i, width, DefaultGridImageHeight - 6, safeImageCount);
var pictureBox = CreatePictureBox(i, width, DefaultGridImageHeight, safeImageCount);
pictureBox.Visible = true;
pictureBox.ExpressionBindings.Add(new ExpressionBinding(
"BeforePrint",
@ -602,6 +604,11 @@ internal static class DynamicReportImageHelper
return $"{FormatValueFunctionName}({valueExpression}, '{field.SourceDbType}', '{EscapeExpressionString(GetEffectiveFormat(field))}', '{EscapeExpressionString(editorType)}')";
}
public static string CreateFontFamilyExpression(string valueExpression)
{
return $"{FontFamilyFunctionName}({valueExpression})";
}
public static string FormatReportDate(DateTime value)
{
var culture = CultureInfo.CurrentCulture;
@ -2006,4 +2013,34 @@ internal static class DynamicReportImageHelper
: DbType.String;
}
}
private sealed class ReportFontFamilyFunction : ICustomFunctionOperatorBrowsable
{
public string Name => FontFamilyFunctionName;
public int MinOperandCount => 1;
public int MaxOperandCount => 1;
public FunctionCategory Category => FunctionCategory.Text;
public string Description => "Returns a report font family with emoji fallback.";
public object Evaluate(params object[] operands)
{
var text = Convert.ToString(operands.ElementAtOrDefault(0), CultureInfo.CurrentCulture);
return DynamicReportFontHelper.GetFontFamilyForText(text);
}
public Type ResultType(params Type[] operands)
{
return typeof(string);
}
public bool IsValidOperandCount(int count)
{
return count == 1;
}
public bool IsValidOperandType(int operandIndex, int operandCount, Type type)
{
return true;
}
}
}

View file

@ -124,6 +124,9 @@ public class DynamicTreeReport : XtraReport
var visibleFields = DynamicReportGridStateHelper.GetVisibleFields(
availableFields,
gridState?.CustomizationData);
var stateWidths = DynamicReportGridStateHelper.GetColumnWidths(
gridState?.CustomizationData,
GetParameter(parameters, "columnWidths"));
if (visibleFields.Count == 0)
{
@ -169,19 +172,24 @@ public class DynamicTreeReport : XtraReport
var orderedRows = OrderTreeRows(sourceRows, treeOptions).ToList();
reportColumns = ApplyImageCounts(reportColumns, orderedRows, imageBaseUrl);
ConfigurePage(reportColumns);
ConfigurePage(reportColumns, localizer, stateWidths);
ConfigureDataSource(orderedRows, reportColumns);
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport, stateWidths);
RequestParameters = false;
}
#pragma warning disable CA1416
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields)
private void ConfigurePage(
IReadOnlyCollection<ReportColumn> visibleFields,
IStringLocalizer<PlatformResource> localizer,
IReadOnlyDictionary<string, float> stateWidths)
{
Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
var requestedContentWidth = visibleFields.Sum(x => x.IsImage
? GetImageColumnWidth(x)
: DynamicReportGridStateHelper.GetPreferredColumnWidth(x.Field, localizer, stateWidths));
Landscape = visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth;
Font = DynamicReportFontHelper.CreateDataFont();
}
@ -423,7 +431,8 @@ public class DynamicTreeReport : XtraReport
string imageBaseUrl,
string currentUserName,
string criteriaText,
bool isSubReport)
bool isSubReport,
IReadOnlyDictionary<string, float> stateWidths)
{
Bands.Clear();
@ -466,7 +475,7 @@ public class DynamicTreeReport : XtraReport
TextAlignment = TextAlignment.MiddleRight
});
var widths = CalculateWidths(fields, pageWidth);
var widths = DynamicReportGridStateHelper.CalculateWidths(fields, pageWidth, localizer, stateWidths);
var headerTable = CreateTable(pageWidth, 28);
var headerRow = new XRTableRow { HeightF = 28 };
var detailTable = CreateTable(pageWidth, detailHeight);
@ -560,12 +569,17 @@ public class DynamicTreeReport : XtraReport
};
var valueExpression = DynamicReportImageHelper.CreateFormatValueExpression($"[{column.BindingName}]", column.Field, column.EditorType);
var textExpression = isTreeColumn
? $"Concat([{TreePrefixColumn}], {valueExpression})"
: valueExpression;
cell.ExpressionBindings.Add(new ExpressionBinding(
"BeforePrint",
"Text",
isTreeColumn
? $"Concat([{TreePrefixColumn}], {valueExpression})"
: valueExpression));
textExpression));
cell.ExpressionBindings.Add(new ExpressionBinding(
"BeforePrint",
"Font.Name",
DynamicReportImageHelper.CreateFontFamilyExpression(textExpression)));
if (isTreeColumn)
{
@ -635,66 +649,6 @@ public class DynamicTreeReport : XtraReport
return cell;
}
private static float[] CalculateWidths(IReadOnlyList<ReportColumn> fields, float pageWidth)
{
var raw = fields.Select(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field)).ToArray();
var total = raw.Sum();
if (total <= 0)
{
return fields.Select(_ => pageWidth / fields.Count).ToArray();
}
return raw.Select(x => pageWidth * x / total).ToArray();
}
private static float GetPreferredColumnWidth(ListFormField field)
{
if (field.Width > 0)
{
return Math.Max(field.Width.Value, 60);
}
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
var name = field.FieldName ?? string.Empty;
var text = $"{caption} {name}".ToLowerInvariant();
if (field.SourceDbType == DbType.Boolean)
{
return 75;
}
if (DynamicReportImageHelper.IsNumeric(field.SourceDbType))
{
return 70;
}
if (field.SourceDbType is DbType.Date or DbType.DateTime or DbType.DateTime2 or DbType.DateTimeOffset)
{
return 115;
}
if (text.Contains("description") ||
text.Contains("explanation") ||
text.Contains("note") ||
text.Contains("comment") ||
text.Contains("address") ||
text.Contains("content") ||
text.Contains("html") ||
text.Contains("text"))
{
return 220;
}
if (text.Contains("name") ||
text.Contains("title") ||
text.Contains("subject"))
{
return 160;
}
return Math.Clamp((caption?.Length ?? 0) * 9F + 36F, 85F, 180F);
}
private static bool HasAnySummary(IEnumerable<ReportColumn> fields)
{
return fields.Any(x => GetSummaryType(x.Field.TotalSummaryJson) != null);
@ -778,7 +732,10 @@ public class DynamicTreeReport : XtraReport
bool IsImage,
bool IsHtml,
string EditorType,
int ImageCount = 1) : DynamicReportSqlBuilder.IReportLookupColumn;
int ImageCount = 1) : DynamicReportSqlBuilder.IReportLookupColumn, DynamicReportGridStateHelper.IReportColumnWidth
{
public float ImageWidth => GetImageColumnWidth(this);
}
private static SummaryFunc? GetSummaryType(string totalSummaryJson)
{

View file

@ -209,6 +209,63 @@ const getReportSort = (grid: GridInstance) => {
.join(MULTIVALUE_DELIMITER)
}
const getReportColumnWidths = (grid: GridInstance) => {
const getVisibleColumns = (grid as any).getVisibleColumns
if (typeof getVisibleColumns !== 'function') {
return undefined
}
const stateColumnWidths: Record<string, number> = {}
const addStateColumnWidths = (columns: any[] | undefined) => {
columns?.forEach((column) => {
const fieldName = column?.dataField || column?.name
const width = Number(column?.width)
if (fieldName && Number.isFinite(width) && width > 0) {
stateColumnWidths[fieldName] = width
}
if (Array.isArray(column?.columns)) {
addStateColumnWidths(column.columns)
}
})
}
if (supportsState(grid)) {
const state = grid.state()
if (Array.isArray(state?.columns)) {
addStateColumnWidths(state.columns)
}
}
const visibleColumns = getVisibleColumns.call(grid) as any[]
const widths = visibleColumns.reduce((result: Record<string, number>, column: any) => {
const fieldName = column?.dataField || column?.name
if (!fieldName || column?.type === 'buttons') {
return result
}
const stateWidth = stateColumnWidths[fieldName]
const columnWidth = Number(column?.width)
const visibleWidth = Number(column?.visibleWidth)
const width =
Number.isFinite(stateWidth) && stateWidth > 0
? stateWidth
: Number.isFinite(columnWidth) && columnWidth > 0
? columnWidth
: Number.isFinite(visibleWidth) && visibleWidth > 0
? visibleWidth
: undefined
if (width) {
result[fieldName] = Math.round(width)
}
return result
}, {})
return Object.keys(widths).length ? widths : undefined
}
const createDynamicListReportUrl = (
grid: GridInstance | undefined,
listFormCode: string,
@ -222,6 +279,7 @@ const createDynamicListReportUrl = (
const searchParams = new URLSearchParams()
const filter = getReportFilter(grid)
const sort = getReportSort(grid)
const columnWidths = getReportColumnWidths(grid)
if (filter) {
searchParams.set('filter', JSON.stringify(filter))
@ -231,6 +289,10 @@ const createDynamicListReportUrl = (
searchParams.set('sort', sort)
}
if (columnWidths) {
searchParams.set('columnWidths', JSON.stringify(columnWidths))
}
const queryString = createReportQueryString(searchParams)
return queryString ? `${reportUrl}?${queryString}` : reportUrl
}