Optimierung von dynamischen Editoren und Debugging-Logs

Neue Funktionalität für dynamische Editoren:
- Einführung eines Shared-Caches (`_DynamicEditorCacheShared`) zur Wiederverwendung von Editoren in `GridControl`.
- Dynamische Editoren werden nun pro Zeile behandelt und bei Bedarf aus dem Cache geladen oder neu erstellt.
- Neue Methode `ClearDynamicEditorCache`, um den Cache bei Dokumentwechsel zu leeren.

Optimierung der Ereignisbehandlung:
- Überarbeitung des `CustomRowCellEdit`-Events für effizientere Zuweisung dynamischer Editoren.
- Vereinfachung der Logik für `ShowingEditor` und Entfernung veralteter Logik für `HiddenEditor`.

Erweiterte Debugging-Optionen:
- Einführung des Flags `LOG_HOTSPOTS`, um gezielt Debugging-Informationen zu aktivieren.
- Verbesserte Logs für SQL-Abfragen, Cache-Zugriffe und Editor-Erstellungen.

Code-Bereinigung und Stabilität:
- Entfernung redundanter Logik und Vereinfachung der Behandlung von Währungsformaten.
- Verbesserte Speicherverwaltung durch explizites Entfernen und Disposen von Editoren.
- Zusätzliche Sicherheitsprüfungen und Fehlerbehandlungen.

Ziel der Änderungen:
Verbesserung der Performance durch Caching, Erhöhung der Stabilität durch erweiterte Fehlerbehandlung und Optimierung der Wartbarkeit durch klarere Logs und vereinfachte Logik.
This commit is contained in:
Developer01
2026-06-11 16:47:32 +02:00
parent d7546e23cc
commit 3e7d700536
5 changed files with 4204 additions and 1928 deletions

View File

@@ -901,7 +901,15 @@ Public Class ClassControlCreator
Dim oColumnName = oRow.Item("SPALTENNAME") Dim oColumnName = oRow.Item("SPALTENNAME")
Dim oAdvancedLookup = oRow.Item("ADVANCED_LOOKUP") Dim oAdvancedLookup = oRow.Item("ADVANCED_LOOKUP")
' *** NEU: Prüfe ob Spalte #TBCOL# verwendet (dynamisch pro Zeile) ***
If oSqlStatement <> String.Empty AndAlso oConnectionId > -1 Then If oSqlStatement <> String.Empty AndAlso oConnectionId > -1 Then
' *** NEU: Skip Spalten mit #TBCOL# (werden in ShowingEditor behandelt) ***
If oSqlStatement.Contains("{#TBCOL#") Then
Logger.Debug($"GridTables_HandleControlValueChange -> Skipping column [{oColumnName}] (has #TBCOL# placeholders, will be resolved per row)")
Continue For
End If
' *** BESTEHENDER CODE: Nur für statische Spalten ({#CTRL#} only) ***
oSqlStatement = clsPatterns.ReplaceAllValues(oSqlStatement, pControlPanel, True) oSqlStatement = clsPatterns.ReplaceAllValues(oSqlStatement, pControlPanel, True)
GridTables_CacheDatatableForColumn(oControlId, oColumnName, oSqlStatement, oConnectionId, oAdvancedLookup) GridTables_CacheDatatableForColumn(oControlId, oColumnName, oSqlStatement, oConnectionId, oAdvancedLookup)

View File

@@ -29,6 +29,7 @@ Namespace ControlCreator
Private _FormulaColumnNames As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) Private _FormulaColumnNames As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
Private _FormulaSqlColumns As New Dictionary(Of String, FormulaSqlDefinition)(StringComparer.OrdinalIgnoreCase) Private _FormulaSqlColumns As New Dictionary(Of String, FormulaSqlDefinition)(StringComparer.OrdinalIgnoreCase)
Private _DynamicEditorColumns As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) Private _DynamicEditorColumns As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
Private Shared _DynamicEditorCacheShared As New Dictionary(Of String, RepositoryItem)
Private _isRefreshingFormula As Boolean = False ' *** NEU: Flag für Formel-Refresh *** Private _isRefreshingFormula As Boolean = False ' *** NEU: Flag für Formel-Refresh ***
Private _currencySymbol As String = "" Private _currencySymbol As String = ""
@@ -41,6 +42,14 @@ Namespace ControlCreator
''' </summary> ''' </summary>
Private Shared _CurrencySymbolByGridName As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase) Private Shared _CurrencySymbolByGridName As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
''' <summary>
''' Leert den Cache für dynamische Editoren. Muss bei jedem Dokumentwechsel aufgerufen werden.
''' </summary>
Public Shared Sub ClearDynamicEditorCache()
SyncLock _DynamicEditorCacheShared
_DynamicEditorCacheShared.Clear()
End SyncLock
End Sub
''' <summary> ''' <summary>
''' Definiert eine SQL-basierte Formelspalte mit allen nötigen Metadaten. ''' Definiert eine SQL-basierte Formelspalte mit allen nötigen Metadaten.
''' </summary> ''' </summary>
@@ -583,7 +592,6 @@ Namespace ControlCreator
Dim oFormulaExpression = ObjectEx.NotNull(oColumnData.Item("FORMULA_EXPRESSION"), String.Empty) Dim oFormulaExpression = ObjectEx.NotNull(oColumnData.Item("FORMULA_EXPRESSION"), String.Empty)
Dim oFormulaSql = ObjectEx.NotNull(oColumnData.Item("FORMULA_SQL"), String.Empty) Dim oFormulaSql = ObjectEx.NotNull(oColumnData.Item("FORMULA_SQL"), String.Empty)
' Entweder/Oder: Beide gleichzeitig → Expression gewinnt, SQL ignoriert
If oFormulaExpression <> String.Empty AndAlso oFormulaSql <> String.Empty Then If oFormulaExpression <> String.Empty AndAlso oFormulaSql <> String.Empty Then
_Logger.Warn("[ConfigureViewColumnsCurrency] Column [{0}] has BOTH FORMULA_EXPRESSION and FORMULA_SQL treating as EXPRESSION only.", oCol.FieldName) _Logger.Warn("[ConfigureViewColumnsCurrency] Column [{0}] has BOTH FORMULA_EXPRESSION and FORMULA_SQL treating as EXPRESSION only.", oCol.FieldName)
oFormulaSql = String.Empty oFormulaSql = String.Empty
@@ -591,19 +599,13 @@ Namespace ControlCreator
_Logger.Debug("[ConfigureViewColumnsCurrency] Column [{0}] is a SQL formula column: {1}", oCol.FieldName, oFormulaSql) _Logger.Debug("[ConfigureViewColumnsCurrency] Column [{0}] is a SQL formula column: {1}", oCol.FieldName, oFormulaSql)
End If End If
' Spalte ist eine Formel-Spalte (Expression ODER SQL) → nur DisplayFormat, kein ColumnEdit
Dim oIsAnyFormula As Boolean = oFormulaExpression <> String.Empty OrElse oFormulaSql <> String.Empty Dim oIsAnyFormula As Boolean = oFormulaExpression <> String.Empty OrElse oFormulaSql <> String.Empty
If oIsAnyFormula Then If oIsAnyFormula Then
' Formel-Spalten (Expression oder SQL): nur DisplayFormat setzen
oCol.DisplayFormat.FormatType = FormatType.Custom oCol.DisplayFormat.FormatType = FormatType.Custom
oCol.DisplayFormat.FormatString = $"#,##0.00 {_currencySymbol}" oCol.DisplayFormat.FormatString = $"#,##0.00 {_currencySymbol}"
_Logger.Debug("[ConfigureViewColumnsCurrency] Formel-Spalte [{0}] (IsExpression=[{1}], IsSql=[{2}]): DisplayFormat=[{3}], RepositoryItems.Count=[{4}]", _Logger.Debug("[ConfigureViewColumnsCurrency] Formel-Spalte [{0}]: DisplayFormat=[{1}]",
oCol.FieldName, oCol.FieldName, oCol.DisplayFormat.FormatString)
oFormulaExpression <> String.Empty,
oFormulaSql <> String.Empty,
oCol.DisplayFormat.FormatString,
pGrid.RepositoryItems.Count)
ElseIf oCol.OptionsColumn.AllowEdit Then ElseIf oCol.OptionsColumn.AllowEdit Then
_Logger.Debug("[ConfigureViewColumnsCurrency] [{0}] VOR ColumnEdit: RepositoryItems.Count=[{1}]", _Logger.Debug("[ConfigureViewColumnsCurrency] [{0}] VOR ColumnEdit: RepositoryItems.Count=[{1}]",
@@ -613,79 +615,8 @@ Namespace ControlCreator
_Logger.Debug("[ConfigureViewColumnsCurrency] [{0}] NACH ColumnEdit: RepositoryItems.Count=[{1}]", _Logger.Debug("[ConfigureViewColumnsCurrency] [{0}] NACH ColumnEdit: RepositoryItems.Count=[{1}]",
oCol.FieldName, pGrid.RepositoryItems.Count) oCol.FieldName, pGrid.RepositoryItems.Count)
Dim assignedEdit = TryCast(oCol.ColumnEdit, RepositoryItemTextEdit)
_Logger.Debug("[ConfigureViewColumnsCurrency] [{0}]: IsSameObject=[{1}], ColumnEdit.DisplayFormat=[{2}], ColumnEdit.HashCode=[{3}]",
oCol.FieldName,
Object.ReferenceEquals(assignedEdit, riTextEdit),
If(assignedEdit IsNot Nothing, assignedEdit.DisplayFormat.FormatString, "N/A"),
If(assignedEdit IsNot Nothing, assignedEdit.GetHashCode(), -1))
For i As Integer = 0 To pGrid.RepositoryItems.Count - 1
_Logger.Debug("[ConfigureViewColumnsCurrency] RepositoryItems[{0}]: Type=[{1}], HashCode=[{2}]",
i, pGrid.RepositoryItems(i).GetType().Name, pGrid.RepositoryItems(i).GetHashCode())
Next
End If End If
Next Next
Dim oTestFired As Boolean = False
AddHandler pGridView.CustomColumnDisplayText,
Sub(sender As Object, e As CustomColumnDisplayTextEventArgs)
If e.Column Is Nothing OrElse e.Value Is Nothing OrElse IsDBNull(e.Value) Then
Return
End If
Dim oColumnData As DataRow = pColumnTable.
Select($"SPALTENNAME = '{e.Column.FieldName}'").
FirstOrDefault()
If oColumnData IsNot Nothing AndAlso
oColumnData.Item("TYPE_COLUMN").ToString() = "CURRENCY" Then
Try
Dim currentSymbol As String = _currencySymbol
Dim gridName As String = pGrid.Name
SyncLock _CurrencySymbolByGridName
If _CurrencySymbolByGridName.ContainsKey(gridName) Then
currentSymbol = _CurrencySymbolByGridName(gridName)
End If
End SyncLock
Dim oValue As Double
If TypeOf e.Value Is Double OrElse TypeOf e.Value Is Decimal Then
oValue = Convert.ToDouble(e.Value)
ElseIf TypeOf e.Value Is String Then
Dim oStringValue As String = e.Value.ToString().Trim()
Dim oDeCulture As CultureInfo = New CultureInfo("de-DE")
If Double.TryParse(oStringValue, NumberStyles.Currency Or NumberStyles.Number, oDeCulture, oValue) Then
ElseIf Double.TryParse(oStringValue, NumberStyles.Currency Or NumberStyles.Number, CultureInfo.InvariantCulture, oValue) Then
Else
oValue = Convert.ToDouble(oStringValue, CultureInfo.CurrentCulture)
End If
Else
oValue = Convert.ToDouble(e.Value)
End If
Dim oDeCultureInfo As CultureInfo = New CultureInfo("de-DE")
e.DisplayText = oValue.ToString("N2", oDeCultureInfo) & " " & currentSymbol
_Logger.Debug("[CustomColumnDisplayText] CURRENCY [{0}]: DisplayText=[{1}], Symbol=[{2}] (from Shared Dict in ConfigureViewColumnsCurrency)",
e.Column.FieldName, e.DisplayText, currentSymbol)
Catch ex As Exception
_Logger.Warn("⚠️ Could not format currency value [{0}] for column [{1}]: {2}",
e.Value, e.Column.FieldName, ex.Message)
Dim fallbackSymbol As String = _currencySymbol
SyncLock _CurrencySymbolByGridName
If _CurrencySymbolByGridName.ContainsKey(pGrid.Name) Then
fallbackSymbol = _CurrencySymbolByGridName(pGrid.Name)
End If
End SyncLock
e.DisplayText = e.Value.ToString() & " " & fallbackSymbol
End Try
End If
End Sub
End Sub End Sub
Public Sub ConfigureViewEvents(pColumnTable As DataTable, pGridView As GridView, pControl As Windows.Forms.Control, pControlId As Integer) Public Sub ConfigureViewEvents(pColumnTable As DataTable, pGridView As GridView, pControl As Windows.Forms.Control, pControlId As Integer)
@@ -828,44 +759,61 @@ Namespace ControlCreator
End If End If
End Sub End Sub
AddHandler pGridView.CustomRowCellEdit, Sub(sender As Object, e As CustomRowCellEditEventArgs) AddHandler pGridView.CustomRowCellEdit,
Sub(sender As Object, e As CustomRowCellEditEventArgs)
Try Try
For Each oRow As DataRow In pColumnTable.Rows ' *** NEU: Dynamische Editoren aus Cache oder neu erstellen ***
Dim oColumnName As String = oRow.Item("SPALTENNAME").ToString() If _DynamicEditorColumns.Contains(e.Column.FieldName) Then
If oColumnName <> e.Column.FieldName Then Continue For Dim oColumnData As DataRow = pColumnTable.
Dim oSqlCommand As String = oRow.ItemEx("SQL_COMMAND", "") Select($"SPALTENNAME = '{e.Column.FieldName}'").
Dim oConnectionId As Integer = oRow.ItemEx("CONNECTION_ID", 1) FirstOrDefault()
' *** KRITISCH: Prüfe ZUERST, ob diese Spalte #TBCOL# Platzhalter hat ***
If oSqlCommand <> "" AndAlso (ContainsTableColumnPlaceholder(oSqlCommand) Or If oColumnData IsNot Nothing Then
clsPatterns.HasComplexPatterns(oSqlCommand)) Then Dim oSqlCommand As String = oColumnData.ItemEx("SQL_COMMAND", "")
_Logger.Debug("[CustomRowCellEdit] Column [{0}] has placeholders SKIPPING, editor set by ShowingEditor", oColumnName) Dim oConnectionId As Integer = oColumnData.ItemEx("CONNECTION_ID", 1)
' *** NICHTS TUN ShowingEditor verwaltet den Editor *** Dim oIsAdvancedLookup As Boolean = oColumnData.ItemEx("ADVANCED_LOOKUP", False)
Exit For
' *** SQL auflösen (für Cache-Key) ***
Dim resolvedSql = ResolveSqlTemplate(oSqlCommand, TryCast(sender, GridView), e.RowHandle)
' *** CACHE-KEY: Spaltenname + aufgelöstes SQL (HashCode) ***
Dim cacheKey As String = $"{e.Column.FieldName}|{resolvedSql.GetHashCode()}"
' *** CACHE-CHECK ***
SyncLock _DynamicEditorCacheShared
If _DynamicEditorCacheShared.ContainsKey(cacheKey) Then
' ✅ CACHE HIT: Editor wiederverwenden
e.RepositoryItem = _DynamicEditorCacheShared(cacheKey)
_Logger.Debug("[CustomRowCellEdit] Using CACHED editor for [{0}] (CacheKey=[{1}])", e.Column.FieldName, cacheKey)
Else
' ❌ CACHE MISS: Neuen Editor erstellen
_Logger.Debug("[CustomRowCellEdit] Creating NEW row-specific editor for [{0}]", e.Column.FieldName)
Dim realEditor = CreateRowSpecificEditor(e.Column.FieldName, resolvedSql, oConnectionId, oIsAdvancedLookup)
If realEditor IsNot Nothing Then
' *** IN CACHE SPEICHERN ***
_DynamicEditorCacheShared(cacheKey) = realEditor
e.RepositoryItem = realEditor
_Logger.Debug("[CustomRowCellEdit] CACHED new editor (Type=[{0}], CacheKey=[{1}])", realEditor.GetType().Name, cacheKey)
Else
_Logger.Warn("[CustomRowCellEdit] CreateRowSpecificEditor returned Nothing for [{0}]", e.Column.FieldName)
End If
End If
End SyncLock
End If
Return
End If End If
Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, oColumnName) ' Standard-Editor aus Cache
Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, e.Column.FieldName)
If oEditorExists Then If oEditorExists Then
' Combobox/Lookup-Editor aus GridTables: immer zuweisen _Logger.Debug("[CustomRowCellEdit] Assigning RepositoryItem for column [{0}] from GridTables cache.", e.Column.FieldName)
Dim oEditor = _GridTables.Item(pControlId).Item(oColumnName) e.RepositoryItem = _GridTables.Item(pControlId).Item(e.Column.FieldName)
_Logger.Debug("Assigning Editor to Column [{0}]", oColumnName)
e.RepositoryItem = oEditor
Else
Dim oColumnType As String = ObjectEx.NotNull(oRow.Item("TYPE_COLUMN"), String.Empty).ToString()
If oColumnType = "CURRENCY" Then
If _FormulaColumnNames.Contains(oColumnName) Then
_Logger.Debug("CURRENCY column [{0}] is formula/readonly CustomColumnDisplayText handles display", oColumnName)
Else
_Logger.Debug("CURRENCY column [{0}] NO e.RepositoryItem set. Display via CustomColumnDisplayText, Edit via GridColumn.ColumnEdit", oColumnName)
End If End If
Else
_Logger.Debug("Editor for Column [{0}] does not exist", oColumnName)
End If
End If
Exit For
Next
Catch ex As Exception Catch ex As Exception
_Logger.Warn("⚠️ Error in CustomRowCellEdit for [{0}]", e.CellValue) _Logger.Warn("⚠️ Error in CustomRowCellEdit for column [{0}]", e.Column.FieldName)
_Logger.Error(ex) _Logger.Error(ex)
End Try End Try
End Sub End Sub
@@ -1010,113 +958,17 @@ Namespace ControlCreator
_Logger.Debug("Showing editor.") _Logger.Debug("Showing editor.")
' Formel-Spalten dürfen keinen Editor öffnen ' Formel-Spalten blockieren
If oView.FocusedColumn IsNot Nothing Then If oView.FocusedColumn IsNot Nothing Then
Dim oFieldName As String = oView.FocusedColumn.FieldName Dim oFieldName As String = oView.FocusedColumn.FieldName
If _FormulaColumnNames.Contains(oFieldName) Then If _FormulaColumnNames.Contains(oFieldName) Then
_Logger.Debug("Cancelling editor column [{0}] is a formula column (Expression or SQL).", oFieldName) _Logger.Debug("Cancelling editor column [{0}] is a formula column.", oFieldName)
e.Cancel = True e.Cancel = True
Return Return
End If End If
End If End If
' *** NEU: Row-specific Editor für #TBCOL# Platzhalter *** CheckNewItemRow:
If oView.FocusedColumn IsNot Nothing Then
Dim oFocusedColumnName As String = oView.FocusedColumn.FieldName
Dim oRowHandle As Integer = oView.FocusedRowHandle
' Spalten-Metadata aus pColumnTable holen
Dim oColumnData As DataRow = pColumnTable.
Select($"SPALTENNAME = '{oFocusedColumnName}'").
FirstOrDefault()
If oColumnData IsNot Nothing Then
Dim oSqlCommand As String = oColumnData.ItemEx("SQL_COMMAND", "")
Dim oConnectionId As Integer = oColumnData.ItemEx("CONNECTION_ID", 1)
' Prüfen ob SQL Platzhalter enthält
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)
' Editor mit aufgelöstem SQL laden
Dim oRowSpecificEditor = CreateRowSpecificEditor(
oFocusedColumnName,
resolvedSql,
oConnectionId,
oColumnData.ItemEx("ADVANCED_LOOKUP", False))
If oRowSpecificEditor IsNot Nothing Then
_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 and refreshed for [{0}]", oFocusedColumnName)
Else
_Logger.Warn("[ShowingEditor] Failed to create row-specific editor for [{0}] cancelling edit", oFocusedColumnName)
e.Cancel = True
Return
End If
End If
End If
End If
' BESTEHENDER CODE: NewItemRow-Handling
If oView.IsNewItemRow(oView.FocusedRowHandle) AndAlso Not newRowModified Then If oView.IsNewItemRow(oView.FocusedRowHandle) AndAlso Not newRowModified Then
_Logger.Debug("Adding new row.") _Logger.Debug("Adding new row.")
oView.AddNewRow() oView.AddNewRow()
@@ -1127,58 +979,6 @@ Namespace ControlCreator
End Try End Try
End Sub 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
AddHandler pGridView.FocusedColumnChanged, AddHandler pGridView.FocusedColumnChanged,
Sub(sender As Object, e As FocusedColumnChangedEventArgs) Sub(sender As Object, e As FocusedColumnChangedEventArgs)
Try Try
@@ -1361,7 +1161,8 @@ Namespace ControlCreator
isAdvancedLookup As Boolean) As RepositoryItem isAdvancedLookup As Boolean) As RepositoryItem
Try Try
_Logger.Debug("[CreateRowSpecificEditor] Executing SQL for column [{0}]: {1}", columnName, resolvedSql) If LOG_HOTSPOTS Then _Logger.Debug("[CreateRowSpecificEditor] Executing SQL for column [{0}]: {1}", columnName, resolvedSql)
' SQL ausführen ' SQL ausführen
Dim oDataTable As DataTable = DatabaseFallback.GetDatatable( Dim oDataTable As DataTable = DatabaseFallback.GetDatatable(
@@ -1374,11 +1175,11 @@ Namespace ControlCreator
Return Nothing Return Nothing
End If End If
_Logger.Debug("[CreateRowSpecificEditor] Retrieved {0} rows for column [{1}]", oDataTable.Rows.Count, columnName) If LOG_HOTSPOTS Then _Logger.Debug("[CreateRowSpecificEditor] Retrieved {0} rows for column [{1}]", oDataTable.Rows.Count, columnName)
' *** NEU: Log erste 5 Rows für Debugging *** ' *** NEU: Log erste 5 Rows für Debugging ***
For i As Integer = 0 To Math.Min(4, oDataTable.Rows.Count - 1) 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)) If LOG_HOTSPOTS Then _Logger.Debug("[CreateRowSpecificEditor] DataTable Row[{0}]: [{1}]", i, oDataTable.Rows(i)(0))
Next Next
' Editor erstellen (analog zu GridTables_GetRepositoryItemForColumn) ' Editor erstellen (analog zu GridTables_GetRepositoryItemForColumn)
@@ -1415,7 +1216,7 @@ Namespace ControlCreator
oEditor.Items.Add(oValue) oEditor.Items.Add(oValue)
oItems.Add(oValue) oItems.Add(oValue)
_Logger.Debug("[CreateRowSpecificEditor] Added ComboBox item: [{0}]", oValue) If LOG_HOTSPOTS Then _Logger.Debug("[CreateRowSpecificEditor] Added ComboBox item: [{0}]", oValue)
Next Next
_Logger.Debug("[CreateRowSpecificEditor] Created ComboBox with {0} items", oEditor.Items.Count) _Logger.Debug("[CreateRowSpecificEditor] Created ComboBox with {0} items", oEditor.Items.Count)

View File

@@ -70,7 +70,7 @@ Public Class clsPatterns
''' </summary> ''' </summary>
Public Shared Sub ClearControlCache() Public Shared Sub ClearControlCache()
_ControlLookupCache = Nothing _ControlLookupCache = Nothing
LOGGER.Debug("Control cache cleared") If LOG_HOTSPOTS Then LOGGER.Debug("Control cache cleared")
End Sub End Sub
''' <summary> ''' <summary>
''' Aktualisiert den Wert eines Controls im Cache ''' Aktualisiert den Wert eines Controls im Cache
@@ -122,7 +122,7 @@ Public Class clsPatterns
LOGGER.Warn($"Unsupported control type for cache update: {ctrl.GetType.Name}") LOGGER.Warn($"Unsupported control type for cache update: {ctrl.GetType.Name}")
End Select End Select
LOGGER.Debug($"Cache updated for control [{controlName}] with value type [{newValue?.GetType().Name}]") If LOG_HOTSPOTS Then LOGGER.Debug($"Cache updated for control [{controlName}] with value type [{newValue?.GetType().Name}]")
Catch ex As Exception Catch ex As Exception
LOGGER.Error(ex) LOGGER.Error(ex)
@@ -144,7 +144,7 @@ Public Class clsPatterns
UpdateControlInCache(kvp.Key, kvp.Value) UpdateControlInCache(kvp.Key, kvp.Value)
Next Next
LOGGER.Debug($"Batch cache update completed for {updates.Count} controls") If LOG_HOTSPOTS Then LOGGER.Debug($"Batch cache update completed for {updates.Count} controls")
End Sub End Sub
''' <summary> ''' <summary>
''' Wraps a pattern-type and -value in the common format: {#type#value} ''' Wraps a pattern-type and -value in the common format: {#type#value}
@@ -175,9 +175,9 @@ Public Class clsPatterns
If Not IsNothing(oResult) Then If Not IsNothing(oResult) Then
oResult = ReplaceUserValues(oResult) oResult = ReplaceUserValues(oResult)
LOGGER.Debug($"input AFTER replacing: [{oResult}]")
End If
End If
LOGGER.Debug($"input AFTER replacing: [{oResult}]")
Return oResult Return oResult
Catch ex As Exception Catch ex As Exception
LOGGER.Error(ex) LOGGER.Error(ex)
@@ -305,7 +305,7 @@ Public Class clsPatterns
End SyncLock End SyncLock
Try Try
LOGGER.Debug($"Starting ReplaceControlValues with input: [{oResult}] for document ID: {CURRENT_DOC_ID}") If LOG_HOTSPOTS Then LOGGER.Debug($"Starting ReplaceControlValues with input: [{oResult}] for document ID: {CURRENT_DOC_ID}")
Dim oTryCounter = 0 Dim oTryCounter = 0
While ContainsPattern(oResult, PATTERN_CTRL) While ContainsPattern(oResult, PATTERN_CTRL)
@@ -325,7 +325,7 @@ Public Class clsPatterns
oColumnName = oSplitName.Last() oColumnName = oSplitName.Last()
End If End If
LOGGER.Debug("Found placeholder for control [{0}].", oControlName) If LOG_HOTSPOTS Then LOGGER.Debug("Found placeholder for control [{0}].", oControlName)
' Beim Cache-Zugriff Lock verwenden ' Beim Cache-Zugriff Lock verwenden
Dim oControl As Control = Nothing Dim oControl As Control = Nothing
SyncLock _ControlLookupCache SyncLock _ControlLookupCache
@@ -349,11 +349,11 @@ Public Class clsPatterns
If oControl IsNot Nothing Then If oControl IsNot Nothing Then
Dim oReplaceValue As String Dim oReplaceValue As String
LOGGER.Debug("oControl.GetType [{0}].", oControl.GetType.ToString) If LOG_HOTSPOTS Then LOGGER.Debug("oControl.GetType [{0}].", oControl.GetType.ToString)
Select Case oControl.GetType Select Case oControl.GetType
Case GetType(TextBox) Case GetType(TextBox)
oReplaceValue = oControl.Text oReplaceValue = oControl.Text
LOGGER.Debug("TextBox- oReplaceValue will be [{0}].", oReplaceValue) If LOG_HOTSPOTS Then LOGGER.Debug("TextBox- oReplaceValue will be [{0}].", oReplaceValue)
Case GetType(TextEdit) Case GetType(TextEdit)
Try Try
oReplaceValue = ClassAllgemeineFunktionen.NotNullString(DirectCast(oControl, TextEdit).EditValue, String.Empty) oReplaceValue = ClassAllgemeineFunktionen.NotNullString(DirectCast(oControl, TextEdit).EditValue, String.Empty)
@@ -361,7 +361,7 @@ Public Class clsPatterns
LOGGER.Warn($"Error in ReplaceValue MemoEdit: {ex.Message}") LOGGER.Warn($"Error in ReplaceValue MemoEdit: {ex.Message}")
oReplaceValue = "" oReplaceValue = ""
End Try End Try
LOGGER.Debug("TextEdit- oReplaceValue will be [{0}].", oReplaceValue) If LOG_HOTSPOTS Then LOGGER.Debug("TextEdit- oReplaceValue will be [{0}].", oReplaceValue)
Case GetType(MemoEdit) Case GetType(MemoEdit)
Try Try
oReplaceValue = ClassAllgemeineFunktionen.NotNullString(DirectCast(oControl, MemoEdit).EditValue, String.Empty) oReplaceValue = ClassAllgemeineFunktionen.NotNullString(DirectCast(oControl, MemoEdit).EditValue, String.Empty)
@@ -369,7 +369,7 @@ Public Class clsPatterns
LOGGER.Warn($"Error in ReplaceValue MemoEdit: {ex.Message}") LOGGER.Warn($"Error in ReplaceValue MemoEdit: {ex.Message}")
oReplaceValue = "" oReplaceValue = ""
End Try End Try
LOGGER.Debug("MemoEdit- oReplaceValue will be [{0}].", oReplaceValue) If LOG_HOTSPOTS Then LOGGER.Debug("MemoEdit- oReplaceValue will be [{0}].", oReplaceValue)
Case GetType(LookupControl3) Case GetType(LookupControl3)
Dim oLookupControl3 As LookupControl3 = oControl Dim oLookupControl3 As LookupControl3 = oControl
@@ -391,7 +391,7 @@ Public Class clsPatterns
oReplaceValue = ERROR_REPLACE_VALUE oReplaceValue = ERROR_REPLACE_VALUE
' ========== FIX END ========== ' ========== FIX END ==========
ElseIf selectedValues.Count > 1 Then ElseIf selectedValues.Count > 1 Then
LOGGER.Debug($"LookupControl3 [{oControlName}] mit mehr als 1 Value") If LOG_HOTSPOTS Then LOGGER.Debug($"LookupControl3 [{oControlName}] mit mehr als 1 Value")
Dim oIndex As Integer = 0 Dim oIndex As Integer = 0
For Each oString As String In selectedValues For Each oString As String In selectedValues
If oIndex = 0 Then If oIndex = 0 Then
@@ -403,12 +403,11 @@ Public Class clsPatterns
Next Next
oIsSQL = False oIsSQL = False
Else ' Count = 1 Else ' Count = 1
LOGGER.Debug($"LookupControl3 [{oControlName}] mit genau einem Value") If LOG_HOTSPOTS Then LOGGER.Debug($"LookupControl3 [{oControlName}] mit genau einem Value")
oReplaceValue = selectedValues(0) oReplaceValue = selectedValues(0)
End If End If
LOGGER.Debug($"oReplaceValue nach Durchlaufen selectedValues: {oReplaceValue}") If LOG_HOTSPOTS Then LOGGER.Debug($"oReplaceValue nach Durchlaufen selectedValues: {oReplaceValue}")
LOGGER.Debug($"oReplaceValue nach Durchlaufen selectedValues: {oReplaceValue}")
Case GetType(Windows.Forms.ComboBox) Case GetType(Windows.Forms.ComboBox)
oReplaceValue = oControl.Text oReplaceValue = oControl.Text
@@ -440,7 +439,7 @@ Public Class clsPatterns
Case Else Case Else
oReplaceValue = ERROR_REPLACE_VALUE oReplaceValue = ERROR_REPLACE_VALUE
End Select End Select
LOGGER.Debug($"[SQL-ESCAPE CHECK] Control: [{oControlName}], oReplaceValue Type: [{If(oReplaceValue?.GetType()?.Name, "NULL")}], Value: [{oReplaceValue}], IsSQL: [{oIsSQL}]") If LOG_HOTSPOTS Then LOGGER.Debug($"[SQL-ESCAPE CHECK] Control: [{oControlName}], oReplaceValue Type: [{If(oReplaceValue?.GetType()?.Name, "NULL")}], Value: [{oReplaceValue}], IsSQL: [{oIsSQL}]")
If oReplaceValue Is Nothing Then If oReplaceValue Is Nothing Then
LOGGER.Warn($"⚠️ oReplaceValue is Nothing for control [{oControlName}]! Setting to ERROR_REPLACE_VALUE") LOGGER.Warn($"⚠️ oReplaceValue is Nothing for control [{oControlName}]! Setting to ERROR_REPLACE_VALUE")
oReplaceValue = ERROR_REPLACE_VALUE oReplaceValue = ERROR_REPLACE_VALUE
@@ -468,7 +467,7 @@ Public Class clsPatterns
End Try End Try
End Function End Function
Private Shared Function SafeSqlEscape(value As Object) As String Private Shared Function SafeSqlEscape(value As Object) As String
LOGGER.Debug($"[SafeSqlEscape] Input Type: [{If(value?.GetType()?.Name, "NULL")}], Value: [{value}]") If LOG_HOTSPOTS Then LOGGER.Debug($"[SafeSqlEscape] Input Type: [{If(value?.GetType()?.Name, "NULL")}], Value: [{value}]")
If value Is Nothing Then If value Is Nothing Then
LOGGER.Warn("[SafeSqlEscape] Value is Nothing → returning ERROR_REPLACE_VALUE") LOGGER.Warn("[SafeSqlEscape] Value is Nothing → returning ERROR_REPLACE_VALUE")
@@ -490,14 +489,14 @@ Public Class clsPatterns
End If End If
Dim escaped = strValue.Replace("'", "''") Dim escaped = strValue.Replace("'", "''")
LOGGER.Debug($"[SafeSqlEscape] Output: [{escaped}]") If LOG_HOTSPOTS Then LOGGER.Debug($"[SafeSqlEscape] Output: [{escaped}]")
Return escaped Return escaped
End Function End Function
Public Shared Function ReplaceWindreamIndicies(pInput As String, pDocument As WMObject, pIsSQL As Boolean) As String Public Shared Function ReplaceWindreamIndicies(pInput As String, pDocument As WMObject, pIsSQL As Boolean) As String
Try Try
Dim oResult = pInput Dim oResult = pInput
Dim oTryCounter As Integer = 0 Dim oTryCounter As Integer = 0
LOGGER.Debug($"Starting ReplaceWindreamIndicies with input: [{oResult}] for document ID: {CURRENT_DOC_ID}") If LOG_HOTSPOTS Then LOGGER.Debug($"Starting ReplaceWindreamIndicies with input: [{oResult}] for document ID: {CURRENT_DOC_ID}")
While ContainsPattern(oResult, PATTERN_WMI) While ContainsPattern(oResult, PATTERN_WMI)
Dim oWMValue As String Dim oWMValue As String
Dim oIndexName As String = GetNextPattern(oResult, PATTERN_WMI).Value Dim oIndexName As String = GetNextPattern(oResult, PATTERN_WMI).Value
@@ -517,9 +516,9 @@ Public Class clsPatterns
If oWMValue IsNot Nothing Then If oWMValue IsNot Nothing Then
If pIsSQL = True Then If pIsSQL = True Then
LOGGER.Debug($"IS_SQL = True - oReplaceValue = {oWMValue}") If LOG_HOTSPOTS Then LOGGER.Debug($"IS_SQL = True - oReplaceValue = {oWMValue}")
oWMValue = oWMValue.ToString().Replace("'", "''") oWMValue = oWMValue.ToString().Replace("'", "''")
LOGGER.Debug($"oReplaceValue = {oWMValue}") If LOG_HOTSPOTS Then LOGGER.Debug($"oReplaceValue = {oWMValue}")
End If End If
oResult = ReplacePattern(oResult, PATTERN_WMI, oWMValue) oResult = ReplacePattern(oResult, PATTERN_WMI, oWMValue)
Else Else
@@ -543,7 +542,7 @@ Public Class clsPatterns
Try Try
Dim result = input Dim result = input
Dim oTryCounter As Integer = 0 Dim oTryCounter As Integer = 0
LOGGER.Debug($"Starting ReplaceIDBAttributes with input: [{result}] for document ID: {CURRENT_DOC_ID}") If LOG_HOTSPOTS Then LOGGER.Debug($"Starting ReplaceIDBAttributes with input: [{result}] for document ID: {CURRENT_DOC_ID}")
While ContainsPattern(result, PATTERN_IDBA) While ContainsPattern(result, PATTERN_IDBA)
Dim indexName As String = GetNextPattern(result, PATTERN_IDBA).Value Dim indexName As String = GetNextPattern(result, PATTERN_IDBA).Value
@@ -568,7 +567,7 @@ Public Class clsPatterns
If oIDBValue IsNot Nothing Or Not IsDBNull(oIDBValue) Then If oIDBValue IsNot Nothing Or Not IsDBNull(oIDBValue) Then
Dim oReplaceValue = "{" + $"#{PATTERN_IDBA}#{indexName}" + "}" Dim oReplaceValue = "{" + $"#{PATTERN_IDBA}#{indexName}" + "}"
If IS_SQL = True Then If IS_SQL = True Then
LOGGER.Debug($"IS_SQL = True - oReplaceValue = [{oReplaceValue}]") If LOG_HOTSPOTS Then LOGGER.Debug($"IS_SQL = True - oReplaceValue = [{oReplaceValue}]")
If indexName <> "ObjectID" And indexName <> "OBJID" And indexName <> "DocID" Then If indexName <> "ObjectID" And indexName <> "OBJID" And indexName <> "DocID" Then
Try Try
oIDBValue = oIDBValue.Replace("'", "''") oIDBValue = oIDBValue.Replace("'", "''")
@@ -578,7 +577,7 @@ Public Class clsPatterns
End Try End Try
End If End If
LOGGER.Debug($"oIDBValue = {oIDBValue}") If LOG_HOTSPOTS Then LOGGER.Debug($"oIDBValue = {oIDBValue}")
End If End If
result = result.Replace(oReplaceValue, oIDBValue) result = result.Replace(oReplaceValue, oIDBValue)
Else Else
@@ -590,7 +589,7 @@ Public Class clsPatterns
' Increase counter by 10 to avoid DDOSing the Database/IDB Service ' Increase counter by 10 to avoid DDOSing the Database/IDB Service
oTryCounter += 10 oTryCounter += 10
End While End While
LOGGER.Debug("sql after ReplaceIDBAttributes: " & input) If LOG_HOTSPOTS Then LOGGER.Debug("sql after ReplaceIDBAttributes: " & input)
Return result Return result
Catch ex As Exception Catch ex As Exception
LOGGER.Error(ex) LOGGER.Error(ex)

View File

@@ -4195,6 +4195,9 @@ Public Class frmValidator
DocCurrency = oCurrency DocCurrency = oCurrency
MyValidationLogger.Info($"[FINAL] DocCurrency = [{DocCurrency}]") MyValidationLogger.Info($"[FINAL] DocCurrency = [{DocCurrency}]")
taskFLOW.ControlCreator.GridControl.ClearDynamicEditorCache()
taskFLOW.ControlCreator.GridControl.ResetCurrencySymbolCache()
Catch ex As Exception Catch ex As Exception
MyValidationLogger.Error($"Währungsladung fehlgeschlagen: {ex.Message}") MyValidationLogger.Error($"Währungsladung fehlgeschlagen: {ex.Message}")
DocCurrency = "EUR" DocCurrency = "EUR"
@@ -8816,4 +8819,8 @@ Public Class frmValidator
CONFIG.Config.NOTES_ONCLICK = bchkitmNotes.Checked CONFIG.Config.NOTES_ONCLICK = bchkitmNotes.Checked
CONFIG.Save() CONFIG.Save()
End Sub End Sub
Private Sub frmValidator_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
End Sub
End Class End Class

File diff suppressed because it is too large Load Diff