Adding context_menu items dinamically with custom functions - vb.net

I create context menu dinamically and want to assign menuitems to my own functions (with arguments). Unfortunatelly that dont go as I would like.
Following example illustrates what I would like to do.
Private Sub dgv_sub_CellMouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dgv_sub.CellMouseUp
If e.Button = Windows.Forms.MouseButtons.Right Then
dgv_sub.Rows(e.RowIndex).Selected = True
context_sub.Items.Clear()
context_sub.Items.Add("Delete row " + dgv_sub.CurrentRow.Index.ToString, Nothing) AddressOf delRow(dgv_sub.CurrentRow.Index))
context_sub.Items.Add("Delete all rows", Nothing) , AddressOf delRow(-1))
context_sub.Show(New Point(Cursor.Position.X, Cursor.Position.Y))
End If
End Sub
Private Sub delRow(ByVal rowtodelete As Integer)
End Sub
How to make this properly and get it to work as described?

This is how I usually do these kind of things:
Have a pre populated ContextMenu
Assign the ContextMenu to my DataGridView
Add Events on every ToolStripMenuItem
In each event, first I do a check to make sure that a row has been selected
If dgv_sub.SelectedRows.Count > 0 Then
then, I get the correct row by using the
SelectedRows(0)
To make things neater, you can also use the DataGridView.MouseDown event to make sure that when the user right click a row, it gets selected.
Private Sub dgv_sub_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dgv_sub.MouseDown
If e.Button = Windows.Forms.MouseButtons.Right Then
Dim hitTest As DataGridView.HitTestInfo
hitTest = dgv_sub.HitTest(e.X, e.Y)
If hitTest IsNot Nothing AndAlso hitTest.RowIndex > -1 Then
dgv_sub.CurrentCell = dgv_sub.Item(hitTest.ColumnIndex, hitTest.RowIndex)
dgv_sub.Rows(hitTest.RowIndex).Selected = True
End If
End If
End Sub
As you need context items to be dynamic, you will have to do these in the MouseDown event aswell.
In order to add an item properly you still need a normal click event:
context_sub.Items.Add("Name of Item", Nothing, AddressOf item_Click)
Then add a Sub like this:
Private Sub item_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
'Add any logic here, you can still use the dgv_sub.SelectedRows here
End Sub
Ideally you create a different Sub for every context menu item you need to add

Related

VB.NET - How to add a large amount of events to a single handle?

Recently I've been working on a program that has a few TextBoxes, CheckBoxes, ComboBoxes, etc., and I found that making one function handle multiple events is pretty simple, you just separate the events with a comma and the code recognizes the inidvidual events.
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click, Button2.Click
MsgBox("Hello World!")
End Sub
However, when you start to have a large number of events that you want handled by the same function, it gets a bit messy.
Private Sub Checks_CheckedChanged(sender As Object, e As EventArgs) Handles chkInput1.CheckedChanged, chkInput2.CheckedChanged, chkInput3.CheckedChanged, chkInput4.CheckedChanged, checkInput5.CheckedChanged, chkOutput.CheckedChanged
MsgBox("Checks Changed!")
End Sub
You can use the line continuation character _ to make it look a little better.
Private Sub Checks_CheckedChanged(sender As Object, e As EventArgs) Handles _
chkInput1.CheckedChanged, chkInput2.CheckedChanged, chkInput3.CheckedChanged, _
chkInput4.CheckedChanged, checkInput5.CheckedChanged, chkOutput.CheckedChanged
MsgBox("Checks Changed!")
End Sub
But you still end up with a nasty block of text. Is there a more clean/concise way of doing this? What I have in mind is that it would be really nice to give an array of object events as an argument but I don't think that's possible.
You could do this by using the
AddHandler ObjectName.EventName, AddressOf EventHandlerName
syntax
It's simple enough to write a Sub that takes an array of object and loops over them to add the handler for each event.
For checkboxes:
Public Sub AddHandlerSub(PassedArray As CheckBox())
For Each item As CheckBox in PassedArray
AddHandler Item.CheckedChanged, AddressOf EventHandlerName
next
End Sub
You can simply iterate the controls in the controls collection and not fuss with an array at all. You can also do further conditions if you want to exclude/add any given control, such as in the example of the TextBox Case in the following example.
Private Sub DataTables_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each Ctrl As Control In Panel1.Controls
Select Case Ctrl.GetType
Case GetType(CheckBox)
AddHandler DirectCast(Ctrl, CheckBox).CheckedChanged, Sub(S As Object, EA As EventArgs)
Dim ChkBox As CheckBox = DirectCast(S, CheckBox)
'do something with ChkBox
End Sub
Case GetType(TextBox)
Select Case Ctrl.Name
Case "TextBox1", "TextBox2" 'Add handle only to these contrls
'Or you could add Case Else and put the below handle within it
'Then this becomes an exclusion case
AddHandler DirectCast(Ctrl, TextBox).TextChanged, Sub(S As Object, EA As EventArgs)
Dim TxtBox As TextBox = DirectCast(S, TextBox)
'do something with TxtBox
End Sub
End Select
End Select
Next
End Sub
Additional Information: You can select procedures as Event Handlers by selecting the control. Then in the Properties window click the lightning bolt to display Events. Selected the event you wish to assign a handler and then the drop down arrow to the right. The resulting list will display all the Subs that match the signature of that event. Select the one you want and the designer will write or append the control to the Handles clause.
Add a procedure to the Form with a signature that matches the event.
Private Sub MultipleButtons(sender As Object, e As EventArgs)
End Sub
In the dropdown the list contains all Subs that match the signature of the event.
The designer writes the Handles clause
Private Sub MultipleButtons(sender As Object, e As EventArgs) Handles Button5.Click
End Sub

Multiple selection in datagridview

I work on a new project that need a multiple row selection/deselection by the user in a datagridview with only a tap on a touch screen.
The form should look like this:
For exemple, if the user want to delete row 2 and 5, he only need to tap once on each line to select/deselect them. After the selection is done, he tap on "Delete Row" button.
I've already try to play with the CellClick event without success!!
Can someone have a clue how can I handle this problem?
After setting MultiSelect property to True and SelectionMode to FullRowSelect you can use a List to store which row of your DataGridView is selected.
On CellClick you can add/remove rows from your List, on RowPostPaint you can select a row if it's included in the List and on RowsRemoved you have to clear the List.
Private intSelectedRows As New List(Of Integer)
Private Sub DataGridView1_CellClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellClick
With CType(sender, DataGridView)
Dim intRow As Integer = .CurrentRow.Index
If Not Me.intSelectedRows.Contains(intRow) Then
Me.intSelectedRows.Add(intRow)
Else
.CurrentRow.Selected = False
Me.intSelectedRows.Remove(intRow)
End If
End With
End Sub
Private Sub DataGridView1_RowPostPaint(sender As Object, e As System.Windows.Forms.DataGridViewRowPostPaintEventArgs) Handles DataGridView1.RowPostPaint
If Me.intSelectedRows.Contains(e.RowIndex) Then
CType(sender, DataGridView).Rows(e.RowIndex).Selected = True
End If
End Sub
Private Sub DataGridView1_RowsRemoved(sender As Object, e As System.Windows.Forms.DataGridViewRowsRemovedEventArgs) Handles DataGridView1.RowsRemoved
Me.intSelectedRows.Clear()
End Sub
If you want to clear selection you can use this code:
Private Sub btnClearSelectedRows_Click(sender As System.Object, e As System.EventArgs) Handles btnClearSelectedRows.Click
For Each intSelectedRow As Integer In Me.intSelectedRows
Me.DataGridView1.Rows(intSelectedRow).Selected = False
Next intSelectedRow
Me.intSelectedRows.Clear()
End Sub

VB.NET - Datagridview: Get index of clicked column

In VB.NET's DataGridView, how can i get the index of the column that was clicked on, instead of the one that that has a selected cell.
I wish to provide the user with the option to right click the column and hide it, via a context menu. This code gives me the index of the column that has a selected cell:
Private Sub dataGridView1_ColumnHeaderMouseClick(sender As Object, ByVal e As DataGridViewCellMouseEventArgs) Handles dataGridView1.ColumnHeaderMouseClick
If e.Button = Windows.Forms.MouseButtons.Right Then
currSelectedColIdx = e.ColumnIndex
ContextMenuStrip1.Show()
End If
End Sub
Edit: The problem happens when i bind the contextmenu to the datagridview via the properties window. If i unbind it, the code works correctly.
You can use CellContextMenuStripNeeded event of DataGridView.
Private Sub DataGridView1_CellContextMenuStripNeeded(sender As Object, e As DataGridViewCellContextMenuStripNeededEventArgs) Handles DataGridView1.CellContextMenuStripNeeded
If e.RowIndex = -1 Then
e.ContextMenuStrip = ContextMenuStrip1
'e.ColumnIndex is the column than you right clicked on it.
End If
End Sub
you can get index of column with : e.ColumnIndex.
Your have probably probleme with declaration of your variable "currSelectedColIdx " :
Please try this code :
Private Sub DataGridView1_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick
If e.Button = Windows.Forms.MouseButtons.Right Then
Dim currSelectedColIdx = e.ColumnIndex
ContextMenuStrip1.Show(Cursor.Position)
End If
End Sub

How to know if the mouse is down on clicking a column divider in a Datagridview Control

I am trying to show a message to the user that will tell them
"don't resize column"
as soon as they try to drag the column divider in a DataGridView.
Is there any event like a DataGridViewColumnDividerMouseDrag in vb.net?
I would like to know how my mouse pointer is behaving when the column divider of a DataGridView is clicked
Thanks in advance...
You could create a global variable that captures the MouseDown on DataGridView then use a DataGridView.ColumnWidthChanged event to capture a change in column width.
You would need to add some code to make the DataGridView save the width before the change and revert it back to that width. This code will allow you to mix MouseDown and DataGridView.ColumnWidthChanged
Not really a columndividermouseDrag event but its a start
Global variables
Dim mousestatus As Boolean
Dim global_e As MouseEventArgs
Capture Events
Private Sub DataGridView2_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles DataGridView2.MouseDown
If e.Button = Windows.Forms.MouseButtons.Left Then
mousestatus = True
global_e = e
End If
End Sub
Private Sub DataGridView2_Mouseup(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles DataGridView2.MouseUp
If e.Button = Windows.Forms.MouseButtons.Left Then
mousestatus = False
global_e = e
End If
End Sub
Private Sub DataGridView2_ColumnWidthChanged(ByVal sender As Object, ByVal e As _
DataGridViewColumnEventArgs) Handles DataGridView2.ColumnWidthChanged
If mousestatus = True Then
MessageBox.Show("don't resize column")
Else
End If
End Sub

drag and drop to tablelayoutpanel from listview

I am trying to build a control which implements a tablelayoutpanel for the design and placement of other controls within the control - I need to add functionality which will allow the tablelayoutpanel to accept content from a listview (It does not even need to process it in any fashion at this point) - I, however, can not get the tablelayout panel to even display that it will accept data - only displays the circle/slash symbol. These are kept in 2 separate child mdi forms within the same parent.
currently I have in my listview form
Private Sub Jboard_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.AllowDrop = True
ListView2.AllowDrop = True
end sub
Private Sub ListView2_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Me.DragOver, ListView2.DragOver
If e.Data.GetDataPresent(GetType(ListViewItem)) Then
e.Effect = DragDropEffects.All
End If
End Sub
Private Sub ListView2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView2.MouseDown
Dim item As ListViewItem = ListView2.HitTest(e.Location).Item
If item IsNot Nothing Then
ListView2.DoDragDrop(item, DragDropEffects.All)
End If
End Sub
on my new tablelayoutpanel control form I have
Me.AllowDrop = True
DboardScheduler1.AllowDrop = True
'dboardscheduler1 is my new control
in the code for the control I have
tablelayoutpanel1.AllowDrop = true
What am I missing?
It looks like you only coded the one side, you also need to tell the TLP(like) control how/what to do. Something like this (not sure of the constraints you want, like JUST LVs and only MOVE).
' NOT mousedown
Private Sub ItemDrag(sender As Object, e As ItemDragEventArgs) Handles ...
If e.Button <> Windows.Forms.MouseButtons.Left Then Exit Sub
' ToDo: Decide what to do with multiples. Singles only assumed
' add the item under the cusor as the first, effect as Move
DoDragDrop(e.Item, DragDropEffects.Move)
End Sub
LV Drag OVer:
' probably:
e.Effect = DragDropEffects.None
' because you cant really drop it here, but the No Action shows that it knows
' a D-D is happening.
TLP Drag OVer:
If (e.Data.GetDataPresent(GetType(ListViewItem)) = False) Then
e.Effect = DragDropEffects.None
Exit Sub
Else
e.Effect = DragDropEffects.Move ' or link maybe
End If
TLP DragDrop:
Dim dragLVI As ListViewItem
' get text and do whatever with it
If (e.Data.GetDataPresent(GetType(ListViewItem)) = False) Then
e.Effect = DragDropEffects.None
Exit Sub
Else
dragLVI = CType(e.Data.GetData(GetType(ListViewItem)), _
ListViewItem)
newTextThing = dragLVI.SubItems(0).Text
End If
Something along those lines. The point is that you have to write code for the piece being dropped on.