Search code examples
vb.netwinformseventsdatagridviewmouseevent

MouseDown event conflicts with CellMouseEnter event


I have two events that work fine on their own, but one blocks the other.

Full Code and GUI at bottom

Goal:
I am trying to DragDrop a TreeNode into a DataGridView and have the Cell I am hovering over be selected/highlighted.

MouseDown and DragDrop events vb.net

Event that highlights cell:

 Public Sub DataGridView1_CellMouseEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellMouseEnter
    Try
        Me.DataGridView1.CurrentCell = Me.DataGridView1(DataGridView1.Columns(e.ColumnIndex).DisplayIndex, DataGridView1.Rows(e.RowIndex).Index)
    Catch
    End Try
 End Sub

Events for drag and Drop:

  • TreeView1_MouseDown - Sets boolean to determine whether mouse is down or not
  • TreeView1_MouseMove - gets the item to be dragged
  • DataGridView1_DragEnter - styling while dragging
  • DataGridView1_Dragdrop - Drops the item into the datagridview

My issue is that (any) MouseDown event blocks the CellMouseEnter event.
I tried mousedown-ing elsewhere in the Form and then hovered over the DataGridView and my CellMouseEnter event did not work.

The result is that the item is dropped into the cell that was selected BEFORE the MouseDown (technically this Cell is still selected b/c CellMouseEnter doesn't update the selected Cell to the hovered Cell)

So the way I see it, I need to build a custom event similar to CellMouseEnter that isn't blocked by MouseDown, but I wouldn't even know where to start. I tried Peek Definition, but I couldn't find the actual method for CellMouseEnter, just Public Event CellMouseEnter As DataGridViewCellEventHandler.

Is there a better way?

Here my drag and drop events:

Private Sub TreeView1_MouseDown(sender As Object, e As MouseEventArgs) Handles TreeView1.MouseDown
    move_item = True
End Sub

Private Sub TreeView1_MouseMove(sender As Object, e As MouseEventArgs) Handles TreeView1.MouseMove
    If move_item Then
        On Error GoTo quit
        Dim item2move As New Label
        item2move.Text = TreeView1.SelectedNode.Text
        item2move.DoDragDrop(item2move.Text, DragDropEffects.Copy)
        Debug.Print(TreeView1.SelectedNode.Text)
    End If
    move_item = False

quit: Exit Sub End Sub

Private Sub DataGridView1_DragEnter(sender As Object, e As DragEventArgs) Handles DataGridView1.DragEnter
    If (e.Data.GetDataPresent(DataFormats.Text)) Then
        e.Effect = DragDropEffects.Copy
    Else
        e.Effect = DragDropEffects.None
    End If
End Sub

Private Sub DataGridView1_DragDrop(sender As Object, e As DragEventArgs) Handles DataGridView1.DragDrop
    With DataGridView1
        Dim Col As Integer = .CurrentCell.ColumnIndex
        Dim row As Integer = .CurrentCell.RowIndex
        .Item(Col, row).Value = e.Data.GetDataPresent(DataFormats.Text)
    End With
End Sub

Full Code: Note Some sample JSON is in the comment at the end, put that in the RichtextBox - rtb_inputjson:

Public Class Form1
    Dim move_item As Boolean

    'Add some rows to the treemap on load
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        For i = 1 To 20
            DataGridView1.Rows.Add()
        Next
    End Sub

    'JSON to treemap
    Private Sub btn_jsontotreemap_Click(sender As Object, e As EventArgs) Handles btn_jsontotreemap.Click
        Try
            Dim json As String = rtb_inputjson.Text
            Dim obj As New JObject
            obj = JObject.Parse(json)
            TreeView1.Nodes.Clear()
            Dim parent As TreeNode = Json2Tree(obj)
            parent.Text = "Root Object"
            TreeView1.Nodes.Add(parent)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

    Private Function Json2Tree(ByVal obj As JObject) As TreeNode
        'creates the parent node
        Dim parent As TreeNode = New TreeNode()

        'loop through the obj all token should be pair <key, value>
        For Each token In obj
            parent.Text = token.Key.ToString()
            'create child node
            Dim child As TreeNode = New TreeNode()
            child.Text = token.Key.ToString()

            'self call :) 
            If token.Value.Type.ToString() = "Object" Then
                Dim o As JObject = CType(token.Value, JObject)
                child = Json2Tree(o)
                child.Text = token.Key.ToString()
                parent.Nodes.Add(child)

                'but if it is an array...
            ElseIf token.Value.Type.ToString() = "Array" Then
                Dim ix As Integer = -1
                For Each itm In token.Value

                    'check each item of the array to see if they are objects or arrays
                    If itm.Type.ToString() = "Object" Then
                        Dim objTN As TreeNode = New TreeNode()
                        ix += 1
                        Dim o As JObject = CType(itm, JObject)

                        'self call :)
                        objTN = Json2Tree(o)
                        objTN.Text = token.Key.ToString() & "[" & ix & "]"
                        child.Nodes.Add(objTN)
                    ElseIf itm.Type.ToString() = "Array" Then
                        ix += 1
                        Dim dataArray As TreeNode = New TreeNode()
                        For Each i In itm
                            dataArray.Text = token.Key.ToString() & "[" & ix & "]"
                            dataArray.Nodes.Add(i.ToString())
                        Next
                        child.Nodes.Add(dataArray)
                    Else
                        child.Nodes.Add(itm.ToString())
                    End If
                Next
                parent.Nodes.Add(child)
            Else
                If token.Value.ToString() = "" Then child.Nodes.Add("N/A") Else child.Nodes.Add(token.Value.ToString())
                parent.Nodes.Add(child)
            End If
        Next
        Return parent
    End Function


    'drag & drop to datagridview
    Private Sub TreeView1_MouseDown(sender As Object, e As MouseEventArgs) Handles TreeView1.MouseDown
        move_item = True
    End Sub

    Private Sub TreeView1_MouseMove(sender As Object, e As MouseEventArgs) Handles TreeView1.MouseMove
        If move_item Then
            On Error GoTo quit
            Dim item2move As New Label
            item2move.Text = TreeView1.SelectedNode.Text
            item2move.DoDragDrop(item2move.Text, DragDropEffects.Copy)
            Debug.Print(TreeView1.SelectedNode.Text)
        End If
        move_item = False
quit:
        Exit Sub
    End Sub

    Public Sub DataGridView1_CellMouseEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellMouseEnter
        Debug.Print("CellMouseEnter event raised")
        Try
            Me.DataGridView1.CurrentCell = Me.DataGridView1(DataGridView1.Columns(e.ColumnIndex).DisplayIndex, DataGridView1.Rows(e.RowIndex).Index)
        Catch
        End Try
    End Sub

    Private Sub DataGridView1_DragDrop(sender As Object, e As DragEventArgs) Handles DataGridView1.DragDrop

        With DataGridView1
            Dim Col As Integer = .CurrentCell.ColumnIndex
            Dim row As Integer = .CurrentCell.RowIndex
            .Item(Col, row).Value = e.Data.GetDataPresent(DataFormats.Text)
        End With
    End Sub

    Private Sub DataGridView1_DragEnter(sender As Object, e As DragEventArgs) Handles DataGridView1.DragEnter
        If (e.Data.GetDataPresent(DataFormats.Text)) Then
            e.Effect = DragDropEffects.Copy
        Else
            e.Effect = DragDropEffects.None
        End If
    End Sub
End Class

Sample JSON for textbox:

{
   "data":[
      {
         "symbol":"A",
         "name":"Agilent Technologies Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"2"
      },
      {
         "symbol":"AA",
         "name":"Alcoa Corporation",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"12042"
      },
      {
         "symbol":"AABA",
         "name":"Altaba Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"7653"
      },
      {
         "symbol":"AAC",
         "name":"AAC Holdings Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"9169"
      },
      {
         "symbol":"AADR",
         "name":"AdvisorShares Dorsey Wright ADR",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"et",
         "iexId":"5"
      },
      {
         "symbol":"AAL",
         "name":"American Airlines Group Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"8148"
      },
      {
         "symbol":"AAMC",
         "name":"Altisource Asset Management Corp Com",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"7760"
      },
      {
         "symbol":"AAME",
         "name":"Atlantic American Corporation",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"7"
      },
      {
         "symbol":"AAN",
         "name":"Aaron's Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"8"
      },
      {
         "symbol":"AAOI",
         "name":"Applied Optoelectronics Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"7790"
      },
      {
         "symbol":"AAON",
         "name":"AAON Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"9"
      },
      {
         "symbol":"AAP",
         "name":"Advance Auto Parts Inc W/I",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"10"
      },
      {
         "symbol":"AAPL",
         "name":"Apple Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"11"
      },
      {
         "symbol":"AAT",
         "name":"American Assets Trust Inc.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"12"
      },
      {
         "symbol":"AAU",
         "name":"Almaden Minerals Ltd.",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"13"
      },
      {
         "symbol":"AAV",
         "name":"Advantage Oil & Gas Ltd",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"14"
      },
      {
         "symbol":"AAWW",
         "name":"Atlas Air Worldwide Holdings",
         "date":"2018-03-19",
         "isEnabled":true,
         "type":"cs",
         "iexId":"15"
      }
   ]
}

Controls and their Names:

enter image description here


Solution

  • Using the DragOver() event, you have the advantage that a user can have a visual feedback of the cell when the Drop() operation will take effect, since the cell where the mouse is hovering becomes active and follows the mouse movement.

    You could also verify whether the Cell under the mouse pointer can receive the drop and avoid selecting it if not.

    Private Sub dataGridView1_DragEnter(sender As Object, e As DragEventArgs)
        e.Effect = If(e.Data.GetDataPresent(DataFormats.Text) = True,
                      DragDropEffects.Copy,
                      DragDropEffects.None)
    
    Private Sub dataGridView1_DragDrop(sender As Object, e As DragEventArgs)
        'Check whether the current Cell supports this Drop()
        DataGridView1.CurrentCell.Value = e.Data.GetDataPresent(DataFormats.Text).ToString()
    End Sub
    
    Private Sub dataGridView1_DragOver(sender As Object, e As DragEventArgs)
        Dim ClientAreaLocation As Point = dataGridView1.PointToClient(New Point(e.X, e.Y))
        Dim CellPosition As DataGridView.HitTestInfo = dataGridView1.HitTest(ClientAreaLocation.X, ClientAreaLocation.Y)
    
        If CellPosition.ColumnIndex > -1 AndAlso CellPosition.RowIndex > -1 Then
            'Enable if this Cell supports this Drop()
            dataGridView1.CurrentCell = dataGridView1(CellPosition.ColumnIndex, CellPosition.RowIndex)
        End If
    End Sub