Vor SetControlValuesChange
This commit is contained in:
@@ -68,7 +68,17 @@ Public Class ClassControlCreator
|
||||
''' </summary>
|
||||
Public Property GridTables As New Dictionary(Of Integer, Dictionary(Of String, RepositoryItem))
|
||||
Public Property GridColumns As New Dictionary(Of Integer, DataTable)
|
||||
Private _globalLookupEventGuard As Boolean = False
|
||||
|
||||
Public Property GlobalLookupEventGuard As Boolean
|
||||
Get
|
||||
Return _globalLookupEventGuard
|
||||
End Get
|
||||
Set(value As Boolean)
|
||||
_globalLookupEventGuard = value
|
||||
Logger.Debug($"GlobalLookupEventGuard -> gesetzt auf [{value}]")
|
||||
End Set
|
||||
End Property
|
||||
|
||||
|
||||
''' <summary>
|
||||
@@ -853,44 +863,239 @@ Public Class ClassControlCreator
|
||||
End Function
|
||||
|
||||
Public Sub GridTables_HandleControlValueChange(pControlPanel As XtraScrollableControl, pColumnsWithSqlAndControlPlaceholders As DataTable)
|
||||
If Not IsNothing(pColumnsWithSqlAndControlPlaceholders) AndAlso pColumnsWithSqlAndControlPlaceholders.Rows.Count > 0 Then
|
||||
For Each oRow As DataRow In pColumnsWithSqlAndControlPlaceholders.Rows
|
||||
If pColumnsWithSqlAndControlPlaceholders Is Nothing OrElse pColumnsWithSqlAndControlPlaceholders.Rows.Count = 0 Then
|
||||
Logger.Debug("No depending controls with SQL statements defined, skipping handling control value change.")
|
||||
Return
|
||||
End If
|
||||
|
||||
' ============================================================================
|
||||
' Schritt 1 - Sichere ALLE Lookup-Werte VOR SQL-Reload
|
||||
' ============================================================================
|
||||
Dim lookupBackups As New Dictionary(Of String, Object)
|
||||
|
||||
For Each oControl As Control In pControlPanel.Controls
|
||||
If TypeOf oControl Is LookupControl3 Then
|
||||
Try
|
||||
Dim oSqlStatement = oRow.ItemEx("SQL_COMMAND", String.Empty)
|
||||
Dim oConnectionId = oRow.ItemEx("CONNECTION_ID", -1)
|
||||
Dim oControlId = oRow.Item("CONTROL_ID")
|
||||
Dim oColumnName = oRow.Item("SPALTENNAME")
|
||||
Dim oAdvancedLookup = oRow.Item("ADVANCED_LOOKUP")
|
||||
|
||||
If oSqlStatement <> String.Empty And oConnectionId > -1 Then
|
||||
oSqlStatement = clsPatterns.ReplaceAllValues(oSqlStatement, pControlPanel, True)
|
||||
|
||||
GridTables_CacheDatatableForColumn(oControlId, oColumnName, oSqlStatement, oConnectionId, oAdvancedLookup)
|
||||
|
||||
|
||||
' === Block to force setting the editor for GridColumns
|
||||
Logger.Debug("Force-setting Editor for all Gridcells..")
|
||||
For Each oControl As Control In pControlPanel.Controls
|
||||
Try
|
||||
Dim oMeta = DirectCast(oControl.Tag, ClassControlCreator.ControlMetadata)
|
||||
If oMeta.Guid = oControlId AndAlso TypeOf oControl Is GridControl Then
|
||||
Dim oGrid As GridControl = DirectCast(oControl, GridControl)
|
||||
DirectCast(oGrid.FocusedView, GridView).FocusInvalidRow()
|
||||
Logger.Debug("Force-setting Editor for Grid [{0}]", oGrid.Name)
|
||||
Exit For
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
' === End
|
||||
|
||||
Dim lookup = DirectCast(oControl, LookupControl3)
|
||||
' Speichere den aktuellen EditValue (kann einzelner Wert oder Liste sein)
|
||||
If lookup.EditValue IsNot Nothing Then
|
||||
Dim controlName As String = lookup.Name
|
||||
lookupBackups(controlName) = lookup.EditValue
|
||||
Logger.Debug($"GridTables_HandleControlValueChange -> Backup für Lookup [{controlName}]: [{lookup.EditValue}]")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Logger.Info("Unexpected Error in Display SQL result for grid column: " & oRow.Item("CONTROL_ID") & " - ERROR: " & ex.Message)
|
||||
Logger.Error($"GridTables_HandleControlValueChange -> Fehler beim Backup von Lookup: {ex.Message}")
|
||||
End Try
|
||||
End If
|
||||
Next
|
||||
|
||||
' ============================================================================
|
||||
' Schritt 2 - Verarbeite Grid-Columns mit SQL-Abhängigkeiten
|
||||
' ============================================================================
|
||||
For Each oRow As DataRow In pColumnsWithSqlAndControlPlaceholders.Rows
|
||||
Try
|
||||
Dim oSqlStatement = oRow.ItemEx("SQL_COMMAND", String.Empty)
|
||||
Dim oConnectionId = oRow.ItemEx("CONNECTION_ID", -1)
|
||||
Dim oControlId = oRow.Item("CONTROL_ID")
|
||||
Dim oColumnName = oRow.Item("SPALTENNAME")
|
||||
Dim oAdvancedLookup = oRow.Item("ADVANCED_LOOKUP")
|
||||
|
||||
If oSqlStatement <> String.Empty AndAlso oConnectionId > -1 Then
|
||||
oSqlStatement = clsPatterns.ReplaceAllValues(oSqlStatement, pControlPanel, True)
|
||||
GridTables_CacheDatatableForColumn(oControlId, oColumnName, oSqlStatement, oConnectionId, oAdvancedLookup)
|
||||
|
||||
' Force-setting Editor for GridColumns
|
||||
Logger.Debug("Force-setting Editor for all Gridcells..")
|
||||
For Each oControl As Control In pControlPanel.Controls
|
||||
Try
|
||||
If oControl.Tag IsNot Nothing AndAlso TypeOf oControl.Tag Is ControlMetadata Then
|
||||
Dim oMeta = DirectCast(oControl.Tag, ControlMetadata)
|
||||
If oMeta.Guid = oControlId AndAlso TypeOf oControl Is GridControl Then
|
||||
Dim oGrid As GridControl = DirectCast(oControl, GridControl)
|
||||
If oGrid.FocusedView IsNot Nothing AndAlso TypeOf oGrid.FocusedView Is GridView Then
|
||||
DirectCast(oGrid.FocusedView, GridView).FocusInvalidRow()
|
||||
Logger.Debug($"Force-setting Editor for Grid [{oGrid.Name}]")
|
||||
End If
|
||||
Exit For
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Error($"GridTables_HandleControlValueChange -> Fehler beim Force-setting Editor: {ex.Message}")
|
||||
End Try
|
||||
Next
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex)
|
||||
Logger.Info($"Unexpected Error in Display SQL result for grid column: {oRow.Item("CONTROL_ID")} - ERROR: {ex.Message}")
|
||||
End Try
|
||||
Next
|
||||
|
||||
' ============================================================================
|
||||
' Schritt 3 - Prüfe und restauriere Lookup-Werte mit SQL-Abhängigkeiten
|
||||
' ============================================================================
|
||||
If lookupBackups.Count > 0 Then
|
||||
Logger.Debug($"GridTables_HandleControlValueChange -> Prüfe {lookupBackups.Count} Lookups auf Wiederherstellung...")
|
||||
|
||||
For Each oControl As Control In pControlPanel.Controls
|
||||
If TypeOf oControl Is LookupControl3 Then
|
||||
Try
|
||||
Dim lookup = DirectCast(oControl, LookupControl3)
|
||||
Dim controlName As String = lookup.Name
|
||||
|
||||
' Wenn wir einen Backup für dieses Lookup haben
|
||||
If lookupBackups.ContainsKey(controlName) Then
|
||||
Dim oldValue = lookupBackups(controlName)
|
||||
|
||||
' Prüfe ob Lookup ein DataSource hat (könnte durch SQL neu geladen worden sein)
|
||||
If lookup.Properties.DataSource IsNot Nothing AndAlso TypeOf lookup.Properties.DataSource Is DataTable Then
|
||||
Dim currentDataSource = DirectCast(lookup.Properties.DataSource, DataTable)
|
||||
|
||||
' Wenn DataSource Rows hat, versuche Werte wiederherzustellen
|
||||
If currentDataSource.Rows.Count > 0 Then
|
||||
RestoreLookupValues(lookup, oldValue, currentDataSource, controlName)
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
|
||||
Catch ex As Exception
|
||||
Logger.Error($"GridTables_HandleControlValueChange -> Fehler beim Prüfen von Lookup: {ex.Message}")
|
||||
Logger.Error(ex)
|
||||
End Try
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private Sub RestoreLookupValues(lookup As LookupControl3, oldValue As Object,
|
||||
newDataSource As DataTable, controlName As String)
|
||||
If lookup Is Nothing OrElse oldValue Is Nothing OrElse newDataSource Is Nothing Then
|
||||
Logger.Warn($"RestoreLookupValues -> [{controlName}] Ungültige Parameter")
|
||||
Return
|
||||
End If
|
||||
|
||||
Try
|
||||
' Bestimme ValueColumn
|
||||
Dim valueColumn As String = String.Empty
|
||||
If String.IsNullOrEmpty(lookup.Properties.ValueMember) AndAlso newDataSource.Columns.Count > 0 Then
|
||||
valueColumn = newDataSource.Columns(0).ColumnName
|
||||
ElseIf Not String.IsNullOrEmpty(lookup.Properties.ValueMember) Then
|
||||
valueColumn = lookup.Properties.ValueMember
|
||||
Else
|
||||
Logger.Warn($"RestoreLookupValues -> [{controlName}] Keine ValueColumn verfügbar")
|
||||
Return
|
||||
End If
|
||||
|
||||
' Build HashSet für effiziente Suche
|
||||
Dim availableValues As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
|
||||
For Each row As DataRow In newDataSource.Rows
|
||||
If Not IsDBNull(row(valueColumn)) Then
|
||||
availableValues.Add(row(valueColumn).ToString())
|
||||
End If
|
||||
Next
|
||||
|
||||
' Prüfe ob der alte Wert noch in der neuen DataSource existiert
|
||||
Dim validValue As Object = Nothing
|
||||
|
||||
' Behandle verschiedene Datentypen (String, List, Array, etc.)
|
||||
If TypeOf oldValue Is String Then
|
||||
Dim valueStr As String = oldValue.ToString()
|
||||
If availableValues.Contains(valueStr) Then
|
||||
validValue = oldValue
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] Wert [{valueStr}] ✓ gefunden")
|
||||
Else
|
||||
Logger.Warn($"RestoreLookupValues -> [{controlName}] Wert [{valueStr}] ✗ nicht mehr vorhanden")
|
||||
End If
|
||||
ElseIf TypeOf oldValue Is IEnumerable Then
|
||||
' Behandle Listen/Arrays
|
||||
Dim validValues As New List(Of Object)
|
||||
For Each item As Object In DirectCast(oldValue, IEnumerable)
|
||||
If item IsNot Nothing Then
|
||||
Dim itemStr As String = item.ToString()
|
||||
If availableValues.Contains(itemStr) Then
|
||||
validValues.Add(item)
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] Wert [{itemStr}] ✓ gefunden")
|
||||
Else
|
||||
Logger.Warn($"RestoreLookupValues -> [{controlName}] Wert [{itemStr}] ✗ nicht mehr vorhanden")
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
|
||||
If validValues.Count > 0 Then
|
||||
validValue = validValues
|
||||
End If
|
||||
Else
|
||||
' Fallback für andere Typen
|
||||
Dim valueStr As String = oldValue.ToString()
|
||||
If availableValues.Contains(valueStr) Then
|
||||
validValue = oldValue
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] Wert [{valueStr}] ✓ gefunden")
|
||||
Else
|
||||
Logger.Warn($"RestoreLookupValues -> [{controlName}] Wert [{valueStr}] ✗ nicht mehr vorhanden")
|
||||
End If
|
||||
End If
|
||||
|
||||
' Gültige Werte wiederherstellen
|
||||
If validValue IsNot Nothing Then
|
||||
' Event-Guard temporär aktivieren um Endlosschleifen zu vermeiden
|
||||
Dim oldGuard As Boolean = GlobalLookupEventGuard
|
||||
GlobalLookupEventGuard = True
|
||||
|
||||
Try
|
||||
' Prüfe ob bereits korrekt gesetzt
|
||||
Dim currentValue = lookup.EditValue
|
||||
If currentValue IsNot Nothing AndAlso currentValue.Equals(validValue) Then
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] Wert bereits korrekt gesetzt")
|
||||
Return
|
||||
End If
|
||||
|
||||
' Setze den Wert neu
|
||||
lookup.EditValue = Nothing
|
||||
Application.DoEvents()
|
||||
|
||||
lookup.EditValue = validValue
|
||||
lookup.Refresh()
|
||||
Application.DoEvents()
|
||||
|
||||
' Validierung
|
||||
Dim newValue = lookup.EditValue
|
||||
If newValue IsNot Nothing AndAlso newValue.Equals(validValue) Then
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] ✓ erfolgreich wiederhergestellt: [{validValue}]")
|
||||
Else
|
||||
Logger.Error($"RestoreLookupValues -> [{controlName}] ✗ Fehler beim Wiederherstellen! Erwartet: [{validValue}], Ist: [{If(newValue, "NULL")}]")
|
||||
|
||||
' Retry mit BeginUpdate/EndUpdate
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] Versuche alternative Methode mit BeginUpdate...")
|
||||
lookup.Properties.BeginUpdate()
|
||||
Try
|
||||
lookup.EditValue = Nothing
|
||||
Application.DoEvents()
|
||||
lookup.EditValue = validValue
|
||||
Finally
|
||||
lookup.Properties.EndUpdate()
|
||||
End Try
|
||||
|
||||
lookup.Refresh()
|
||||
Application.DoEvents()
|
||||
|
||||
Dim retryValue = lookup.EditValue
|
||||
If retryValue IsNot Nothing AndAlso retryValue.Equals(validValue) Then
|
||||
Logger.Debug($"RestoreLookupValues -> [{controlName}] ✓ Alternative Methode erfolgreich")
|
||||
Else
|
||||
Logger.Error($"RestoreLookupValues -> [{controlName}] ✗ Auch alternative Methode fehlgeschlagen!")
|
||||
End If
|
||||
End If
|
||||
|
||||
Finally
|
||||
' Event-Guard wiederherstellen
|
||||
GlobalLookupEventGuard = oldGuard
|
||||
End Try
|
||||
Else
|
||||
Logger.Info($"RestoreLookupValues -> [{controlName}] Keine gültigen Werte zum Wiederherstellen")
|
||||
End If
|
||||
|
||||
Catch ex As Exception
|
||||
Logger.Error($"RestoreLookupValues -> Fehler bei [{controlName}]: {ex.Message}")
|
||||
Logger.Error(ex)
|
||||
End Try
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
@@ -28,13 +28,31 @@ Namespace ControlCreator
|
||||
Private _FormulaColumnNames As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
|
||||
Private _isRefreshingFormula As Boolean = False ' *** NEU: Flag für Formel-Refresh ***
|
||||
Private _currencySymbol As String = "€"
|
||||
''' <summary>
|
||||
''' SHARED Dictionary: Speichert das aktuelle Währungssymbol PRO GridView (via Name).
|
||||
''' Dies ist notwendig, weil UpdateCurrencyFormat auf einer NEUEN GridControl-Instanz
|
||||
''' aufgerufen wird, der CustomColumnDisplayText-Handler aber auf der URSPRÜNGLICHEN
|
||||
''' Instanz registriert wurde. Durch das Shared Dictionary können alle Instanzen
|
||||
''' auf das aktuelle Symbol zugreifen.
|
||||
''' </summary>
|
||||
Private Shared _CurrencySymbolByGridName As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
|
||||
Public Sub New(pLogConfig As LogConfig, pGridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)), pCurrencySymbol As String)
|
||||
_LogConfig = pLogConfig
|
||||
_Logger = pLogConfig.GetLogger()
|
||||
_GridTables = pGridTables
|
||||
_currencySymbol = pCurrencySymbol
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Setzt den Shared Currency-Cache zurück. Muss beim Laden eines neuen Dokuments
|
||||
''' aufgerufen werden, bevor UpdateCurrencyFormat die neuen Werte schreibt.
|
||||
''' Verhindert, dass ein veraltetes Währungssymbol aus einem vorherigen Dokument
|
||||
''' durch den CustomColumnDisplayText-Handler verwendet wird.
|
||||
''' </summary>
|
||||
Public Shared Sub ResetCurrencySymbolCache()
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
_CurrencySymbolByGridName.Clear()
|
||||
End SyncLock
|
||||
End Sub
|
||||
Public Function CreateGridColumns(pColumnTable As DataTable) As DataTable
|
||||
Dim oDataTable As New DataTable
|
||||
Dim columnsWithExpressions As New List(Of Tuple(Of DataColumn, String))
|
||||
@@ -202,15 +220,129 @@ Namespace ControlCreator
|
||||
Return oEditor
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' Setzt das Währungssymbol für ein bestimmtes Grid im Shared Cache.
|
||||
''' Muss bei JEDEM Dokumentwechsel aufgerufen werden (auch bei EUR),
|
||||
''' damit der CustomColumnDisplayText-Handler sofort den korrekten Wert hat.
|
||||
''' </summary>
|
||||
Public Shared Sub SetCurrencySymbolForGrid(gridName As String, currencySymbol As String)
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
_CurrencySymbolByGridName(gridName) = currencySymbol
|
||||
End SyncLock
|
||||
End Sub
|
||||
' Hilfsroutine: passt NUR das Summary-Item an (ohne FormatInfo)
|
||||
Private Sub ApplyCurrencySummaryFormat(oCol As GridColumn)
|
||||
oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
|
||||
' Variante A: Standard-Währungsformat aus aktueller Kultur
|
||||
' oCol.SummaryItem.DisplayFormat = "SUM: {0:C2}"
|
||||
_Logger.Debug("Applying currency summary format for column [{0}] with symbol [{1}]", oCol.FieldName, _currencySymbol)
|
||||
|
||||
' Variante B: Kulturunabhängig, Symbol explizit anhängen
|
||||
oCol.SummaryItem.DisplayFormat = $"SUM: {{0:N2}} {_currencySymbol}"
|
||||
oCol.SummaryItem.DisplayFormat = $"{{0:N2}} {_currencySymbol}"
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' Aktualisiert die Währungsformatierung für alle CURRENCY-Spalten mit neuem Währungssymbol.
|
||||
''' Betrifft DisplayFormat, ColumnEdit (für editierbare Spalten) und Summary-Footer.
|
||||
''' </summary>
|
||||
''' <summary>
|
||||
'''
|
||||
Public Sub UpdateCurrencyFormat(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl, pNewCurrencySymbol As String)
|
||||
Try
|
||||
_Logger.Info("[UpdateCurrencyFormat] *** START *** [{0}] → [{1}] für [{2}]", _currencySymbol, pNewCurrencySymbol, pGrid.Name)
|
||||
|
||||
' *** KERN-FIX: Speichere Symbol im SHARED Dictionary ***
|
||||
Dim gridName As String = pGrid.Name
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
If _CurrencySymbolByGridName.ContainsKey(gridName) Then
|
||||
_CurrencySymbolByGridName(gridName) = pNewCurrencySymbol
|
||||
Else
|
||||
_CurrencySymbolByGridName.Add(gridName, pNewCurrencySymbol)
|
||||
End If
|
||||
End SyncLock
|
||||
_Logger.Debug("[UpdateCurrencyFormat] Shared Dictionary updated: [{0}] = [{1}]", gridName, pNewCurrencySymbol)
|
||||
|
||||
_currencySymbol = pNewCurrencySymbol
|
||||
|
||||
Dim oCultureInfo As CultureInfo = New CultureInfo("de-DE")
|
||||
oCultureInfo.NumberFormat.CurrencySymbol = _currencySymbol
|
||||
|
||||
Dim riTextEdit As New RepositoryItemTextEdit()
|
||||
riTextEdit.MaskSettings.Configure(Of MaskSettings.Numeric)(
|
||||
Sub(settings)
|
||||
settings.MaskExpression = "c"
|
||||
settings.Culture = oCultureInfo
|
||||
End Sub)
|
||||
riTextEdit.UseMaskAsDisplayFormat = False
|
||||
riTextEdit.DisplayFormat.FormatType = FormatType.Custom
|
||||
riTextEdit.DisplayFormat.FormatString = $"#,##0.00 {_currencySymbol}"
|
||||
_Logger.Debug("[UpdateCurrencyFormat] riTextEdit: DisplayFormat=[{0}]",
|
||||
riTextEdit.DisplayFormat.FormatString)
|
||||
|
||||
pGridView.BeginUpdate()
|
||||
Try
|
||||
' Schritt 1: Altes RepositoryItem entfernen (ohne vorher ColumnEdit=Nothing)
|
||||
' ColumnEdit NICHT auf Nothing setzen – das würde einen Zwischenrender auslösen
|
||||
Dim oldItems = pGrid.RepositoryItems.OfType(Of RepositoryItemTextEdit)().
|
||||
Where(Function(item) item.MaskSettings.MaskExpression = "c").ToList()
|
||||
For Each oldItem In oldItems
|
||||
_Logger.Debug("[UpdateCurrencyFormat] Removing old riTextEdit: DisplayFormat=[{0}]",
|
||||
oldItem.DisplayFormat.FormatString)
|
||||
pGrid.RepositoryItems.Remove(oldItem)
|
||||
Next
|
||||
|
||||
' Schritt 2: CURRENCY-Spalten konfigurieren
|
||||
For Each oCol As GridColumn In pGridView.Columns
|
||||
Dim oColumnData As DataRow = pColumnTable.
|
||||
Select($"SPALTENNAME = '{oCol.FieldName}'").
|
||||
FirstOrDefault()
|
||||
If oColumnData Is Nothing Then Continue For
|
||||
|
||||
If ObjectEx.NotNull(oColumnData.Item("TYPE_COLUMN"), String.Empty).ToString() <> "CURRENCY" Then Continue For
|
||||
|
||||
Dim oIsFormula As Boolean =
|
||||
ObjectEx.NotNull(oColumnData.Item("FORMULA_EXPRESSION"), String.Empty) <> String.Empty
|
||||
|
||||
' DisplayFormat immer aktualisieren
|
||||
oCol.DisplayFormat.FormatType = FormatType.Custom
|
||||
oCol.DisplayFormat.FormatString = $"#,##0.00 {_currencySymbol}"
|
||||
|
||||
If Not oIsFormula AndAlso oCol.OptionsColumn.AllowEdit Then
|
||||
' Direkt neues RepositoryItem setzen – kein Umweg über RepositoryItems-Collection
|
||||
oCol.ColumnEdit = riTextEdit
|
||||
_Logger.Debug("[UpdateCurrencyFormat] ColumnEdit=[{0}] für [{1}]",
|
||||
DirectCast(oCol.ColumnEdit, RepositoryItemTextEdit).DisplayFormat.FormatString,
|
||||
oCol.FieldName)
|
||||
End If
|
||||
|
||||
If ObjectEx.NotNull(oColumnData.Item("SUMMARY_FUNCTION"), String.Empty) =
|
||||
Constants.AGGREGATE_TOTAL_CURRENCY Then
|
||||
oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
|
||||
oCol.SummaryItem.DisplayFormat = $"{{0:N2}} {_currencySymbol}"
|
||||
End If
|
||||
Next
|
||||
Finally
|
||||
pGridView.EndUpdate()
|
||||
End Try
|
||||
|
||||
' *** KEIN DataSource-Rebind ***
|
||||
' DataSource-Rebind (pGrid.DataSource = Nothing / = oCurrentDataSource) wirft
|
||||
' den gecachten Display-Text weg und erzwingt einen Neu-Render durch DevExpress.
|
||||
' Dabei greift DevExpress auf die Mask-Formatierung des RepositoryItems zurück
|
||||
' (z.B. "c" mit EUR-Culture) statt auf DisplayFormat → EUR bleibt sichtbar.
|
||||
' Stattdessen: LayoutChanged + alle Zeilen invalidieren → DevExpress rendert
|
||||
' die Zellen neu mit dem aktualisierten DisplayFormat und ColumnEdit.
|
||||
pGridView.LayoutChanged()
|
||||
For i As Integer = 0 To pGridView.DataRowCount - 1
|
||||
pGridView.InvalidateRow(i)
|
||||
Next
|
||||
pGridView.UpdateTotalSummary()
|
||||
|
||||
_Logger.Info("[UpdateCurrencyFormat] *** END *** _currencySymbol=[{0}]", _currencySymbol)
|
||||
|
||||
Catch ex As Exception
|
||||
_Logger.Error("[UpdateCurrencyFormat] Fehler: {0}", ex.Message)
|
||||
_Logger.Error(ex)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Public Sub ConfigureViewColumns(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl)
|
||||
Dim oShouldDisplayFooter As Boolean = False
|
||||
For Each oCol As GridColumn In pGridView.Columns
|
||||
@@ -256,8 +388,13 @@ Namespace ControlCreator
|
||||
oCol.DisplayFormat.FormatString = "N2"
|
||||
|
||||
Case "CURRENCY"
|
||||
oCol.DisplayFormat.FormatType = FormatType.Custom
|
||||
oCol.DisplayFormat.FormatString = $"N2 {_currencySymbol}"
|
||||
' *** DisplayFormat wird NICHT hier gesetzt ***
|
||||
' ConfigureViewColumnsCurrency übernimmt die CURRENCY-Formatierung
|
||||
' mit dem korrekten _currencySymbol. Dieses Standardformat würde
|
||||
' später von UpdateCurrencyFormat überschrieben werden müssen –
|
||||
' für Formel-Spalten (kein ColumnEdit) greift aber nur DisplayFormat,
|
||||
' weshalb ein falscher Initialwert hier persistent bleibt.
|
||||
_Logger.Debug("CURRENCY column [{0}]: DisplayFormat wird von ConfigureViewColumnsCurrency gesetzt", oCol.FieldName)
|
||||
|
||||
End Select
|
||||
|
||||
@@ -266,15 +403,16 @@ Namespace ControlCreator
|
||||
Select Case oSummaryFunction
|
||||
Case Constants.AGGREGATE_TOTAL_INTEGER
|
||||
oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
|
||||
oCol.SummaryItem.DisplayFormat = "SUM: {0:N0}"
|
||||
oCol.SummaryItem.DisplayFormat = "{0:N0}"
|
||||
oShouldDisplayFooter = True
|
||||
|
||||
Case Constants.AGGREGATE_TOTAL_FLOAT
|
||||
oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
|
||||
oCol.SummaryItem.DisplayFormat = "SUM: {0:N2}"
|
||||
oCol.SummaryItem.DisplayFormat = "{0:N2}"
|
||||
oShouldDisplayFooter = True
|
||||
|
||||
Case Constants.AGGREGATE_TOTAL_CURRENCY
|
||||
_Logger.Debug(Of String)("Applying currency summary format for column [{0}]", oCol.FieldName)
|
||||
ApplyCurrencySummaryFormat(oCol)
|
||||
oShouldDisplayFooter = True
|
||||
|
||||
@@ -311,49 +449,140 @@ Namespace ControlCreator
|
||||
End If
|
||||
End Sub
|
||||
Public Sub ConfigureViewColumnsCurrency(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl)
|
||||
|
||||
Dim oCultureInfo As CultureInfo = New CultureInfo("de-DE")
|
||||
oCultureInfo.NumberFormat.CurrencySymbol = _currencySymbol
|
||||
|
||||
Dim riTextEdit As RepositoryItemTextEdit = New RepositoryItemTextEdit()
|
||||
riTextEdit.MaskSettings.Configure(Of MaskSettings.Numeric)(Sub(settings)
|
||||
settings.MaskExpression = "c"
|
||||
settings.Culture = oCultureInfo
|
||||
End Sub)
|
||||
riTextEdit.UseMaskAsDisplayFormat = True
|
||||
pGrid.RepositoryItems.Add(riTextEdit)
|
||||
riTextEdit.MaskSettings.Configure(Of MaskSettings.Numeric)(
|
||||
Sub(settings)
|
||||
settings.MaskExpression = "c"
|
||||
settings.Culture = oCultureInfo
|
||||
End Sub)
|
||||
riTextEdit.UseMaskAsDisplayFormat = False
|
||||
riTextEdit.DisplayFormat.FormatType = FormatType.Custom
|
||||
riTextEdit.DisplayFormat.FormatString = $"#,##0.00 {_currencySymbol}"
|
||||
|
||||
' *** DIAGNOSE 1: Zustand der RepositoryItems VOR der Zuweisung ***
|
||||
_Logger.Debug("[ConfigureViewColumnsCurrency] riTextEdit erstellt: DisplayFormat=[{0}], HashCode=[{1}]",
|
||||
riTextEdit.DisplayFormat.FormatString, riTextEdit.GetHashCode())
|
||||
_Logger.Debug("[ConfigureViewColumnsCurrency] pGrid.RepositoryItems.Count VOR Schleife=[{0}]",
|
||||
pGrid.RepositoryItems.Count)
|
||||
|
||||
For Each oCol As GridColumn In pGridView.Columns
|
||||
Dim oColumnData As DataRow = pColumnTable.
|
||||
Select($"SPALTENNAME = '{oCol.FieldName}'").
|
||||
FirstOrDefault()
|
||||
If oColumnData Is Nothing Then Continue For
|
||||
|
||||
If oColumnData Is Nothing Then
|
||||
Continue For
|
||||
End If
|
||||
Dim oColumnType As String = ObjectEx.NotNull(oColumnData.Item("TYPE_COLUMN"), String.Empty).ToString()
|
||||
If oColumnType <> "CURRENCY" Then Continue For
|
||||
|
||||
' *** NEU: Prüfe ob Spalte editierbar ist ***
|
||||
If Not oCol.OptionsColumn.AllowEdit Then
|
||||
_Logger.Debug("Skipping ColumnEdit for read-only column [{0}]", oCol.FieldName)
|
||||
Continue For
|
||||
End If
|
||||
|
||||
' Formel-Spalten dürfen kein ColumnEdit bekommen, da der RepositoryItem-Cache
|
||||
' den berechneten Wert nach RefreshRowCell überschreibt.
|
||||
Dim oFormulaExpression = ObjectEx.NotNull(oColumnData.Item("FORMULA_EXPRESSION"), String.Empty)
|
||||
If oFormulaExpression <> String.Empty Then
|
||||
_Logger.Debug("Skipping ColumnEdit assignment for formula column [{0}] – using DisplayFormat only.", oCol.FieldName)
|
||||
Continue For
|
||||
Dim oIsFormula As Boolean = oFormulaExpression <> String.Empty
|
||||
|
||||
If oIsFormula Then
|
||||
oCol.DisplayFormat.FormatType = FormatType.Custom
|
||||
oCol.DisplayFormat.FormatString = $"#,##0.00 {_currencySymbol}"
|
||||
_Logger.Debug("[ConfigureViewColumnsCurrency] Formel-Spalte [{0}]: DisplayFormat=[{1}], RepositoryItems.Count=[{2}]",
|
||||
oCol.FieldName, oCol.DisplayFormat.FormatString, pGrid.RepositoryItems.Count)
|
||||
|
||||
ElseIf oCol.OptionsColumn.AllowEdit Then
|
||||
' *** DIAGNOSE 2: RepositoryItems-Count VOR und NACH ColumnEdit-Zuweisung ***
|
||||
_Logger.Debug("[ConfigureViewColumnsCurrency] [{0}] VOR ColumnEdit: RepositoryItems.Count=[{1}]",
|
||||
oCol.FieldName, pGrid.RepositoryItems.Count)
|
||||
|
||||
oCol.ColumnEdit = riTextEdit
|
||||
|
||||
' *** DIAGNOSE 3: Prüfen ob DevExpress das Item intern zu RepositoryItems hinzugefügt hat ***
|
||||
_Logger.Debug("[ConfigureViewColumnsCurrency] [{0}] NACH ColumnEdit: RepositoryItems.Count=[{1}]",
|
||||
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))
|
||||
|
||||
' *** DIAGNOSE 4: Alle Items in RepositoryItems ausgeben ***
|
||||
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
|
||||
Next
|
||||
|
||||
' *** DIAGNOSE 5: CustomColumnDisplayText – feuert es überhaupt? ***
|
||||
' Temporär direkt hier einen einmaligen Test-Handler registrieren
|
||||
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 oColumnType As String = oColumnData.Item("TYPE_COLUMN")
|
||||
' Prüfe ob Spalte vom Typ CURRENCY ist
|
||||
Dim oColumnData As DataRow = pColumnTable.
|
||||
Select($"SPALTENNAME = '{e.Column.FieldName}'").
|
||||
FirstOrDefault()
|
||||
|
||||
Select Case oColumnType
|
||||
Case "CURRENCY"
|
||||
' *** WICHTIG: NUR ColumnEdit setzen, KEIN DisplayFormat mehr! ***
|
||||
oCol.ColumnEdit = riTextEdit
|
||||
End Select
|
||||
Next
|
||||
If oColumnData IsNot Nothing AndAlso
|
||||
oColumnData.Item("TYPE_COLUMN").ToString() = "CURRENCY" Then
|
||||
|
||||
Try
|
||||
' *** KERN-FIX: Hole Symbol aus SHARED Dictionary statt Instanz-Feld ***
|
||||
Dim currentSymbol As String = _currencySymbol ' Fallback
|
||||
Dim gridName As String = pGrid.Name ' <-- FIX: pGrid statt pControl
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
If _CurrencySymbolByGridName.ContainsKey(gridName) Then
|
||||
currentSymbol = _CurrencySymbolByGridName(gridName)
|
||||
End If
|
||||
End SyncLock
|
||||
|
||||
Dim oValue As Double
|
||||
' *** KRITISCH: Robustes Parsing unabhängig vom Dezimaltrenner ***
|
||||
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()
|
||||
|
||||
' Versuche zuerst deutsches Format (1.234,56)
|
||||
Dim oDeCulture As CultureInfo = New CultureInfo("de-DE")
|
||||
If Double.TryParse(oStringValue, NumberStyles.Currency Or NumberStyles.Number, oDeCulture, oValue) Then
|
||||
' Erfolgreich mit deutschem Format geparst
|
||||
ElseIf Double.TryParse(oStringValue, NumberStyles.Currency Or NumberStyles.Number, CultureInfo.InvariantCulture, oValue) Then
|
||||
' Erfolgreich mit invariantem Format (Punkt als Dezimaltrenner)
|
||||
Else
|
||||
' Fallback: Systemkultur
|
||||
oValue = Convert.ToDouble(oStringValue, CultureInfo.CurrentCulture)
|
||||
End If
|
||||
Else
|
||||
oValue = Convert.ToDouble(e.Value)
|
||||
End If
|
||||
|
||||
' Formatierung IMMER mit deutscher Kultur (Komma als Dezimaltrenner)
|
||||
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)
|
||||
' Fallback: Original-Wert + Symbol
|
||||
Dim fallbackSymbol As String = _currencySymbol
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
If _CurrencySymbolByGridName.ContainsKey(pGrid.Name) Then ' <-- FIX: pGrid statt pControl
|
||||
fallbackSymbol = _CurrencySymbolByGridName(pGrid.Name)
|
||||
End If
|
||||
End SyncLock
|
||||
e.DisplayText = e.Value.ToString() & " " & fallbackSymbol
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
End Sub
|
||||
|
||||
Public Sub ConfigureViewEvents(pColumnTable As DataTable, pGridView As GridView, pControl As Windows.Forms.Control, pControlId As Integer)
|
||||
' Formel-Spalten-Namen einmalig cachen für View_ShowingEditor
|
||||
_FormulaColumnNames.Clear()
|
||||
@@ -405,6 +634,15 @@ Namespace ControlCreator
|
||||
oColumnData.Item("TYPE_COLUMN").ToString() = "CURRENCY" Then
|
||||
|
||||
Try
|
||||
' *** KERN-FIX: Hole Symbol aus SHARED Dictionary statt Instanz-Feld ***
|
||||
Dim currentSymbol As String = _currencySymbol ' Fallback
|
||||
Dim gridName As String = pControl.Name
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
If _CurrencySymbolByGridName.ContainsKey(gridName) Then
|
||||
currentSymbol = _CurrencySymbolByGridName(gridName)
|
||||
End If
|
||||
End SyncLock
|
||||
|
||||
Dim oValue As Double
|
||||
' *** KRITISCH: Robustes Parsing unabhängig vom Dezimaltrenner ***
|
||||
If TypeOf e.Value Is Double OrElse TypeOf e.Value Is Decimal Then
|
||||
@@ -428,13 +666,22 @@ Namespace ControlCreator
|
||||
|
||||
' Formatierung IMMER mit deutscher Kultur (Komma als Dezimaltrenner)
|
||||
Dim oDeCultureInfo As CultureInfo = New CultureInfo("de-DE")
|
||||
e.DisplayText = oValue.ToString("N2", oDeCultureInfo) & " " & _currencySymbol
|
||||
e.DisplayText = oValue.ToString("N2", oDeCultureInfo) & " " & currentSymbol
|
||||
|
||||
_Logger.Debug("[CustomColumnDisplayText] CURRENCY [{0}]: DisplayText=[{1}], Symbol=[{2}] (from Shared Dict in ConfigureViewEvents)",
|
||||
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)
|
||||
' Fallback: Original-Wert + Symbol
|
||||
e.DisplayText = e.Value.ToString() & " " & _currencySymbol
|
||||
Dim fallbackSymbol As String = _currencySymbol
|
||||
SyncLock _CurrencySymbolByGridName
|
||||
If _CurrencySymbolByGridName.ContainsKey(pControl.Name) Then
|
||||
fallbackSymbol = _CurrencySymbolByGridName(pControl.Name)
|
||||
End If
|
||||
End SyncLock
|
||||
e.DisplayText = e.Value.ToString() & " " & fallbackSymbol
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
@@ -442,19 +689,47 @@ Namespace ControlCreator
|
||||
AddHandler pGridView.CustomRowCellEdit, Sub(sender As Object, e As CustomRowCellEditEventArgs)
|
||||
Try
|
||||
For Each oRow As DataRow In pColumnTable.Rows
|
||||
Dim oColumnName = oRow.Item("SPALTENNAME")
|
||||
Dim oColumnName As String = oRow.Item("SPALTENNAME").ToString()
|
||||
If oColumnName <> e.Column.FieldName Then Continue For
|
||||
|
||||
Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, oColumnName)
|
||||
If oColumnName <> e.Column.FieldName Then
|
||||
Continue For
|
||||
End If
|
||||
|
||||
If oEditorExists Then
|
||||
' Combobox/Lookup-Editor aus GridTables: immer zuweisen
|
||||
Dim oEditor = _GridTables.Item(pControlId).Item(oColumnName)
|
||||
_Logger.Debug("Assigning Editor to Column [{0}]", oColumnName)
|
||||
e.RepositoryItem = oEditor
|
||||
Else
|
||||
_Logger.Debug("Editor for Column [{0}] does not exist", oColumnName)
|
||||
Dim oColumnType As String = ObjectEx.NotNull(oRow.Item("TYPE_COLUMN"), String.Empty).ToString()
|
||||
If oColumnType = "CURRENCY" Then
|
||||
' *** KERN-FIX ***
|
||||
' Für CURRENCY-Spalten wird e.RepositoryItem NIEMALS gesetzt.
|
||||
'
|
||||
' Grund: Sobald e.RepositoryItem gesetzt ist, übernimmt das
|
||||
' RepositoryItem die komplette Zelldarstellung – DevExpress
|
||||
' übergeht CustomColumnDisplayText vollständig. Das RepositoryItem
|
||||
' verwendet intern die Mask-Culture ("c" = EUR) zur Anzeige,
|
||||
' unabhängig von DisplayFormat oder _currencySymbol.
|
||||
'
|
||||
' Korrekte Architektur:
|
||||
' - Anzeige (nicht editiert): CustomColumnDisplayText
|
||||
' → formatiert mit _currencySymbol (CHF)
|
||||
' - Bearbeitung (editiert): ColumnEdit an der GridColumn
|
||||
' → wird von DevExpress beim Öffnen
|
||||
' des Editors automatisch verwendet
|
||||
'
|
||||
' CustomRowCellEdit muss NICHT eingreifen – GridColumn.ColumnEdit
|
||||
' ist bereits gesetzt und wird für den Editiermodus korrekt genutzt.
|
||||
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
|
||||
Else
|
||||
_Logger.Debug("Editor for Column [{0}] does not exist", oColumnName)
|
||||
End If
|
||||
End If
|
||||
Exit For
|
||||
Next
|
||||
Catch ex As Exception
|
||||
_Logger.Warn("⚠️ Error in CustomRowCellEdit for [{0}]", e.CellValue)
|
||||
|
||||
1804
app/TaskFlow/Log_Waehrung.txt
Normal file
1804
app/TaskFlow/Log_Waehrung.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' übernehmen, indem Sie "*" eingeben:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2.8.4.0")>
|
||||
<Assembly: AssemblyVersion("2.8.5.0")>
|
||||
<Assembly: AssemblyFileVersion("1.0.0.0")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
|
||||
@@ -1284,6 +1284,7 @@
|
||||
<Content Include="DataColumnExpression.txt" />
|
||||
<Content Include="DD_Icons_ICO_PMANAGER_48px.ico" />
|
||||
<Content Include="DD_taskFLOW_ICON.ico" />
|
||||
<Content Include="Log_Waehrung.txt" />
|
||||
<Content Include="MailLicense.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports WINDREAMLib
|
||||
Imports DigitalData.Controls.LookupGrid
|
||||
Imports DevExpress.XtraGrid
|
||||
Imports DevExpress.XtraGrid.Views.Grid
|
||||
Imports DevExpress.XtraGrid.Columns
|
||||
Imports DevExpress.Xpo.Helpers.AssociatedCollectionCriteriaHelper
|
||||
Imports DevExpress.XtraEditors
|
||||
Imports DevExpress.XtraGrid
|
||||
Imports DevExpress.XtraGrid.Columns
|
||||
Imports DevExpress.XtraGrid.Views.Grid
|
||||
Imports DigitalData.Controls.LookupGrid
|
||||
Imports WINDREAMLib
|
||||
''' <summary>
|
||||
''' Defines common Functions for Checking for and replacing placeholders.
|
||||
''' This Class also includes a child class `Pattern` for passing around Patterns.
|
||||
@@ -150,34 +151,39 @@ Public Class clsPatterns
|
||||
End Function
|
||||
|
||||
Public Shared Function ReplaceAllValues(input As String, panel As DevExpress.XtraEditors.XtraScrollableControl, is_SQL As Boolean) As String
|
||||
Dim oResult = input
|
||||
Try
|
||||
Dim result = input
|
||||
|
||||
If Not HasAnyPatterns(result) Then
|
||||
Return result
|
||||
If Not HasAnyPatterns(oResult) Then
|
||||
Return oResult
|
||||
End If
|
||||
|
||||
LOGGER.Debug($"input BEFORE replacing: [{result}]")
|
||||
result = ReplaceInternalValues(result)
|
||||
LOGGER.Debug($"input BEFORE replacing: [{oResult}]")
|
||||
oResult = ReplaceInternalValues(oResult)
|
||||
|
||||
If Not IsNothing(CURRENT_WMFILE) Then
|
||||
result = ReplaceWindreamIndicies(result, CURRENT_WMFILE, is_SQL)
|
||||
oResult = ReplaceWindreamIndicies(oResult, CURRENT_WMFILE, is_SQL)
|
||||
End If
|
||||
If IDB_ACTIVE = True Then
|
||||
result = ReplaceIDBAttributes(result, is_SQL)
|
||||
oResult = ReplaceIDBAttributes(oResult, is_SQL)
|
||||
End If
|
||||
'vorher hinter result = ReplaceInternalValues(result)
|
||||
result = ReplaceControlValues(result, panel, is_SQL)
|
||||
oResult = ReplaceControlValues(oResult, panel, is_SQL)
|
||||
|
||||
If Not IsNothing(result) Then
|
||||
result = ReplaceUserValues(result)
|
||||
LOGGER.Debug($"input AFTER replacing: [{result}]")
|
||||
If Not IsNothing(oResult) Then
|
||||
oResult = ReplaceUserValues(oResult)
|
||||
LOGGER.Debug($"input AFTER replacing: [{oResult}]")
|
||||
End If
|
||||
|
||||
Return result
|
||||
Return oResult
|
||||
Catch ex As Exception
|
||||
LOGGER.Error(ex)
|
||||
LOGGER.Info("Error in ReplaceAllValues:" & ex.Message)
|
||||
LOGGER.Error($"❌ CRITICAL ERROR in ReplaceAllValues!")
|
||||
LOGGER.Error($" Input: [{input}]")
|
||||
LOGGER.Error($" Last successful result: [{oResult}]")
|
||||
LOGGER.Error($" Exception Type: [{ex.GetType().Name}]")
|
||||
LOGGER.Error($" Message: [{ex.Message}]")
|
||||
LOGGER.Error($" StackTrace: [{ex.StackTrace}]")
|
||||
Return input
|
||||
End Try
|
||||
End Function
|
||||
@@ -354,12 +360,28 @@ Public Class clsPatterns
|
||||
Case GetType(LookupControl3)
|
||||
Dim oLookupControl3 As LookupControl3 = oControl
|
||||
|
||||
If oLookupControl3.Properties.SelectedValues.Count > 1 Then
|
||||
LOGGER.Debug($"LookupControl3 mit mehr als 1 Value")
|
||||
Dim oIndex As Integer = 0
|
||||
For Each oString As String In oLookupControl3.Properties.SelectedValues
|
||||
If oIndex = 0 Then
|
||||
|
||||
' ========== FIX START: NULL-Check ==========
|
||||
Dim selectedValues As List(Of String) = Nothing
|
||||
Try
|
||||
selectedValues = oLookupControl3.Properties.SelectedValues
|
||||
Catch ex As Exception
|
||||
LOGGER.Warn($"⚠️ LookupControl [{oControlName}] SelectedValues not accessible: {ex.Message}")
|
||||
selectedValues = Nothing
|
||||
End Try
|
||||
|
||||
If selectedValues Is Nothing Then
|
||||
LOGGER.Warn($"⚠️ LookupControl [{oControlName}] SelectedValues is Nothing! Using ERROR_REPLACE_VALUE")
|
||||
oReplaceValue = ERROR_REPLACE_VALUE
|
||||
ElseIf selectedValues.Count = 0 Then
|
||||
LOGGER.Warn($"⚠️ LookupControl [{oControlName}] SelectedValues is empty! Using ERROR_REPLACE_VALUE")
|
||||
oReplaceValue = ERROR_REPLACE_VALUE
|
||||
' ========== FIX END ==========
|
||||
ElseIf selectedValues.Count > 1 Then
|
||||
LOGGER.Debug($"LookupControl3 [{oControlName}] mit mehr als 1 Value")
|
||||
Dim oIndex As Integer = 0
|
||||
For Each oString As String In selectedValues
|
||||
If oIndex = 0 Then
|
||||
oReplaceValue = oString
|
||||
Else
|
||||
oReplaceValue += "', '" + oString
|
||||
@@ -367,13 +389,12 @@ Public Class clsPatterns
|
||||
oIndex += 1
|
||||
Next
|
||||
oIsSQL = False
|
||||
ElseIf oLookupControl3.Properties.SelectedValues.Count = 1 Then
|
||||
LOGGER.Debug($"LookupControl3 mit genau einem Value")
|
||||
oReplaceValue = oLookupControl3.Properties.SelectedValues(0)
|
||||
Else
|
||||
' LOGGER.Warn($"SelectedValues of LookUpControl scheint empty oder leer zu sein! Ersetzen mit ErrorReplaceValue!")
|
||||
oReplaceValue = ERROR_REPLACE_VALUE
|
||||
Else ' Count = 1
|
||||
LOGGER.Debug($"LookupControl3 [{oControlName}] mit genau einem Value")
|
||||
oReplaceValue = selectedValues(0)
|
||||
End If
|
||||
|
||||
LOGGER.Debug($"oReplaceValue nach Durchlaufen selectedValues: {oReplaceValue}")
|
||||
LOGGER.Debug($"oReplaceValue nach Durchlaufen selectedValues: {oReplaceValue}")
|
||||
|
||||
Case GetType(Windows.Forms.ComboBox)
|
||||
@@ -381,7 +402,7 @@ Public Class clsPatterns
|
||||
|
||||
Case GetType(CheckBox)
|
||||
Dim oCheckBox As CheckBox = oControl
|
||||
oReplaceValue = oCheckBox.Checked
|
||||
oReplaceValue = If(oCheckBox.Checked, "1", "0") ' Explizite String-Konvertierung
|
||||
|
||||
Case GetType(GridControl)
|
||||
Dim oGrid As GridControl = oControl
|
||||
@@ -406,10 +427,18 @@ Public Class clsPatterns
|
||||
Case Else
|
||||
oReplaceValue = ERROR_REPLACE_VALUE
|
||||
End Select
|
||||
LOGGER.Debug($"[SQL-ESCAPE CHECK] Control: [{oControlName}], oReplaceValue Type: [{If(oReplaceValue?.GetType()?.Name, "NULL")}], Value: [{oReplaceValue}], IsSQL: [{oIsSQL}]")
|
||||
If oReplaceValue Is Nothing Then
|
||||
LOGGER.Warn($"⚠️ oReplaceValue is Nothing for control [{oControlName}]! Setting to ERROR_REPLACE_VALUE")
|
||||
oReplaceValue = ERROR_REPLACE_VALUE
|
||||
End If
|
||||
|
||||
If Not TypeOf oReplaceValue Is String Then
|
||||
LOGGER.Warn($"⚠️ oReplaceValue is not a String for control [{oControlName}]! Type: [{oReplaceValue.GetType().Name}]. Converting to String.")
|
||||
oReplaceValue = oReplaceValue.ToString()
|
||||
End If
|
||||
If oIsSQL = True Then
|
||||
'LOGGER.Debug($"IS_SQL = True - oReplaceValue = {oReplaceValue}")
|
||||
'LOGGER.Debug($"oReplaceValue = {oReplaceValue}")
|
||||
oReplaceValue = oReplaceValue.Replace("'", "''")
|
||||
oReplaceValue = SafeSqlEscape(oReplaceValue)
|
||||
End If
|
||||
oResult = ReplacePattern(oResult, PATTERN_CTRL, oReplaceValue)
|
||||
Else
|
||||
@@ -425,16 +454,46 @@ Public Class clsPatterns
|
||||
Return oResult
|
||||
End Try
|
||||
End Function
|
||||
Private Shared Function SafeSqlEscape(value As Object) As String
|
||||
LOGGER.Debug($"[SafeSqlEscape] Input Type: [{If(value?.GetType()?.Name, "NULL")}], Value: [{value}]")
|
||||
|
||||
If value Is Nothing Then
|
||||
LOGGER.Warn("[SafeSqlEscape] Value is Nothing → returning ERROR_REPLACE_VALUE")
|
||||
Return ERROR_REPLACE_VALUE
|
||||
End If
|
||||
|
||||
Dim strValue As String
|
||||
Try
|
||||
strValue = value.ToString()
|
||||
Catch ex As Exception
|
||||
LOGGER.Error(ex)
|
||||
LOGGER.Warn($"[SafeSqlEscape] ToString() failed: {ex.Message} → returning ERROR_REPLACE_VALUE")
|
||||
Return ERROR_REPLACE_VALUE
|
||||
End Try
|
||||
|
||||
If String.IsNullOrEmpty(strValue) Then
|
||||
LOGGER.Warn("[SafeSqlEscape] String is empty → returning ERROR_REPLACE_VALUE")
|
||||
Return ERROR_REPLACE_VALUE
|
||||
End If
|
||||
|
||||
Dim escaped = strValue.Replace("'", "''")
|
||||
LOGGER.Debug($"[SafeSqlEscape] Output: [{escaped}]")
|
||||
Return escaped
|
||||
End Function
|
||||
Public Shared Function ReplaceWindreamIndicies(pInput As String, pDocument As WMObject, pIsSQL As Boolean) As String
|
||||
Try
|
||||
Dim oResult = pInput
|
||||
Dim oTryCounter As Integer = 0
|
||||
|
||||
While ContainsPattern(oResult, PATTERN_WMI)
|
||||
|
||||
Dim oWMValue As String
|
||||
Dim oIndexName As String = GetNextPattern(oResult, PATTERN_WMI).Value
|
||||
Dim oWMValue As String = pDocument.GetVariableValue(oIndexName)
|
||||
If oIndexName = "@@DISPLAY_ONLY" Then
|
||||
oWMValue = String.Empty
|
||||
Else
|
||||
oWMValue = pDocument.GetVariableValue(oIndexName)
|
||||
End If
|
||||
|
||||
|
||||
If IsNothing(oWMValue) And oTryCounter = MAX_TRY_COUNT Then
|
||||
Throw New Exception("Max tries in ReplaceWindreamIndicies exceeded.")
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
|
||||
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
|
||||
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADw
|
||||
CAAAAk1TRnQBSQFMAgEBAgEAAfABCwHwAQsBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
|
||||
CAAAAk1TRnQBSQFMAgEBAgEAAfgBCwH4AQsBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
|
||||
AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
|
||||
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
|
||||
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
|
||||
@@ -1506,13 +1506,13 @@
|
||||
<value>Aktionen</value>
|
||||
</data>
|
||||
<data name="RibbonControl1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>1178, 158</value>
|
||||
<value>1178, 126</value>
|
||||
</data>
|
||||
<data name="RibbonStatusBar1.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>0, 666</value>
|
||||
<value>0, 670</value>
|
||||
</data>
|
||||
<data name="RibbonStatusBar1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>1178, 22</value>
|
||||
<value>1178, 18</value>
|
||||
</data>
|
||||
<data name=">>RibbonStatusBar1.Name" xml:space="preserve">
|
||||
<value>RibbonStatusBar1</value>
|
||||
@@ -1539,7 +1539,7 @@
|
||||
<value>4</value>
|
||||
</data>
|
||||
<data name="GridControlWorkflows.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>945, 484</value>
|
||||
<value>945, 549</value>
|
||||
</data>
|
||||
<data name="GridControlWorkflows.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>10</value>
|
||||
@@ -1782,7 +1782,7 @@
|
||||
<value>233</value>
|
||||
</data>
|
||||
<data name="NavBarControl1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>233, 508</value>
|
||||
<value>233, 573</value>
|
||||
</data>
|
||||
<data name="NavBarControl1.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>5</value>
|
||||
@@ -1809,10 +1809,10 @@
|
||||
<value>Tahoma, 9pt</value>
|
||||
</data>
|
||||
<data name="Panel1.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>0, 158</value>
|
||||
<value>0, 101</value>
|
||||
</data>
|
||||
<data name="Panel1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>1178, 508</value>
|
||||
<value>1178, 573</value>
|
||||
</data>
|
||||
<data name="Panel1.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>4</value>
|
||||
@@ -2111,7 +2111,7 @@
|
||||
<value>9, 19</value>
|
||||
</data>
|
||||
<data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
|
||||
<value>1178, 688</value>
|
||||
<value>1473, 860</value>
|
||||
</data>
|
||||
<data name="$this.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Tahoma, 12pt</value>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -468,7 +468,55 @@ Public Class frmValidatorSearch
|
||||
ToolStripDropDownButtonFile.Visible = False
|
||||
End Sub
|
||||
|
||||
Private Sub EnsureFormIsVisible()
|
||||
Try
|
||||
' Aktuellen Bildschirm basierend auf der Formularposition ermitteln
|
||||
Dim currentScreen As Screen = Screen.FromPoint(Me.Location)
|
||||
Dim workingArea As Rectangle = currentScreen.WorkingArea
|
||||
|
||||
' Prüfen ob das Formular vollständig außerhalb des sichtbaren Bereichs liegt
|
||||
Dim formBounds As New Rectangle(Me.Location, Me.Size)
|
||||
|
||||
' Wenn das Formular nicht mit dem Arbeitsbereich überschneidet
|
||||
If Not workingArea.IntersectsWith(formBounds) Then
|
||||
CenterFormOnScreen()
|
||||
Exit Sub
|
||||
End If
|
||||
|
||||
' Optional: Prüfen ob das Formular zu weit außerhalb liegt (z.B. nur 20% sichtbar)
|
||||
Dim visibleArea As Rectangle = Rectangle.Intersect(workingArea, formBounds)
|
||||
Dim visiblePercentage As Double = (visibleArea.Width * visibleArea.Height) / (formBounds.Width * formBounds.Height)
|
||||
|
||||
If visiblePercentage < 0.2 Then ' Weniger als 20% sichtbar
|
||||
CenterFormOnScreen()
|
||||
End If
|
||||
|
||||
Catch ex As Exception
|
||||
LOGGER.Error(ex)
|
||||
' Bei Fehler sicherheitshalber zentrieren
|
||||
CenterFormOnScreen()
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Private Sub CenterFormOnScreen()
|
||||
Try
|
||||
' Formular auf dem primären Bildschirm zentrieren
|
||||
Me.StartPosition = FormStartPosition.CenterScreen
|
||||
|
||||
' Alternative: Auf aktuellem Bildschirm zentrieren
|
||||
Dim currentScreen As Screen = Screen.PrimaryScreen
|
||||
Dim x As Integer = currentScreen.WorkingArea.Left + (currentScreen.WorkingArea.Width - Me.Width) \ 2
|
||||
Dim y As Integer = currentScreen.WorkingArea.Top + (currentScreen.WorkingArea.Height - Me.Height) \ 2
|
||||
Me.Location = New Point(x, y)
|
||||
|
||||
LOGGER.Info("Formular wurde zentriert, da es außerhalb des sichtbaren Bereichs lag")
|
||||
Catch ex As Exception
|
||||
LOGGER.Error(ex)
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub frmValidatorSearch_Shown(sender As Object, e As EventArgs) Handles Me.Shown
|
||||
' Prüfen ob das Formular im sichtbaren Bereich liegt
|
||||
EnsureFormIsVisible()
|
||||
formLoaded = True
|
||||
End Sub
|
||||
|
||||
|
||||
Reference in New Issue
Block a user