Partial Public Class Logic
    Partial Public Class DataGridViewUtility

        Public Class ColumnVisibleControlMenu ' /////////////////////////////////////////////////////////////////////////////////////////////////////////
            Inherits Windows.Forms.ContextMenuStrip

            Private m_InnerStrip As Windows.Forms.ContextMenuStrip
            Public ReadOnly Property InnerMenuStrip As Windows.Forms.ContextMenuStrip
                Get
                    Return m_InnerStrip
                End Get
            End Property
            Public CellMenuStrip As Windows.Forms.ContextMenuStrip

            Private PresentTarget As ColumnDisplayInf.Collection ' just to present target
            Private NoVisibleResetableColumnNames As System.Collections.Generic.SortedSet(Of String)
            Private AppendMenuItems As Windows.Forms.ToolStripMenuItem()

            Public Sub New(Optional ByVal NoVisibleResetableColumnNames() As String = Nothing, _
                           Optional ByVal PresentTarget As ColumnDisplayInf.Collection = Nothing, _
                           Optional ByVal CellContextMenu As Windows.Forms.ContextMenuStrip = Nothing, _
                           Optional ByVal AppendMenuItems As Windows.Forms.ToolStripMenuItem() = Nothing)
                MyBase.New()
                Me.NoVisibleResetableColumnNames = Nothing
                If Not NoVisibleResetableColumnNames Is Nothing AndAlso 0 < NoVisibleResetableColumnNames.Length Then
                    Me.NoVisibleResetableColumnNames = New System.Collections.Generic.SortedSet(Of String)(NoVisibleResetableColumnNames, New ControlNameComparer)
                End If
                Me.PresentTarget = PresentTarget
                Me.m_InnerStrip = New Windows.Forms.ContextMenuStrip
                Me.CellMenuStrip = CellContextMenu
                Me.AppendMenuItems = AppendMenuItems
            End Sub

            Protected Overridable Sub MenuItemClick(ByVal sender As Object, ByVal e As System.EventArgs)

                Dim Item As Windows.Forms.ToolStripMenuItem = TryCast(sender, ToolStripMenuItem)
                If Item Is Nothing Then Return

                Dim Col As DataGridViewColumn = TryCast(Item.Tag, DataGridViewColumn)
                If Not Col Is Nothing AndAlso Col.Visible <> Item.Checked Then
                    Col.Visible = Item.Checked
                    If Not PresentTarget Is Nothing Then PresentTarget.Present(Col.DataGridView)
                End If

            End Sub

            Private Function AddMenuItem(ByVal Col As DataGridViewColumn) As Boolean

                If String.IsNullOrWhiteSpace(Col.HeaderText) Or Col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill Or _
                    (Not NoVisibleResetableColumnNames Is Nothing AndAlso NoVisibleResetableColumnNames.Contains(Col.Name)) Then
                    Return False
                End If

                Dim menuAdd As Windows.Forms.ToolStripMenuItem = m_InnerStrip.Items.Add(Col.HeaderText)
                If Not menuAdd Is Nothing Then
                    menuAdd.CheckOnClick = True
                    menuAdd.Checked = Col.Visible
                    menuAdd.Tag = Col
                    AddHandler menuAdd.CheckStateChanged, AddressOf MenuItemClick
                    Return True
                End If

                Return False

            End Function

            Protected Overrides Sub OnOpening(ByVal e As System.ComponentModel.CancelEventArgs)

                Dim Owner As System.Windows.Forms.DataGridView = TryCast(SourceControl, System.Windows.Forms.DataGridView)
                e.Cancel = True

                Dim isOpenHeaderMenu As Boolean = False, ptShow As New Point(Me.Left, Me.Top)
                If Not Owner Is Nothing Then

                    ScrollTo(Owner.CurrentCell)
                    Dim rcOwner As Rectangle = Owner.Parent.RectangleToScreen(Owner.Bounds)

                    If rcOwner.Contains(MousePosition) Then

                        If IsPositionOnHeader(Owner, MousePosition) Or
                          (Owner.CurrentCell Is Nothing OrElse (Owner.CurrentCell.ColumnIndex < 0 Or Owner.CurrentCell.RowIndex < 0)) Then
                            ' show header menu
                            isOpenHeaderMenu = True
                            ptShow = MousePosition
                        Else
                            ' show cell menu
                            Dim rcCurrentCellOnDisplay As Rectangle = Owner.RectangleToScreen(
                                Owner.GetCellDisplayRectangle(Owner.CurrentCell.ColumnIndex, Owner.CurrentCell.RowIndex, True))
                            If rcCurrentCellOnDisplay.Contains(MousePosition) Then
                                ptShow = MousePosition
                            Else
                                Dim ofs As Integer = Owner.CurrentCell.GetContentBounds(Owner.CurrentCell.RowIndex).Height / 2
                                ptShow.X = rcCurrentCellOnDisplay.X + ofs
                                ptShow.Y = rcCurrentCellOnDisplay.Y + ofs
                            End If
                        End If
                    Else

                        If Owner.CurrentCell Is Nothing OrElse (Owner.CurrentCell.ColumnIndex < 0 Or Owner.CurrentCell.RowIndex < 0) Then
                            ' show header menu
                            isOpenHeaderMenu = True
                            ptShow.X = rcOwner.X
                            ptShow.Y = rcOwner.Y
                        Else
                            ' show cell menu
                            Dim rcCurrentCellOnDisplay As Rectangle = Owner.RectangleToScreen(
                                Owner.GetCellDisplayRectangle(Owner.CurrentCell.ColumnIndex, Owner.CurrentCell.RowIndex, False))
                            Dim ofs As Integer = Owner.CurrentCell.GetContentBounds(Owner.CurrentCell.RowIndex).Height / 2
                            ptShow.X = rcCurrentCellOnDisplay.X + ofs
                            ptShow.Y = rcCurrentCellOnDisplay.Y + ofs
                        End If

                    End If ' << If rcOwner.Contains(MousePosition) Then

                    If isOpenHeaderMenu Then

                        m_InnerStrip.Items.Clear() ' Create My Header Menu ///////////////////////////////////////////////////////////////////
                        Dim Col As DataGridViewColumn = Nothing

                        Dim ctFrozenItems As Integer = 0 ' frozen column ////////////////////////////////////////////////////////////////
                        Col = Owner.Columns.GetFirstColumn(DataGridViewElementStates.Frozen)
                        While Not Col Is Nothing
                            If AddMenuItem(Col) Then ctFrozenItems += 1
                            Col = Owner.Columns.GetNextColumn(Col, DataGridViewElementStates.Frozen, DataGridViewElementStates.None)
                        End While

                        Dim ctItems As Integer = 0 ' normal column //////////////////////////////////////////////////////////////////////
                        Col = Owner.Columns.GetFirstColumn(DataGridViewElementStates.None, DataGridViewElementStates.Frozen)
                        While Not Col Is Nothing
                            If AddMenuItem(Col) Then ctItems += 1
                            Col = Owner.Columns.GetNextColumn(Col, DataGridViewElementStates.None, DataGridViewElementStates.Frozen)
                        End While

                        If 0 < m_InnerStrip.Items.Count Then
                            If 0 < ctFrozenItems And 0 < ctItems Then m_InnerStrip.Items.Insert(ctFrozenItems, New ToolStripSeparator())
                            Dim ctAppend As Integer = 0
                            If Not AppendMenuItems Is Nothing Then
                                For Each item As Windows.Forms.ToolStripMenuItem In AppendMenuItems
                                    If Not item Is Nothing Then ctAppend += 1
                                Next
                            End If
                            If 0 < ctAppend Then
                                m_InnerStrip.Items.Add(New ToolStripSeparator())
                                For Each item As Windows.Forms.ToolStripMenuItem In AppendMenuItems
                                    If Not item Is Nothing Then m_InnerStrip.Items.Add(item)
                                Next
                            End If
                            InnerMenuStrip.Show(ptShow)
                        End If

                    ElseIf Not CellMenuStrip Is Nothing Then
                        CellMenuStrip.Show(ptShow)

                    End If

                End If ' << If Not Owner Is Nothing Then

            End Sub ' << Protected Overrides Sub OnOpening(ByVal e As System.ComponentModel.CancelEventArgs)

            Private Class ControlNameComparer
                Implements System.Collections.Generic.IComparer(Of String)
                Public Function Compare(ByVal x As String, ByVal y As String) As Integer Implements System.Collections.Generic.IComparer(Of String).Compare
                    Return String.Compare(x, y, True)
                End Function
            End Class

        End Class ' VisibleControlMenu

    End Class
End Class
