Skip to content

MyTabPage.vb

Two changes in this file

  1. Added a static field s_CoreModule_ContextMenuForm for the new singleton popup form.
  2. Modified ShowContextMenu to dispatch on PageType and route core module right-clicks to the new popup, with a defensive rebuild guard.

The original Pulse tab code path is left intact.

Singleton field

MyTabPage.vb (declarations area)

Private Shared s_PulseTab_ContextMenuForm As FPulseTab_Popup
Private Shared s_CoreModule_ContextMenuForm As FCoreModule_Popup
Private WithEvents m_popAddFilterPopup As New DevExpress.XtraBars.PopupMenu
Private WithEvents m_popContextMenu As DevExpress.XtraBars.PopupMenu

s_CoreModule_ContextMenuForm sits next to s_PulseTab_ContextMenuForm, with the same Private Shared access. Lazy-initialized — the form is constructed on the first right-click that needs it. Once created, it lives for the lifetime of the application.

ShowContextMenu — dispatch by PageType

Before:

Public Sub ShowContextMenu(Position As System.Drawing.Point)

    If m_popContextMenu Is Nothing Then DefinePopContextMenu()

    If m_popContextMenu IsNot Nothing Then
        s_PulseTab_ContextMenuForm.Activator = Me
        m_popContextMenu.ShowPopup(Position)
    End If
End Sub

After:

Public Sub ShowContextMenu(Position As System.Drawing.Point)

    ' Core module tabs get their own popup; Pulse reporting tabs get
    ' the original tab popup.
    If PageType = ePageType.CORE_MODULE Then
        If s_CoreModule_ContextMenuForm Is Nothing Then
            s_CoreModule_ContextMenuForm = New FCoreModule_Popup
        End If

        ' Defensive: if the popup has lost its links for any reason
        ' (e.g. after user switch / settings reload), rebuild them.
        If s_CoreModule_ContextMenuForm.PopupMenu1.ItemLinks Is Nothing _
           OrElse s_CoreModule_ContextMenuForm.PopupMenu1.ItemLinks.Count = 0 Then
            s_CoreModule_ContextMenuForm.DefineContextMenu()
        End If

        s_CoreModule_ContextMenuForm.Activator = Me
        s_CoreModule_ContextMenuForm.PopupMenu1.ShowPopup(Position)
        Exit Sub
    End If

    If m_popContextMenu Is Nothing Then DefinePopContextMenu()

    If m_popContextMenu IsNot Nothing Then
        s_PulseTab_ContextMenuForm.Activator = Me
        s_PulseTab_ContextMenuForm.colorPickEditTabColor.EditValue = Me.TabBackColor
        m_popContextMenu.ShowPopup(Position)
    End If
End Sub

Branch logic

PageType returns: - ePageType.CORE_MODULE when m_PrivateCoreModule IsNot Nothing — i.e. the page is a core module tab in tcMain. - ePageType.PULSE_TAB_PAGE otherwise — a Pulse reporting tab inside an inner MyTabControl.

The branch sends each kind of page to the right popup. Crucially, the Pulse path (the m_popContextMenu block at the bottom) is byte-identical to its original — no behavior changes for Pulse tabs.

The defensive rebuild

If s_CoreModule_ContextMenuForm.PopupMenu1.ItemLinks Is Nothing _
   OrElse s_CoreModule_ContextMenuForm.PopupMenu1.ItemLinks.Count = 0 Then
    s_CoreModule_ContextMenuForm.DefineContextMenu()
End If

In normal flow this branch fires once: on the very first right-click after the form is constructed, ItemLinks.Count = 0 (the designer's LinksPersistInfo is unreliable when item-to-manager registration hasn't completed yet — see Architecture). After DefineContextMenu() runs once, links stay populated and this branch is skipped on every subsequent right-click.

The check is also a safety net: if anything in a future code change ever clears ItemLinks, the next right-click will silently re-populate them rather than presenting an empty menu.

Why pass Position through unchanged

The Position parameter comes in as a screen point (the caller in MyTabControl.HandletabMouseClick passes PointToScreen(e.Location)). PopupMenu.ShowPopup(Point) accepts screen coordinates, so we forward as-is. No conversion needed.

What did not change

  • MyTabPage_Click (the body-MouseClick handler) is unchanged. It still calls ShowContextMenu(MousePosition). Since core module pages have no body to click on, this path is effectively only used for Pulse tabs and works correctly.
  • DefinePopContextMenu (the Pulse-popup populator) is unchanged.
  • MyTabPage.Dispose is unchanged.
  • The New(v_bCreateSplitContainer) constructor that wires m_popContextMenu = s_PulseTab_ContextMenuForm.PopupMenu1 is unchanged. Core module pages use the parameter-less New() constructor, which never set m_popContextMenu — and that remains true.
  • PageType is unchanged.
  • Every other handler in MyTabPage.vb is unchanged.

Risk assessment

  • Blast radius: Any caller of ShowContextMenu. There are three: two in MyTabControl.HandletabMouseClick and one in MyTabPage_Click. None pass extra context — they just hand off a screen-space Point. The new branch's behavior depends only on PageType, which is a pure read of m_PrivateCoreModule.
  • Pulse tab regression risk: Low. The Pulse path is unchanged byte-for-byte after the new dispatch branch.
  • Reversibility: Trivial — remove the If PageType = ePageType.CORE_MODULE block and the s_CoreModule_ContextMenuForm field.