Devexpress Raporların Culture Uygulaması
This commit is contained in:
parent
ecb32a502a
commit
a321e90a53
6 changed files with 455 additions and 72 deletions
|
|
@ -29,6 +29,7 @@ public class DynamicFormReport : XtraReport
|
||||||
private const string QueryName = "DynamicForm";
|
private const string QueryName = "DynamicForm";
|
||||||
private const int HorizontalMargin = 35;
|
private const int HorizontalMargin = 35;
|
||||||
private const float RowHeight = 24;
|
private const float RowHeight = 24;
|
||||||
|
private const float ColumnGap = 10;
|
||||||
private static readonly System.Drawing.Color FieldBorderColor = System.Drawing.Color.FromArgb(226, 232, 240);
|
private static readonly System.Drawing.Color FieldBorderColor = System.Drawing.Color.FromArgb(226, 232, 240);
|
||||||
private static IServiceProvider _serviceProvider;
|
private static IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
|
@ -208,19 +209,28 @@ public class DynamicFormReport : XtraReport
|
||||||
y += 26;
|
y += 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var field in group.Fields)
|
var columnCount = GetSafeColumnCount(group.ColumnCount);
|
||||||
|
var cellWidth = GetCellWidth(pageWidth, columnCount);
|
||||||
|
foreach (var rowFields in CreateLayoutRows(group.Fields, columnCount))
|
||||||
{
|
{
|
||||||
var rowHeight = GetFieldRowHeight(field);
|
var rowHeight = rowFields.Max(x => GetFieldRowHeight(x.Field));
|
||||||
var labelWidth = pageWidth * 0.28F;
|
foreach (var item in rowFields)
|
||||||
detail.Controls.Add(CreateCaptionLabel(field, localizer, 0, y, labelWidth, rowHeight));
|
|
||||||
if (field.IsImage)
|
|
||||||
{
|
{
|
||||||
detail.Controls.Add(CreateImageValuePanel(field, row, imageBaseUrl, labelWidth, y, pageWidth - labelWidth, rowHeight));
|
var x = item.ColumnIndex * (cellWidth + ColumnGap);
|
||||||
}
|
var width = cellWidth * item.ColumnSpan + ColumnGap * (item.ColumnSpan - 1);
|
||||||
else
|
var labelWidth = Math.Min(width * 0.36F, 110F);
|
||||||
{
|
|
||||||
detail.Controls.Add(CreateValueLabel(field, row, labelWidth, y, pageWidth - labelWidth, rowHeight));
|
detail.Controls.Add(CreateCaptionLabel(item.Field, localizer, x, y, labelWidth, rowHeight));
|
||||||
|
if (item.Field.IsImage)
|
||||||
|
{
|
||||||
|
detail.Controls.Add(CreateImageValuePanel(item.Field, row, imageBaseUrl, x + labelWidth, y, width - labelWidth, rowHeight));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
detail.Controls.Add(CreateValueLabel(item.Field, row, x + labelWidth, y, width - labelWidth, rowHeight));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
y += rowHeight + 3;
|
y += rowHeight + 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,19 +427,19 @@ public class DynamicFormReport : XtraReport
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = Convert.ToString(rawValue, CultureInfo.InvariantCulture);
|
var value = Convert.ToString(rawValue, CultureInfo.InvariantCulture);
|
||||||
if (IsBooleanEditor(field) && TryFormatBoolean(value, out var booleanText))
|
|
||||||
{
|
|
||||||
text = booleanText;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null && field.StaticLookupValues?.TryGetValue(value, out var displayText) == true)
|
if (value != null && field.StaticLookupValues?.TryGetValue(value, out var displayText) == true)
|
||||||
{
|
{
|
||||||
text = displayText;
|
text = displayText;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
text = value ?? string.Empty;
|
if (DynamicReportImageHelper.TryFormatReportValue(field.Field, rawValue, field.EditorType, out var formattedValue))
|
||||||
|
{
|
||||||
|
text = formattedValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = Convert.ToString(rawValue, CultureInfo.CurrentCulture) ?? string.Empty;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,12 +449,68 @@ public class DynamicFormReport : XtraReport
|
||||||
{
|
{
|
||||||
var groupHeight = groups
|
var groupHeight = groups
|
||||||
.Sum(group => (string.IsNullOrWhiteSpace(group.Caption) ? 0 : 26) +
|
.Sum(group => (string.IsNullOrWhiteSpace(group.Caption) ? 0 : 26) +
|
||||||
group.Fields.Sum(field => GetFieldRowHeight(field) + 3) + 8);
|
CalculateGroupFieldsHeight(group) + 8);
|
||||||
|
|
||||||
var subFormHeight = subFormCount * 32;
|
var subFormHeight = subFormCount * 32;
|
||||||
return Math.Max(groupHeight + subFormHeight, RowHeight);
|
return Math.Max(groupHeight + subFormHeight, RowHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static float CalculateGroupFieldsHeight(FormGroup group)
|
||||||
|
{
|
||||||
|
var columnCount = GetSafeColumnCount(group.ColumnCount);
|
||||||
|
return CreateLayoutRows(group.Fields, columnCount)
|
||||||
|
.Sum(row => row.Max(x => GetFieldRowHeight(x.Field)) + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float GetCellWidth(float pageWidth, int columnCount)
|
||||||
|
{
|
||||||
|
return (pageWidth - ColumnGap * (columnCount - 1)) / columnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetSafeColumnCount(int columnCount)
|
||||||
|
{
|
||||||
|
return Math.Max(columnCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetSafeColumnSpan(int columnSpan, int columnCount)
|
||||||
|
{
|
||||||
|
return Math.Clamp(columnSpan <= 0 ? 1 : columnSpan, 1, columnCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<IReadOnlyList<FormLayoutItem>> CreateLayoutRows(
|
||||||
|
IReadOnlyList<FormReportField> fields,
|
||||||
|
int columnCount)
|
||||||
|
{
|
||||||
|
var row = new List<FormLayoutItem>();
|
||||||
|
var currentColumn = 0;
|
||||||
|
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
var columnSpan = GetSafeColumnSpan(field.ColumnSpan, columnCount);
|
||||||
|
if (currentColumn > 0 && currentColumn + columnSpan > columnCount)
|
||||||
|
{
|
||||||
|
yield return row;
|
||||||
|
row = [];
|
||||||
|
currentColumn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.Add(new FormLayoutItem(field, currentColumn, columnSpan));
|
||||||
|
currentColumn += columnSpan;
|
||||||
|
|
||||||
|
if (currentColumn >= columnCount)
|
||||||
|
{
|
||||||
|
yield return row;
|
||||||
|
row = [];
|
||||||
|
currentColumn = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.Count > 0)
|
||||||
|
{
|
||||||
|
yield return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static float GetFieldRowHeight(FormReportField field)
|
private static float GetFieldRowHeight(FormReportField field)
|
||||||
{
|
{
|
||||||
if (field.IsImage)
|
if (field.IsImage)
|
||||||
|
|
@ -475,12 +541,12 @@ public class DynamicFormReport : XtraReport
|
||||||
var orderedFields = formItems.Count > 0
|
var orderedFields = formItems.Count > 0
|
||||||
? formItems
|
? formItems
|
||||||
.Where(x => fieldsByName.ContainsKey(x.DataField))
|
.Where(x => fieldsByName.ContainsKey(x.DataField))
|
||||||
.Select(x => new FieldWithEditor(fieldsByName[x.DataField], x.EditorType2))
|
.Select(x => new FieldWithEditor(fieldsByName[x.DataField], x.EditorType2, x.ColSpan))
|
||||||
.ToList()
|
.ToList()
|
||||||
: fields
|
: fields
|
||||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
|
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
|
||||||
.Where(x => x.IsActive != false && x.Visible == true)
|
.Where(x => x.IsActive != false && x.Visible == true)
|
||||||
.Select(x => new FieldWithEditor(x, null))
|
.Select(x => new FieldWithEditor(x, null, 1))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
||||||
|
|
@ -489,7 +555,7 @@ public class DynamicFormReport : XtraReport
|
||||||
.Select(x =>
|
.Select(x =>
|
||||||
{
|
{
|
||||||
var selectField = selectFieldsByName[x.Field.FieldName];
|
var selectField = selectFieldsByName[x.Field.FieldName];
|
||||||
return CreateFormReportField(x.Field, selectField, x.EditorType, defaultValueHelper);
|
return CreateFormReportField(x.Field, selectField, x.EditorType, x.ColumnSpan, defaultValueHelper);
|
||||||
})
|
})
|
||||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
@ -501,7 +567,7 @@ public class DynamicFormReport : XtraReport
|
||||||
var groups = GetEditingFormGroups(listForm).ToList();
|
var groups = GetEditingFormGroups(listForm).ToList();
|
||||||
if (groups.Count == 0)
|
if (groups.Count == 0)
|
||||||
{
|
{
|
||||||
yield return new FormGroup(null, fields);
|
yield return new FormGroup(null, 1, fields);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,7 +587,7 @@ public class DynamicFormReport : XtraReport
|
||||||
|
|
||||||
if (groupFields.Count > 0)
|
if (groupFields.Count > 0)
|
||||||
{
|
{
|
||||||
yield return new FormGroup(group.Caption, groupFields);
|
yield return new FormGroup(group.Caption, group.ColCount, groupFields);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -530,7 +596,7 @@ public class DynamicFormReport : XtraReport
|
||||||
.ToList();
|
.ToList();
|
||||||
if (remainingFields.Count > 0)
|
if (remainingFields.Count > 0)
|
||||||
{
|
{
|
||||||
yield return new FormGroup(null, remainingFields);
|
yield return new FormGroup(null, 1, remainingFields);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,6 +675,7 @@ public class DynamicFormReport : XtraReport
|
||||||
ListFormField field,
|
ListFormField field,
|
||||||
SelectField selectField,
|
SelectField selectField,
|
||||||
string editorType,
|
string editorType,
|
||||||
|
int columnSpan,
|
||||||
DefaultValueHelper defaultValueHelper)
|
DefaultValueHelper defaultValueHelper)
|
||||||
{
|
{
|
||||||
var bindingName = DynamicReportImageHelper.GetReportFieldName(selectField);
|
var bindingName = DynamicReportImageHelper.GetReportFieldName(selectField);
|
||||||
|
|
@ -616,12 +683,12 @@ public class DynamicFormReport : XtraReport
|
||||||
|
|
||||||
if (lookup == null || selectField?.JoinOptions != null)
|
if (lookup == null || selectField?.JoinOptions != null)
|
||||||
{
|
{
|
||||||
return new FormReportField(field, bindingName, editorType, null, null, DynamicReportImageHelper.IsImageEditor(editorType));
|
return new FormReportField(field, bindingName, editorType, columnSpan, null, null, DynamicReportImageHelper.IsImageEditor(editorType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DynamicReportSqlBuilder.IsReportQueryableLookup(lookup))
|
if (DynamicReportSqlBuilder.IsReportQueryableLookup(lookup))
|
||||||
{
|
{
|
||||||
return new FormReportField(field, DynamicReportSqlBuilder.GetLookupBindingName(field), editorType, lookup, null, false);
|
return new FormReportField(field, DynamicReportSqlBuilder.GetLookupBindingName(field), editorType, columnSpan, lookup, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
|
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
|
||||||
|
|
@ -631,12 +698,13 @@ public class DynamicFormReport : XtraReport
|
||||||
field,
|
field,
|
||||||
staticLookupValues?.Count > 0 ? DynamicReportSqlBuilder.GetLookupBindingName(field) : bindingName,
|
staticLookupValues?.Count > 0 ? DynamicReportSqlBuilder.GetLookupBindingName(field) : bindingName,
|
||||||
editorType,
|
editorType,
|
||||||
|
columnSpan,
|
||||||
lookup,
|
lookup,
|
||||||
staticLookupValues,
|
staticLookupValues,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FormReportField(field, bindingName, editorType, null, null, DynamicReportImageHelper.IsImageEditor(editorType));
|
return new FormReportField(field, bindingName, editorType, columnSpan, null, null, DynamicReportImageHelper.IsImageEditor(editorType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CreateKeyFilter(string keyFieldName, string id)
|
private static string CreateKeyFilter(string keyFieldName, string id)
|
||||||
|
|
@ -660,47 +728,19 @@ public class DynamicFormReport : XtraReport
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 IsHtmlEditor(FormReportField field)
|
private static bool IsHtmlEditor(FormReportField field)
|
||||||
{
|
{
|
||||||
return field.EditorType == PlatformConsts.EditorTypes.dxHtmlEditor;
|
return field.EditorType == PlatformConsts.EditorTypes.dxHtmlEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryFormatBoolean(string value, out string text)
|
private sealed record FieldWithEditor(ListFormField Field, string EditorType, int ColumnSpan);
|
||||||
{
|
private sealed record FormGroup(string Caption, int ColumnCount, IReadOnlyList<FormReportField> Fields);
|
||||||
if (bool.TryParse(value, out var boolean))
|
private sealed record FormLayoutItem(FormReportField Field, int ColumnIndex, int ColumnSpan);
|
||||||
{
|
|
||||||
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 sealed record FieldWithEditor(ListFormField Field, string EditorType);
|
|
||||||
private sealed record FormGroup(string Caption, IReadOnlyList<FormReportField> Fields);
|
|
||||||
private sealed record FormReportField(
|
private sealed record FormReportField(
|
||||||
ListFormField Field,
|
ListFormField Field,
|
||||||
string BindingName,
|
string BindingName,
|
||||||
string EditorType,
|
string EditorType,
|
||||||
|
int ColumnSpan,
|
||||||
LookupDto Lookup,
|
LookupDto Lookup,
|
||||||
Dictionary<string, string> StaticLookupValues,
|
Dictionary<string, string> StaticLookupValues,
|
||||||
bool IsImage) : DynamicReportSqlBuilder.IReportLookupColumn;
|
bool IsImage) : DynamicReportSqlBuilder.IReportLookupColumn;
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ public class DynamicGridReport : XtraReport
|
||||||
field,
|
field,
|
||||||
selectField,
|
selectField,
|
||||||
defaultValueHelper,
|
defaultValueHelper,
|
||||||
|
editorType,
|
||||||
DynamicReportImageHelper.IsImageEditor(editorType),
|
DynamicReportImageHelper.IsImageEditor(editorType),
|
||||||
editorType == PlatformConsts.EditorTypes.dxHtmlEditor);
|
editorType == PlatformConsts.EditorTypes.dxHtmlEditor);
|
||||||
})
|
})
|
||||||
|
|
@ -366,7 +367,10 @@ public class DynamicGridReport : XtraReport
|
||||||
Multiline = false,
|
Multiline = false,
|
||||||
CanGrow = false
|
CanGrow = false
|
||||||
};
|
};
|
||||||
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{column.BindingName}]"));
|
cell.ExpressionBindings.Add(new ExpressionBinding(
|
||||||
|
"BeforePrint",
|
||||||
|
"Text",
|
||||||
|
DynamicReportImageHelper.CreateFormatValueExpression($"[{column.BindingName}]", field, column.EditorType)));
|
||||||
|
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
@ -412,7 +416,10 @@ public class DynamicGridReport : XtraReport
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"sumSum([{column.BindingName}])"));
|
cell.ExpressionBindings.Add(new ExpressionBinding(
|
||||||
|
"BeforePrint",
|
||||||
|
"Text",
|
||||||
|
DynamicReportImageHelper.CreateFormatValueExpression($"sumSum([{column.BindingName}])", field, column.EditorType)));
|
||||||
cell.Summary = new XRSummary
|
cell.Summary = new XRSummary
|
||||||
{
|
{
|
||||||
Running = SummaryRunning.Report,
|
Running = SummaryRunning.Report,
|
||||||
|
|
@ -509,6 +516,7 @@ public class DynamicGridReport : XtraReport
|
||||||
ListFormField field,
|
ListFormField field,
|
||||||
SelectField selectField,
|
SelectField selectField,
|
||||||
DefaultValueHelper defaultValueHelper,
|
DefaultValueHelper defaultValueHelper,
|
||||||
|
string editorType,
|
||||||
bool isImage,
|
bool isImage,
|
||||||
bool isHtml)
|
bool isHtml)
|
||||||
{
|
{
|
||||||
|
|
@ -517,12 +525,12 @@ public class DynamicGridReport : XtraReport
|
||||||
|
|
||||||
if (lookup == null || selectField?.JoinOptions != null)
|
if (lookup == null || selectField?.JoinOptions != null)
|
||||||
{
|
{
|
||||||
return new ReportColumn(field, bindingName, null, null, isImage, isHtml);
|
return new ReportColumn(field, bindingName, null, null, isImage, isHtml, editorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DynamicReportSqlBuilder.IsReportQueryableLookup(lookup))
|
if (DynamicReportSqlBuilder.IsReportQueryableLookup(lookup))
|
||||||
{
|
{
|
||||||
return new ReportColumn(field, DynamicReportSqlBuilder.GetLookupBindingName(field), lookup, null, isImage, isHtml);
|
return new ReportColumn(field, DynamicReportSqlBuilder.GetLookupBindingName(field), lookup, null, isImage, isHtml, editorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
|
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
|
||||||
|
|
@ -534,10 +542,11 @@ public class DynamicGridReport : XtraReport
|
||||||
lookup,
|
lookup,
|
||||||
staticLookupValues,
|
staticLookupValues,
|
||||||
isImage,
|
isImage,
|
||||||
isHtml);
|
isHtml,
|
||||||
|
editorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ReportColumn(field, bindingName, null, null, isImage, isHtml);
|
return new ReportColumn(field, bindingName, null, null, isImage, isHtml, editorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record ReportColumn(
|
private sealed record ReportColumn(
|
||||||
|
|
@ -547,6 +556,7 @@ public class DynamicGridReport : XtraReport
|
||||||
Dictionary<string, string> StaticLookupValues,
|
Dictionary<string, string> StaticLookupValues,
|
||||||
bool IsImage,
|
bool IsImage,
|
||||||
bool IsHtml,
|
bool IsHtml,
|
||||||
|
string EditorType,
|
||||||
int ImageCount = 1) : DynamicReportSqlBuilder.IReportLookupColumn;
|
int ImageCount = 1) : DynamicReportSqlBuilder.IReportLookupColumn;
|
||||||
|
|
||||||
private static SummaryFunc? GetSummaryType(string totalSummaryJson)
|
private static SummaryFunc? GetSummaryType(string totalSummaryJson)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ public partial class DynamicReport : XtraReport
|
||||||
// Language verilerini yükle
|
// Language verilerini yükle
|
||||||
LoadLanguageParameters();
|
LoadLanguageParameters();
|
||||||
|
|
||||||
|
pageInfo1.TextFormatString = "{0:d}";
|
||||||
xrRichText1.BeforePrint += XrRichText1_BeforePrint;
|
xrRichText1.BeforePrint += XrRichText1_BeforePrint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,11 +59,11 @@ public partial class DynamicReport : XtraReport
|
||||||
SetParameter("name", language.DisplayName);
|
SetParameter("name", language.DisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetParameter("date", DateTime.Now.ToString("dd.MM.yyyy"));
|
SetParameter("date", DynamicReportImageHelper.FormatReportDate(DateTime.Now));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
SetParameter("date", DateTime.Now.ToString("dd.MM.yyyy"));
|
SetParameter("date", DynamicReportImageHelper.FormatReportDate(DateTime.Now));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ internal static class DynamicReportImageHelper
|
||||||
private const int MaxFormImages = 8;
|
private const int MaxFormImages = 8;
|
||||||
private const string ImageUrlFunctionName = "SozReportImageUrl";
|
private const string ImageUrlFunctionName = "SozReportImageUrl";
|
||||||
private const string ImageSourceFunctionName = "SozReportImageSource";
|
private const string ImageSourceFunctionName = "SozReportImageSource";
|
||||||
|
private const string FormatValueFunctionName = "SozReportFormatValue";
|
||||||
private static readonly Regex ReportPagingRegex = new(
|
private static readonly Regex ReportPagingRegex = new(
|
||||||
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
|
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||||
|
|
@ -47,6 +48,7 @@ internal static class DynamicReportImageHelper
|
||||||
{
|
{
|
||||||
RegisterImageFunction(new ReportImageUrlFunction());
|
RegisterImageFunction(new ReportImageUrlFunction());
|
||||||
RegisterImageFunction(new ReportImageSourceFunction());
|
RegisterImageFunction(new ReportImageSourceFunction());
|
||||||
|
RegisterImageFunction(new ReportFormatValueFunction());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, SelectField> CreateSelectFieldMap(IEnumerable<SelectField> selectFields)
|
public static Dictionary<string, SelectField> CreateSelectFieldMap(IEnumerable<SelectField> selectFields)
|
||||||
|
|
@ -449,6 +451,261 @@ internal static class DynamicReportImageHelper
|
||||||
!string.IsNullOrWhiteSpace(Convert.ToString(value, CultureInfo.InvariantCulture));
|
!string.IsNullOrWhiteSpace(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IDisposable ApplyReportCulture(IDictionary<string, string> parameters)
|
||||||
|
{
|
||||||
|
var previousCulture = CultureInfo.CurrentCulture;
|
||||||
|
var previousUiCulture = CultureInfo.CurrentUICulture;
|
||||||
|
|
||||||
|
if (TryGetCultureName(parameters, out var cultureName))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var culture = CultureInfo.GetCultureInfo(cultureName);
|
||||||
|
CultureInfo.CurrentCulture = culture;
|
||||||
|
CultureInfo.CurrentUICulture = culture;
|
||||||
|
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
|
||||||
|
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
|
||||||
|
}
|
||||||
|
catch (CultureNotFoundException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RestoreCultureScope(previousCulture, previousUiCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryFormatReportValue(
|
||||||
|
ListFormField field,
|
||||||
|
object value,
|
||||||
|
string editorType,
|
||||||
|
out string text)
|
||||||
|
{
|
||||||
|
return TryFormatReportValue(value, field.SourceDbType, field.Format, editorType, out text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CreateFormatValueExpression(
|
||||||
|
string valueExpression,
|
||||||
|
ListFormField field,
|
||||||
|
string editorType)
|
||||||
|
{
|
||||||
|
return $"{FormatValueFunctionName}({valueExpression}, '{field.SourceDbType}', '{EscapeExpressionString(field.Format)}', '{EscapeExpressionString(editorType)}')";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatReportDate(DateTime value)
|
||||||
|
{
|
||||||
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
return value.ToString(culture.DateTimeFormat.ShortDatePattern, culture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryFormatReportValue(
|
||||||
|
object value,
|
||||||
|
DbType dbType,
|
||||||
|
string format,
|
||||||
|
string editorType,
|
||||||
|
out string text)
|
||||||
|
{
|
||||||
|
text = null;
|
||||||
|
if (value == null || value == DBNull.Value)
|
||||||
|
{
|
||||||
|
text = string.Empty;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var invariantValue = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||||
|
if (IsBooleanField(editorType, dbType) && TryFormatBoolean(invariantValue, out text))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryFormatDateValue(dbType, format, value, out text) ||
|
||||||
|
TryFormatNumericValue(dbType, format, value, out text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetCultureName(IDictionary<string, string> parameters, out string cultureName)
|
||||||
|
{
|
||||||
|
foreach (var name in new[] { "cultureName", "culture", "ui-culture" })
|
||||||
|
{
|
||||||
|
if (parameters.TryGetValue(name, out cultureName) &&
|
||||||
|
!string.IsNullOrWhiteSpace(cultureName))
|
||||||
|
{
|
||||||
|
cultureName = Uri.UnescapeDataString(cultureName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cultureName = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryFormatDateValue(DbType dbType, string format, object value, out string text)
|
||||||
|
{
|
||||||
|
text = null;
|
||||||
|
if (!IsDateField(dbType) || !TryConvertToDateTime(value, out var date))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
var customFormat = GetCustomFormat(format);
|
||||||
|
if (!string.IsNullOrWhiteSpace(customFormat))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
text = date.ToString(customFormat, culture);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text = dbType == DbType.Date || date.TimeOfDay == TimeSpan.Zero
|
||||||
|
? date.ToString(culture.DateTimeFormat.ShortDatePattern, culture)
|
||||||
|
: date.ToString("g", culture);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryFormatNumericValue(DbType dbType, string format, object value, out string text)
|
||||||
|
{
|
||||||
|
text = null;
|
||||||
|
if (!IsNumeric(dbType) || !TryConvertToDecimal(value, out var number))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
var customFormat = GetCustomFormat(format);
|
||||||
|
if (!string.IsNullOrWhiteSpace(customFormat))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
text = number.ToString(customFormat, culture);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text = number.ToString("G29", culture);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryFormatBoolean(string value, out string text)
|
||||||
|
{
|
||||||
|
if (bool.TryParse(value, out var boolean))
|
||||||
|
{
|
||||||
|
text = FormatBoolean(boolean);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == "1" || string.Equals(value, "true", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
text = FormatBoolean(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == "0" || string.Equals(value, "false", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
text = FormatBoolean(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatBoolean(bool value)
|
||||||
|
{
|
||||||
|
return CultureInfo.CurrentCulture.TwoLetterISOLanguageName.Equals("tr", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? value ? "Evet" : "Hayır"
|
||||||
|
: value ? "Yes" : "No";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsBooleanField(string editorType, DbType dbType)
|
||||||
|
{
|
||||||
|
return editorType is PlatformConsts.EditorTypes.dxCheckBox or PlatformConsts.EditorTypes.dxSwitch ||
|
||||||
|
dbType == DbType.Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCustomFormat(string format)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(format) ? null : format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsDateField(DbType dbType)
|
||||||
|
{
|
||||||
|
return dbType is DbType.Date or DbType.DateTime or DbType.DateTime2 or DbType.DateTimeOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryConvertToDateTime(object value, out DateTime date)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case DateTime dateTime:
|
||||||
|
date = dateTime;
|
||||||
|
return true;
|
||||||
|
case DateTimeOffset dateTimeOffset:
|
||||||
|
date = dateTimeOffset.LocalDateTime;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||||
|
if (DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out date) ||
|
||||||
|
DateTime.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out date))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
date = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryConvertToDecimal(object value, out decimal number)
|
||||||
|
{
|
||||||
|
if (value is IConvertible)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
number = Convert.ToDecimal(value, CultureInfo.InvariantCulture);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (InvalidCastException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||||
|
return decimal.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out number) ||
|
||||||
|
decimal.TryParse(text, NumberStyles.Any, CultureInfo.CurrentCulture, out number);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class RestoreCultureScope : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CultureInfo _culture;
|
||||||
|
private readonly CultureInfo _uiCulture;
|
||||||
|
|
||||||
|
public RestoreCultureScope(CultureInfo culture, CultureInfo uiCulture)
|
||||||
|
{
|
||||||
|
_culture = culture;
|
||||||
|
_uiCulture = uiCulture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
CultureInfo.CurrentCulture = _culture;
|
||||||
|
CultureInfo.CurrentUICulture = _uiCulture;
|
||||||
|
System.Threading.Thread.CurrentThread.CurrentCulture = _culture;
|
||||||
|
System.Threading.Thread.CurrentThread.CurrentUICulture = _uiCulture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool HasImageValue(object value)
|
private static bool HasImageValue(object value)
|
||||||
{
|
{
|
||||||
return HasValue(value) && NormalizeImageValue(value).Any(IsPotentialImageSource);
|
return HasValue(value) && NormalizeImageValue(value).Any(IsPotentialImageSource);
|
||||||
|
|
@ -1043,4 +1300,47 @@ internal static class DynamicReportImageHelper
|
||||||
return GetImageSourceObjectAt(rawValue, baseUrl, index);
|
return GetImageSourceObjectAt(rawValue, baseUrl, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class ReportFormatValueFunction : ICustomFunctionOperatorBrowsable
|
||||||
|
{
|
||||||
|
public string Name => FormatValueFunctionName;
|
||||||
|
public int MinOperandCount => 4;
|
||||||
|
public int MaxOperandCount => 4;
|
||||||
|
public FunctionCategory Category => FunctionCategory.Text;
|
||||||
|
public string Description => "Formats a report value using the active culture.";
|
||||||
|
|
||||||
|
public object Evaluate(params object[] operands)
|
||||||
|
{
|
||||||
|
var value = operands.ElementAtOrDefault(0);
|
||||||
|
var dbType = ToDbType(Convert.ToString(operands.ElementAtOrDefault(1), CultureInfo.InvariantCulture));
|
||||||
|
var format = Convert.ToString(operands.ElementAtOrDefault(2), CultureInfo.InvariantCulture);
|
||||||
|
var editorType = Convert.ToString(operands.ElementAtOrDefault(3), CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
return TryFormatReportValue(value, dbType, format, editorType, out var text)
|
||||||
|
? text
|
||||||
|
: Convert.ToString(value, CultureInfo.CurrentCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type ResultType(params Type[] operands)
|
||||||
|
{
|
||||||
|
return typeof(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidOperandCount(int count)
|
||||||
|
{
|
||||||
|
return count == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidOperandType(int operandIndex, int operandCount, Type type)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DbType ToDbType(string value)
|
||||||
|
{
|
||||||
|
return Enum.TryParse<DbType>(value, true, out var dbType)
|
||||||
|
? dbType
|
||||||
|
: DbType.String;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (reportName, parameters) = ParseReportUrl(url);
|
var (reportName, parameters) = ParseReportUrl(url);
|
||||||
|
using var _ = Sozsoft.Reports.PredefinedReports.DynamicReportImageHelper.ApplyReportCulture(parameters);
|
||||||
|
|
||||||
if (IsDynamicGridReport(reportName))
|
if (IsDynamicGridReport(reportName))
|
||||||
{
|
{
|
||||||
|
|
@ -238,6 +239,11 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
||||||
var filters = new List<string>();
|
var filters = new List<string>();
|
||||||
foreach (var param in parameters)
|
foreach (var param in parameters)
|
||||||
{
|
{
|
||||||
|
if (IsReportInfrastructureParameter(param.Key))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var columnName = char.ToUpper(param.Key[0]) + param.Key.Substring(1);
|
var columnName = char.ToUpper(param.Key[0]) + param.Key.Substring(1);
|
||||||
filters.Add($"[{columnName}] = '{param.Value.Replace("'", "''")}'");
|
filters.Add($"[{columnName}] = '{param.Value.Replace("'", "''")}'");
|
||||||
}
|
}
|
||||||
|
|
@ -245,6 +251,13 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
||||||
query.FilterString = string.Join(" AND ", filters);
|
query.FilterString = string.Join(" AND ", filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsReportInfrastructureParameter(string name)
|
||||||
|
{
|
||||||
|
return string.Equals(name, "cultureName", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(name, "culture", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(name, "ui-culture", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private static (string ReportName, Dictionary<string, string> Parameters) ParseReportUrl(string url)
|
private static (string ReportName, Dictionary<string, string> Parameters) ParseReportUrl(string url)
|
||||||
{
|
{
|
||||||
var decodedUrl = DecodeRepeatedly(url);
|
var decodedUrl = DecodeRepeatedly(url);
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,14 @@ const DevexpressReportViewer: React.FC = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const token = useStoreState((state) => state.auth.session.token)
|
const token = useStoreState((state) => state.auth.session.token)
|
||||||
const themeMode = useStoreState((state) => state.theme.mode)
|
const themeMode = useStoreState((state) => state.theme.mode)
|
||||||
|
const cultureName = useStoreState((state) => state.locale.currentLang)
|
||||||
|
const configCultureName = useStoreState(
|
||||||
|
(state) => state.abpConfig.config?.localization.currentCulture.cultureName,
|
||||||
|
)
|
||||||
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
|
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
|
||||||
const previewModelRef = useRef<any>(null)
|
const previewModelRef = useRef<any>(null)
|
||||||
const isDarkMode = themeMode === MODE_DARK
|
const isDarkMode = themeMode === MODE_DARK
|
||||||
|
const reportCultureName = cultureName || configCultureName
|
||||||
|
|
||||||
const getReportPreview = useCallback((eventOrModel?: any) => {
|
const getReportPreview = useCallback((eventOrModel?: any) => {
|
||||||
const eventArgs = eventOrModel?.args
|
const eventArgs = eventOrModel?.args
|
||||||
|
|
@ -148,19 +153,33 @@ const DevexpressReportViewer: React.FC = () => {
|
||||||
ajaxSetup.ajaxSettings.headers = {
|
ajaxSetup.ajaxSettings.headers = {
|
||||||
...ajaxHeaders,
|
...ajaxHeaders,
|
||||||
Authorization: authorization,
|
Authorization: authorization,
|
||||||
|
...(reportCultureName ? { 'Accept-Language': reportCultureName } : {}),
|
||||||
}
|
}
|
||||||
setIsRequestAuthReady(true)
|
setIsRequestAuthReady(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Authorization: _ajaxAuthorization, ...nextAjaxHeaders } = ajaxHeaders
|
const {
|
||||||
|
Authorization: _ajaxAuthorization,
|
||||||
|
'Accept-Language': _ajaxAcceptLanguage,
|
||||||
|
...nextAjaxHeaders
|
||||||
|
} = ajaxHeaders
|
||||||
|
if (reportCultureName) {
|
||||||
|
nextAjaxHeaders['Accept-Language'] = reportCultureName
|
||||||
|
}
|
||||||
ajaxSetup.ajaxSettings.headers = nextAjaxHeaders
|
ajaxSetup.ajaxSettings.headers = nextAjaxHeaders
|
||||||
setIsRequestAuthReady(true)
|
setIsRequestAuthReady(true)
|
||||||
}, [token])
|
}, [token, reportCultureName])
|
||||||
|
|
||||||
const reportUrlWithParams = useMemo(() => {
|
const reportUrlWithParams = useMemo(() => {
|
||||||
return createReportUrl({ report, id, listFormCode }, location.search)
|
const searchParams = new URLSearchParams(location.search)
|
||||||
}, [report, id, listFormCode, location.search])
|
if (reportCultureName) {
|
||||||
|
searchParams.set('cultureName', reportCultureName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = searchParams.toString()
|
||||||
|
return createReportUrl({ report, id, listFormCode }, search ? `?${search}` : '')
|
||||||
|
}, [report, id, listFormCode, location.search, reportCultureName])
|
||||||
|
|
||||||
if (!reportUrlWithParams) return null
|
if (!reportUrlWithParams) return null
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue