From 5556bec7aaaf7c61379a53ddd63ffc1b4dd5b006 Mon Sep 17 00:00:00 2001 From: Alex Bochkov Date: Tue, 23 Dec 2025 14:42:03 -0700 Subject: [PATCH 1/2] Format numeric grid values with locale separators --- AxialSqlTools/AxialSqlToolsPackage.cs | 110 +++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/AxialSqlTools/AxialSqlToolsPackage.cs b/AxialSqlTools/AxialSqlToolsPackage.cs index 21f71f8..cf7e50c 100644 --- a/AxialSqlTools/AxialSqlToolsPackage.cs +++ b/AxialSqlTools/AxialSqlToolsPackage.cs @@ -27,6 +27,7 @@ using System.Data; using System.Linq; using AxialSqlTools.Properties; +using System.Globalization; namespace AxialSqlTools { @@ -539,23 +540,26 @@ public static void SQLResultsControl_ScriptExecutionCompleted(object QEOLESQLExe var grid = GridAccess.GetNonPublicField(gridContainer, "m_grid") as GridControl; var gridStorage = grid.GridStorage; var schemaTable = GridAccess.GetNonPublicField(gridStorage, "m_schemaTable") as DataTable; + var setCellDataMethod = GetGridStorageSetCellDataMethod(gridStorage); var gridColumns = GridAccess.GetNonPublicField(grid, "m_Columns") as GridColumnCollection; if (gridColumns != null) { - string[] typeToAlignRight = new string[] { "tinyint", "smallint", "int", "bigint", "money", "decimal", "numeric" }; + string[] typeToAlignRight = new string[] { "tinyint", "smallint", "int", "bigint", "money", "smallmoney", "decimal", "numeric", "float", "real" }; List columnsToAlignRight = new List { }; + List columnsToFormatNumbers = new List { }; for (int c = 0; c < schemaTable.Rows.Count; c++) { int columnOrdinal = (int)schemaTable.Rows[c][1]; - var sqlDataTypeName = schemaTable.Rows[c][24]; + var sqlDataTypeName = schemaTable.Rows[c][24]?.ToString(); if (typeToAlignRight.Contains(sqlDataTypeName)) { columnsToAlignRight.Add(columnOrdinal); + columnsToFormatNumbers.Add(columnOrdinal); } } @@ -589,6 +593,23 @@ public static void SQLResultsControl_ScriptExecutionCompleted(object QEOLESQLExe } } + if (setCellDataMethod != null && columnsToFormatNumbers.Count > 0) + { + for (long rowIndex = 0; rowIndex < gridStorage.NumRows(); rowIndex++) + { + foreach (var columnOrdinal in columnsToFormatNumbers) + { + var columnIndex = columnOrdinal + 1; + var cellText = gridStorage.GetCellDataAsString(rowIndex, columnIndex); + if (TryFormatNumberWithGroupSeparators(cellText, out var formattedText) + && !string.Equals(cellText, formattedText, StringComparison.Ordinal)) + { + TrySetGridStorageCellData(gridStorage, setCellDataMethod, rowIndex, columnIndex, formattedText); + } + } + } + } + grid.Refresh(); } @@ -662,6 +683,91 @@ public static void SQLResultsControl_ScriptExecutionCompleted(object QEOLESQLExe } + + private static bool TryFormatNumberWithGroupSeparators(string value, out string formattedValue) + { + formattedValue = value; + + if (string.IsNullOrWhiteSpace(value) || string.Equals(value, "NULL", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + var numberStyles = NumberStyles.Number | NumberStyles.AllowExponent; + var culture = CultureInfo.CurrentCulture; + + if (!decimal.TryParse(value, numberStyles, culture, out var parsedValue) + && !decimal.TryParse(value, numberStyles, CultureInfo.InvariantCulture, out parsedValue)) + { + return false; + } + + int decimalPlaces = GetDecimalPlaces(value, culture); + var format = $"N{decimalPlaces}"; + formattedValue = parsedValue.ToString(format, culture); + + return true; + } + + private static int GetDecimalPlaces(string value, CultureInfo culture) + { + var decimalSeparator = culture.NumberFormat.NumberDecimalSeparator; + var decimalIndex = value.LastIndexOf(decimalSeparator, StringComparison.Ordinal); + + if (decimalIndex < 0) + { + var alternativeSeparator = decimalSeparator == "." ? "," : "."; + decimalIndex = value.LastIndexOf(alternativeSeparator, StringComparison.Ordinal); + } + + if (decimalIndex < 0) + { + return 0; + } + + return value.Length - decimalIndex - 1; + } + + private static MethodInfo GetGridStorageSetCellDataMethod(object gridStorage) + { + if (gridStorage == null) + { + return null; + } + + var methods = gridStorage.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var candidateNames = new[] { "SetCellDataAsString", "SetCellDataFromString", "SetCellData" }; + + foreach (var candidateName in candidateNames) + { + var method = methods.FirstOrDefault(m => m.Name == candidateName && m.GetParameters().Length == 3); + if (method != null) + { + return method; + } + } + + return null; + } + + private static void TrySetGridStorageCellData(object gridStorage, MethodInfo setCellDataMethod, long rowIndex, int columnIndex, string formattedText) + { + try + { + var parameters = setCellDataMethod.GetParameters(); + var values = new object[parameters.Length]; + + values[0] = Convert.ChangeType(rowIndex, parameters[0].ParameterType, CultureInfo.InvariantCulture); + values[1] = Convert.ChangeType(columnIndex, parameters[1].ParameterType, CultureInfo.InvariantCulture); + values[2] = formattedText; + + setCellDataMethod.Invoke(gridStorage, values); + } + catch (Exception ex) + { + _logger.Error(ex, "An exception occurred"); + } + } private void CommandEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) { //ThreadHelper.ThrowIfNotOnUIThread(); From 41c37a825b2cd458d5285958c1940d6e4c13dfd1 Mon Sep 17 00:00:00 2001 From: Alex Bochkov Date: Tue, 23 Dec 2025 15:01:28 -0700 Subject: [PATCH 2/2] doesn't work --- AxialSqlTools/AxialSqlToolsPackage.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/AxialSqlTools/AxialSqlToolsPackage.cs b/AxialSqlTools/AxialSqlToolsPackage.cs index cf7e50c..27b549a 100644 --- a/AxialSqlTools/AxialSqlToolsPackage.cs +++ b/AxialSqlTools/AxialSqlToolsPackage.cs @@ -542,14 +542,14 @@ public static void SQLResultsControl_ScriptExecutionCompleted(object QEOLESQLExe var schemaTable = GridAccess.GetNonPublicField(gridStorage, "m_schemaTable") as DataTable; var setCellDataMethod = GetGridStorageSetCellDataMethod(gridStorage); + List columnsToAlignRight = new List { }; + List columnsToFormatNumbers = new List { }; + var gridColumns = GridAccess.GetNonPublicField(grid, "m_Columns") as GridColumnCollection; if (gridColumns != null) { - string[] typeToAlignRight = new string[] { "tinyint", "smallint", "int", "bigint", "money", "smallmoney", "decimal", "numeric", "float", "real" }; - - List columnsToAlignRight = new List { }; - List columnsToFormatNumbers = new List { }; + string[] typeToAlignRight = new string[] { "tinyint", "smallint", "int", "bigint", "money", "smallmoney", "decimal", "numeric" }; for (int c = 0; c < schemaTable.Rows.Count; c++) { @@ -602,7 +602,8 @@ public static void SQLResultsControl_ScriptExecutionCompleted(object QEOLESQLExe var columnIndex = columnOrdinal + 1; var cellText = gridStorage.GetCellDataAsString(rowIndex, columnIndex); if (TryFormatNumberWithGroupSeparators(cellText, out var formattedText) - && !string.Equals(cellText, formattedText, StringComparison.Ordinal)) + //&& !string.Equals(cellText, formattedText, StringComparison.Ordinal) + ) { TrySetGridStorageCellData(gridStorage, setCellDataMethod, rowIndex, columnIndex, formattedText); } @@ -736,7 +737,7 @@ private static MethodInfo GetGridStorageSetCellDataMethod(object gridStorage) } var methods = gridStorage.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - var candidateNames = new[] { "SetCellDataAsString", "SetCellDataFromString", "SetCellData" }; + var candidateNames = new[] { "SetCellDataAsString", "SetCellDataFromControl", "SetCellData" }; foreach (var candidateName in candidateNames) {