none
Host a DataGridView on a ContextMenuStrip using ToolStripControlHost

    Question

  • I'm trying to show a small DataGridView in my ContextMenuStrip that I have built.  The DataGridView will show a unique list of values in a DataGridViewTextboxColumn "Sample ID" when its column header is right-clicked.  I can get the unique values just fine, but I'm having trouble displaying the small DataGridView in the ContextMenuStrip.  It currently looks like this.

    Notice, there is no data in the DataGridView.  I have confirmed that the DataGridView.DataSource has several rows in it, yet the DataGridView shows nothing.  Plus, I need to increase the size of my DataGridView, thus increasing the size of the ContextMenuStrip to show the DataGridView.

    Here is my code (sorry if its too much, just wanted to make sure you see the whole picture):

    Public Class ContextMenu_Column_String
        Inherits ContextMenuStrip
    
        Private dvs As DataGridViewSettings
        Private dgv As DataGridView
        Private ht As DataGridView.HitTestInfo
        Private dfs As DataGridViewFilterAndSorter
    
        Public Sub New(ByVal sender As DataGridViewSettings, ByVal e As System.Windows.Forms.MouseEventArgs)
    
                Me.dvs = CType(sender, DataGridViewSettings)
                Me.dgv = Me.dvs.DataGridView
                Me.ht = Me.dgv.HitTest(e.X, e.Y)
                Me.dfs = Me.dvs.DataGridViewFilterAndSorter
    
                Dim dtUniqueValues As DataTable = Me.dfs.MasterDataTable.Copy.AsDataView.ToTable(True, Me.dgv.Columns(ht.ColumnIndex).DataPropertyName)
                dtUniqueValues.Columns.Add("Select", Type.GetType("System.Boolean"))
                Dim dv As DataView = dtUniqueValues.AsDataView
                dv.Sort = Me.dgv.Columns(ht.ColumnIndex).DataPropertyName
    
                Dim chk As New DataGridViewCheckBoxColumn
                With chk
                    .Name = "Select"
                    .Width = 25
                    .DataPropertyName = "booSelect"
                End With
    
                Dim tex As New DataGridViewTextBoxColumn
                With tex
                    .Name = "Values"
                    .Width = 100
                    .DataPropertyName = Me.dgv.Columns(ht.ColumnIndex).DataPropertyName
                End With
    
                Dim dgvUnique As New DataGridView
    
                dgvUnique.Columns.Add(chk)
                dgvUnique.Columns.Add(tex)
                dgvUnique.Width = 125
                dgvUnique.CellBorderStyle = DataGridViewCellBorderStyle.None
                dgvUnique.ColumnHeadersVisible = False
                dgvUnique.AutoGenerateColumns = False
                dgvUnique.DataSource = dtUniqueValues
    
                Dim CMS As New ContextMenuStrip
                Dim CM_SubMenu As New ToolStripMenuItem()
                Dim IC As New IconConverter
    
                With CMS.Items
                    .Add(New ToolStripMenuItem("Copy", IC.ConvertTo(My.Resources.copy, GetType(Image)), New EventHandler(AddressOf Me.CopySelectedCells), "nmCopy"))
                    .Add(New ToolStripSeparator)
                    .Add(New ToolStripMenuItem("Sort A to Z", My.Resources.SortHS, New EventHandler(AddressOf Me.SortAZ), "nmSortAZ"))
                    .Add(New ToolStripMenuItem("Sort Z to A", Nothing, New EventHandler(AddressOf Me.SortZA), "nmSortZA"))
                    .Add(New ToolStripMenuItem("Save My Sortings", Nothing, New EventHandler(AddressOf Me.SaveMySortings)))
                    .Add(New ToolStripSeparator)
                    .Add(New ToolStripMenuItem("Fill Column With ...", Nothing, New EventHandler(AddressOf Me.FillColumnWith), "nmFillColumnWith"))
                    .Add(New ToolStripSeparator)
                    .Add(New ToolStripMenuItem("Hide Column", Nothing, New EventHandler(AddressOf Me.HideColumn)))
                    .Add(New ToolStripMenuItem("Edit Column Settings", Nothing, New EventHandler(AddressOf Me.EditColumnSettings)))
                    .Add(New ToolStripSeparator)
    
                    With CM_SubMenu
                        .Text = "Add Column Filter"
                        .Name = "nmAddColumnFilter"
                        '.Image = 
                        With .DropDownItems
                            .Add("Equals ...", Nothing, New EventHandler(AddressOf Me.Equal))
                            .Add("Does Not Equal ...", Nothing, New EventHandler(AddressOf Me.NotEqual))
                            .Add("Begins With ...", Nothing, New EventHandler(AddressOf Me.BeginWith))
                            .Add("Does Not Begin With ...", Nothing, New EventHandler(AddressOf Me.NotBeginWith))
                            .Add("Ends With ...", Nothing, New EventHandler(AddressOf Me.EndWith))
                            .Add("Does Not End With ...", Nothing, New EventHandler(AddressOf Me.NotEndWith))
                            .Add("Contains ...", Nothing, New EventHandler(AddressOf Me.Contain))
                            .Add("Does Not Contain ...", Nothing, New EventHandler(AddressOf Me.NotContains))
                            .Add("Between ...", Nothing, New EventHandler(AddressOf Me.Between))
                            .Add("Not Between ...", Nothing, New EventHandler(AddressOf Me.NotBetween))
                            .Add("Is Blank", Nothing, New EventHandler(AddressOf Me.Blank))
                            .Add("Is Not Blank", Nothing, New EventHandler(AddressOf Me.NotBlank))
                        End With
                        .DisplayStyle = ToolStripItemDisplayStyle.Text
                    End With
                    .AddRange(New ToolStripItem() {CM_SubMenu})
    
                    .Add(New ToolStripControlHost(dgvUnique))
                End With
    
                ' disable Fill Column With button if the column is Read-Only
                CMS.Items("nmFillColumnWith").Enabled = Not Me.dgv.Columns(Me.ht.ColumnIndex).ReadOnly
    
                CMS.Show(Me.dgv, e.X, e.Y)
    
        End Sub
    End Class

    Anyone have a solution?  I figure using the ToolStripControlHost class is the only way to do this, right?

    Thanks,


    Ryan


    • Edited by Ryan0827 Monday, April 30, 2012 1:38 PM code update
    Friday, April 27, 2012 10:28 PM

Answers

  • Hi Ryan,

    I think initializing the BindingContext as my last post suggested can help you solve the issue.  I customized the codes as following:

    Private Sub DataGridView1_MouseClick(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseClick
            Dim dgv As DataGridView = CType(sender, DataGridView)
            Dim ht As DataGridView.HitTestInfo = dgv.HitTest(e.X, e.Y)
            Dim dt As DataTable = CType(dgv.DataSource, DataTable)
    
            Dim CMS As New ContextMenuStrip
            Dim d1 As New DataGridView
            d1.BindingContext = New BindingContext
            Dim TMH As New ToolStripControlHost(d1)
    
            ' create a table of unique values from the column that was right-clicked
            Dim dtUniqueValues As DataTable = dt.Copy.AsDataView.ToTable(True, dgv.Columns(ht.ColumnIndex).DataPropertyName)
    
            ' add a Boolean column to the DataTable
            dtUniqueValues.Columns.Add("booSelect", Type.GetType("System.Boolean"), False)
    
            ' sort the table
            Dim dv As New DataView(dtUniqueValues, Nothing, dgv.Columns(ht.ColumnIndex).DataPropertyName, DataViewRowState.CurrentRows)
    
            ' create a CheckBox column so the user can select values
            Dim chk As New DataGridViewCheckBoxColumn
            With chk
                .Name = "Select"
                .Width = 5
                .DataPropertyName = "booSelect"
            End With
    
            ' create a TextBox column to show unique values
            Dim tex As New DataGridViewTextBoxColumn
            With tex
                .Name = "Values"
                .DataPropertyName = "vchAnalyteCode"
            End With
    
            ' create a binding source to ensure sychronization between DataGridView and DataTable
            Dim bs As New BindingSource
            bs.DataSource = dtUniqueValues
            d1.DataSource = bs
    
            ' set properties of the DataGridView that will be displayed in the ContextMenuStrip
            With CType(TMH.Control, DataGridView)
                .AutoGenerateColumns = False
                .Columns.Add(chk)
                .Columns(chk.Name).Width = 15
                .Columns.Add(tex)
                .DataSource = bs
                .Width = 186
                .Height = 200
                .CellBorderStyle = DataGridViewCellBorderStyle.None
                .ColumnHeadersVisible = False
                .RowHeadersVisible = False
            End With
    
            ' add the ToolStripControlHost to the ContextMenuStrip to display our unique values
            CMS.Items.Add(TMH)
    
            CMS.Show(dgv, e.X, e.Y)
        End Sub

    Please pay attention to the codes in bold.

    Good day!

    Thanks


    Michael Sun [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, May 01, 2012 2:18 AM
    Moderator

All replies

  • Hi Ryan,

    there is a lot of class didn't delare.

    Pleasse post some correct code here.

    Have a nice day.


    Ghost,
    Call me ghost for short, Thanks
    To get the better answer, it should be a better question.

    Monday, April 30, 2012 8:57 AM
  • Hi Ryan,

    I did not use your original codes, but I think we can intialize the DataGridView's BindingContext property to make the databinding work. 

    Here are some testing codes to add a ToolStrip for a TextBox for your references:

    Private Sub TextBox1_KeyDown(sender As System.Object, e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyDown
            If e.KeyData = Keys.F1 Then
                d1.BindingContext = New BindingContext
                d1.DataSource = MyTable
    
                Dim host As New ToolStripControlHost(d1)
                ContextMenuStrip1.Items.Clear()
                ContextMenuStrip1.Items.Add(host)
                ContextMenuStrip1.Show(TextBox1, 0, 27)
            End If
        End Sub

    Good day!

    Thanks


    Michael Sun [MSFT]
    MSDN Community Support | Feedback to us

    • Proposed as answer by CrazyGhost_Von Monday, April 30, 2012 1:45 PM
    • Unproposed as answer by Ryan0827 Monday, April 30, 2012 1:51 PM
    Monday, April 30, 2012 10:15 AM
    Moderator
  • Ghost,

    Thanks for the reply.  What classes do you need to see?  I believe all the relevant code is posted.


    Ryan

    Monday, April 30, 2012 1:37 PM
  • For example: DataGridViewSettings

    But I think DataGridViewSettings class is not directly related to this issue. Right?

    So you can make a simple code snippet to show your issue, rather than post such complex one.

    Have a nice day.


    Ghost,
    Call me ghost for short, Thanks
    To get the better answer, it should be a better question.

    Monday, April 30, 2012 1:45 PM
  • Ghost,

    Correct, the DataGridViewSettings class is not directly related to this issue.

    I shortened the code for better readability.  To test my code you can setup a Form with a DataGridView on it.  Then you can fill the DataGridView with whatever data you please.  Then put this code in the DataGridView_MouseClick Event.  Note:  This code assumes you have a DataTable as the DataGridViews.DataSource.

        Private Sub DataGridView1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseClick
    
            Dim dgv As DataGridView = CType(sender, DataGridView)
            Dim ht As DataGridView.HitTestInfo = dgv.HitTest(e.X, e.Y)
            Dim dt As DataTable = CType(dgv.DataSource, DataTable)
    
            Dim CMS As New ContextMenuStrip
            Dim TMH As New ToolStripControlHost(New DataGridView)
    
            ' create a table of unique values from the column that was right-clicked
            Dim dtUniqueValues As DataTable = dt.Copy.AsDataView.ToTable(True, dgv.Columns(ht.ColumnIndex).DataPropertyName)
    
            ' add a Boolean column to the DataTable
            dtUniqueValues.Columns.Add("booSelect", Type.GetType("System.Boolean"), False)
    
            ' sort the table
            Dim dv As New DataView(dtUniqueValues, Nothing, dgv.Columns(ht.ColumnIndex).DataPropertyName, DataViewRowState.CurrentRows)
    
            ' create a CheckBox column so the user can select values
            Dim chk As New DataGridViewCheckBoxColumn
            With chk
                .Name = "Select"
                .Width = 5
                .DataPropertyName = "booSelect"
            End With
    
            ' create a TextBox column to show unique values
            Dim tex As New DataGridViewTextBoxColumn
            With tex
                .Name = "Values"
                .DataPropertyName = "vchAnalyteCode"
            End With
    
            ' create a binding source to ensure sychronization between DataGridView and DataTable
            Dim bs As New BindingSource
            bs.DataSource = dtUniqueValues
    
            ' set properties of the DataGridView that will be displayed in the ContextMenuStrip
            With CType(TMH.Control, DataGridView)
                .AutoGenerateColumns = False
                .Columns.Add(chk)
                .Columns(chk.Name).Width = 15
                .Columns.Add(tex)
                .DataSource = bs
                .Width = 186
                .Height = 200
                .CellBorderStyle = DataGridViewCellBorderStyle.None
                .ColumnHeadersVisible = False
                .RowHeadersVisible = False
            End With
    
            ' add the ToolStripControlHost to the ContextMenuStrip to display our unique values
            CMS.Items.Add(TMH)
    
            CMS.Show(dgv, e.X, e.Y)
    
        End Sub

    The goal is to display a DataGridView in a ContextMenuStrip.  This DataGridView will contain a CheckBox column and TextBox column that displays all the unique values in the column that was right-clicked.  I will use the values the user selects in the DataGridView as a filter.  Does this help?

    Thanks for sticking with this,


    Ryan

    Monday, April 30, 2012 2:07 PM
  • Hi Ryan,

    I think initializing the BindingContext as my last post suggested can help you solve the issue.  I customized the codes as following:

    Private Sub DataGridView1_MouseClick(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseClick
            Dim dgv As DataGridView = CType(sender, DataGridView)
            Dim ht As DataGridView.HitTestInfo = dgv.HitTest(e.X, e.Y)
            Dim dt As DataTable = CType(dgv.DataSource, DataTable)
    
            Dim CMS As New ContextMenuStrip
            Dim d1 As New DataGridView
            d1.BindingContext = New BindingContext
            Dim TMH As New ToolStripControlHost(d1)
    
            ' create a table of unique values from the column that was right-clicked
            Dim dtUniqueValues As DataTable = dt.Copy.AsDataView.ToTable(True, dgv.Columns(ht.ColumnIndex).DataPropertyName)
    
            ' add a Boolean column to the DataTable
            dtUniqueValues.Columns.Add("booSelect", Type.GetType("System.Boolean"), False)
    
            ' sort the table
            Dim dv As New DataView(dtUniqueValues, Nothing, dgv.Columns(ht.ColumnIndex).DataPropertyName, DataViewRowState.CurrentRows)
    
            ' create a CheckBox column so the user can select values
            Dim chk As New DataGridViewCheckBoxColumn
            With chk
                .Name = "Select"
                .Width = 5
                .DataPropertyName = "booSelect"
            End With
    
            ' create a TextBox column to show unique values
            Dim tex As New DataGridViewTextBoxColumn
            With tex
                .Name = "Values"
                .DataPropertyName = "vchAnalyteCode"
            End With
    
            ' create a binding source to ensure sychronization between DataGridView and DataTable
            Dim bs As New BindingSource
            bs.DataSource = dtUniqueValues
            d1.DataSource = bs
    
            ' set properties of the DataGridView that will be displayed in the ContextMenuStrip
            With CType(TMH.Control, DataGridView)
                .AutoGenerateColumns = False
                .Columns.Add(chk)
                .Columns(chk.Name).Width = 15
                .Columns.Add(tex)
                .DataSource = bs
                .Width = 186
                .Height = 200
                .CellBorderStyle = DataGridViewCellBorderStyle.None
                .ColumnHeadersVisible = False
                .RowHeadersVisible = False
            End With
    
            ' add the ToolStripControlHost to the ContextMenuStrip to display our unique values
            CMS.Items.Add(TMH)
    
            CMS.Show(dgv, e.X, e.Y)
        End Sub

    Please pay attention to the codes in bold.

    Good day!

    Thanks


    Michael Sun [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, May 01, 2012 2:18 AM
    Moderator