Fix: InvalidRowHandle bei Doppelklick auf GroupRow in Item_Scope

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 <noreply@anthropic.com>
This commit is contained in:
Developer01
2026-08-11 15:19:12 +02:00
parent de4f348be5
commit d8eb019d01

View File

@@ -2532,70 +2532,60 @@ Public Class frmMain
' ========== HITINFO NUR EINMAL BERECHNEN ========== ' ========== HITINFO NUR EINMAL BERECHNEN ==========
Dim hitInfo As GridHitInfo = GridViewWorkflows.CalcHitInfo(GridCursorLocation) Dim hitInfo As GridHitInfo = GridViewWorkflows.CalcHitInfo(GridCursorLocation)
' ===== FRÜHE VALIDIERUNG: UNGÜLTIGE ROWHANDLES ABFANGEN ===== ' ===== EFFEKTIVEN ROWHANDLE ERMITTELN (MIT FALLBACK) =====
If hitInfo.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then ' hitInfo basiert auf der zwischengespeicherten Klick-Position (GridCursorLocation).
LOGGER.Warn($"⚠️ Item_Scope: InvalidRowHandle detected (click on empty grid area?) - exiting") ' Diese kann zwischen Klick und Verarbeitung ungültig werden - z.B. wenn eine Gruppe
Exit Function ' 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 End If
' ===== ZUSÄTZLICH: Prüfen ob überhaupt eine Zeile getroffen wurde ===== ' ===== GROUP-/DATAROW DIREKT AM ROWHANDLE PRÜFEN =====
If Not hitInfo.InRow Then ' Robuster als hitInfo.InGroupRow/InDataRow: funktioniert unverändert für den
LOGGER.Warn($"⚠️ Item_Scope: Click not in any row (HitTest:[{hitInfo.HitTest}]) - exiting") ' 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 Exit Function
End If End If
' ===== STARTEDROM NORMALISIEREN ===== ' ===== STARTEDROM NORMALISIEREN =====
If startedFrom = "DOUBLECLICK" Then If startedFrom = "DOUBLECLICK" Then
startedFrom = If(hitInfo.InGroupRow, "CMGROUP", "CMROW") startedFrom = If(isGroupRow, "CMGROUP", "CMROW")
LOGGER.Debug($"User clicked {If(hitInfo.InGroupRow, "group", "normal")} row.") LOGGER.Debug($"User clicked {If(isGroupRow, "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.")
End If End If
' ========== PROFIL-ID ERMITTELN (OPTIMIERT) ========== ' ========== PROFIL-ID ERMITTELN ==========
' ========== PROFIL-ID ERMITTELN (MIT FALLBACK-LOGIK) ==========
Dim oHitProfilID As Object = Nothing Dim oHitProfilID As Object = Nothing
' ===== SCHRITT 1: Direkte Erkennung versuchen ===== If isGroupRow Then
If hitInfo.InGroupRow Then
' ✅ Gruppenkopf: Profil-ID aus erster Datenzeile der Gruppe ' ✅ Gruppenkopf: Profil-ID aus erster Datenzeile der Gruppe
oHitProfilID = GridViewWorkflows.GetRowCellValue( oHitProfilID = GridViewWorkflows.GetRowCellValue(
GridViewWorkflows.GetDataRowHandleByGroupRowHandle(hitInfo.RowHandle), GridViewWorkflows.GetDataRowHandleByGroupRowHandle(effectiveRowHandle),
GridViewWorkflows.Columns("PROFILE_ID")) GridViewWorkflows.Columns("PROFILE_ID"))
LOGGER.Debug("Clicked on Group Row") LOGGER.Debug("Clicked on Group Row")
ElseIf hitInfo.InDataRow Then ElseIf isDataRow Then
' ✅ Normale Datenzeile: Profil-ID direkt aus dieser Zeile ' ✅ Normale Datenzeile: Profil-ID direkt aus dieser Zeile
oHitProfilID = GridViewWorkflows.GetRowCellValue( oHitProfilID = GridViewWorkflows.GetRowCellValue(
hitInfo.RowHandle, effectiveRowHandle,
GridViewWorkflows.Columns("PROFILE_ID")) GridViewWorkflows.Columns("PROFILE_ID"))
LOGGER.Debug("Clicked on Data Row") 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 End If
LOGGER.Debug("Clicked ProfileId: [{0}], Started From: [{1}]", oHitProfilID, startedFrom) LOGGER.Debug("Clicked ProfileId: [{0}], Started From: [{1}]", oHitProfilID, startedFrom)
@@ -2619,15 +2609,15 @@ Public Class frmMain
Dim oGroupRowHandle As Integer Dim oGroupRowHandle As Integer
' KRITISCH: Den RICHTIGEN Gruppen-Handle ermitteln ' KRITISCH: Den RICHTIGEN Gruppen-Handle ermitteln
If GridViewWorkflows.IsGroupRow(hitInfo.RowHandle) Then If isGroupRow Then
' User hat direkt auf die Gruppen-Zeile geklickt ' User hat direkt auf die Gruppen-Zeile geklickt
oGroupRowHandle = hitInfo.RowHandle oGroupRowHandle = effectiveRowHandle
LOGGER.Debug($"User clicked directly on group row, handle: {oGroupRowHandle}") LOGGER.Debug($"User clicked directly on group row, handle: {oGroupRowHandle}")
Else Else
' User hat auf eine Daten-Zeile INNERHALB einer Gruppe geklickt ' User hat auf eine Daten-Zeile INNERHALB einer Gruppe geklickt
' → Parent-Gruppe ermitteln ' → Parent-Gruppe ermitteln
oGroupRowHandle = GridViewWorkflows.GetParentRowHandle(hitInfo.RowHandle) oGroupRowHandle = GridViewWorkflows.GetParentRowHandle(effectiveRowHandle)
LOGGER.Debug($"User clicked on data row {hitInfo.RowHandle}, parent group handle: {oGroupRowHandle}") LOGGER.Debug($"User clicked on data row {effectiveRowHandle}, parent group handle: {oGroupRowHandle}")
If Not GridViewWorkflows.IsGroupRow(oGroupRowHandle) Then If Not GridViewWorkflows.IsGroupRow(oGroupRowHandle) Then
LOGGER.Warn($"⚠️ Parent handle {oGroupRowHandle} is not a group row!") LOGGER.Warn($"⚠️ Parent handle {oGroupRowHandle} is not a group row!")
@@ -2756,12 +2746,12 @@ Public Class frmMain
' ========== PROFIL-TITEL ERMITTELN ========== ' ========== PROFIL-TITEL ERMITTELN ==========
Dim PROFIL_TITLE As String = String.Empty Dim PROFIL_TITLE As String = String.Empty
If hitInfo.InGroupRow Then If isGroupRow Then
GridViewItem_Clicked = "GROUP" GridViewItem_Clicked = "GROUP"
startedFrom = "CMGROUP" startedFrom = "CMGROUP"
LOGGER.Debug($"Item_Scope: InGroupRow") LOGGER.Debug($"Item_Scope: InGroupRow")
Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText(hitInfo.RowHandle) Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText(effectiveRowHandle)
LOGGER.Debug($"Item_Scope: groupRowText {groupRowText}") LOGGER.Debug($"Item_Scope: groupRowText {groupRowText}")
If GRID_LOAD_TYPE = "OVERVIEW" Then If GRID_LOAD_TYPE = "OVERVIEW" Then
@@ -2770,14 +2760,14 @@ Public Class frmMain
PROFIL_TITLE = If(splitIndex >= 0, groupRowText.Substring(0, splitIndex).Trim(), groupRowText) PROFIL_TITLE = If(splitIndex >= 0, groupRowText.Substring(0, splitIndex).Trim(), groupRowText)
End If End If
ElseIf hitInfo.InDataRow Then ElseIf isDataRow Then
GridViewItem_Clicked = "ROW" GridViewItem_Clicked = "ROW"
LOGGER.Debug($"Item_Scope: InDataRow") LOGGER.Debug($"Item_Scope: InDataRow")
If GRID_LOAD_TYPE = "OVERVIEW" Then If GRID_LOAD_TYPE = "OVERVIEW" Then
LOGGER.Debug($"Item_Scope: GRID_LOAD_TYPE = OVERVIEW") LOGGER.Debug($"Item_Scope: GRID_LOAD_TYPE = OVERVIEW")
Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText( Dim groupRowText = GridViewWorkflows.GetGroupRowDisplayText(
GridViewWorkflows.GetParentRowHandle(hitInfo.RowHandle)) GridViewWorkflows.GetParentRowHandle(effectiveRowHandle))
LOGGER.Debug($"Item_Scope: OVERVIEWgroupRowText {groupRowText}") LOGGER.Debug($"Item_Scope: OVERVIEWgroupRowText {groupRowText}")
groupRowText = groupRowText.Replace("Profile (Fixed): ", "").Trim() 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 If Not IsNothing(CURRENT_CLICKED_PROFILE_ID) AndAlso IsNumeric(CURRENT_CLICKED_PROFILE_ID) Then
LOGGER.Debug($"Item_Scope: Valid PROFIL_ID") 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 ' GRUPPE: Workflow ohne spezifisches Dokument
Reset_Current() Reset_Current()
CURRENT_ProfilGUID = CURRENT_CLICKED_PROFILE_ID 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) Load_Profil_from_Grid(CURRENT_CLICKED_PROFILE_ID)
ElseIf hitInfo.InDataRow Then ElseIf isDataRow Then
' EINZELNE ZEILE: Mit spezifischem Dokument ' EINZELNE ZEILE: Mit spezifischem Dokument
LOGGER.Debug($"Item_Scope: hitInfo.InDataRow...") LOGGER.Debug($"Item_Scope: isDataRow...")
' ========== DOKUMENT-DATEN ABRUFEN ========== ' ========== DOKUMENT-DATEN ABRUFEN ==========
Dim oFocusedDocGUID = GridViewWorkflows.GetFocusedRowCellValue(GridViewWorkflows.Columns("GUID")) Dim oFocusedDocGUID = GridViewWorkflows.GetFocusedRowCellValue(GridViewWorkflows.Columns("GUID"))
@@ -2823,7 +2813,7 @@ Public Class frmMain
' Validierungen ' Validierungen
If oFocusedDocID Is Nothing Then 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.Caption = "Error getting DocID!"
bsiMessage.ItemAppearance.Normal.BackColor = Color.Red bsiMessage.ItemAppearance.Normal.BackColor = Color.Red
bsiMessage.ItemAppearance.Normal.ForeColor = Color.Black bsiMessage.ItemAppearance.Normal.ForeColor = Color.Black
@@ -2831,7 +2821,7 @@ Public Class frmMain
End If End If
If oFocusedDocGUID Is Nothing Then 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.Caption = "Error getting DocGUID!"
bsiMessage.ItemAppearance.Normal.BackColor = Color.Red bsiMessage.ItemAppearance.Normal.BackColor = Color.Red
bsiMessage.ItemAppearance.Normal.ForeColor = Color.Black bsiMessage.ItemAppearance.Normal.ForeColor = Color.Black
@@ -3380,8 +3370,17 @@ Public Class frmMain
' ===== FRÜHE VALIDIERUNG ===== ' ===== FRÜHE VALIDIERUNG =====
If hitInfo.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then If hitInfo.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then
LOGGER.Warn("⚠️ DoubleClick: InvalidRowHandle - ignoring") Dim focused = GridViewWorkflows.FocusedRowHandle
Exit Sub 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 End If
If Not hitInfo.InRow Then If Not hitInfo.InRow Then
@@ -3548,8 +3547,14 @@ Public Class frmMain
Dim groupRowButtonClicked = (hi.HitTest = GridHitTest.RowGroupButton) Dim groupRowButtonClicked = (hi.HitTest = GridHitTest.RowGroupButton)
' ===== UNGÜLTIGE CLICKS ABFANGEN ===== ' ===== UNGÜLTIGE CLICKS ABFANGEN =====
If hi.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then If hi.RowHandle = DevExpress.XtraGrid.GridControl.InvalidRowHandle Then
LOGGER.Debug("MouseDown: InvalidRowHandle - ignoring click") Dim focused = GridViewWorkflows.FocusedRowHandle
Exit Sub 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 End If
If Not hi.InRow Then If Not hi.InRow Then