diff --git a/app/TaskFlow/ClassControlCreator.vb b/app/TaskFlow/ClassControlCreator.vb
index 428cd5d..184d70f 100644
--- a/app/TaskFlow/ClassControlCreator.vb
+++ b/app/TaskFlow/ClassControlCreator.vb
@@ -69,6 +69,8 @@ Public Class ClassControlCreator
Public Property GridTables As New Dictionary(Of Integer, Dictionary(Of String, RepositoryItem))
Public Property GridColumns As New Dictionary(Of Integer, DataTable)
+
+
'''
''' Standard Eigenschaften für alle Controls
'''
@@ -533,12 +535,11 @@ Public Class ClassControlCreator
End Function
Public Function CreateExistingGridControl(row As DataRow, DT_MY_COLUMNS As DataTable, designMode As Boolean, pcurrencySymbol As String) As GridControl
- Dim oGridControlCreator = New ControlCreator.GridControl(LogConfig, GridTables)
+ Dim oGridControlCreator = New ControlCreator.GridControl(LogConfig, GridTables, pcurrencySymbol)
Dim oControl As GridControl = CreateBaseControl(New GridControl(), row, designMode)
Dim oControlId = DirectCast(oControl.Tag, ControlMetadata).Guid
Dim oView As GridView
Dim oControlName = oControl.Name
-
oControl.ForceInitialize()
oView = oControl.MainView
@@ -635,9 +636,13 @@ Public Class ClassControlCreator
End Try
End If
- oGridControlCreator.ConfigureViewColumns(DT_MY_COLUMNS, oView, oControl, pcurrencySymbol)
+ ' *** KORRIGIERT: ConfigureViewColumns OHNE currencySymbol-Parameter ***
+ oGridControlCreator.ConfigureViewColumns(DT_MY_COLUMNS, oView, oControl)
+
+ ' *** NEU: ConfigureViewColumnsCurrency() für editierbare Währungsspalten ***
+ oGridControlCreator.ConfigureViewColumnsCurrency(DT_MY_COLUMNS, oView, oControl)
+
oGridControlCreator.ConfigureViewEvents(DT_MY_COLUMNS, oView, oControl, oControlId)
- ' 08.11.2021: Fix editor being empty on first open
oView.FocusInvalidRow()
Return oControl
diff --git a/app/TaskFlow/ClassFormat.vb b/app/TaskFlow/ClassFormat.vb
index 40e1923..331c44b 100644
--- a/app/TaskFlow/ClassFormat.vb
+++ b/app/TaskFlow/ClassFormat.vb
@@ -28,6 +28,60 @@ Public Class ClassFormat
End Select
End Function
+ '''
+ ''' Normalisiert einen numerischen String für die invariante Kultur-Konvertierung.
+ ''' Entfernt Tausendertrennzeichen und ersetzt Dezimaltrennzeichen durch Punkt.
+ '''
+ Private Shared Function NormalizeNumericString(pValue As String) As String
+ If String.IsNullOrWhiteSpace(pValue) Then
+ Return pValue
+ End If
+
+ Dim normalized As String = pValue.Trim()
+
+ ' Entferne Währungssymbole und Leerzeichen
+ normalized = System.Text.RegularExpressions.Regex.Replace(normalized, "[€$£¥\s]", "")
+
+ ' Prüfe, ob der String sowohl Punkt als auch Komma enthält
+ Dim hasDot As Boolean = normalized.Contains(".")
+ Dim hasComma As Boolean = normalized.Contains(",")
+
+ If hasDot AndAlso hasComma Then
+ ' Beide vorhanden: Das letzte ist der Dezimaltrenner
+ Dim lastDotPos As Integer = normalized.LastIndexOf(".")
+ Dim lastCommaPos As Integer = normalized.LastIndexOf(",")
+
+ If lastDotPos > lastCommaPos Then
+ ' Punkt ist Dezimaltrenner, Komma ist Tausendertrenner
+ normalized = normalized.Replace(",", "")
+ Else
+ ' Komma ist Dezimaltrenner, Punkt ist Tausendertrenner
+ normalized = normalized.Replace(".", "").Replace(",", ".")
+ End If
+ ElseIf hasComma Then
+ ' Nur Komma: Könnte Dezimal- oder Tausendertrenner sein
+ ' Wenn mehr als ein Komma → Tausendertrenner
+ ' Wenn nur ein Komma und <= 3 Stellen danach → Dezimaltrenner
+ Dim commaCount As Integer = normalized.Count(Function(c) c = ","c)
+ If commaCount = 1 Then
+ Dim lastCommaPos As Integer = normalized.LastIndexOf(",")
+ Dim digitsAfterComma As Integer = normalized.Length - lastCommaPos - 1
+ If digitsAfterComma <= 3 Then
+ ' Wahrscheinlich Dezimaltrenner
+ normalized = normalized.Replace(",", ".")
+ Else
+ ' Wahrscheinlich Tausendertrenner
+ normalized = normalized.Replace(",", "")
+ End If
+ Else
+ ' Mehrere Kommas → Tausendertrenner
+ normalized = normalized.Replace(",", "")
+ End If
+ End If
+ ' Wenn nur Punkt vorhanden: bereits im richtigen Format
+
+ Return normalized
+ End Function
'''
''' Converts a string according to the type information, using the invariant culture
@@ -41,25 +95,35 @@ Public Class ClassFormat
Select Case pType
Case ClassControlCreator.CONTROL_TYPE_DOUBLE
- If Double.TryParse(pValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
- Return oConvertedValue
- End If
-
- Case ClassControlCreator.CONTROL_TYPE_CURRENCY
Try
- LOGGER.Debug($"GetConvertedValue: Converting {pValue.ToString} to Currency ")
- If Double.TryParse(pValue, NumberStyles.Currency, CultureInfo.InvariantCulture, oConvertedValue) Then
+ Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
+ If Double.TryParse(normalizedValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
Return oConvertedValue
End If
Catch ex As Exception
LOGGER.Error(ex)
End Try
+ Case ClassControlCreator.CONTROL_TYPE_CURRENCY
+ Try
+ Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
+ LOGGER.Debug($"GetConvertedValue: Converting {pValue.ToString} (normalized: {normalizedValue}) to Currency ")
+ If Double.TryParse(normalizedValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
+ Return oConvertedValue
+ End If
+ Catch ex As Exception
+ LOGGER.Error(ex)
+ End Try
Case ClassControlCreator.CONTROL_TYPE_INTEGER
- If Integer.TryParse(pValue, NumberStyles.Integer, CultureInfo.InvariantCulture, oConvertedValue) Then
- Return oConvertedValue
- End If
+ Try
+ Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
+ If Integer.TryParse(normalizedValue, NumberStyles.Integer, CultureInfo.InvariantCulture, oConvertedValue) Then
+ Return oConvertedValue
+ End If
+ Catch ex As Exception
+ LOGGER.Error(ex)
+ End Try
Case Else
LOGGER.Debug($"GetConvertedValue - Case ELSE - pType is {pType}")
Try
diff --git a/app/TaskFlow/ClassIDBData.vb b/app/TaskFlow/ClassIDBData.vb
index ca0542c..35a7efd 100644
--- a/app/TaskFlow/ClassIDBData.vb
+++ b/app/TaskFlow/ClassIDBData.vb
@@ -209,7 +209,7 @@
If IDB_USES_WMFILESTORE Then
oID_IS_FOREIGN = 1
End If
-
+ oTerm2Delete = oTerm2Delete.Replace("'", "''")
Dim oDELSQL = $"EXEC PRIDB_DELETE_TERM_OBJECT_METADATA {CURRENT_DOC_ID},'{oAttributeName}','{oTerm2Delete}','{USER_USERNAME}','{USER_LANGUAGE}',{oID_IS_FOREIGN};"
LOGGER.Debug($"Delete_Term_Object_From_Metadata: {oDELSQL}")
'DatabaseFallback.ExecuteNonQueryIDB(oDELSQL)
@@ -322,6 +322,7 @@
Return True
Else
'oNewValue = oNewValue.Replace("'", "' + NCHAR(39) + '")
+ oNewValue = oNewValue.Replace("'", "''")
Dim oPRIDB_NEW_OBJ_DATA = $"DECLARE @NEW_OBJ_MD_ID BIGINT " & vbNewLine & $"EXEC PRIDB_NEW_OBJ_DATA {CURRENT_DOC_ID},'{oAttributeName}','{USER_USERNAME}','{oNewValue}','{USER_LANGUAGE}',0,@OMD_ID = @NEW_OBJ_MD_ID OUTPUT;"
LOGGER.Debug(oPRIDB_NEW_OBJ_DATA)
' Return DatabaseFallback.ExecuteNonQueryIDB(oPRIDB_NEW_OBJ_DATA)
diff --git a/app/TaskFlow/ControlCreator/GridControl.vb b/app/TaskFlow/ControlCreator/GridControl.vb
index 918b71d..b2a6eca 100644
--- a/app/TaskFlow/ControlCreator/GridControl.vb
+++ b/app/TaskFlow/ControlCreator/GridControl.vb
@@ -27,10 +27,12 @@ Namespace ControlCreator
Private isApplyingInheritedValue As Boolean
Private _FormulaColumnNames As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
Private _isRefreshingFormula As Boolean = False ' *** NEU: Flag für Formel-Refresh ***
- Public Sub New(pLogConfig As LogConfig, pGridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)))
+ Private _currencySymbol As String = "€"
+ 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
Public Function CreateGridColumns(pColumnTable As DataTable) As DataTable
@@ -201,15 +203,15 @@ Namespace ControlCreator
End If
End Function
' Hilfsroutine: passt NUR das Summary-Item an (ohne FormatInfo)
- Private Sub ApplyCurrencySummaryFormat(oCol As GridColumn, currencySymbol As String)
+ 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}"
' Variante B: Kulturunabhängig, Symbol explizit anhängen
- oCol.SummaryItem.DisplayFormat = $"SUM: {{0:N2}} {currencySymbol}"
+ oCol.SummaryItem.DisplayFormat = $"SUM: {{0:N2}} {_currencySymbol}"
End Sub
- Public Sub ConfigureViewColumns(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl, pcurrencySymbol As String)
+ 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
Dim oColumnData As DataRow = pColumnTable.
@@ -255,7 +257,8 @@ Namespace ControlCreator
Case "CURRENCY"
oCol.DisplayFormat.FormatType = FormatType.Custom
- oCol.DisplayFormat.FormatString = "C2"
+ oCol.DisplayFormat.FormatString = $"N2 {_currencySymbol}"
+
End Select
Dim oSummaryFunction As String = oColumnData.Item("SUMMARY_FUNCTION")
@@ -272,7 +275,7 @@ Namespace ControlCreator
oShouldDisplayFooter = True
Case Constants.AGGREGATE_TOTAL_CURRENCY
- ApplyCurrencySummaryFormat(oCol, pcurrencySymbol)
+ ApplyCurrencySummaryFormat(oCol)
oShouldDisplayFooter = True
Case Constants.AGGREGATE_TOTAL_AVG
@@ -307,10 +310,10 @@ Namespace ControlCreator
End With
End If
End Sub
- Public Sub ConfigureViewColumnsCurrency(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl, pCurrency As String)
+ 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 = pCurrency
+ oCultureInfo.NumberFormat.CurrencySymbol = _currencySymbol
Dim riTextEdit As RepositoryItemTextEdit = New RepositoryItemTextEdit()
riTextEdit.MaskSettings.Configure(Of MaskSettings.Numeric)(Sub(settings)
settings.MaskExpression = "c"
@@ -328,6 +331,12 @@ Namespace ControlCreator
Continue For
End If
+ ' *** 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)
@@ -340,7 +349,7 @@ Namespace ControlCreator
Select Case oColumnType
Case "CURRENCY"
- oCol.DisplayFormat.FormatType = FormatType.Custom
+ ' *** WICHTIG: NUR ColumnEdit setzen, KEIN DisplayFormat mehr! ***
oCol.ColumnEdit = riTextEdit
End Select
Next
@@ -380,6 +389,55 @@ Namespace ControlCreator
newRowModified = False
End Try
End Sub
+ ' *** NEU: CustomColumnDisplayText für robuste CURRENCY-Formatierung ***
+ 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
+
+ ' Prüfe ob Spalte vom Typ CURRENCY ist
+ 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 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) & " " & _currencySymbol
+
+ 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
+ End Try
+ End If
+ End Sub
AddHandler pGridView.CustomRowCellEdit, Sub(sender As Object, e As CustomRowCellEditEventArgs)
Try
@@ -800,16 +858,7 @@ Namespace ControlCreator
Return entry
End Function
- Private Sub View_CustomColumnDisplayText(ByVal eSender As Object, ByVal e As CustomColumnDisplayTextEventArgs)
- If IsNothing(e.Value) Then
- Exit Sub
- End If
- Dim view As GridView = eSender
- 'Dim view As GridView = TryCast(GridView1, GridView)
- If e.Column.FieldName = "SpalteCurrency" Then
- ' e.DisplayText = e.Value.ToString().Replace("€", "CHF")
- End If
- End Sub
+
Private Sub View_PopupMenuShowing(sender As Object, e As PopupMenuShowingEventArgs)
Dim view As GridView = TryCast(sender, GridView)
Dim oFocusedColumn As GridColumn = view.FocusedColumn
diff --git a/app/TaskFlow/My Project/AssemblyInfo.vb b/app/TaskFlow/My Project/AssemblyInfo.vb
index a7bdb3a..791a22a 100644
--- a/app/TaskFlow/My Project/AssemblyInfo.vb
+++ b/app/TaskFlow/My Project/AssemblyInfo.vb
@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
-
+
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
'
-
+
diff --git a/app/TaskFlow/frmMain.resx b/app/TaskFlow/frmMain.resx
index 77b0e07..89056e1 100644
--- a/app/TaskFlow/frmMain.resx
+++ b/app/TaskFlow/frmMain.resx
@@ -125,7 +125,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADw
- CAAAAk1TRnQBSQFMAgEBAgEAAcgBCwHIAQsBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+ CAAAAk1TRnQBSQFMAgEBAgEAAfABCwHwAQsBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
@@ -1496,36 +1496,9 @@
0, 0
-
- Allgemein
-
-
- Auswertungen
-
-
- Verwaltung
-
-
- Grundeinstellungen
-
-
- Workflow
-
-
- Funktionen/App Start
-
-
- Ad Hoc Workflows
-
Start
-
- Funktionen
-
-
- Workflow Tabelle
-
Tabelle
@@ -1583,24 +1556,6 @@
0
-
- True
-
-
- Tahoma, 9.75pt, style=Bold
-
-
- 3, 3
-
-
- 127, 16
-
-
- 0
-
-
- Choose a profile ...
-
lblCaptionMainGrid
@@ -1692,12 +1647,6 @@
863, 17
-
- 219, 26
-
-
- Starte Validierung für Profil
-
220, 30
@@ -1880,6 +1829,69 @@
2
+
+ Allgemein
+
+
+ Auswertungen
+
+
+ Verwaltung
+
+
+ Grundeinstellungen
+
+
+ Workflow
+
+
+ Funktionen/App Start
+
+
+ Ad Hoc Workflows
+
+
+ Funktionen
+
+
+ Workflow Tabelle
+
+
+ True
+
+
+ Tahoma, 9.75pt, style=Bold
+
+
+ 3, 3
+
+
+ 127, 16
+
+
+ 0
+
+
+ Choose a profile ...
+
+
+ lblCaptionMainGrid
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Panel2
+
+
+ 0
+
+
+ 219, 26
+
+
+ Starte Validierung für Profil
+
986, 17
@@ -1889,27 +1901,6 @@
1220, 17
-
- 290, 30
-
-
- Popup Erinnerung deaktivieren
-
-
- 287, 6
-
-
- 290, 30
-
-
- In den Vordergrund
-
-
- 290, 30
-
-
- Out of Range - Fenster wiederherstellen
-
291, 100
@@ -2005,6 +1996,27 @@
True
+
+ 290, 30
+
+
+ Popup Erinnerung deaktivieren
+
+
+ 287, 6
+
+
+ 290, 30
+
+
+ In den Vordergrund
+
+
+ 290, 30
+
+
+ Out of Range - Fenster wiederherstellen
+
605, 17
@@ -3432,6 +3444,9 @@
&Ansicht
+
+ &Hintergrund
+
&Seiten Layout
@@ -3444,9 +3459,6 @@
Bars
-
- &Hintergrund
-
PDF Dokument
diff --git a/app/TaskFlow/frmMain.vb b/app/TaskFlow/frmMain.vb
index 0da3c4f..6e3dbef 100644
--- a/app/TaskFlow/frmMain.vb
+++ b/app/TaskFlow/frmMain.vb
@@ -1977,6 +1977,8 @@ Public Class frmMain
Dim useWaitCursorApplied As Boolean = False
Dim previousMessage As String = bsiMessage.Caption
Dim messageApplied As Boolean = False
+ Dim oHandle As Object = Nothing
+ Dim overlayStartedHere As Boolean = False
If LOG_HOTSPOTS Then
perfStart = DateTime.Now
@@ -1987,6 +1989,14 @@ Public Class frmMain
CURRENT_ProfilGUID = pProfilID
WM_AHWF_docPath = String.Empty
+ ' ========== OVERLAY ANZEIGEN ==========
+ If Not _overlayActive Then
+ oHandle = SplashScreenManager.ShowOverlayForm(Me)
+ _overlayActive = True
+ overlayStartedHere = True
+ End If
+
+
' ========== UI-VORBEREITUNG ==========
Me.UseWaitCursor = True
useWaitCursorApplied = True
@@ -2091,6 +2101,13 @@ Public Class frmMain
MsgBox("Unexpected error in Load_Profil_from_Grid: " & ex.Message & vbNewLine & ADDITIONAL_TITLE & " will try to reload the overview - Please try again!", MsgBoxStyle.Information, ADDITIONAL_TITLE)
Dim task = Decide_Load(False, True)
Finally
+ ' ========== OVERLAY SCHLIESSEN (FALLBACK) ==========
+ If overlayStartedHere AndAlso _overlayActive Then
+ SplashScreenManager.CloseOverlayForm(oHandle)
+ LOGGER.Debug("Overlay closed in Load_Profil_from_Grid")
+ _overlayActive = False
+ End If
+
' ========== UI AUFRÄUMEN ==========
If useWaitCursorApplied Then
Me.UseWaitCursor = False
diff --git a/app/TaskFlow/frmValidator.vb b/app/TaskFlow/frmValidator.vb
index 77b7cab..cea128c 100644
--- a/app/TaskFlow/frmValidator.vb
+++ b/app/TaskFlow/frmValidator.vb
@@ -135,9 +135,10 @@ Public Class frmValidator
Private _CachedControlsByGuid As Dictionary(Of Integer, Control)
Private _isUpdatingLookup As Boolean = False
Private _suppressLookupEvents As Boolean = False
- Private _overlayActive As Boolean = False
Private _isShowingErrorDialog As Boolean = False ' ← NEU: Klassenvariable oben hinzufügen
Private _overlayHandle As Object = Nothing ' ← NEU: Klassenvariable
+ Private _overlayRefCount As Integer = 0 ' ← NEU: Zähler für verschachtelte Calls
+ Private _overlayLock As New Object() ' ← NEU: Thread-Safe Lock
Private Class Translation_Strings
@@ -163,7 +164,70 @@ Public Class frmValidator
End Sub
+ ' ========== NEU: Zentrale Overlay-Verwaltung ==========
+ '''
+ ''' Thread-sicheres Öffnen des Overlays (verschachtelte Calls werden ignoriert)
+ '''
+ Private Function ShowOverlaySafe() As Boolean
+ SyncLock _overlayLock
+ If _overlayRefCount = 0 Then
+ Try
+ _overlayHandle = SplashScreenManager.ShowOverlayForm(Me)
+ MyValidationLogger.Debug($"[Overlay] Geöffnet (RefCount: 0 → 1)")
+ Catch ex As Exception
+ MyValidationLogger.Error($"[Overlay] Fehler beim Öffnen: {ex.Message}")
+ Return False
+ End Try
+ Else
+ MyValidationLogger.Debug($"[Overlay] Bereits offen (RefCount: {_overlayRefCount} → {_overlayRefCount + 1})")
+ End If
+ _overlayRefCount += 1
+ Return True
+ End SyncLock
+ End Function
+ '''
+ ''' Thread-sicheres Schließen des Overlays (nur wenn RefCount = 0)
+ '''
+ Private Sub CloseOverlaySafe()
+ SyncLock _overlayLock
+ If _overlayRefCount > 0 Then
+ _overlayRefCount -= 1
+ MyValidationLogger.Debug($"[Overlay] RefCount: {_overlayRefCount + 1} → {_overlayRefCount}")
+ End If
+
+ If _overlayRefCount = 0 AndAlso _overlayHandle IsNot Nothing Then
+ Try
+ SplashScreenManager.CloseOverlayForm(_overlayHandle)
+ MyValidationLogger.Debug("[Overlay] ✓ Geschlossen")
+ Catch ex As Exception
+ MyValidationLogger.Warn($"[Overlay] Fehler beim Schließen: {ex.Message}")
+ Finally
+ _overlayHandle = Nothing
+ End Try
+ End If
+ End SyncLock
+ End Sub
+
+ '''
+ ''' Erzwingt sofortiges Schließen (nur für Fehlerfall / FormClosing)
+ '''
+ Private Sub ForceCloseOverlay()
+ SyncLock _overlayLock
+ If _overlayHandle IsNot Nothing Then
+ Try
+ SplashScreenManager.CloseOverlayForm(_overlayHandle)
+ MyValidationLogger.Debug("[Overlay] ⚠️ FORCE CLOSED")
+ Catch ex As Exception
+ MyValidationLogger.Warn($"[Overlay] Force-Close failed: {ex.Message}")
+ Finally
+ _overlayHandle = Nothing
+ _overlayRefCount = 0
+ End Try
+ End If
+ End SyncLock
+ End Sub
+ ' ========== ENDE NEU ==========
Private Function GetOperationMode() As OperationMode
Dim oOperationMode As OperationMode
@@ -2015,6 +2079,11 @@ Public Class frmValidator
End Try
End Sub
Private Sub GridView_CellValueChanged(sender As Object, e As DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs)
+ ' Prevent cascading events during validation
+ If _isShowingErrorDialog Then
+ MyValidationLogger.Debug("GridView_CellValueChanged suppressed (currently showing error dialog)")
+ Return
+ End If
Dim oView As GridView = sender
Dim oGrid As GridControl = oView.GridControl
Dim oMeta As ClassControlCreator.ControlMetadata = oGrid.Tag
@@ -2439,7 +2508,10 @@ Public Class frmValidator
Catch ex As Exception
MyValidationLogger.Error(ex)
- MyValidationLogger.Warn($"⚠️ Error while Control2Set for [{oControlname2Set}]: " & ex.Message)
+ MyValidationLogger.Warn($"⚠️ SetControlValues_FromControl - Error in SetControlValues_FromControl for control [{pControl?.Name}]: {ex.Message}")
+ ' WICHTIG: Overlay schließen bei Fehler
+ CloseOverlaySafe()
+
Finally
_SetControlValue_In_Action = False
End Try
@@ -2806,6 +2878,12 @@ Public Class frmValidator
End Sub
Public Sub OnCmbselectedIndex(sender As System.Object, e As System.EventArgs)
+ ' Prevent cascading events during validation
+ If _isShowingErrorDialog Then
+ MyValidationLogger.Debug($"OnCmbselectedIndex: Currently showing error dialog, skipping event handling to prevent cascade.")
+ Return
+ End If
+
Dim oCombobox As Windows.Forms.ComboBox = sender
If oCombobox.SelectedIndex <> -1 And _Indexe_Loaded = True Then
Dim oMeta As ClassControlCreator.ControlMetadata = oCombobox.Tag
@@ -3261,34 +3339,55 @@ Public Class frmValidator
- Private Function CreateWMObject() As String
- MyValidationLogger.Debug($"in GetWMDocFileString...'")
-
+ Private Function CreateWMObject() As Boolean
Dim oWMOwnPath As String
- If WM_AHWF_docPath <> String.Empty Then
- oWMOwnPath = WM_AHWF_docPath
- WMDocPathWindows = oWMOwnPath
- Else
- oWMOwnPath = WMDocPathWindows.Replace(WMSUFFIX, "")
- End If
-
- MyValidationLogger.Debug($"oWMOwnPath: {oWMOwnPath}")
Try
- Dim oNormalizedPath = WINDREAM_MOD.GetNormalizedPath(oWMOwnPath, 1)
- CURRENT_WMFILE = WINDREAM_MOD.Session.GetWMObjectByPath(WMEntity.WMEntityDocument, oNormalizedPath)
- MyValidationLogger.Debug("CURRENT_WMFILE: [{0}]", CURRENT_WMFILE)
- Return True
+ MyValidationLogger.Debug($"in GetWMDocFileString...'")
+
+
+ If WM_AHWF_docPath <> String.Empty Then
+ oWMOwnPath = WM_AHWF_docPath
+ WMDocPathWindows = oWMOwnPath
+ Else
+ oWMOwnPath = WMDocPathWindows.Replace(WMSUFFIX, "")
+ End If
+
+ Try
+ Dim oNormalizedPath = WINDREAM_MOD.GetNormalizedPath(oWMOwnPath, 1)
+ CURRENT_WMFILE = WINDREAM_MOD.Session.GetWMObjectByPath(WMEntity.WMEntityDocument, oNormalizedPath)
+ MyValidationLogger.Debug("CURRENT_WMFILE: [{0}]", CURRENT_WMFILE)
+ Return True ' ← Erfolg
+
+ Catch comEx As System.Runtime.InteropServices.COMException
+ ' ========== OPTIMIERT: Detaillierte COM-Fehlerbehandlung ==========
+ MyValidationLogger.Error($"COM-Fehler HRESULT: {comEx.ErrorCode:X8}, Pfad: [{oWMOwnPath}]")
+
+ Select Case comEx.ErrorCode
+ Case &H8004C005 ' Objekt nicht gefunden
+ errormessage = $"Dokument [{oWMOwnPath}] existiert nicht in windream!"
+ Case &H80040E14 ' Zugriff verweigert
+ errormessage = $"Keine Berechtigung für [{oWMOwnPath}]!"
+ Case Else
+ errormessage = $"windream-Fehler: {comEx.Message}"
+ End Select
+
+ ' ========== FIX 2: OpenfrmError() statt frmError.ShowDialog() ==========
+ OpenfrmError(errormessage)
+ ' ========== ENDE FIX 2 ==========
+
+ Return False ' ← Fehler
+ End Try
+
Catch ex As Exception
- Dim _err1 As Boolean = False
MyValidationLogger.Error(ex)
MyValidationLogger.Info("Unexpected error creating WMObject(1) in GetWMDocFileString: " & ex.Message)
MyValidationLogger.Info("Error Number: " & Err.Number.ToString)
errormessage = $"Could not create a WMObject(1) for [{oWMOwnPath}]!"
- frmError.ShowDialog()
+ ' ========== FIX 4: Auch hier OpenfrmError() verwenden ==========
+ OpenfrmError(errormessage)
Return False
-
+ ' ========== ENDE FIX 4 ==========
End Try
-
End Function
Private Function GetDocPathWindows(_CheckStandard As Integer)
Try
@@ -3377,7 +3476,6 @@ Public Class frmValidator
Dim oMilliseconts As Double
clsPatterns.ClearControlCache() ' Cache-Invalidierung
-
Dim perfStart As DateTime = DateTime.MinValue
Dim perfLastCheck As DateTime = DateTime.MinValue
If LOG_HOTSPOTS Then
@@ -3676,6 +3774,7 @@ Public Class frmValidator
End If
MyValidationLogger.Debug("frmValidator: LoadNextDocument finished!")
+
Catch ex As Exception
MyValidationLogger.Error(ex)
errormessage = "unexpected error in Load_Next_Document:" & ex.Message
@@ -3686,7 +3785,6 @@ Public Class frmValidator
If layoutSuspended Then
PanelValidatorControl.ResumeLayout()
End If
-
If LOG_HOTSPOTS Then
' ========== DIAGNOSE ENDE ==========
MyValidationLogger.Info($"[INFO] Load_Next_Document ENDE")
@@ -5089,7 +5187,7 @@ Public Class frmValidator
End Sub
Private Sub frmValidation_Shown(sender As Object, e As System.EventArgs) Handles Me.Shown
- Dim oHandle = SplashScreenManager.ShowOverlayForm(Me)
+ ShowOverlaySafe()
Dim perfStart As DateTime = DateTime.MinValue
Dim perfLastCheck As DateTime = DateTime.MinValue
If LOG_HOTSPOTS Then
@@ -5185,49 +5283,69 @@ Public Class frmValidator
End If
MyValidationLogger.Debug("frmValidation_Shown finished!")
+ ' ✅ Overlay explizit schließen
+ Try
+ If SplashScreenManager.Default IsNot Nothing AndAlso SplashScreenManager.Default.IsSplashFormVisible Then
+ SplashScreenManager.CloseForm(False)
+ MyValidationLogger.Debug("✓ Overlay closed in frmValidation_Shown")
+ Else
+ MyValidationLogger.Debug("ℹ Overlay war bereits geschlossen")
+ End If
+ ' ✅ UI-Refresh erzwingen
+ Application.DoEvents()
+ Me.Refresh()
+ Catch ex As Exception
+ MyValidationLogger.Error($"❌ Error closing overlay in Shown: {ex.Message}")
+ End Try
+
If LOG_HOTSPOTS Then
MyValidationLogger.Info($"[PERF] frmValidation_Shown GESAMT: {(DateTime.Now - perfStart).TotalMilliseconds}ms")
End If
Finally
- SplashScreenManager.CloseOverlayForm(oHandle)
+ CloseOverlaySafe()
+ ' ✅ UI-Refresh erzwingen
+ Try
+ Application.DoEvents()
+ Me.Refresh()
+ Me.BringToFront()
+ Catch ex As Exception
+ MyValidationLogger.Error($"❌ Error in UI refresh: {ex.Message}")
+ End Try
End Try
+ Try
+ ' Alle offenen Forms durchsuchen
+ For Each frm As Form In Application.OpenForms
+ If frm.GetType().FullName.StartsWith("DevExpress.XtraSplashScreen") Then
+ MyValidationLogger.Warn($"⚠️ Unerwartete SplashForm gefunden: {frm.Name}")
+ frm.Close()
+ End If
+ Next
+ Catch ex As Exception
+ MyValidationLogger.Error($"Error checking for splash forms: {ex.Message}")
+ End Try
+
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
- ' ========== FIX 1: Button-State-Management ==========
If btnSave.Enabled = False Then
MyValidationLogger.Warn("btnSave_Click: Button bereits disabled, Exit Sub")
Exit Sub
End If
btnSave.Enabled = False
- ' ========== ENDE FIX 1 ==========
-
- ' ========== FIX 2: Overlay-Handle global speichern ==========
- _overlayHandle = SplashScreenManager.ShowOverlayForm(Me)
- _overlayActive = True
- ' ========== ENDE FIX 2 ==========
-
Try
If ForceGridValidation() = True Then
- Finish_WFStep()
+ ShowOverlaySafe()
+ Try
+ Finish_WFStep()
+ Finally
+ CloseOverlaySafe()
+ End Try
End If
Finally
- ' ========== FIX 3: Overlay IMMER schließen ==========
- _overlayActive = False
- Try
- SplashScreenManager.CloseOverlayForm(_overlayHandle)
- Catch ex As Exception
- MyValidationLogger.Warn($"⚠️ [btnSave_Click] Overlay-Close failed: {ex.Message}")
- End Try
- _overlayHandle = Nothing
- ' ========== ENDE FIX 3 ==========
-
- ' ========== FIX 4: Button nur re-enablen wenn Form nicht schließt ==========
If Not _FormClosing AndAlso Not Me.IsDisposed Then
btnSave.Enabled = True
Else
MyValidationLogger.Debug("btnSave_Click: Form closing, Button bleibt disabled")
End If
- ' ========== ENDE FIX 4 ==========
End Try
End Sub
@@ -5374,13 +5492,6 @@ Public Class frmValidator
End Function
Sub Finish_WFStep(Optional includeFI As Boolean = True)
- Dim oHandle As Object = Nothing
- Dim overlayStartedHere As Boolean = False
- If Not _overlayActive Then
- oHandle = SplashScreenManager.ShowOverlayForm(Me)
- _overlayActive = True
- overlayStartedHere = True
- End If
Dim perfStart As DateTime = DateTime.MinValue
Dim perfLastCheck As DateTime = DateTime.MinValue
If LOG_HOTSPOTS Then
@@ -5392,6 +5503,7 @@ Public Class frmValidator
MyValidationLogger.Debug("Abschluss für DocID " & CURRENT_DOC_ID & " wird gestartet ...")
Dim oErrorOcurred As Boolean = False
If OverrideAll = False Then
+
If Check_UpdateIndexe() = True Then
If LOG_HOTSPOTS Then
MyValidationLogger.Info($"[PERF] Finish_WFStep nach Check_UpdateIndexe: {(DateTime.Now - perfLastCheck).TotalMilliseconds}ms")
@@ -5399,10 +5511,7 @@ Public Class frmValidator
End If
If PROFIL_FINISH_SQL <> String.Empty Then
If btnFinish_continue() = False Then
- If overlayStartedHere Then
- _overlayActive = False
- SplashScreenManager.CloseOverlayForm(oHandle)
- End If
+ CloseOverlaySafe()
Exit Sub
End If
End If
@@ -5544,6 +5653,8 @@ Public Class frmValidator
My.Settings.Save()
frmError.ShowDialog()
oErrorOcurred = True
+ Else
+
End If
End If
If oErrorOcurred = True Then
@@ -5562,7 +5673,9 @@ Public Class frmValidator
End If
Try
If Override = True And Override_SQLCommand <> "" Then
+
DatabaseFallback.ExecuteNonQueryECM(Override_SQLCommand)
+
End If
If LOG_HOTSPOTS Then
MyValidationLogger.Info($"[PERF] Finish_WFStep nach Override-SQL: {(DateTime.Now - perfLastCheck).TotalMilliseconds}ms")
@@ -5728,10 +5841,6 @@ Public Class frmValidator
frmError.ShowDialog()
oErrorOcurred = True
MyValidationLogger.Info("Unexpected error in Finish: " & ex.Message, True)
- If overlayStartedHere Then
- _overlayActive = False
- SplashScreenManager.CloseOverlayForm(oHandle)
- End If
Exit Sub
End Try
Else
@@ -5739,17 +5848,6 @@ Public Class frmValidator
MyValidationLogger.Warn($"⚠️ [Finish_WFStep] Validierung fehlgeschlagen → OpenfrmError")
OpenfrmError(oErrMsgMissingInput) ' ← Statt: frmError.ShowDialog()
oErrorOcurred = True
- ' ========== FIX: Overlay schließen NACH Dialog ==========
- If overlayStartedHere Then
- _overlayActive = False
- Try
- SplashScreenManager.CloseOverlayForm(_overlayHandle)
- Catch ex As Exception
- MyValidationLogger.Warn($"⚠️ [Finish_WFStep] Overlay-Close failed: {ex.Message}")
- End Try
- _overlayHandle = Nothing
- End If
- ' ========== ENDE FIX ==========
Exit Sub
End If
Else
@@ -5764,7 +5862,7 @@ Public Class frmValidator
perfLastCheck = DateTime.Now
End If
OverrideAll = False
- ' Overlay wird weiter unten geschlossen (vor Load_Next_Document bzw. BeginInvoke)
+
End If
If oErrorOcurred = True Then
@@ -5782,13 +5880,11 @@ Public Class frmValidator
End If
End If
+
If CURRENT_JUMP_DOC_GUID <> 0 Then
CURRENT_DOC_GUID = 0
MyValidationLogger.Info($"[Finish_WFStep] CURRENT_JUMP_DOC_GUID <> 0 → verzögertes Close()")
- If overlayStartedHere Then
- _overlayActive = False
- SplashScreenManager.CloseOverlayForm(oHandle)
- End If
+ CloseOverlaySafe()
BeginInvoke(New Action(Sub()
If Not Me.IsDisposed Then
MyValidationLogger.Debug("[BeginInvoke] Führe Me.Close() aus")
@@ -5801,27 +5897,12 @@ Public Class frmValidator
Else
Load_Next_Document(False)
- If overlayStartedHere Then
- _overlayActive = False
- SplashScreenManager.CloseOverlayForm(oHandle)
- End If
If LOG_HOTSPOTS Then
MyValidationLogger.Info($"[PERF] Finish_WFStep nach Load_Next_Document: {(DateTime.Now - perfLastCheck).TotalMilliseconds}ms")
perfLastCheck = DateTime.Now
End If
Focus_FirstControl()
End If
- ' ========== FIX: Overlay schließen am Ende ==========
- If overlayStartedHere Then
- _overlayActive = False
- Try
- SplashScreenManager.CloseOverlayForm(_overlayHandle)
- Catch ex As Exception
- MyValidationLogger.Warn($"⚠️ [Finish_WFStep] Overlay-Close failed: {ex.Message}")
- End Try
- _overlayHandle = Nothing
- End If
- ' ========== ENDE FIX ==========
If LOG_HOTSPOTS Then
MyValidationLogger.Info($"[PERF] Finish_WFStep GESAMT: {(DateTime.Now - perfStart).TotalMilliseconds}ms")
End If
@@ -6842,11 +6923,7 @@ Public Class frmValidator
Return
End If
_isShowingErrorDialog = True
- ' ========== ENDE FIX ==========
-
- ' ========== KRITISCH: Overlay NICHT hier schließen! ==========
- ' Das macht der Aufrufer (Finish_WFStep oder btnSave_Click)!
- ' ========== ENDE KRITISCH ==========
+ CloseOverlaySafe()
' 2. Events blockieren
_suppressLookupEvents = True
@@ -7120,7 +7197,8 @@ Public Class frmValidator
End Sub
Sub Datei_ueberspringen()
- Dim oHandle = SplashScreenManager.ShowOverlayForm(Me)
+ ShowOverlaySafe()
+
Dim perfStart As DateTime = If(LOG_HOTSPOTS, DateTime.Now, Nothing)
Dim perfLastCheck As DateTime = perfStart
If LOG_HOTSPOTS Then
@@ -7183,7 +7261,7 @@ Public Class frmValidator
MyValidationLogger.Info($" frmValidator.IsDisposed: {Me.IsDisposed}")
' ========== ENDE DIAGNOSE ==========
End If
- SplashScreenManager.CloseOverlayForm(oHandle)
+ CloseOverlaySafe()
End Try
End Sub
@@ -7413,7 +7491,7 @@ Public Class frmValidator
Private Sub bbtniRefresh_ItemClick(sender As Object, e As ItemClickEventArgs) Handles bbtniRefresh.ItemClick
' ========== KRITISCH: Events KOMPLETT blockieren während Refresh ==========
_suppressLookupEvents = True
- Dim oHandle = SplashScreenManager.ShowOverlayForm(Me)
+ ShowOverlaySafe()
Try
Reload_Controls("")
@@ -7424,8 +7502,8 @@ Public Class frmValidator
listChangedLookup.Clear()
SetStatusLabel("All Data refreshed", "Yellow")
Finally
- _suppressLookupEvents = False ' ← Erst NACH allem wieder freigeben
- SplashScreenManager.CloseOverlayForm(oHandle)
+ _suppressLookupEvents = False '
+ CloseOverlaySafe()
End Try
End Sub
@@ -7520,15 +7598,12 @@ Public Class frmValidator
End Sub
Private Sub BbtnItm_ItemClick(sender As Object, e As ItemClickEventArgs) Handles BbtnitmSave.ItemClick
- ' ========== FIX 1: Button-State-Management ==========
If BbtnitmSave.Enabled = False Then
MyValidationLogger.Warn("BbtnitmSave_ItemClick: Button bereits disabled, Exit Sub")
Exit Sub
End If
BbtnitmSave.Enabled = False
- ' ========== ENDE FIX 1 ==========
-
- Dim oHandle = SplashScreenManager.ShowOverlayForm(Me)
+ ShowOverlaySafe() ' ✅ Overlay hier öffnen
Try
' ========== FIX 2: Nur EINEN Check-Aufruf ==========
If Check_UpdateIndexe() = True Then
@@ -7545,16 +7620,15 @@ Public Class frmValidator
MsgBox("Unexpeceted error while savign data! Please check Your log and try again.", MsgBoxStyle.Critical)
End If
- ' ========== ENDE FIX 2 ==========
+
Finally
- ' ========== FIX 3: Button nur re-enablen wenn Form nicht schließt ==========
+ CloseOverlaySafe()
If Not _FormClosing AndAlso Not Me.IsDisposed Then
BbtnitmSave.Enabled = True
Else
MyValidationLogger.Debug("BbtnitmSave_ItemClick: Form closing, Button bleibt disabled")
End If
- SplashScreenManager.CloseOverlayForm(oHandle)
- ' ========== ENDE FIX 3 ==========
+
End Try
End Sub
Private Sub SaveDevExpressGridControl_Layout(pProfilID As Integer, pControlID As Integer, pGridView As DevExpress.XtraGrid.Views.Grid.GridView)