DynamicForm, DynamicList ve DynamicReport güncellemesi

This commit is contained in:
Sedat ÖZTÜRK 2026-06-16 17:46:20 +03:00
parent ec438afab5
commit fdf48c04ee
14 changed files with 947 additions and 436 deletions

View file

@ -1584,28 +1584,37 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
Text ="App.Platform.View",
UrlTarget="_blank",
AuthName = listFormName,
Url=$"/admin/reports/TemplateReport/view?id=@Id",
Url=$"/admin/reports/DynamicReport/view?id=@Id",
IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId !== 'Listeler')"
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')"
},
new() {
Hint = "App.Platform.Design",
Text ="App.Platform.Design",
UrlTarget="_blank",
AuthName = listFormName + ".Update",
Url=$"/admin/reports/TemplateReport/design?id=@Id",
Url=$"/admin/reports/DynamicReport/design?id=@Id",
IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId !== 'Listeler')"
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')"
},
new() {
Hint = "App.Platform.View",
Text ="App.Platform.View",
UrlTarget="_blank",
AuthName = listFormName,
Url=$"/admin/reports/DynamicListFormReport/view?listFormCode=@Name",
Url=$"/admin/reports/DynamicGrid/view?listFormCode=@Name",
IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Listeler')"
},
new() {
Hint = "App.Platform.View",
Text ="App.Platform.View",
UrlTarget="_blank",
AuthName = listFormName,
Url=$"/admin/reports/DynamicGrid/view?listFormCode=@Name&id=@Id",
IsVisible = true,
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Formlar')"
},
}),
}
);

View file

@ -3599,7 +3599,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 450, true, true, true, true, false, true),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
new() {
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items= [
Order=1, ColCount=2, ColSpan=1, ItemType="group", Items= [
new EditingFormItemDto { Order = 1, DataField = "CultureName", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 2, DataField = "UiCultureName", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "DisplayName", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },

View file

@ -417,7 +417,7 @@
"authority": ["App.Files"]
},
{
"key": "admin.devexpressReportView",
"key": "admin.devexpressDynamicReportView",
"path": "/admin/reports/:id/view",
"componentType": "normal",
"componentPath": "@/views/report/DevexpressReportViewer",
@ -425,7 +425,7 @@
"authority": []
},
{
"key": "admin.devexpressReportDesigner",
"key": "admin.devexpressDynamicReportDesigner",
"path": "/admin/reports/:id/design",
"componentType": "normal",
"componentPath": "@/views/report/DevexpressReportDesigner",

View file

@ -1362,11 +1362,6 @@
}
],
"ReportCategories": [
{
"name": "Taahhütnameler",
"description": "Kursiyeler ile ilgili taahhütname raporları",
"icon": "✍️"
},
{
"name": "Raporlar",
"description": "Şirket içi genel tüm raporlar",
@ -1387,15 +1382,22 @@
{
"name": "Standard Taahhütname",
"description": "Standard Taahhütname",
"categoryName": "Taahhütnameler",
"htmlContent": "<h1>Taahhütname</h1><p>Bu belge, tarafların belirli yükümlülüklerini ve taahhütlerini belirtir.</p>",
"categoryName": "Raporlar",
"htmlContent": "<h1>Taahhütname</h1><p>http://localhost:3000/admin/reports/DynamicReport/view?id=ede12de7-0e94-4b84-a953-6c359606478b</p>",
"status": "active"
},
{
"name": "App.Definitions.AiBot",
"name": "App.Languages.LanguageText",
"description": "Liste Raporları",
"categoryName": "Listeler",
"htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicListFormReport/view?listFormCode=App.Languages.LanguageText&filter=[%22CultureName%22,%22=%22,%22en%22]</p>",
"htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicGridReport/view?listFormCode=App.Languages.LanguageText&filter=[%22CultureName%22,%22=%22,%22en%22]</p>",
"status": "active"
},
{
"name": "App.Languages.Language",
"description": "Form Raporları",
"categoryName": "Formlar",
"htmlContent": "<h1>Liste Raporları.</h1><p>http://localhost:3000/admin/reports/DynamicFormReport/view?listFormCode=App.Languages.Language&id=e2c8a2e0-af22-f7bb-5d88-3a21e3079997</p>",
"status": "active"
}
],

View file

@ -0,0 +1,849 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using DevExpress.DataAccess.ConnectionParameters;
using DevExpress.DataAccess.Sql;
using DevExpress.Drawing;
using DevExpress.XtraPrinting;
using DevExpress.XtraReports.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Sozsoft.Platform;
using Sozsoft.Platform.DynamicData;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Enums;
using Sozsoft.Platform.ListForms;
using Sozsoft.Platform.Localization;
using Sozsoft.Platform.Queries;
namespace Sozsoft.Reports.PredefinedReports;
public class DynamicFormReport : XtraReport
{
public const string ReportName = nameof(DynamicFormReport);
private const string QueryName = "DynamicForm";
private const int HorizontalMargin = 35;
private const float RowHeight = 24;
private static IServiceProvider _serviceProvider;
public static void SetServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public static DynamicFormReport Create(IDictionary<string, string> parameters)
{
var report = new DynamicFormReport();
report.Build(parameters);
return report;
}
public DynamicFormReport()
{
}
private void Build(IDictionary<string, string> parameters)
{
if (_serviceProvider == null)
{
throw new InvalidOperationException("DynamicFormReport service provider is not configured.");
}
if (!parameters.TryGetValue("listFormCode", out var listFormCode) || string.IsNullOrWhiteSpace(listFormCode))
{
throw new ArgumentException("listFormCode parameter is required.");
}
if (!parameters.TryGetValue("id", out var id) || string.IsNullOrWhiteSpace(id))
{
throw new ArgumentException("id parameter is required.");
}
using var scope = _serviceProvider.CreateScope();
var authManager = scope.ServiceProvider.GetRequiredService<IListFormAuthorizationManager>();
var listFormManager = scope.ServiceProvider.GetRequiredService<IListFormManager>();
var listFormFieldManager = scope.ServiceProvider.GetRequiredService<IListFormFieldManager>();
var customizationManager = scope.ServiceProvider.GetRequiredService<IListFormCustomizationManager>();
var dynamicDataManager = scope.ServiceProvider.GetRequiredService<IDynamicDataManager>();
var selectQueryManager = scope.ServiceProvider.GetRequiredService<ISelectQueryManager>();
var defaultValueHelper = scope.ServiceProvider.GetRequiredService<DefaultValueHelper>();
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
if (!authManager.CanAccess(listFormCode).GetAwaiter().GetResult())
{
throw new UnauthorizedAccessException($"No read permission for list form '{listFormCode}'.");
}
var listForm = listFormManager.GetUserListForm(listFormCode).GetAwaiter().GetResult();
var fields = listFormFieldManager.GetUserListFormFields(listFormCode).GetAwaiter().GetResult()
.Where(x => x.CanRead)
.OrderBy(x => x.ListOrderNo ?? int.MaxValue)
.ThenBy(x => x.CaptionName)
.ToList();
var customizations = customizationManager.GetUsersServerCustomizations(listFormCode).GetAwaiter().GetResult();
var (dynamicDataRepository, connectionString, dataSourceType) = dynamicDataManager
.GetAsync(listForm.IsTenant, listForm.DataSourceCode)
.GetAwaiter()
.GetResult();
var queryParams = new QueryParameters
{
ListFormCode = listFormCode,
Filter = GetParameter(parameters, "filter") ?? CreateKeyFilter(listForm.KeyFieldName, id),
Take = 1,
Skip = 0
};
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
var reportFields = GetReportFields(listForm, fields, selectQueryManager, defaultValueHelper);
var selectQuery = ApplyLookupQueryColumns(
selectQueryManager.SelectQuery,
listForm,
reportFields,
defaultValueHelper);
selectQuery = ApplyQueryParameters(selectQuery, selectQueryManager.SelectQueryParameters);
var row = GetFirstRow(dynamicDataRepository.QueryAsync(selectQuery, connectionString).GetAwaiter().GetResult());
ConfigurePage();
ConfigureDataSource(selectQuery, connectionString, dataSourceType);
BuildLayout(listForm, reportFields, row, localizer);
RequestParameters = false;
}
private void ConfigurePage()
{
Margins = new System.Drawing.Printing.Margins(HorizontalMargin, HorizontalMargin, 35, 35);
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
Landscape = false;
Font = new DXFont("Arial", 8.25F);
}
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
{
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
dataSource.Queries.Add(new CustomSqlQuery(QueryName, selectQuery));
ComponentStorage.Add(dataSource);
DataSource = dataSource;
DataMember = QueryName;
}
private void BuildLayout(
ListForm listForm,
IReadOnlyList<FormReportField> fields,
IDictionary<string, object> row,
IStringLocalizer<PlatformResource> localizer)
{
Bands.Clear();
var topMargin = new TopMarginBand { HeightF = Margins.Top };
var bottomMargin = new BottomMarginBand { HeightF = 32 };
var reportHeader = new ReportHeaderBand { HeightF = 38 };
var detail = new DetailBand { HeightF = CalculateDetailHeight(fields, listForm, row) };
Bands.AddRange([topMargin, reportHeader, detail, bottomMargin]);
var pageWidth = PageWidth - Margins.Left - Margins.Right;
reportHeader.Controls.Add(new XRLabel
{
Text = GetLocalizedReportTitle(listForm, localizer),
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth, 30),
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
TextAlignment = TextAlignment.MiddleLeft
});
bottomMargin.Controls.Add(new XRPageInfo
{
PageInfo = PageInfo.DateTime,
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth / 2, 18),
Font = new DXFont("Arial", 7.5F),
TextAlignment = TextAlignment.MiddleLeft
});
bottomMargin.Controls.Add(new XRPageInfo
{
TextFormatString = "Sayfa {0} / {1}",
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 0, pageWidth / 2, 18),
Font = new DXFont("Arial", 8F),
TextAlignment = TextAlignment.MiddleRight
});
var y = 0F;
foreach (var group in GetFormGroups(listForm, fields))
{
if (!string.IsNullOrWhiteSpace(group.Caption))
{
detail.Controls.Add(new XRLabel
{
Text = Localize(localizer, group.Caption, group.Caption),
BoundsF = new System.Drawing.RectangleF(0, y, pageWidth, 22),
Font = new DXFont("Arial", 9F, DXFontStyle.Bold),
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
Padding = new PaddingInfo(5, 5, 2, 2),
TextAlignment = TextAlignment.MiddleLeft
});
y += 26;
}
foreach (var field in group.Fields)
{
var rowHeight = GetFieldRowHeight(field);
var labelWidth = pageWidth * 0.28F;
detail.Controls.Add(CreateCaptionLabel(field, localizer, 0, y, labelWidth, rowHeight));
detail.Controls.Add(CreateValueLabel(field, labelWidth, y, pageWidth - labelWidth, rowHeight));
y += rowHeight + 3;
}
y += 8;
}
foreach (var subForm in GetListSubForms(listForm, row))
{
detail.Controls.Add(new XRSubreport
{
BoundsF = new System.Drawing.RectangleF(0, y, pageWidth, 24),
ReportSource = DynamicGridReport.Create(CreateSubReportParameters(subForm.Form, subForm.Filter))
});
y += 28;
}
detail.HeightF = Math.Max(y, RowHeight);
}
private static XRLabel CreateCaptionLabel(
FormReportField field,
IStringLocalizer<PlatformResource> localizer,
float x,
float y,
float width,
float height)
{
return new XRLabel
{
Text = GetLocalizedFieldCaption(field.Field, localizer),
BoundsF = new System.Drawing.RectangleF(x, y, width, height),
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
BackColor = System.Drawing.Color.FromArgb(248, 250, 252),
Borders = BorderSide.All,
BorderWidth = 0.5F,
Padding = new PaddingInfo(5, 5, 2, 2),
TextAlignment = TextAlignment.MiddleLeft,
Multiline = true
};
}
private static XRLabel CreateValueLabel(FormReportField field, float x, float y, float width, float height)
{
var label = new XRLabel
{
BoundsF = new System.Drawing.RectangleF(x, y, width, height),
Font = new DXFont("Arial", 8F),
Borders = BorderSide.All,
BorderWidth = 0.5F,
Padding = new PaddingInfo(5, 5, 2, 2),
TextAlignment = ToTextAlignment(field.Field.Alignment, field.Field.SourceDbType),
Multiline = true,
CanGrow = true
};
label.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{field.BindingName}]"));
if (IsBooleanEditor(field) || field.StaticLookupValues?.Count > 0)
{
label.BeforePrint += (_, _) =>
{
var value = Convert.ToString(label.Text, CultureInfo.InvariantCulture);
if (IsBooleanEditor(field) && TryFormatBoolean(value, out var booleanText))
{
label.Text = booleanText;
}
else if (value != null && field.StaticLookupValues?.TryGetValue(value, out var displayText) == true)
{
label.Text = displayText;
}
};
}
return label;
}
private static float CalculateDetailHeight(
IReadOnlyList<FormReportField> fields,
ListForm listForm,
IDictionary<string, object> row)
{
var groupHeight = GetFormGroups(listForm, fields)
.Sum(group => (string.IsNullOrWhiteSpace(group.Caption) ? 0 : 26) +
group.Fields.Sum(field => GetFieldRowHeight(field) + 3) + 8);
var subFormHeight = GetListSubForms(listForm, row).Count() * 32;
return Math.Max(groupHeight + subFormHeight, RowHeight);
}
private static float GetFieldRowHeight(FormReportField field)
{
return field.EditorType is PlatformConsts.EditorTypes.dxTextArea or PlatformConsts.EditorTypes.dxHtmlEditor
? 48
: RowHeight;
}
private static List<FormReportField> GetReportFields(
ListForm listForm,
List<ListFormField> fields,
ISelectQueryManager selectQueryManager,
DefaultValueHelper defaultValueHelper)
{
var fieldsByName = fields
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
.Where(x => x.IsActive != false)
.ToDictionary(x => x.FieldName, StringComparer.OrdinalIgnoreCase);
var formItems = GetEditingFormItems(listForm)
.Where(x => !string.IsNullOrWhiteSpace(x.DataField))
.ToList();
var orderedFields = formItems.Count > 0
? formItems
.Where(x => fieldsByName.ContainsKey(x.DataField))
.Select(x => new FieldWithEditor(fieldsByName[x.DataField], x.EditorType2))
.ToList()
: fields
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
.Where(x => x.IsActive != false && x.Visible == true)
.Select(x => new FieldWithEditor(x, null))
.ToList();
return orderedFields
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.Field.FieldName))
.Select(x =>
{
var selectField = selectQueryManager.SelectFields.First(s => s.FieldName == x.Field.FieldName);
return CreateFormReportField(x.Field, selectField, x.EditorType, defaultValueHelper);
})
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
.ToList();
}
private static IEnumerable<FormGroup> GetFormGroups(ListForm listForm, IReadOnlyList<FormReportField> fields)
{
var fieldsByName = fields.ToDictionary(x => x.Field.FieldName, StringComparer.OrdinalIgnoreCase);
var groups = GetEditingFormGroups(listForm).ToList();
if (groups.Count == 0)
{
yield return new FormGroup(null, fields);
yield break;
}
var usedFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var group in groups)
{
var groupFields = (group.Items ?? [])
.OrderBy(x => x.Order)
.Where(x => !string.IsNullOrWhiteSpace(x.DataField) && fieldsByName.ContainsKey(x.DataField))
.Select(x => fieldsByName[x.DataField])
.ToList();
foreach (var field in groupFields)
{
usedFields.Add(field.Field.FieldName);
}
if (groupFields.Count > 0)
{
yield return new FormGroup(group.Caption, groupFields);
}
}
var remainingFields = fields
.Where(x => !usedFields.Contains(x.Field.FieldName))
.ToList();
if (remainingFields.Count > 0)
{
yield return new FormGroup(null, remainingFields);
}
}
private static IEnumerable<EditingFormDto> GetEditingFormGroups(ListForm listForm)
{
if (string.IsNullOrWhiteSpace(listForm.EditingFormJson))
{
return [];
}
try
{
return JsonSerializer.Deserialize<List<EditingFormDto>>(listForm.EditingFormJson) ?? [];
}
catch (JsonException)
{
return [];
}
}
private static IEnumerable<EditingFormItemDto> GetEditingFormItems(ListForm listForm)
{
return GetEditingFormGroups(listForm)
.OrderBy(x => x.Order)
.SelectMany(x => (x.Items ?? []).OrderBy(i => i.Order));
}
private static IEnumerable<(SubFormDto Form, string Filter)> GetListSubForms(
ListForm listForm,
IDictionary<string, object> row)
{
if (string.IsNullOrWhiteSpace(listForm.SubFormsJson) || row == null)
{
yield break;
}
List<SubFormDto> subForms;
try
{
subForms = JsonSerializer.Deserialize<List<SubFormDto>>(listForm.SubFormsJson) ?? [];
}
catch (JsonException)
{
yield break;
}
foreach (var subForm in subForms.Where(x => string.Equals(x.TabType, ListFormTypeEnum.List, StringComparison.OrdinalIgnoreCase)))
{
var relation = subForm.Relation?
.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.ParentFieldName) &&
!string.IsNullOrWhiteSpace(x.ChildFieldName) &&
TryGetRowValue(row, x.ParentFieldName, out var value) &&
value != null &&
value != DBNull.Value);
if (relation == null || !TryGetRowValue(row, relation.ParentFieldName, out var parentValue))
{
continue;
}
yield return (subForm, CreateFilter(relation.ChildFieldName, "=", parentValue));
}
}
private static Dictionary<string, string> CreateSubReportParameters(SubFormDto subForm, string filter)
{
return new Dictionary<string, string>
{
["listFormCode"] = subForm.Code,
["filter"] = filter
};
}
private static FormReportField CreateFormReportField(
ListFormField field,
SelectField selectField,
string editorType,
DefaultValueHelper defaultValueHelper)
{
var bindingName = GetReportFieldName(selectField);
var lookup = GetLookup(field);
if (lookup == null || selectField?.JoinOptions != null)
{
return new FormReportField(field, bindingName, editorType, null, null);
}
if (IsReportQueryableLookup(lookup))
{
return new FormReportField(field, GetLookupBindingName(field), editorType, lookup, null);
}
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
{
return new FormReportField(
field,
bindingName,
editorType,
lookup,
GetStaticLookupValues(lookup, defaultValueHelper));
}
return new FormReportField(field, bindingName, editorType, null, null);
}
private static string CreateKeyFilter(string keyFieldName, string id)
{
return CreateFilter(keyFieldName, "=", id);
}
private static string CreateFilter(string fieldName, string op, object value)
{
return JsonSerializer.Serialize(new object[] { fieldName, op, value });
}
private static IDictionary<string, object> GetFirstRow(IEnumerable<dynamic> rows)
{
var row = rows?.FirstOrDefault();
if (row is IDictionary<string, object> dictionary)
{
return dictionary;
}
return null;
}
private static bool TryGetRowValue(IDictionary<string, object> row, string name, out object value)
{
foreach (var item in row)
{
if (string.Equals(item.Key, name, StringComparison.OrdinalIgnoreCase))
{
value = item.Value;
return true;
}
}
value = null;
return false;
}
private static string ToDevExpressConnectionString(string connectionString, DataSourceTypeEnum dataSourceType)
{
if (string.IsNullOrWhiteSpace(connectionString) ||
connectionString.Contains("XpoProvider=", StringComparison.OrdinalIgnoreCase))
{
return connectionString;
}
var provider = dataSourceType switch
{
DataSourceTypeEnum.Mssql => "MSSqlServer",
DataSourceTypeEnum.Postgresql => "Postgres",
_ => null
};
return string.IsNullOrWhiteSpace(provider)
? connectionString
: $"XpoProvider={provider};{connectionString}";
}
private static string ApplyQueryParameters(string sql, Dictionary<string, object> parameters)
{
if (string.IsNullOrWhiteSpace(sql) || parameters == null || parameters.Count == 0)
{
return sql;
}
foreach (var parameter in parameters.OrderByDescending(x => x.Key.Length))
{
var key = parameter.Key.StartsWith("@") ? parameter.Key : "@" + parameter.Key;
sql = Regex.Replace(
sql,
Regex.Escape(key) + @"\b",
ToSqlLiteral(parameter.Value),
RegexOptions.CultureInvariant);
}
return sql;
}
private static string ToSqlLiteral(object value)
{
if (value == null || value == DBNull.Value)
{
return "NULL";
}
return value switch
{
string text => $"'{text.Replace("'", "''")}'",
Guid guid => $"'{guid}'",
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
bool boolean => boolean ? "1" : "0",
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
=> Convert.ToString(value, CultureInfo.InvariantCulture),
_ => $"'{value.ToString()?.Replace("'", "''")}'"
};
}
private static string GetReportFieldName(SelectField selectField)
{
if (selectField?.JoinOptions != null && !string.IsNullOrWhiteSpace(selectField.JoinOptions.FieldNameAlias))
{
return selectField.JoinOptions.FieldNameAlias;
}
return selectField?.FieldName;
}
private static LookupDto GetLookup(ListFormField field)
{
if (string.IsNullOrWhiteSpace(field.LookupJson))
{
return null;
}
try
{
return JsonSerializer.Deserialize<LookupDto>(field.LookupJson);
}
catch (JsonException)
{
return null;
}
}
private static Dictionary<string, string> GetStaticLookupValues(
LookupDto lookup,
DefaultValueHelper defaultValueHelper)
{
if (string.IsNullOrWhiteSpace(lookup.LookupQuery))
{
return null;
}
try
{
var lookupJson = defaultValueHelper.GetDefaultValue(lookup.LookupQuery);
using var document = JsonDocument.Parse(lookupJson);
if (document.RootElement.ValueKind != JsonValueKind.Array)
{
return null;
}
var valueExpr = lookup.ValueExpr ?? "Key";
var displayExpr = lookup.DisplayExpr ?? "Name";
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in document.RootElement.EnumerateArray())
{
if (!TryGetProperty(item, valueExpr, out var valueElement) ||
!TryGetProperty(item, displayExpr, out var displayElement))
{
continue;
}
var value = GetJsonScalar(valueElement);
if (value == null)
{
continue;
}
values[value] = GetJsonScalar(displayElement) ?? value;
}
return values.Count == 0 ? null : values;
}
catch (JsonException)
{
return null;
}
}
private static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement value)
{
foreach (var property in element.EnumerateObject())
{
if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
{
value = property.Value;
return true;
}
}
value = default;
return false;
}
private static string GetJsonScalar(JsonElement element)
{
return element.ValueKind switch
{
JsonValueKind.String => element.GetString(),
JsonValueKind.Number => element.GetRawText(),
JsonValueKind.True => bool.TrueString,
JsonValueKind.False => bool.FalseString,
JsonValueKind.Null => null,
_ => element.ToString()
};
}
private static string ApplyLookupQueryColumns(
string selectQuery,
ListForm listForm,
IReadOnlyList<FormReportField> reportFields,
DefaultValueHelper defaultValueHelper)
{
var lookupFields = reportFields
.Where(x => x.Lookup != null && IsReportQueryableLookup(x.Lookup))
.ToList();
if (lookupFields.Count == 0 || string.IsNullOrWhiteSpace(selectQuery))
{
return selectQuery;
}
var tableAlias = string.IsNullOrWhiteSpace(listForm.TableName)
? listForm.SelectCommand
: listForm.TableName;
var selectParts = new List<string>();
var joinParts = new List<string>();
foreach (var field in lookupFields)
{
var lookup = field.Lookup;
var alias = GetLookupAlias(field.Field);
var valueExpr = QuoteSqlIdentifier(lookup.ValueExpr ?? "Key");
var displayExpr = QuoteSqlIdentifier(lookup.DisplayExpr ?? "Name");
var lookupQuery = NormalizeLookupQuery(defaultValueHelper.GetDefaultValue(lookup.LookupQuery));
selectParts.Add($"{alias}.{displayExpr} AS {QuoteSqlIdentifier(field.BindingName)}");
joinParts.Add(
$"LEFT JOIN ({lookupQuery}) AS {alias} " +
$"ON {alias}.{valueExpr} = {QuoteSqlIdentifier(tableAlias)}.{QuoteSqlIdentifier(field.Field.FieldName)}");
}
var fromIndex = selectQuery.IndexOf(" FROM ", StringComparison.OrdinalIgnoreCase);
if (fromIndex < 0)
{
return selectQuery;
}
selectQuery = selectQuery.Insert(fromIndex, "," + string.Join(',', selectParts));
var joinInsertIndex = FindClauseIndex(selectQuery, " WHERE ");
if (joinInsertIndex < 0)
{
joinInsertIndex = FindClauseIndex(selectQuery, " ORDER BY ");
}
if (joinInsertIndex < 0)
{
return selectQuery + " " + string.Join(' ', joinParts);
}
return selectQuery.Insert(joinInsertIndex, " " + string.Join(' ', joinParts));
}
private static bool IsReportQueryableLookup(LookupDto lookup)
{
return lookup.DataSourceType == UiLookupDataSourceTypeEnum.Query &&
string.IsNullOrWhiteSpace(lookup.CascadeParentFields) &&
!string.IsNullOrWhiteSpace(lookup.LookupQuery) &&
!Regex.IsMatch(lookup.LookupQuery, @"@\s*param\d+", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private static int FindClauseIndex(string sql, string clause)
{
return sql.IndexOf(clause, StringComparison.OrdinalIgnoreCase);
}
private static string NormalizeLookupQuery(string lookupQuery)
{
var query = lookupQuery.Trim().TrimEnd(';');
return Regex.Replace(
query,
@"\s+ORDER\s+BY\s+[\s\S]*$",
string.Empty,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private static string GetLookupBindingName(ListFormField field)
{
return $"{field.FieldName}__LookupText";
}
private static string GetLookupAlias(ListFormField field)
{
return QuoteSqlIdentifier($"Lookup_{Regex.Replace(field.FieldName, @"\W+", "_")}");
}
private static string QuoteSqlIdentifier(string identifier)
{
return $"\"{identifier.Replace("\"", "\"\"")}\"";
}
private static string GetLocalizedReportTitle(ListForm listForm, IStringLocalizer<PlatformResource> localizer)
{
var title = string.IsNullOrWhiteSpace(listForm.Title) ? listForm.Name : listForm.Title;
return Localize(localizer, title, listForm.ListFormCode);
}
private static string GetLocalizedFieldCaption(ListFormField field, IStringLocalizer<PlatformResource> localizer)
{
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
return Localize(localizer, caption, field.FieldName);
}
private static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
{
if (string.IsNullOrWhiteSpace(key))
{
return fallback;
}
var localized = localizer[key];
return localized.ResourceNotFound || string.IsNullOrWhiteSpace(localized.Value)
? key
: localized.Value;
}
private static TextAlignment ToTextAlignment(string alignment, DbType dbType)
{
return alignment?.ToLowerInvariant() switch
{
"center" => TextAlignment.MiddleCenter,
"right" => TextAlignment.MiddleRight,
"left" => TextAlignment.MiddleLeft,
_ => IsNumeric(dbType) ? TextAlignment.MiddleRight : TextAlignment.MiddleLeft
};
}
private static bool IsNumeric(DbType dbType)
{
return dbType is DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32
or DbType.Int64 or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64;
}
private static bool IsBooleanEditor(FormReportField field)
{
return field.EditorType is PlatformConsts.EditorTypes.dxCheckBox or PlatformConsts.EditorTypes.dxSwitch ||
field.Field.SourceDbType == DbType.Boolean;
}
private static bool TryFormatBoolean(string value, out string text)
{
if (bool.TryParse(value, out var boolean))
{
text = boolean ? "Evet" : "Hayır";
return true;
}
if (value == "1" || string.Equals(value, "true", StringComparison.OrdinalIgnoreCase))
{
text = "Evet";
return true;
}
if (value == "0" || string.Equals(value, "false", StringComparison.OrdinalIgnoreCase))
{
text = "Hayır";
return true;
}
text = null;
return false;
}
private static string GetParameter(IDictionary<string, string> parameters, string name)
{
return parameters.TryGetValue(name, out var value) ? value : null;
}
private sealed record FieldWithEditor(ListFormField Field, string EditorType);
private sealed record FormGroup(string Caption, IReadOnlyList<FormReportField> Fields);
private sealed record FormReportField(
ListFormField Field,
string BindingName,
string EditorType,
LookupDto Lookup,
Dictionary<string, string> StaticLookupValues);
}

View file

@ -21,10 +21,10 @@ using Sozsoft.Platform.Queries;
namespace Sozsoft.Reports.PredefinedReports;
public class DynamicListFormReport : XtraReport
public class DynamicGridReport : XtraReport
{
public const string ReportName = nameof(DynamicListFormReport);
private const string QueryName = "DynamicList";
public const string ReportName = nameof(DynamicGridReport);
private const string QueryName = "DynamicGrid";
private const int A4PortraitWidth = 827;
private const int HorizontalMargin = 35;
private const int LandscapeColumnThreshold = 5;
@ -35,14 +35,14 @@ public class DynamicListFormReport : XtraReport
_serviceProvider = serviceProvider;
}
public static DynamicListFormReport Create(IDictionary<string, string> parameters)
public static DynamicGridReport Create(IDictionary<string, string> parameters)
{
var report = new DynamicListFormReport();
var report = new DynamicGridReport();
report.Build(parameters);
return report;
}
public DynamicListFormReport()
public DynamicGridReport()
{
}

View file

@ -3,7 +3,7 @@ using Sozsoft.Platform.Enums;
namespace Sozsoft.Reports.PredefinedReports
{
partial class TemplateReport
partial class DynamicReport
{
/// <summary>
/// Required designer variable.
@ -36,7 +36,7 @@ namespace Sozsoft.Reports.PredefinedReports
DevExpress.DataAccess.Sql.Column column1 = new DevExpress.DataAccess.Sql.Column();
DevExpress.DataAccess.Sql.ColumnExpression columnExpression1 = new DevExpress.DataAccess.Sql.ColumnExpression();
DevExpress.DataAccess.Sql.Table table1 = new DevExpress.DataAccess.Sql.Table();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TemplateReport));
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DynamicReport));
this.TopMargin = new DevExpress.XtraReports.UI.TopMarginBand();
this.BottomMargin = new DevExpress.XtraReports.UI.BottomMarginBand();
this.pageInfo1 = new DevExpress.XtraReports.UI.XRPageInfo();

View file

@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
namespace Sozsoft.Reports.PredefinedReports;
public partial class TemplateReport : XtraReport
public partial class DynamicReport : XtraReport
{
private Dictionary<string, string> _parameters;
private static IServiceProvider _serviceProvider;
@ -22,7 +22,7 @@ public partial class TemplateReport : XtraReport
_serviceProvider = serviceProvider;
}
public TemplateReport()
public DynamicReport()
{
InitializeComponent();

View file

@ -1,264 +0,0 @@
using Sozsoft.Platform.Enums;
namespace Sozsoft.Reports.PredefinedReports
{
partial class TestReport
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
DevExpress.DataAccess.Sql.SelectQuery selectQuery1 = new DevExpress.DataAccess.Sql.SelectQuery();
DevExpress.DataAccess.Sql.Column column1 = new DevExpress.DataAccess.Sql.Column();
DevExpress.DataAccess.Sql.ColumnExpression columnExpression1 = new DevExpress.DataAccess.Sql.ColumnExpression();
DevExpress.DataAccess.Sql.Table table1 = new DevExpress.DataAccess.Sql.Table();
DevExpress.DataAccess.Sql.Column column2 = new DevExpress.DataAccess.Sql.Column();
DevExpress.DataAccess.Sql.ColumnExpression columnExpression2 = new DevExpress.DataAccess.Sql.ColumnExpression();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TemplateReport));
this.TopMargin = new DevExpress.XtraReports.UI.TopMarginBand();
this.BottomMargin = new DevExpress.XtraReports.UI.BottomMarginBand();
this.pageInfo1 = new DevExpress.XtraReports.UI.XRPageInfo();
this.pageInfo2 = new DevExpress.XtraReports.UI.XRPageInfo();
this.ReportHeader = new DevExpress.XtraReports.UI.ReportHeaderBand();
this.label1 = new DevExpress.XtraReports.UI.XRLabel();
this.Detail = new DevExpress.XtraReports.UI.DetailBand();
this.table2 = new DevExpress.XtraReports.UI.XRTable();
this.tableRow1 = new DevExpress.XtraReports.UI.XRTableRow();
this.tableCell1 = new DevExpress.XtraReports.UI.XRTableCell();
this.tableCell2 = new DevExpress.XtraReports.UI.XRTableCell();
this.sqlDataSource1 = new DevExpress.DataAccess.Sql.SqlDataSource(this.components);
this.Title = new DevExpress.XtraReports.UI.XRControlStyle();
this.DetailCaption1 = new DevExpress.XtraReports.UI.XRControlStyle();
this.DetailData1 = new DevExpress.XtraReports.UI.XRControlStyle();
this.DetailData3_Odd = new DevExpress.XtraReports.UI.XRControlStyle();
this.PageInfo = new DevExpress.XtraReports.UI.XRControlStyle();
((System.ComponentModel.ISupportInitialize)(this.table2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
//
// TopMargin
//
this.TopMargin.Name = "TopMargin";
//
// BottomMargin
//
this.BottomMargin.Controls.AddRange(new DevExpress.XtraReports.UI.XRControl[] {
this.pageInfo1,
this.pageInfo2});
this.BottomMargin.Name = "BottomMargin";
//
// pageInfo1
//
this.pageInfo1.LocationFloat = new DevExpress.Utils.PointFloat(0F, 0F);
this.pageInfo1.Name = "pageInfo1";
this.pageInfo1.PageInfo = DevExpress.XtraPrinting.PageInfo.DateTime;
this.pageInfo1.SizeF = new System.Drawing.SizeF(325F, 23F);
this.pageInfo1.StyleName = "PageInfo";
//
// pageInfo2
//
this.pageInfo2.LocationFloat = new DevExpress.Utils.PointFloat(325F, 0F);
this.pageInfo2.Name = "pageInfo2";
this.pageInfo2.SizeF = new System.Drawing.SizeF(325F, 23F);
this.pageInfo2.StyleName = "PageInfo";
this.pageInfo2.TextAlignment = DevExpress.XtraPrinting.TextAlignment.TopRight;
this.pageInfo2.TextFormatString = "Page {0} of {1}";
//
// ReportHeader
//
this.ReportHeader.Controls.AddRange(new DevExpress.XtraReports.UI.XRControl[] {
this.label1});
this.ReportHeader.HeightF = 60F;
this.ReportHeader.Name = "ReportHeader";
//
// label1
//
this.label1.LocationFloat = new DevExpress.Utils.PointFloat(0F, 0F);
this.label1.Name = "label1";
this.label1.SizeF = new System.Drawing.SizeF(650F, 24.19433F);
this.label1.StyleName = "Title";
this.label1.Text = "Sector Report";
//
// Detail
//
this.Detail.Controls.AddRange(new DevExpress.XtraReports.UI.XRControl[] {
this.table2});
this.Detail.HeightF = 25F;
this.Detail.Name = "Detail";
//
// table2
//
this.table2.LocationFloat = new DevExpress.Utils.PointFloat(0F, 0F);
this.table2.Name = "table2";
this.table2.OddStyleName = "DetailData3_Odd";
this.table2.Rows.AddRange(new DevExpress.XtraReports.UI.XRTableRow[] {
this.tableRow1});
this.table2.SizeF = new System.Drawing.SizeF(650F, 25F);
//
// tableRow1
//
this.tableRow1.Cells.AddRange(new DevExpress.XtraReports.UI.XRTableCell[] {
this.tableCell1,
this.tableCell2});
this.tableRow1.Name = "tableRow1";
this.tableRow1.Weight = 1D;
//
// tableCell1
//
this.tableCell1.Borders = DevExpress.XtraPrinting.BorderSide.None;
this.tableCell1.ExpressionBindings.AddRange(new DevExpress.XtraReports.UI.ExpressionBinding[] {
new DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Id]")});
this.tableCell1.Name = "tableCell1";
this.tableCell1.StyleName = "DetailData1";
this.tableCell1.StylePriority.UseBorders = false;
this.tableCell1.Text = "Id";
this.tableCell1.Weight = 0.3D;
//
// tableCell2
//
this.tableCell2.ExpressionBindings.AddRange(new DevExpress.XtraReports.UI.ExpressionBinding[] {
new DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Name]")});
this.tableCell2.Name = "tableCell2";
this.tableCell2.StyleName = "DetailData1";
this.tableCell2.Text = "Name";
this.tableCell2.Weight = 0.7D;
//
// sqlDataSource1
//
this.sqlDataSource1.ConnectionName = "SqlServer";
this.sqlDataSource1.Name = "sqlDataSource1";
columnExpression1.ColumnName = "Id";
table1.Name = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Sector));
columnExpression1.Table = table1;
column1.Expression = columnExpression1;
columnExpression2.ColumnName = "Name";
columnExpression2.Table = table1;
column2.Expression = columnExpression2;
selectQuery1.Columns.Add(column1);
selectQuery1.Columns.Add(column2);
selectQuery1.Name = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Sector));
selectQuery1.Tables.Add(table1);
this.sqlDataSource1.Queries.AddRange(new DevExpress.DataAccess.Sql.SqlQuery[] {
selectQuery1});
this.sqlDataSource1.ResultSchemaSerializable = resources.GetString("sqlDataSource1.ResultSchemaSerializable");
//
// Title
//
this.Title.BackColor = System.Drawing.Color.Transparent;
this.Title.BorderColor = System.Drawing.Color.Black;
this.Title.Borders = DevExpress.XtraPrinting.BorderSide.None;
this.Title.BorderWidth = 1F;
this.Title.Font = new DevExpress.Drawing.DXFont("Arial", 14.25F);
this.Title.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(70)))), ((int)(((byte)(80)))));
this.Title.Name = "Title";
this.Title.Padding = new DevExpress.XtraPrinting.PaddingInfo(6, 6, 0, 0, 100F);
//
// DetailCaption1
//
this.DetailCaption1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(181)))), ((int)(((byte)(211)))), ((int)(((byte)(142)))));
this.DetailCaption1.BorderColor = System.Drawing.Color.White;
this.DetailCaption1.Borders = DevExpress.XtraPrinting.BorderSide.Left;
this.DetailCaption1.BorderWidth = 2F;
this.DetailCaption1.Font = new DevExpress.Drawing.DXFont("Arial", 8.25F, DevExpress.Drawing.DXFontStyle.Bold);
this.DetailCaption1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(70)))), ((int)(((byte)(80)))));
this.DetailCaption1.Name = "DetailCaption1";
this.DetailCaption1.Padding = new DevExpress.XtraPrinting.PaddingInfo(6, 6, 0, 0, 100F);
this.DetailCaption1.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleLeft;
//
// DetailData1
//
this.DetailData1.BorderColor = System.Drawing.Color.Transparent;
this.DetailData1.Borders = DevExpress.XtraPrinting.BorderSide.Left;
this.DetailData1.BorderWidth = 2F;
this.DetailData1.Font = new DevExpress.Drawing.DXFont("Arial", 8.25F);
this.DetailData1.ForeColor = System.Drawing.Color.Black;
this.DetailData1.Name = "DetailData1";
this.DetailData1.Padding = new DevExpress.XtraPrinting.PaddingInfo(6, 6, 0, 0, 100F);
this.DetailData1.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleLeft;
//
// DetailData3_Odd
//
this.DetailData3_Odd.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(245)))), ((int)(((byte)(248)))));
this.DetailData3_Odd.BorderColor = System.Drawing.Color.Transparent;
this.DetailData3_Odd.Borders = DevExpress.XtraPrinting.BorderSide.None;
this.DetailData3_Odd.BorderWidth = 1F;
this.DetailData3_Odd.Font = new DevExpress.Drawing.DXFont("Arial", 8.25F);
this.DetailData3_Odd.ForeColor = System.Drawing.Color.Black;
this.DetailData3_Odd.Name = "DetailData3_Odd";
this.DetailData3_Odd.Padding = new DevExpress.XtraPrinting.PaddingInfo(6, 6, 0, 0, 100F);
this.DetailData3_Odd.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleLeft;
//
// PageInfo
//
this.PageInfo.Font = new DevExpress.Drawing.DXFont("Arial", 8.25F, DevExpress.Drawing.DXFontStyle.Bold);
this.PageInfo.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(70)))), ((int)(((byte)(80)))));
this.PageInfo.Name = "PageInfo";
this.PageInfo.Padding = new DevExpress.XtraPrinting.PaddingInfo(6, 6, 0, 0, 100F);
//
// TestReport
//
this.Bands.AddRange(new DevExpress.XtraReports.UI.Band[] {
this.TopMargin,
this.BottomMargin,
this.ReportHeader,
this.Detail});
this.ComponentStorage.AddRange(new System.ComponentModel.IComponent[] {
this.sqlDataSource1});
this.DataMember = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Sector));
this.DataSource = this.sqlDataSource1;
this.Font = new DevExpress.Drawing.DXFont("Arial", 9.75F);
this.StyleSheet.AddRange(new DevExpress.XtraReports.UI.XRControlStyle[] {
this.Title,
this.DetailCaption1,
this.DetailData1,
this.DetailData3_Odd,
this.PageInfo});
this.Version = "22.2";
((System.ComponentModel.ISupportInitialize)(this.table2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this)).EndInit();
}
#endregion
private DevExpress.XtraReports.UI.TopMarginBand TopMargin;
private DevExpress.XtraReports.UI.BottomMarginBand BottomMargin;
private DevExpress.XtraReports.UI.XRPageInfo pageInfo1;
private DevExpress.XtraReports.UI.XRPageInfo pageInfo2;
private DevExpress.XtraReports.UI.ReportHeaderBand ReportHeader;
private DevExpress.XtraReports.UI.XRLabel label1;
private DevExpress.XtraReports.UI.DetailBand Detail;
private DevExpress.XtraReports.UI.XRTable table2;
private DevExpress.XtraReports.UI.XRTableRow tableRow1;
private DevExpress.XtraReports.UI.XRTableCell tableCell1;
private DevExpress.XtraReports.UI.XRTableCell tableCell2;
private DevExpress.DataAccess.Sql.SqlDataSource sqlDataSource1;
private DevExpress.XtraReports.UI.XRControlStyle Title;
private DevExpress.XtraReports.UI.XRControlStyle DetailCaption1;
private DevExpress.XtraReports.UI.XRControlStyle DetailData1;
private DevExpress.XtraReports.UI.XRControlStyle DetailData3_Odd;
private DevExpress.XtraReports.UI.XRControlStyle PageInfo;
}
}

View file

@ -1,9 +0,0 @@
namespace Sozsoft.Reports.PredefinedReports;
public partial class TestReport : DevExpress.XtraReports.UI.XtraReport
{
public TestReport()
{
InitializeComponent();
}
}

View file

@ -1,123 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="sqlDataSource1.ResultSchemaSerializable" xml:space="preserve">
<value>PERhdGFTZXQgTmFtZT0ic3FsRGF0YVNvdXJjZTEiPjxWaWV3IE5hbWU9IkNhdGVnb3JpZXMiPjxGaWVsZCBOYW1lPSJDYXRlZ29yeUlEIiBUeXBlPSJJbnQ2NCIgLz48RmllbGQgTmFtZT0iQ2F0ZWdvcnlOYW1lIiBUeXBlPSJTdHJpbmciIC8+PEZpZWxkIE5hbWU9IkRlc2NyaXB0aW9uIiBUeXBlPSJTdHJpbmciIC8+PEZpZWxkIE5hbWU9IlBpY3R1cmUiIFR5cGU9IkJ5dGVBcnJheSIgLz48RmllbGQgTmFtZT0iSWNvbjE3IiBUeXBlPSJCeXRlQXJyYXkiIC8+PEZpZWxkIE5hbWU9Ikljb24yNSIgVHlwZT0iQnl0ZUFycmF5IiAvPjwvVmlldz48L0RhdGFTZXQ+</value>
</data>
</root>

View file

@ -19,9 +19,10 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
{
_serviceProvider = serviceProvider;
// TemplateReport için ServiceProvider'ı set et
Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicListFormReport.SetServiceProvider(serviceProvider);
// DynamicReport için ServiceProvider'ı set et
Sozsoft.Reports.PredefinedReports.DynamicReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicGridReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicFormReport.SetServiceProvider(serviceProvider);
if (!Directory.Exists(reportDirectory))
{
@ -33,9 +34,10 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
{
_serviceProvider = serviceProvider;
// TemplateReport için ServiceProvider'ı set et
Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicListFormReport.SetServiceProvider(serviceProvider);
// DynamicReport için ServiceProvider'ı set et
Sozsoft.Reports.PredefinedReports.DynamicReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicGridReport.SetServiceProvider(serviceProvider);
Sozsoft.Reports.PredefinedReports.DynamicFormReport.SetServiceProvider(serviceProvider);
if (!Directory.Exists(reportDirectory))
{
@ -81,10 +83,20 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
try
{
var (reportName, parameters) = ParseReportUrl(url);
if (reportName == Sozsoft.Reports.PredefinedReports.DynamicListFormReport.ReportName)
if (IsDynamicGridReport(reportName))
{
var report = Sozsoft.Reports.PredefinedReports.DynamicListFormReport.Create(parameters);
XtraReport report = HasParameter(parameters, "id")
? Sozsoft.Reports.PredefinedReports.DynamicFormReport.Create(parameters)
: Sozsoft.Reports.PredefinedReports.DynamicGridReport.Create(parameters);
using MemoryStream ms = new MemoryStream();
report.SaveLayoutToXml(ms);
return ms.ToArray();
}
if (IsDynamicFormReport(reportName))
{
var report = Sozsoft.Reports.PredefinedReports.DynamicFormReport.Create(parameters);
using MemoryStream ms = new MemoryStream();
report.SaveLayoutToXml(ms);
return ms.ToArray();
@ -171,7 +183,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
{
var decodedUrl = DecodeRepeatedly(url);
var reportName = decodedUrl;
var parameters = new Dictionary<string, string>();
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var queryIndex = decodedUrl.IndexOf('?');
if (queryIndex < 0)
@ -195,6 +207,23 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
return (reportName, parameters);
}
private static bool IsDynamicGridReport(string reportName)
{
return string.Equals(reportName, "DynamicGrid", StringComparison.OrdinalIgnoreCase) ||
string.Equals(reportName, Sozsoft.Reports.PredefinedReports.DynamicGridReport.ReportName, StringComparison.OrdinalIgnoreCase);
}
private static bool IsDynamicFormReport(string reportName)
{
return string.Equals(reportName, "DynamicForm", StringComparison.OrdinalIgnoreCase) ||
string.Equals(reportName, Sozsoft.Reports.PredefinedReports.DynamicFormReport.ReportName, StringComparison.OrdinalIgnoreCase);
}
private static bool HasParameter(Dictionary<string, string> parameters, string name)
{
return parameters.TryGetValue(name, out var value) && !string.IsNullOrWhiteSpace(value);
}
private static string DecodeRepeatedly(string value)
{
var decoded = value;

View file

@ -84,12 +84,17 @@ const loadViewerCss = (isDarkMode: boolean) => {
const DevexpressReportViewer: React.FC = () => {
const { translate } = useLocalization()
const { id } = useParams<{ id: string }>()
const { id, listFormCode, recordId } = useParams<{
id: string
listFormCode: string
recordId: string
}>()
const location = useLocation()
const token = useStoreState((state) => state.auth.session.token)
const themeMode = useStoreState((state) => state.theme.mode)
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
const isDarkMode = themeMode === MODE_DARK
const formRecordId = recordId ?? (listFormCode ? id : undefined)
useEffect(() => {
loadViewerCss(isDarkMode)
@ -116,11 +121,24 @@ const DevexpressReportViewer: React.FC = () => {
}, [token])
const reportUrlWithParams = useMemo(() => {
const searchParams = new URLSearchParams(location.search)
if (listFormCode) {
searchParams.set('listFormCode', listFormCode)
if (formRecordId) {
searchParams.set('id', formRecordId)
return `DynamicFormReport?${searchParams.toString()}`
}
return `DynamicListFormReport?${searchParams.toString()}`
}
if (!id) return ''
return location.search ? `${id}${location.search}` : id
}, [id, location.search])
}, [id, listFormCode, formRecordId, location.search])
if (!id) return null
if (!reportUrlWithParams) return null
return (
<Container className={getViewerClassName(isDarkMode)}>