Erweiterung der Platzhalter- und Speicherverwaltung

Die `GridControl`-Klasse wurde erweitert, um dynamische
Platzhalterverarbeitung zu unterstützen. Dies umfasst die
Einführung eines neuen Feldes `_ParentControl`, die Anpassung
des Konstruktors und die Erweiterung der Methode
`ResolveSqlTemplate`.

Neue Ereignisse wie `HiddenEditor` wurden hinzugefügt, um
Speicherlecks zu vermeiden, und die `ShowingEditor`-Logik
wurde verbessert, um alte Editoren korrekt zu entfernen und
zu entsorgen. Dynamische Editor-Spalten werden nun gecacht,
und Debugging-Informationen wurden in mehreren Methoden
ergänzt.

Formulare wie `frmColumn_Detail`, `frmFormDesigner`,
`frmMassValidator` und `frmValidator` wurden angepasst, um
das neue Verhalten zu unterstützen. Verbesserte
Fehlerbehandlung und Protokollierung erhöhen die Stabilität
und Nachverfolgbarkeit.
This commit is contained in:
Developer01
2026-06-10 12:06:50 +02:00
parent c0a17f5cd4
commit d7546e23cc
7 changed files with 2000 additions and 13 deletions

View File

@@ -544,8 +544,9 @@ Public Class ClassControlCreator
Return oControl
End Function
Public Function CreateExistingGridControl(row As DataRow, DT_MY_COLUMNS As DataTable, designMode As Boolean, pcurrencySymbol As String) As GridControl
Dim oGridControlCreator = New ControlCreator.GridControl(LogConfig, GridTables, pcurrencySymbol)
Public Function CreateExistingGridControl(row As DataRow, DT_MY_COLUMNS As DataTable, designMode As Boolean,
pcurrencySymbol As String, pParentControl As Control) As GridControl
Dim oGridControlCreator = New ControlCreator.GridControl(LogConfig, GridTables, pcurrencySymbol, pParentControl)
Dim oControl As GridControl = CreateBaseControl(New GridControl(), row, designMode)
Dim oControlId = DirectCast(oControl.Tag, ControlMetadata).Guid
Dim oView As GridView

View File

@@ -22,11 +22,13 @@ Namespace ControlCreator
Private ReadOnly _LogConfig As LogConfig
Private ReadOnly _Logger As Logger
Private ReadOnly _GridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem))
Private ReadOnly _ParentControl As Control
Private newRowModified As Boolean
Private isApplyingInheritedValue As Boolean
Private _FormulaColumnNames As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
Private _FormulaSqlColumns As New Dictionary(Of String, FormulaSqlDefinition)(StringComparer.OrdinalIgnoreCase)
Private _DynamicEditorColumns As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
Private _isRefreshingFormula As Boolean = False ' *** NEU: Flag für Formel-Refresh ***
Private _currencySymbol As String = ""
@@ -71,9 +73,9 @@ Namespace ControlCreator
''' </summary>
Private Function ResolveSqlTemplate(sqlTemplate As String, pView As GridView, rowHandle As Integer) As String
Dim resolvedSql As String = sqlTemplate
' *** SCHRITT 1: {#TBCOL#...} Platzhalter ersetzen (BESTEHENDER CODE) ***
Dim pattern As String = "\{#TBCOL#([^}]+)\}"
Dim matches = Regex.Matches(sqlTemplate, pattern)
For Each match As Match In matches
Dim colName = match.Groups(1).Value
Dim cellValue = pView.GetRowCellValue(rowHandle, colName)
@@ -94,14 +96,28 @@ Namespace ControlCreator
resolvedSql = resolvedSql.Replace(match.Value, safeValue)
_Logger.Debug("Resolved SQL placeholder [{0}] with value [{1}] → {2}", match.Value, cellValue, safeValue)
Next
' *** SCHRITT 2: Weitere Patterns via clsPatterns ersetzen (NEU) ***
If _ParentControl IsNot Nothing Then
Try
_Logger.Debug("Applying clsPatterns.ReplaceAllValues() to SQL: {0}", resolvedSql)
resolvedSql = clsPatterns.ReplaceAllValues(resolvedSql, _ParentControl, True)
_Logger.Debug("After clsPatterns: {0}", resolvedSql)
Catch ex As Exception
_Logger.Warn("⚠️ clsPatterns.ReplaceAllValues() failed: {0}", ex.Message)
_Logger.Error(ex)
End Try
Else
_Logger.Debug("ParentControl is Nothing skipping clsPatterns.ReplaceAllValues()")
End If
_Logger.Debug("Final resolved SQL: {0}", resolvedSql)
Return resolvedSql
End Function
Public Sub New(pLogConfig As LogConfig, pGridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)), pCurrencySymbol As String)
Public Sub New(pLogConfig As LogConfig, pGridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)), pCurrencySymbol As String, pParentControl As Control)
_LogConfig = pLogConfig
_Logger = pLogConfig.GetLogger()
_GridTables = pGridTables
_currencySymbol = pCurrencySymbol
_ParentControl = pParentControl ' *** NEU ***
End Sub
''' <summary>
''' Setzt den Shared Currency-Cache zurück. Muss beim Laden eines neuen Dokuments
@@ -676,6 +692,7 @@ Namespace ControlCreator
' *** Formel-Spalten einmalig cachen + Validierung: Entweder EXPRESSION oder SQL, nie beides ***
_FormulaColumnNames.Clear()
_FormulaSqlColumns.Clear()
_DynamicEditorColumns.Clear()
For Each r As DataRow In pColumnTable.Rows
Dim oColName = r.Item("SPALTENNAME").ToString()
@@ -709,6 +726,13 @@ Namespace ControlCreator
_Logger.Debug("[ConfigureViewEvents] Column [{0}] registered as FORMULA_SQL column. ReferencedColumns=[{1}]",
oColName, String.Join(", ", GetReferencedSqlColumnNames(oSql_FORMULA)))
End If
' *** NEU: Dynamische Editor-Spalten cachen ***
Dim oSqlCommand As String = r.ItemEx("SQL_COMMAND", "")
If oSqlCommand <> "" AndAlso (ContainsTableColumnPlaceholder(oSqlCommand) OrElse clsPatterns.HasComplexPatterns(oSqlCommand)) Then
_DynamicEditorColumns.Add(oColName)
_Logger.Debug("[ConfigureViewEvents] Column [{0}] registered as DYNAMIC_EDITOR column (has placeholders)", oColName)
End If
Next
AddHandler pGridView.InitNewRow, Sub(sender As Object, e As InitNewRowEventArgs)
@@ -811,6 +835,13 @@ Namespace ControlCreator
If oColumnName <> e.Column.FieldName Then Continue For
Dim oSqlCommand As String = oRow.ItemEx("SQL_COMMAND", "")
Dim oConnectionId As Integer = oRow.ItemEx("CONNECTION_ID", 1)
' *** KRITISCH: Prüfe ZUERST, ob diese Spalte #TBCOL# Platzhalter hat ***
If oSqlCommand <> "" AndAlso (ContainsTableColumnPlaceholder(oSqlCommand) Or
clsPatterns.HasComplexPatterns(oSqlCommand)) Then
_Logger.Debug("[CustomRowCellEdit] Column [{0}] has placeholders SKIPPING, editor set by ShowingEditor", oColumnName)
' *** NICHTS TUN ShowingEditor verwaltet den Editor ***
Exit For
End If
Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, oColumnName)
@@ -1007,6 +1038,30 @@ Namespace ControlCreator
If oSqlCommand <> "" AndAlso ContainsTableColumnPlaceholder(oSqlCommand) Then
_Logger.Debug("[ShowingEditor] Column [{0}] has SQL with #TBCOL# placeholders creating row-specific editor", oFocusedColumnName)
' *** KRITISCH: Alten Editor VORHER entfernen ***
Dim oldEditor = oView.FocusedColumn.ColumnEdit
If oldEditor IsNot Nothing Then
_Logger.Debug("[ShowingEditor] Removing old ColumnEdit (Type=[{0}], HashCode=[{1}])",
oldEditor.GetType().Name, oldEditor.GetHashCode())
' Editor von Spalte entfernen
oView.FocusedColumn.ColumnEdit = Nothing
' *** NEU: Editor aus RepositoryItems entfernen, falls vorhanden ***
If oView.GridControl.RepositoryItems.Contains(oldEditor) Then
oView.GridControl.RepositoryItems.Remove(oldEditor)
_Logger.Debug("[ShowingEditor] Removed old editor from RepositoryItems")
End If
' *** KRITISCH: Dispose() aufrufen um Memory Leaks zu vermeiden ***
Try
oldEditor.Dispose()
_Logger.Debug("[ShowingEditor] Disposed old editor")
Catch disposeEx As Exception
_Logger.Warn("[ShowingEditor] Could not dispose old editor: {0}", disposeEx.Message)
End Try
End If
' Platzhalter ersetzen mit aktuellen Zeilenwerten
Dim resolvedSql As String = ResolveSqlTemplate(oSqlCommand, oView, oRowHandle)
@@ -1018,9 +1073,40 @@ Namespace ControlCreator
oColumnData.ItemEx("ADVANCED_LOOKUP", False))
If oRowSpecificEditor IsNot Nothing Then
' Editor temporär zur Spalte hinzufügen
_Logger.Debug("[ShowingEditor] Created new editor (Type=[{0}], HashCode=[{1}])",
oRowSpecificEditor.GetType().Name,
oRowSpecificEditor.GetHashCode())
' *** KRITISCH: DataSource-Größe loggen für Debugging ***
If TypeOf oRowSpecificEditor Is RepositoryItemLookupControl3 Then
Dim lookupEditor = DirectCast(oRowSpecificEditor, RepositoryItemLookupControl3)
If lookupEditor.DataSource IsNot Nothing AndAlso TypeOf lookupEditor.DataSource Is DataTable Then
Dim dt = DirectCast(lookupEditor.DataSource, DataTable)
_Logger.Debug("[ShowingEditor] LookupEditor DataSource: [{0}] rows", dt.Rows.Count)
' Log first 3 values for verification
For i As Integer = 0 To Math.Min(2, dt.Rows.Count - 1)
_Logger.Debug("[ShowingEditor] Row[{0}]: [{1}]", i, dt.Rows(i)(0))
Next
End If
ElseIf TypeOf oRowSpecificEditor Is RepositoryItemComboBox Then
Dim comboEditor = DirectCast(oRowSpecificEditor, RepositoryItemComboBox)
_Logger.Debug("[ShowingEditor] ComboBoxEditor Items: [{0}] items", comboEditor.Items.Count)
' Log first 3 items
For i As Integer = 0 To Math.Min(2, comboEditor.Items.Count - 1)
_Logger.Debug("[ShowingEditor] Item[{0}]: [{1}]", i, comboEditor.Items(i))
Next
End If
' *** KRITISCH: Editor zur GridControl.RepositoryItems hinzufügen ***
' Dies stellt sicher, dass DevExpress den Editor korrekt verwaltet
oView.GridControl.RepositoryItems.Add(oRowSpecificEditor)
_Logger.Debug("[ShowingEditor] Added editor to RepositoryItems (Total count: {0})",
oView.GridControl.RepositoryItems.Count)
' Editor zur Spalte zuweisen
oView.FocusedColumn.ColumnEdit = oRowSpecificEditor
_Logger.Debug("[ShowingEditor] Row-specific editor assigned for [{0}]", oFocusedColumnName)
_Logger.Debug("[ShowingEditor] Row-specific editor assigned and refreshed for [{0}]", oFocusedColumnName)
Else
_Logger.Warn("[ShowingEditor] Failed to create row-specific editor for [{0}] cancelling edit", oFocusedColumnName)
e.Cancel = True
@@ -1036,6 +1122,59 @@ Namespace ControlCreator
oView.AddNewRow()
End If
Catch ex As Exception
_Logger.Error("[ShowingEditor] Error: {0}", ex.Message)
_Logger.Error(ex)
End Try
End Sub
' *** NEU: HiddenEditor-Event für Cleanup ***
AddHandler pGridView.HiddenEditor,
Sub(sender As Object, e As EventArgs)
Try
Dim oView As GridView = TryCast(sender, GridView)
If oView Is Nothing OrElse oView.FocusedColumn Is Nothing Then Return
Dim oFocusedColumnName As String = oView.FocusedColumn.FieldName
' Prüfen ob diese Spalte dynamische Editoren verwendet
Dim oColumnData As DataRow = pColumnTable.
Select($"SPALTENNAME = '{oFocusedColumnName}'").
FirstOrDefault()
If oColumnData IsNot Nothing Then
Dim oSqlCommand As String = oColumnData.ItemEx("SQL_COMMAND", "")
' Wenn Spalte #TBCOL# verwendet → Editor nach Schließen entfernen
If oSqlCommand <> "" AndAlso ContainsTableColumnPlaceholder(oSqlCommand) Then
Dim currentEditor = oView.FocusedColumn.ColumnEdit
If currentEditor IsNot Nothing Then
_Logger.Debug("[HiddenEditor] Cleaning up row-specific editor for [{0}] (HashCode=[{1}])",
oFocusedColumnName, currentEditor.GetHashCode())
' Editor von Spalte entfernen
oView.FocusedColumn.ColumnEdit = Nothing
' Editor aus RepositoryItems entfernen
If oView.GridControl.RepositoryItems.Contains(currentEditor) Then
oView.GridControl.RepositoryItems.Remove(currentEditor)
_Logger.Debug("[HiddenEditor] Removed editor from RepositoryItems (Remaining: {0})",
oView.GridControl.RepositoryItems.Count)
End If
' *** KRITISCH: Dispose aufrufen ***
Try
currentEditor.Dispose()
_Logger.Debug("[HiddenEditor] Disposed editor successfully")
Catch disposeEx As Exception
_Logger.Warn("[HiddenEditor] Could not dispose editor: {0}", disposeEx.Message)
End Try
End If
End If
End If
Catch ex As Exception
_Logger.Error("[HiddenEditor] Error: {0}", ex.Message)
_Logger.Error(ex)
End Try
End Sub
@@ -1237,6 +1376,11 @@ Namespace ControlCreator
_Logger.Debug("[CreateRowSpecificEditor] Retrieved {0} rows for column [{1}]", oDataTable.Rows.Count, columnName)
' *** NEU: Log erste 5 Rows für Debugging ***
For i As Integer = 0 To Math.Min(4, oDataTable.Rows.Count - 1)
_Logger.Debug("[CreateRowSpecificEditor] DataTable Row[{0}]: [{1}]", i, oDataTable.Rows(i)(0))
Next
' Editor erstellen (analog zu GridTables_GetRepositoryItemForColumn)
If isAdvancedLookup Then
Dim oEditor = New RepositoryItemLookupControl3 With {
@@ -1244,6 +1388,8 @@ Namespace ControlCreator
.ValueMember = oDataTable.Columns(0).ColumnName,
.DataSource = oDataTable
}
_Logger.Debug("[CreateRowSpecificEditor] Created LookupControl3 with DisplayMember=[{0}], ValueMember=[{1}]",
oEditor.DisplayMember, oEditor.ValueMember)
Return oEditor
Else
Dim oEditor = New RepositoryItemComboBox()
@@ -1269,8 +1415,10 @@ Namespace ControlCreator
oEditor.Items.Add(oValue)
oItems.Add(oValue)
_Logger.Debug("[CreateRowSpecificEditor] Added ComboBox item: [{0}]", oValue)
Next
_Logger.Debug("[CreateRowSpecificEditor] Created ComboBox with {0} items", oEditor.Items.Count)
Return oEditor
End If

View File

@@ -195,7 +195,10 @@ Public Class frmColumn_Detail
CURRENT_INDEX_ID = GUIDTextBox.Text
Dim oForm2 As New frmSQLEditor(LOGCONFIG, DatabaseECM) With {
.SQLCommand = SQL_COMMANDTextBox.Text,
.SQLConnection = 0
.SQLConnection = 0,
.PlaceholdersManualPrefix = "CTRL",
.PlaceholdersManualTitle = "Controls",
.PlaceholdersManual = CURRENT_CONTROL_NAME_LIST.ToDictionary(Function(name) name, Function(name) name)
}
oForm2.ShowDialog()
@@ -241,7 +244,10 @@ Public Class frmColumn_Detail
CURRENT_INDEX_ID = GUIDTextBox.Text
Dim oForm2 As New frmSQLEditor(LOGCONFIG, DatabaseECM) With {
.SQLCommand = FORMULA_SQLTextBox.Text,
.SQLConnection = 0
.SQLConnection = 0,
.PlaceholdersManualPrefix = "CTRL",
.PlaceholdersManualTitle = "Controls",
.PlaceholdersManual = CURRENT_CONTROL_NAME_LIST.ToDictionary(Function(name) name, Function(name) name)
}
oForm2.ShowDialog()

View File

@@ -298,7 +298,7 @@ Public Class frmFormDesigner
Dim oDTColumnsPerDevExGrid As DataTable = DatabaseFallback.GetDatatableECM(oSQL) ', "FDesignLaodControls")
Dim table = ControlCreator.CreateExistingGridControl(row, oDTColumnsPerDevExGrid, True, "EUR")
Dim table = ControlCreator.CreateExistingGridControl(row, oDTColumnsPerDevExGrid, True, "EUR", Panel1)
AddHandler table.MouseClick, AddressOf gridControl_MouseClick
' AddHandler table.ColumnHeaderMouseClick, AddressOf table_ColumnHeaderMouseClick

View File

@@ -354,7 +354,7 @@ Public Class frmMassValidator
LOGGER.Debug("Versuch Tabelle zu laden")
Dim oDTMyColumns As DataTable = DatabaseFallback.GetDatatableECM($"SELECT * FROM TBPM_CONTROL_TABLE WHERE CONTROL_ID = {oControlRow.Item("GUID")} ORDER BY SEQUENCE") ', "MV_LoadControls1")
oControl = ControlCreator.CreateExistingGridControl(oControlRow, oDTMyColumns, False, CURRENT_DOC_CURRENCY)
oControl = ControlCreator.CreateExistingGridControl(oControlRow, oDTMyColumns, False, CURRENT_DOC_CURRENCY, pnldesigner)
End Select
If oControl IsNot Nothing AndAlso TypeOf oControl IsNot Label Then

View File

@@ -1675,7 +1675,7 @@ Public Class frmValidator
Continue For
End If
Dim oGrid = ControlCreator.CreateExistingGridControl(oControlRow, oFilteredDatatable, False, DocCurrency)
Dim oGrid = ControlCreator.CreateExistingGridControl(oControlRow, oFilteredDatatable, False, DocCurrency, PanelValidatorControl)
oMyControl = oGrid
' NEU: GridView Event registrieren
AddHandler DirectCast(oGrid.MainView, GridView).CellValueChanged, AddressOf GridView_CellValueChanged
@@ -4447,7 +4447,7 @@ Public Class frmValidator
End If
' GridControl-Helper erstellen und Währungsformat aktualisieren
Dim oGridControlHelper As New ControlCreator.GridControl(LOGCONFIG, ControlCreator.GridTables, currencySymbol)
Dim oGridControlHelper As New ControlCreator.GridControl(LOGCONFIG, ControlCreator.GridTables, currencySymbol, PanelValidatorControl)
oGridControlHelper.UpdateCurrencyFormat(oColumnTable, oView, oGrid, currencySymbol)
MyValidationLogger.Info("[UpdateGridCurrencyFormats] ✓ Updated GridControl [{0}] to currency [{1}]", oGrid.Name, DocCurrency)

File diff suppressed because it is too large Load Diff