From d8eb019d01f5d2d2e3613d794493ee6f763877bb Mon Sep 17 00:00:00 2001 From: Developer01 Date: Tue, 11 Aug 2026 15:19:12 +0200 Subject: [PATCH] Fix: InvalidRowHandle bei Doppelklick auf GroupRow in Item_Scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Item_Scope() hat den RowHandle über CalcHitInfo(GridCursorLocation) neu berechnet - basierend auf einer zwischengespeicherten Klick- Position. Klappt sich eine Gruppe zwischen Klick und Verarbeitung auf/zu (automatisch durch den DoubleClick-Handler oder manuell durch den User), verschiebt sich die RowHandle-Nummerierung und die gecachte Position zeigt ins Leere -> InvalidRowHandle -> Doppelklick auf Gruppenkopf verpuffte wirkungslos. - Fallback auf GridViewWorkflows.FocusedRowHandle, wenn der aus GridCursorLocation berechnete RowHandle ungültig ist. - Group-/DataRow-Erkennung erfolgt jetzt einheitlich per IsGroupRow/IsDataRow(effectiveRowHandle) statt über die potenziell unzuverlässigen hitInfo.InGroupRow/InDataRow-Flags; dieser Pfad war zuvor bereits als Bug-Workaround vorhanden und wird nun durchgängig genutzt (Schritt-1/Schritt-2-Duplizierung entfernt). - Doppelten "STARTEDROM NORMALISIEREN"-Codeblock (Copy-Paste-Rest) bereinigt. Betrifft sowohl den Doppelklick-Pfad als auch die manuellen Aufrufe über die Kontextmenü-Buttons (CMROW/CMGROUP), da beide denselben Codepfad in Item_Scope durchlaufen. Co-Authored-By: Claude Sonnet 5 --- app/TaskFlow/frmMain.vb | 129 +++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/app/TaskFlow/frmMain.vb b/app/TaskFlow/frmMain.vb index 6957164..0e9ead8 100644 --- a/app/TaskFlow/frmMain.vb +++ b/app/TaskFlow/frmMain.vb @@ -2532,70 +2532,60 @@ Public Class frmMain ' ========== HITINFO NUR EINMAL BERECHNEN ========== Dim hitInfo As GridHitInfo = GridViewWorkflows.CalcHitInfo(GridCursorLocation) - ' ===== FRÜHE VALIDIERUNG: UNGÜLTIGE ROWHANDLES ABFANGEN ===== - If hitInfo.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then - LOGGER.Warn($"⚠️ Item_Scope: InvalidRowHandle detected (click on empty grid area?) - exiting") - Exit Function + ' ===== EFFEKTIVEN ROWHANDLE ERMITTELN (MIT FALLBACK) ===== + ' hitInfo basiert auf der zwischengespeicherten Klick-Position (GridCursorLocation). + ' Diese kann zwischen Klick und Verarbeitung ungültig werden - z.B. wenn eine Gruppe + ' zwischenzeitlich auf-/zugeklappt wurde (automatisch durch den DoubleClick-Handler + ' oder manuell durch den User) und sich dadurch die RowHandle-Nummerierung verschoben hat. + ' In diesem Fall auf FocusedRowHandle ausweichen, das vom Grid selbst nachgeführt wird + ' und i.d.R. weiterhin die angeklickte Zeile referenziert. + Dim effectiveRowHandle As Integer = hitInfo.RowHandle + + If effectiveRowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then + Dim focusedRowHandle = GridViewWorkflows.FocusedRowHandle + If focusedRowHandle <> DevExpress.XtraGrid.GridControl.InvalidRowHandle Then + LOGGER.Debug($"Item_Scope: CalcHitInfo(GridCursorLocation) invalid - falling back to FocusedRowHandle [{focusedRowHandle}]") + effectiveRowHandle = focusedRowHandle + Else + LOGGER.Warn($"⚠️ Item_Scope: InvalidRowHandle detected (click on empty grid area?) - exiting") + Exit Function + End If End If - ' ===== ZUSÄTZLICH: Prüfen ob überhaupt eine Zeile getroffen wurde ===== - If Not hitInfo.InRow Then - LOGGER.Warn($"⚠️ Item_Scope: Click not in any row (HitTest:[{hitInfo.HitTest}]) - exiting") + ' ===== GROUP-/DATAROW DIREKT AM ROWHANDLE PRÜFEN ===== + ' Robuster als hitInfo.InGroupRow/InDataRow: funktioniert unverändert für den + ' FocusedRowHandle-Fallback und umgeht einen bekannten DevExpress-Bug, bei dem + ' diese hitInfo-Flags trotz gültigem RowHandle falsch gesetzt sein können. + Dim isGroupRow As Boolean = GridViewWorkflows.IsGroupRow(effectiveRowHandle) + Dim isDataRow As Boolean = GridViewWorkflows.IsDataRow(effectiveRowHandle) + + If Not isGroupRow AndAlso Not isDataRow Then + LOGGER.Warn($"⚠️ Item_Scope: RowHandle [{effectiveRowHandle}] ist weder Group- noch DataRow - exiting") Exit Function End If ' ===== STARTEDROM NORMALISIEREN ===== If startedFrom = "DOUBLECLICK" Then - startedFrom = If(hitInfo.InGroupRow, "CMGROUP", "CMROW") - LOGGER.Debug($"User clicked {If(hitInfo.InGroupRow, "group", "normal")} row.") - End If - ' ========== STARTEDROM NORMALISIEREN ========== - If startedFrom = "DOUBLECLICK" Then - startedFrom = If(hitInfo.InGroupRow, "CMGROUP", "CMROW") - LOGGER.Debug($"User clicked {If(hitInfo.InGroupRow, "group", "normal")} row.") + startedFrom = If(isGroupRow, "CMGROUP", "CMROW") + LOGGER.Debug($"User clicked {If(isGroupRow, "group", "normal")} row.") End If - ' ========== PROFIL-ID ERMITTELN (OPTIMIERT) ========== - ' ========== PROFIL-ID ERMITTELN (MIT FALLBACK-LOGIK) ========== + ' ========== PROFIL-ID ERMITTELN ========== Dim oHitProfilID As Object = Nothing - ' ===== SCHRITT 1: Direkte Erkennung versuchen ===== - If hitInfo.InGroupRow Then + If isGroupRow Then ' ✅ Gruppenkopf: Profil-ID aus erster Datenzeile der Gruppe oHitProfilID = GridViewWorkflows.GetRowCellValue( - GridViewWorkflows.GetDataRowHandleByGroupRowHandle(hitInfo.RowHandle), + GridViewWorkflows.GetDataRowHandleByGroupRowHandle(effectiveRowHandle), GridViewWorkflows.Columns("PROFILE_ID")) LOGGER.Debug("Clicked on Group Row") - ElseIf hitInfo.InDataRow Then + ElseIf isDataRow Then ' ✅ Normale Datenzeile: Profil-ID direkt aus dieser Zeile oHitProfilID = GridViewWorkflows.GetRowCellValue( - hitInfo.RowHandle, + effectiveRowHandle, GridViewWorkflows.Columns("PROFILE_ID")) LOGGER.Debug("Clicked on Data Row") - - Else - ' ===== SCHRITT 2: FALLBACK für "unklare" Klicks ===== - ' Wenn hitInfo weder InGroupRow noch InDataRow ist, RowHandle direkt prüfen - LOGGER.Warn($"⚠️ hitInfo ist WEDER InGroupRow noch InDataRow - RowHandle:[{hitInfo.RowHandle}]") - - If GridViewWorkflows.IsGroupRow(hitInfo.RowHandle) Then - ' Es ist doch eine Gruppe (DevExpress Bug-Workaround) - LOGGER.Debug("Fallback: IsGroupRow(RowHandle) = True") - oHitProfilID = GridViewWorkflows.GetRowCellValue( - GridViewWorkflows.GetDataRowHandleByGroupRowHandle(hitInfo.RowHandle), - GridViewWorkflows.Columns("PROFILE_ID")) - - ElseIf GridViewWorkflows.IsDataRow(hitInfo.RowHandle) Then - ' Es ist doch eine Datenzeile (DevExpress Bug-Workaround) - LOGGER.Debug("Fallback: IsDataRow(RowHandle) = True") - oHitProfilID = GridViewWorkflows.GetRowCellValue( - hitInfo.RowHandle, - GridViewWorkflows.Columns("PROFILE_ID")) - - Else - LOGGER.Error($"⚠️ RowHandle [{hitInfo.RowHandle}] ist weder Group noch DataRow!") - End If End If LOGGER.Debug("Clicked ProfileId: [{0}], Started From: [{1}]", oHitProfilID, startedFrom) @@ -2619,15 +2609,15 @@ Public Class frmMain Dim oGroupRowHandle As Integer ' KRITISCH: Den RICHTIGEN Gruppen-Handle ermitteln - If GridViewWorkflows.IsGroupRow(hitInfo.RowHandle) Then + If isGroupRow Then ' User hat direkt auf die Gruppen-Zeile geklickt - oGroupRowHandle = hitInfo.RowHandle + oGroupRowHandle = effectiveRowHandle LOGGER.Debug($"User clicked directly on group row, handle: {oGroupRowHandle}") Else ' User hat auf eine Daten-Zeile INNERHALB einer Gruppe geklickt ' → Parent-Gruppe ermitteln - oGroupRowHandle = GridViewWorkflows.GetParentRowHandle(hitInfo.RowHandle) - LOGGER.Debug($"User clicked on data row {hitInfo.RowHandle}, parent group handle: {oGroupRowHandle}") + oGroupRowHandle = GridViewWorkflows.GetParentRowHandle(effectiveRowHandle) + LOGGER.Debug($"User clicked on data row {effectiveRowHandle}, parent group handle: {oGroupRowHandle}") If Not GridViewWorkflows.IsGroupRow(oGroupRowHandle) Then LOGGER.Warn($"⚠️ Parent handle {oGroupRowHandle} is not a group row!") @@ -2756,12 +2746,12 @@ Public Class frmMain ' ========== PROFIL-TITEL ERMITTELN ========== Dim PROFIL_TITLE As String = String.Empty - If hitInfo.InGroupRow Then + If isGroupRow Then GridViewItem_Clicked = "GROUP" startedFrom = "CMGROUP" LOGGER.Debug($"Item_Scope: InGroupRow") - Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText(hitInfo.RowHandle) + Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText(effectiveRowHandle) LOGGER.Debug($"Item_Scope: groupRowText {groupRowText}") If GRID_LOAD_TYPE = "OVERVIEW" Then @@ -2770,14 +2760,14 @@ Public Class frmMain PROFIL_TITLE = If(splitIndex >= 0, groupRowText.Substring(0, splitIndex).Trim(), groupRowText) End If - ElseIf hitInfo.InDataRow Then + ElseIf isDataRow Then GridViewItem_Clicked = "ROW" LOGGER.Debug($"Item_Scope: InDataRow") If GRID_LOAD_TYPE = "OVERVIEW" Then LOGGER.Debug($"Item_Scope: GRID_LOAD_TYPE = OVERVIEW") Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText( - GridViewWorkflows.GetParentRowHandle(hitInfo.RowHandle)) + GridViewWorkflows.GetParentRowHandle(effectiveRowHandle)) LOGGER.Debug($"Item_Scope: OVERVIEWgroupRowText {groupRowText}") groupRowText = groupRowText.Replace("Profile (Fixed): ", "").Trim() @@ -2806,16 +2796,16 @@ Public Class frmMain If Not IsNothing(CURRENT_CLICKED_PROFILE_ID) AndAlso IsNumeric(CURRENT_CLICKED_PROFILE_ID) Then LOGGER.Debug($"Item_Scope: Valid PROFIL_ID") - If hitInfo.InGroupRow OrElse (startedFrom = "CMGROUP" AndAlso hitInfo.InDataRow) Then + If isGroupRow OrElse (startedFrom = "CMGROUP" AndAlso isDataRow) Then ' GRUPPE: Workflow ohne spezifisches Dokument Reset_Current() CURRENT_ProfilGUID = CURRENT_CLICKED_PROFILE_ID - LOGGER.Debug($"Item_Scope: hitInfo.InGroupRow...CURRENT_CLICKED_PROFILE_ID [{CURRENT_CLICKED_PROFILE_ID}]") + LOGGER.Debug($"Item_Scope: isGroupRow...CURRENT_CLICKED_PROFILE_ID [{CURRENT_CLICKED_PROFILE_ID}]") Load_Profil_from_Grid(CURRENT_CLICKED_PROFILE_ID) - ElseIf hitInfo.InDataRow Then + ElseIf isDataRow Then ' EINZELNE ZEILE: Mit spezifischem Dokument - LOGGER.Debug($"Item_Scope: hitInfo.InDataRow...") + LOGGER.Debug($"Item_Scope: isDataRow...") ' ========== DOKUMENT-DATEN ABRUFEN ========== Dim oFocusedDocGUID = GridViewWorkflows.GetFocusedRowCellValue(GridViewWorkflows.Columns("GUID")) @@ -2823,7 +2813,7 @@ Public Class frmMain ' Validierungen If oFocusedDocID Is Nothing Then - LOGGER.Warn("⚠️ In hitInfo.InDataRow: DocID is nothing!!!") + LOGGER.Warn("⚠️ In isDataRow: DocID is nothing!!!") bsiMessage.Caption = "Error getting DocID!" bsiMessage.ItemAppearance.Normal.BackColor = Color.Red bsiMessage.ItemAppearance.Normal.ForeColor = Color.Black @@ -2831,7 +2821,7 @@ Public Class frmMain End If If oFocusedDocGUID Is Nothing Then - LOGGER.Warn("⚠️ In hitInfo.InDataRow: oFocusedDocGUID is nothing!!!") + LOGGER.Warn("⚠️ In isDataRow: oFocusedDocGUID is nothing!!!") bsiMessage.Caption = "Error getting DocGUID!" bsiMessage.ItemAppearance.Normal.BackColor = Color.Red bsiMessage.ItemAppearance.Normal.ForeColor = Color.Black @@ -3380,8 +3370,17 @@ Public Class frmMain ' ===== FRÜHE VALIDIERUNG ===== If hitInfo.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then - LOGGER.Warn("⚠️ DoubleClick: InvalidRowHandle - ignoring") - Exit Sub + Dim focused = GridViewWorkflows.FocusedRowHandle + If focused <> DevExpress.XtraGrid.GridControl.InvalidRowHandle Then + LOGGER.Debug($"Item_Scope: hitInfo invalid, falling back to FocusedRowHandle [{focused}]") + ' hitInfo/rowHandle entsprechend neu aufbauen + CURRENT_CLICKED_PROFILE_ID = GridViewWorkflows.GetRowCellValue(GridViewWorkflows.GetDataRowHandleByGroupRowHandle(focused), GridViewWorkflows.Columns("PROFILE_ID")) + LOGGER.Debug($"Item_Scope: Fallback PROFILE_ID = [{CURRENT_CLICKED_PROFILE_ID}]") + + Else + LOGGER.Warn("⚠️ Item_Scope: InvalidRowHandle detected...") + Exit Sub + End If End If If Not hitInfo.InRow Then @@ -3548,8 +3547,14 @@ Public Class frmMain Dim groupRowButtonClicked = (hi.HitTest = GridHitTest.RowGroupButton) ' ===== UNGÜLTIGE CLICKS ABFANGEN ===== If hi.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then - LOGGER.Debug("MouseDown: InvalidRowHandle - ignoring click") - Exit Sub + Dim focused = GridViewWorkflows.FocusedRowHandle + If focused <> DevExpress.XtraGrid.GridControl.InvalidRowHandle Then + LOGGER.Debug($"Item_Scope: hitInfo invalid, falling back to FocusedRowHandle [{focused}]") + ' hitInfo/rowHandle entsprechend neu aufbauen + Else + LOGGER.Warn("⚠️ MouseDown: InvalidRowHandle - ignoring click") + Exit Sub + End If End If If Not hi.InRow Then