Unterstützung für dynamische SQL-Templates hinzugefügt

Die Änderungen umfassen:
- Hinzufügen von Platzhalter-Checks für SQL-Templates (`ContainsTableColumnPlaceholder`) und deren Verarbeitung.
- Implementierung zeilenspezifischer Editoren (`CreateRowSpecificEditor`) basierend auf aufgelösten SQL-Templates.
- Erweiterung der Editor-Logik in `GridControl.vb`, um dynamische Editoren zu unterstützen.
- Modularisierung und Verbesserung der SQL-Formel-Verarbeitung (`TriggerSqlFormulasAfterExpressionUpdate`, `ExecuteSqlFormulas`).
- Erweiterung der Validierung in `frmValidator.vb`, um Platzhalter in SQL-Befehlen zu prüfen.
- Optimierung der Logging-Ausgaben für bessere Nachvollziehbarkeit.
- Refactoring zur Verbesserung der Lesbarkeit und Konsistenz.
- Erweiterung der Komponenten in `Product.wxs` (z. B. Hinzufügen von `DLLLicenseManager.dll`).
- Zusätzliche Validierungen für Spaltenformeln und neue Zeilen.

Diese Änderungen verbessern die Flexibilität, Stabilität und Nachvollziehbarkeit der Anwendung.
This commit is contained in:
Developer01
2026-06-09 08:55:48 +02:00
parent e49713246b
commit 6260c215f2
4 changed files with 1915 additions and 214 deletions

View File

@@ -110,26 +110,27 @@
</Component>
<Component Id="DDLibs" Guid="BA2979E3-3778-48B8-B0D8-4B77825B9293">
<File Id="LookupGrid" Name="DigitalData.Controls.LookupGrid.dll" Source="DigitalData.Controls.LookupGrid.dll"/>
<File Id="DDSnapPanel" Name="DigitalData.Controls.SnapPanel.dll" Source="DigitalData.Controls.SnapPanel.dll"/>
<File Id="DDCommonGUIs" Name="DigitalData.GUIs.Common.dll" Source="DigitalData.GUIs.Common.dll"/>
<File Id="DDConfig" Name="DigitalData.Modules.Config.dll" Source="DigitalData.Modules.Config.dll"/>
<File Id="DDLogging" Name="DigitalData.Modules.Logging.dll" Source="DigitalData.Modules.Logging.dll"/>
<File Id="DDInterfaces" Name="DigitalData.Modules.Interfaces.dll" Source="DigitalData.Modules.Interfaces.dll"/>
<File Id="DDBase" Name="DigitalData.Modules.Base.dll" Source="DigitalData.Modules.Base.dll"/>
<File Id="DDFilesystem" Name="DigitalData.Modules.Filesystem.dll" Source="DigitalData.Modules.Filesystem.dll"/>
<File Id="DDEncryption" Name="DigitalData.Modules.Encryption.dll" Source="DigitalData.Modules.Encryption.dll"/>
<File Id="DDWindream" Name="DigitalData.Modules.Windream.dll" Source="DigitalData.Modules.Windream.dll"/>
<File Id="DDWindows" Name="DigitalData.Modules.Windows.dll" Source="DigitalData.Modules.Windows.dll"/>
<File Id="DDZooflow" Name="DigitalData.Modules.Zooflow.dll" Source="DigitalData.Modules.Zooflow.dll"/>
<File Id="DDDatabase" Name="DigitalData.Modules.Database.dll" Source="DigitalData.Modules.Database.dll"/>
<File Id="DDPatterns" Name="DigitalData.Modules.Patterns.dll" Source="DigitalData.Modules.Patterns.dll"/>
<File Id="DDEDMIAPI" Name="DigitalData.Modules.EDMI.API.dll" Source="DigitalData.Modules.EDMI.API.dll"/>
<File Id="DDDocumentViewer" Name="DigitalData.Controls.DocumentViewer.dll" Source="DigitalData.Controls.DocumentViewer.dll"/>
<File Id="Messaging" Name="DigitalData.Modules.Messaging.dll" Source="DigitalData.Modules.Messaging.dll" KeyPath="no" />
<File Id="Messaging.License" Name="MailLicense.xml" Source="MailLicense.xml" KeyPath="no" />
<File Id="Limilabs.Mail" Name="Mail.dll" Source="Mail.dll" KeyPath="no" />
<File Id="NLog" Name="NLog.dll" Source="NLog.dll"/>
<File Id="LookupGrid" Name="DigitalData.Controls.LookupGrid.dll" Source="DigitalData.Controls.LookupGrid.dll"/>
<File Id="DDSnapPanel" Name="DigitalData.Controls.SnapPanel.dll" Source="DigitalData.Controls.SnapPanel.dll"/>
<File Id="DDCommonGUIs" Name="DigitalData.GUIs.Common.dll" Source="DigitalData.GUIs.Common.dll"/>
<File Id="DDConfig" Name="DigitalData.Modules.Config.dll" Source="DigitalData.Modules.Config.dll"/>
<File Id="DDLogging" Name="DigitalData.Modules.Logging.dll" Source="DigitalData.Modules.Logging.dll"/>
<File Id="DDInterfaces" Name="DigitalData.Modules.Interfaces.dll" Source="DigitalData.Modules.Interfaces.dll"/>
<File Id="DDBase" Name="DigitalData.Modules.Base.dll" Source="DigitalData.Modules.Base.dll"/>
<File Id="DDFilesystem" Name="DigitalData.Modules.Filesystem.dll" Source="DigitalData.Modules.Filesystem.dll"/>
<File Id="DDEncryption" Name="DigitalData.Modules.Encryption.dll" Source="DigitalData.Modules.Encryption.dll"/>
<File Id="DDWindream" Name="DigitalData.Modules.Windream.dll" Source="DigitalData.Modules.Windream.dll"/>
<File Id="DDWindows" Name="DigitalData.Modules.Windows.dll" Source="DigitalData.Modules.Windows.dll"/>
<File Id="DDZooflow" Name="DigitalData.Modules.Zooflow.dll" Source="DigitalData.Modules.Zooflow.dll"/>
<File Id="DDDatabase" Name="DigitalData.Modules.Database.dll" Source="DigitalData.Modules.Database.dll"/>
<File Id="DDPatterns" Name="DigitalData.Modules.Patterns.dll" Source="DigitalData.Modules.Patterns.dll"/>
<File Id="DDEDMIAPI" Name="DigitalData.Modules.EDMI.API.dll" Source="DigitalData.Modules.EDMI.API.dll"/>
<File Id="DDDocumentViewer" Name="DigitalData.Controls.DocumentViewer.dll" Source="DigitalData.Controls.DocumentViewer.dll"/>
<File Id="DDLicenseManager" Name="DLLLicenseManager.dll" Source="DLLLicenseManager.dll" KeyPath="no" />
<File Id="Messaging" Name="DigitalData.Modules.Messaging.dll" Source="DigitalData.Modules.Messaging.dll" KeyPath="no" />
<File Id="Messaging.License" Name="MailLicense.xml" Source="MailLicense.xml" KeyPath="no" />
<File Id="Limilabs.Mail" Name="Mail.dll" Source="Mail.dll" KeyPath="no" />
<File Id="NLog" Name="NLog.dll" Source="NLog.dll"/>
</Component>
<Component Id="RuntimeLibs" Guid="F7170744-3DB5-4275-ACCD-7F3B9BDE1D6E">
<File Id="Newtonsoft.Json" Name="Newtonsoft.Json.dll" Source="Newtonsoft.Json.dll" KeyPath="yes" />

View File

@@ -45,7 +45,9 @@ Namespace ControlCreator
Private Class FormulaSqlDefinition
Public Property SqlTemplate As String
Public Property ReferencedColumns As List(Of String)
Public Property ConnectionId As Integer
End Class
''' <summary>
''' Extrahiert alle {#TBCOL#ColumnName}-Platzhalter aus einem SQL-Template.
''' </summary>
@@ -218,6 +220,13 @@ Namespace ControlCreator
Dim oComboboxDataTable As DataTable = Nothing
Dim oColumnName As String = oRow.Item("SPALTENNAME")
_Logger.Debug("Working on SQL for Column[{0}]...", oColumnName)
' *** NEU: Platzhalter-Check ***
If ContainsTableColumnPlaceholder(oSqlCommand) Then
_Logger.Debug("...SQL contains #TBCOL# placeholders skipping static caching, will be resolved per row.")
Continue For ' Überspringen wird in CustomRowCellEdit behandelt
End If
If Not clsPatterns.HasComplexPatterns(oSqlCommand) Then
_Logger.Debug("SQL has no complex patterns!")
'oComboboxDataTable = ClassDatabase.Return_Datatable_ConId(oSqlCommand, oConnectionId)
@@ -562,6 +571,8 @@ Namespace ControlCreator
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)
oFormulaSql = String.Empty
ElseIf oFormulaSql <> String.Empty Then
_Logger.Debug("[ConfigureViewColumnsCurrency] Column [{0}] is a SQL formula column: {1}", oCol.FieldName, oFormulaSql)
End If
' Spalte ist eine Formel-Spalte (Expression ODER SQL) → nur DisplayFormat, kein ColumnEdit
@@ -669,10 +680,10 @@ Namespace ControlCreator
For Each r As DataRow In pColumnTable.Rows
Dim oColName = r.Item("SPALTENNAME").ToString()
Dim oExpr = ObjectEx.NotNull(r.Item("FORMULA_EXPRESSION"), String.Empty).ToString()
Dim oSql = ObjectEx.NotNull(r.Item("FORMULA_SQL"), String.Empty).ToString()
Dim oSql_FORMULA = ObjectEx.NotNull(r.Item("FORMULA_SQL"), String.Empty).ToString()
' *** VALIDIERUNG: Beides gleichzeitig ist nicht erlaubt ***
If oExpr <> String.Empty AndAlso oSql <> String.Empty Then
If oExpr <> String.Empty AndAlso oSql_FORMULA <> String.Empty Then
_Logger.Warn("⚠️ Column [{0}] has BOTH FORMULA_EXPRESSION and FORMULA_SQL this is not allowed! FORMULA_SQL will be ignored.", oColName)
MsgBox(String.Format(
"Die Spalte '{0}' enthält sowohl eine FORMULA_EXPRESSION als auch eine FORMULA_SQL." & vbCrLf &
@@ -680,22 +691,23 @@ Namespace ControlCreator
"FORMULA_SQL wird ignoriert. Bitte korrigieren Sie die Konfiguration im Tabellen-Designer.",
oColName), MsgBoxStyle.Exclamation, "Ungültige Spalten-Konfiguration")
' FORMULA_EXPRESSION hat Vorrang → SQL ignorieren
oSql = String.Empty
oSql_FORMULA = String.Empty
End If
If oExpr <> String.Empty Then
_FormulaColumnNames.Add(oColName)
_Logger.Debug("[ConfigureViewEvents] Column [{0}] registered as FORMULA_EXPRESSION column.", oColName)
ElseIf oSql <> String.Empty Then
ElseIf oSql_FORMULA <> String.Empty Then
Dim oConnectionId As Integer = r.ItemEx("CONNECTION_ID", 0)
_FormulaSqlColumns(oColName) = New FormulaSqlDefinition() With {
.SqlTemplate = oSql,
.ReferencedColumns = GetReferencedSqlColumnNames(oSql)
}
.SqlTemplate = oSql_FORMULA,
.ReferencedColumns = GetReferencedSqlColumnNames(oSql_FORMULA),
.ConnectionId = oConnectionId ' *** Hier speichern ***
}
' SQL-Spalten auch in _FormulaColumnNames aufnehmen → Editor-Blockade + ReadOnly
_FormulaColumnNames.Add(oColName)
_Logger.Debug("[ConfigureViewEvents] Column [{0}] registered as FORMULA_SQL column. ReferencedColumns=[{1}]",
oColName, String.Join(", ", GetReferencedSqlColumnNames(oSql)))
oColName, String.Join(", ", GetReferencedSqlColumnNames(oSql_FORMULA)))
End If
Next
@@ -797,6 +809,8 @@ Namespace ControlCreator
For Each oRow As DataRow In pColumnTable.Rows
Dim oColumnName As String = oRow.Item("SPALTENNAME").ToString()
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)
Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, oColumnName)
@@ -958,31 +972,73 @@ Namespace ControlCreator
AddHandler pControl.LostFocus, AddressOf Control_LostFocus
AddHandler pGridView.ShowingEditor,
Sub(sender As Object, e As CancelEventArgs)
Try
Dim oView As GridView = TryCast(sender, GridView)
If oView Is Nothing Then Return
Sub(sender As Object, e As CancelEventArgs)
Try
Dim oView As GridView = TryCast(sender, GridView)
If oView Is Nothing Then Return
_Logger.Debug("Showing editor.")
_Logger.Debug("Showing editor.")
' Formel-Spalten dürfen keinen Editor öffnen (gilt für EXPRESSION UND SQL)
If oView.FocusedColumn IsNot Nothing Then
Dim oFieldName As String = oView.FocusedColumn.FieldName
If _FormulaColumnNames.Contains(oFieldName) Then
_Logger.Debug("Cancelling editor column [{0}] is a formula column (Expression or SQL).", oFieldName)
e.Cancel = True
Return
' Formel-Spalten dürfen keinen Editor öffnen
If oView.FocusedColumn IsNot Nothing Then
Dim oFieldName As String = oView.FocusedColumn.FieldName
If _FormulaColumnNames.Contains(oFieldName) Then
_Logger.Debug("Cancelling editor column [{0}] is a formula column (Expression or SQL).", oFieldName)
e.Cancel = True
Return
End If
End If
' *** NEU: Row-specific Editor für #TBCOL# Platzhalter ***
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)
' 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
' Editor temporär zur Spalte hinzufügen
oView.FocusedColumn.ColumnEdit = oRowSpecificEditor
_Logger.Debug("[ShowingEditor] Row-specific editor assigned 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
If oView.IsNewItemRow(oView.FocusedRowHandle) AndAlso Not newRowModified Then
_Logger.Debug("Adding new row.")
oView.AddNewRow()
End If
Catch ex As Exception
_Logger.Error(ex)
End Try
End Sub
' BESTEHENDER CODE: NewItemRow-Handling
If oView.IsNewItemRow(oView.FocusedRowHandle) AndAlso Not newRowModified Then
_Logger.Debug("Adding new row.")
oView.AddNewRow()
End If
Catch ex As Exception
_Logger.Error(ex)
End Try
End Sub
AddHandler pGridView.FocusedColumnChanged,
Sub(sender As Object, e As FocusedColumnChangedEventArgs)
@@ -1054,6 +1110,8 @@ Namespace ControlCreator
_Logger.Debug("[FormulaRefresh] FALLBACK DisplayText for [{0}]: [{1}]",
oFormulaColumnName, oView.GetRowCellDisplayText(oRowHandle, oGridColumn))
Next
' Dies fängt den Fall ab, dass eine SQL-Spalte eine Expression-Spalte referenziert
TriggerSqlFormulasAfterExpressionUpdate(oView, oRowHandle, oFormulaColumnsToRefresh)
Catch ex As Exception
_Logger.Error(ex)
End Try
@@ -1146,6 +1204,224 @@ Namespace ControlCreator
End Try
End Sub
End Sub
''' <summary>
''' Prüft, ob ein SQL-Command Tabellen-Spalten-Platzhalter ({#TBCOL#...}) enthält.
''' </summary>
Private Function ContainsTableColumnPlaceholder(sqlCommand As String) As Boolean
If String.IsNullOrWhiteSpace(sqlCommand) Then Return False
Return sqlCommand.IndexOf("{#TBCOL#", StringComparison.OrdinalIgnoreCase) >= 0
End Function
''' <summary>
''' Erstellt einen zeilenspezifischen Editor (Combobox/Lookup) basierend auf aufgelöstem SQL.
''' Wird verwendet, wenn SQL_COMMAND Platzhalter wie {#TBCOL#...} enthält.
''' </summary>
Private Function CreateRowSpecificEditor(
columnName As String,
resolvedSql As String,
connectionId As Integer,
isAdvancedLookup As Boolean) As RepositoryItem
Try
_Logger.Debug("[CreateRowSpecificEditor] Executing SQL for column [{0}]: {1}", columnName, resolvedSql)
' SQL ausführen
Dim oDataTable As DataTable = DatabaseFallback.GetDatatable(
New GetDatatableOptions(resolvedSql, DatabaseType.ECM) With {
.ConnectionId = connectionId
})
If oDataTable Is Nothing OrElse oDataTable.Rows.Count = 0 Then
_Logger.Warn("[CreateRowSpecificEditor] No data returned for column [{0}]", columnName)
Return Nothing
End If
_Logger.Debug("[CreateRowSpecificEditor] Retrieved {0} rows for column [{1}]", oDataTable.Rows.Count, columnName)
' Editor erstellen (analog zu GridTables_GetRepositoryItemForColumn)
If isAdvancedLookup Then
Dim oEditor = New RepositoryItemLookupControl3 With {
.DisplayMember = oDataTable.Columns(0).ColumnName,
.ValueMember = oDataTable.Columns(0).ColumnName,
.DataSource = oDataTable
}
Return oEditor
Else
Dim oEditor = New RepositoryItemComboBox()
Dim oItems As New List(Of String)
AddHandler oEditor.Validating,
Sub(_sender As ComboBoxEdit, _e As CancelEventArgs)
If oItems.Contains(_sender.EditValue) Then
_e.Cancel = False
Else
_e.Cancel = True
End If
End Sub
For Each oRow2 As DataRow In oDataTable.Rows
Dim oValue = oRow2.Item(0).ToString()
Try
If oRow2.ItemArray.Length > 1 Then
oValue &= $" | {oRow2.Item(1)}"
End If
Catch ex As Exception
End Try
oEditor.Items.Add(oValue)
oItems.Add(oValue)
Next
Return oEditor
End If
Catch ex As Exception
_Logger.Error("[CreateRowSpecificEditor] Failed to create editor for column [{0}]: {1}", columnName, ex.Message)
_Logger.Error(ex)
Return Nothing
End Try
End Function
''' <summary>
''' Triggert SQL-Formeln, die Expression-Spalten referenzieren (z.B. Gesamt referenziert Brutto).
''' Wird aufgerufen NACHDEM Expression-Spalten aktualisiert wurden.
''' </summary>
Private Sub TriggerSqlFormulasAfterExpressionUpdate(
pView As GridView,
pColumnTable As DataTable,
pRowHandle As Integer,
pUpdatedExpressionColumns As List(Of String))
If pUpdatedExpressionColumns.Count = 0 Then Return
' Finde alle SQL-Formeln, die eine der aktualisierten Expression-Spalten referenzieren
Dim oSqlColumnsToRefresh As New List(Of String)
For Each kvp In _FormulaSqlColumns
For Each updatedExprCol In pUpdatedExpressionColumns
If kvp.Value.ReferencedColumns.Any(
Function(col) String.Equals(col, updatedExprCol, StringComparison.OrdinalIgnoreCase)) Then
If Not oSqlColumnsToRefresh.Contains(kvp.Key) Then
oSqlColumnsToRefresh.Add(kvp.Key)
End If
Exit For
End If
Next
Next
If oSqlColumnsToRefresh.Count > 0 Then
_Logger.Debug("[FormulaSql] Expression columns [{0}] trigger SQL refresh for: [{1}]",
String.Join(", ", pUpdatedExpressionColumns),
String.Join(", ", oSqlColumnsToRefresh))
ExecuteSqlFormulas(pView, pRowHandle, oSqlColumnsToRefresh)
End If
End Sub
''' <summary>
''' Führt SQL-Formeln aus und aktualisiert die Zellwerte.
''' Ausgelagert in separate Methode, um Code-Duplikation zu vermeiden.
''' </summary>
Private Sub ExecuteSqlFormulas(
pView As GridView,
pRowHandle As Integer,
pSqlColumnsToRefresh As List(Of String))
pView.GridControl.BeginInvoke(New Action(
Sub()
Try
If Not pView.IsValidRowHandle(pRowHandle) Then Return
For Each oSqlColumnName As String In pSqlColumnsToRefresh
Dim oDefinition = _FormulaSqlColumns(oSqlColumnName)
' Prüfen ob ALLE referenzierten Spalten einen Wert haben
Dim allValuesPresent As Boolean = True
For Each refCol In oDefinition.ReferencedColumns
Dim cellVal = pView.GetRowCellValue(pRowHandle, refCol)
If cellVal Is Nothing OrElse IsDBNull(cellVal) Then
_Logger.Debug("[FormulaSql] Column [{0}] has NULL value skipping SQL for [{1}]", refCol, oSqlColumnName)
allValuesPresent = False
Exit For
End If
Next
If Not allValuesPresent Then Continue For
' Pattern ersetzen und SQL ausführen
Dim resolvedSql = ResolveSqlTemplate(oDefinition.SqlTemplate, pView, pRowHandle)
_Logger.Debug("[FormulaSql] Executing SQL for [{0}]: [{1}]", oSqlColumnName, resolvedSql)
Try
' *** VEREINFACHT: ConnectionId direkt aus Definition ***
Dim oConnectionId As Integer = oDefinition.ConnectionId
_Logger.Debug("[FormulaSql] Using ConnectionId [{0}] for column [{1}]", oConnectionId, oSqlColumnName)
Dim oResultTable As DataTable = DatabaseFallback.GetDatatable(
New GetDatatableOptions(resolvedSql, DatabaseType.ECM) With {
.ConnectionId = oConnectionId
})
If oResultTable IsNot Nothing AndAlso oResultTable.Rows.Count > 0 Then
Dim oResult = oResultTable.Rows(0).Item(0)
_Logger.Debug("[FormulaSql] Result for [{0}]: [{1}]", oSqlColumnName, oResult)
_isRefreshingFormula = True
Try
pView.SetRowCellValue(pRowHandle, oSqlColumnName,
If(oResult Is Nothing OrElse IsDBNull(oResult), DBNull.Value, oResult))
pView.RefreshRowCell(pRowHandle, pView.Columns.ColumnByFieldName(oSqlColumnName))
Finally
_isRefreshingFormula = False
End Try
Else
_Logger.Warn("[FormulaSql] No result returned for [{0}]", oSqlColumnName)
End If
Catch sqlEx As Exception
_Logger.Warn("⚠️ [FormulaSql] SQL execution failed for [{0}]: {1}", oSqlColumnName, sqlEx.Message)
_Logger.Error(sqlEx)
End Try
Next
Catch ex As Exception
_Logger.Error(ex)
End Try
End Sub))
End Sub
''' <summary>
''' Triggert SQL-Formeln, die Expression-Spalten referenzieren (z.B. Gesamt referenziert Brutto).
''' Wird aufgerufen NACHDEM Expression-Spalten aktualisiert wurden.
''' </summary>
Private Sub TriggerSqlFormulasAfterExpressionUpdate(
pView As GridView,
pRowHandle As Integer,
pUpdatedExpressionColumns As List(Of String))
If pUpdatedExpressionColumns.Count = 0 Then Return
' Finde alle SQL-Formeln, die eine der aktualisierten Expression-Spalten referenzieren
Dim oSqlColumnsToRefresh As New List(Of String)
For Each kvp In _FormulaSqlColumns
For Each updatedExprCol In pUpdatedExpressionColumns
If kvp.Value.ReferencedColumns.Any(
Function(col) String.Equals(col, updatedExprCol, StringComparison.OrdinalIgnoreCase)) Then
If Not oSqlColumnsToRefresh.Contains(kvp.Key) Then
oSqlColumnsToRefresh.Add(kvp.Key)
End If
Exit For
End If
Next
Next
If oSqlColumnsToRefresh.Count > 0 Then
_Logger.Debug("[FormulaSql] Expression columns [{0}] trigger SQL refresh for: [{1}]",
String.Join(", ", pUpdatedExpressionColumns),
String.Join(", ", oSqlColumnsToRefresh))
' *** pColumnTable nicht mehr nötig ***
ExecuteSqlFormulas(pView, pRowHandle, oSqlColumnsToRefresh)
End If
End Sub
Private Sub HandleInheritedColumnValue(pView As GridView, pColumnDefinition As DataTable, pArgs As CellValueChangedEventArgs)
If pView Is Nothing OrElse pArgs Is Nothing OrElse pArgs.Column Is Nothing Then
Return

View File

@@ -5778,26 +5778,30 @@ Public Class frmValidator
Dim oAdvancedLookup = oRow.Item("ADVANCED_LOOKUP")
oSqlCommand = clsPatterns.ReplaceAllValues(oSqlCommand, PanelValidatorControl, True)
' Prüfen ob Platzhalter enthalten sind
If Not ContainsTableColumnPlaceholder(oSqlCommand?.ToString()) Then
Try
Dim oDTRESULT_FOR_COLUMN As DataTable = GetCachedDatatable(oSqlCommand, oCONNID)
Try
Dim oDTRESULT_FOR_COLUMN As DataTable = GetCachedDatatable(oSqlCommand, oCONNID)
If Not IsNothing(oDTRESULT_FOR_COLUMN) Then
MyValidationLogger.Debug($"Trying to create a DropDown(FIV) for CONTROL-ID [{oDEPENDING_CTRL_ID}] - RowCount: [{oDTRESULT_FOR_COLUMN.Rows.Count}] ")
If Not IsNothing(oDTRESULT_FOR_COLUMN) Then
MyValidationLogger.Debug($"Trying to create a DropDown(FIV) for CONTROL-ID [{oDEPENDING_CTRL_ID}] - RowCount: [{oDTRESULT_FOR_COLUMN.Rows.Count}] ")
' Dictionary-Lookup statt Loop
Dim oControl As Control = Nothing
If _CachedControlsByGuid.TryGetValue(oDEPENDING_CTRL_ID, oControl) Then
ControlCreator.GridTables_CacheDatatableForColumn(oDEPENDING_CTRL_ID, oDEPENDING_COLUMN, oSqlCommand, oCONNID, oAdvancedLookup)
' Dictionary-Lookup statt Loop
Dim oControl As Control = Nothing
If _CachedControlsByGuid.TryGetValue(oDEPENDING_CTRL_ID, oControl) Then
ControlCreator.GridTables_CacheDatatableForColumn(oDEPENDING_CTRL_ID, oDEPENDING_COLUMN, oSqlCommand, oCONNID, oAdvancedLookup)
Else
MyValidationLogger.Warn($"⚠️ Control mit ID {oDEPENDING_CTRL_ID} nicht gefunden!")
End If
Else
MyValidationLogger.Warn($"⚠️ Control mit ID {oDEPENDING_CTRL_ID} nicht gefunden!")
MyValidationLogger.Warn($"⚠️ FillIndexValues - oDTRESULT_FOR_COLUMN is nothing!")
End If
Else
MyValidationLogger.Warn($"⚠️ FillIndexValues - oDTRESULT_FOR_COLUMN is nothing!")
End If
Catch ex As Exception
MyValidationLogger.Warn($"⚠️ FillIndexValues - Unexpected error in creating Grid-Dropdown-Column [{oDEPENDING_COLUMN}] for CONTROL-ID [{oDEPENDING_CTRL_ID}]: " & ex.Message)
End Try
Catch ex As Exception
MyValidationLogger.Warn($"⚠️ FillIndexValues - Unexpected error in creating Grid-Dropdown-Column [{oDEPENDING_COLUMN}] for CONTROL-ID [{oDEPENDING_CTRL_ID}]: " & ex.Message)
End Try
Else
MyValidationLogger.Debug($"⚠️ FillIndexValues - SQL-Command for Grid-Dropdown-Column contains #CTRL# placeholder, skipping dropdown creation for CONTROL-ID [{oDEPENDING_CTRL_ID}] - Column [{oDEPENDING_COLUMN}]")
End If
Next
Catch ex As Exception
MyValidationLogger.Warn($"⚠️ FillIndexValues - Unexpected error in creating dropdown for Grid: " & ex.Message)
@@ -5935,7 +5939,19 @@ Public Class frmValidator
End If
End Try
End Sub
''' <summary>
''' Prüft, ob ein SQL-Command Grid-Spalten-Platzhalter enthält
''' </summary>
''' <param name="sqlCommand">Der zu prüfende SQL-Command</param>
''' <returns>True wenn Platzhalter enthalten sind, sonst False</returns>
Private Function ContainsTableColumnPlaceholder(sqlCommand As String) As Boolean
If String.IsNullOrWhiteSpace(sqlCommand) Then
Return False
End If
' Prüft auf #TBCOL# Platzhalter (case-insensitive)
Return sqlCommand.IndexOf("#TBCOL#", StringComparison.OrdinalIgnoreCase) >= 0
End Function
Private Sub ApplyCurrencyMask(pTextEdit As TextEdit)
If pTextEdit Is Nothing Then Return
Try

File diff suppressed because it is too large Load Diff