ListView: MultiSelect items with mouse drag - vb.net

In ListView, I can press Ctrl + Shift and click on the item to select. But, I want to drag the mouse to select the items (like DataGridView). I tried this code (below) and I had a problem like this:
My code:
Private mouseDownLV As Boolean
Private Sub ListView1_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseDown
mouseDownLV = True
End Sub
Private Sub ListView1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseMove
If mouseDownLV Then
Try
Dim i = ListView1.HitTest(e.Location).Item.Index
ListView1.Items(i).Selected = True
Catch ' ex As Exception
End Try
End If
End Sub
Private Sub ListView1_MouseUp(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseUp
mouseDownLV = False
End Sub

Actually you need to iterate through the currently displayed ListViewItem objects and toggle the Selected property of the items that intersect with the mouse movement. Here's a way to achieve that:
Declare a class member named startPoint:
Private startPoint As Point
Handle the MouseDown event to set the starting position:
Private Sub ListView1_MouseDown(sender As Object, e As MouseEventArgs) Handles ListView1.MouseDown
Dim s = DirectCast(sender, ListView)
If e.Button = MouseButtons.Left AndAlso
s.Items.Count > 1 Then
startPoint = e.Location
End If
End Sub
Handle the MouseMove event to toggle the Selected property:
Private Sub ListView1_MouseMove(sender As Object, e As MouseEventArgs) Handles ListView1.MouseMove
Dim s = DirectCast(sender, ListView)
If e.Button = MouseButtons.Left AndAlso s.Items.Count > 1 Then
Dim selRect As New Rectangle(Math.Min(startPoint.X, e.Location.X),
Math.Min(startPoint.Y, e.Location.Y),
Math.Abs(e.Location.X - startPoint.X),
Math.Abs(e.Location.Y - startPoint.Y))
Dim cr = s.ClientRectangle
'Toggle selection...
For Each item In s.Items.Cast(Of ListViewItem).
Where(Function(x) x.Bounds.IntersectsWith(cr))
item.Selected = selRect.IntersectsWith(item.Bounds)
Next
End If
End Sub
A quick demo to check that:
But what if you have many items where the size of the client area is not large enough to display them all and thus the vertical scrollbar is visible? You will get something like this:
As you can see, the vertical scrollbar does not move and you won't be able to continue selecting/deselecting the hidden items. To fix that, we need some more code:
Import the signature of the GetScrollPos function somewhere in your class:
Imports System.Runtime.InteropServices
'...
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Shared Function GetScrollPos(hWnd As IntPtr,
nBar As Orientation) As Integer
End Function
Note: Passing a System.Windows.Forms.Orientation value instead of an Interger.
Change the MouseDown event to:
Private Sub ListView1_MouseDown(sender As Object, e As MouseEventArgs) Handles ListView1.MouseDown
Dim s = DirectCast(sender, ListView)
If e.Button = MouseButtons.Left AndAlso
s.Items.Count > 1 Then
Dim vsp = GetScrollPos(s.Handle, Orientation.Vertical)
Dim yOffset = s.Font.Height * vsp
startPoint = New Point(e.X, e.Y + yOffset)
End If
End Sub
And the MouseMove event to:
Private Sub ListView1_MouseMove(sender As Object, e As MouseEventArgs) Handles ListView1.MouseMove
Dim s = DirectCast(sender, ListView)
If e.Button = MouseButtons.Left AndAlso s.Items.Count > 1 Then
Dim vsp = GetScrollPos(s.Handle, Orientation.Vertical)
Dim yOffset = s.Font.Height * vsp
Dim selRect As New Rectangle(Math.Min(startPoint.X, e.Location.X),
Math.Min(startPoint.Y - yOffset, e.Location.Y),
Math.Abs(e.Location.X - startPoint.X),
Math.Abs(e.Location.Y - startPoint.Y + yOffset))
Dim cr = s.ClientRectangle
'Toggle selection...
For Each item In s.Items.Cast(Of ListViewItem).
Where(Function(x) x.Bounds.IntersectsWith(cr))
item.Selected = selRect.IntersectsWith(item.Bounds)
Next
'Scroll if needed...
Dim p = s.PointToClient(Cursor.Position)
Dim lvi = s.GetItemAt(p.X, p.Y)
If lvi Is Nothing Then Return
Dim fh = s.Font.Height
If lvi.Index > 0 AndAlso (p.Y - lvi.Bounds.Height * 1.5) <= fh Then
s.Items(lvi.Index - 1).EnsureVisible()
ElseIf lvi.Index < s.Items.Count - 1 AndAlso
(p.Y + lvi.Bounds.Height * 1.5) > (s.Height - fh) Then
s.Items(lvi.Index + 1).EnsureVisible()
End If
End If
End Sub
The outcome is:
Here is VB.NET custom ListView control for this problem, and another in C#.

Related

(VB) Suggestions with implementing a player shoot event

I'm currently working on a game in Visual Basic similar to an arcade shooter. The player moves their ship left and right across the screen using the arrow keys, while they 'shoot' using the spacebar. Problem is, I'm not sure where to place the bullet's movement so it will constantly move (not just spawn on a key-down event) and be updated by the Render() function. Any help or suggestions would be greatly appreciated.
My current code is displayed below; it's my first time using a game loop, so apologies if anything's misused.
Public Class frmMain
'Diming drawing surface & controls
Dim g, bbg As Graphics
Dim backBuff As Bitmap
Dim keys(256) As Boolean
Dim clientWidth, clientHeight As Integer
Dim timer As Stopwatch
Dim interval, startTick As Long
'Diming playerShip
Dim playerSize As Long = 64
Dim playerShip As New Rectangle(180, 430, playerSize, playerSize)
Dim playerLoc As New Point(playerShip.Location)
Dim playerSpr As Image = My.Resources.sprPlayer
Dim playerSpeed As Long
'Diming playerBullet
Dim playerBulletWidth As Long = 9
Dim playerBulletHeight As Long = 20
Dim playerBullet As New Rectangle(playerLoc.X, playerLoc.Y - 20, playerBulletWidth, playerBulletHeight)
Dim playerBulletLoc As New Point(playerBullet.Location)
Dim playerBulletSpr As Image = My.Resources.sprPlayerBullet
Dim playerBulletSpeed As Long
Dim playerShoot As Boolean = False
Public Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Configuring specific properties of the form
Me.DoubleBuffered = True
Me.MaximizeBox = False
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D
'Me.BackColor = Color.Black
'configuring timer controls
interval = 16
timer = New Stopwatch()
'Assigning values to empty variables
clientWidth = 450
clientHeight = 550
playerSpeed = 5
playerBulletSpeed = 5
'Configuring drawing surface
g = Me.CreateGraphics
backBuff = New Bitmap(clientWidth, clientHeight, Imaging.PixelFormat.Format32bppPArgb)
bbg = Graphics.FromImage(backBuff)
'Initially draw playerShip
bbg.DrawImage(playerSpr, playerShip)
'bbg.DrawImage(playerBulletSpr, playerBullet)
End Sub
Private Sub frmMain_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
keys(e.KeyCode) = True
End Sub
Private Sub frmMain_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
keys(e.KeyCode) = False
End Sub
Private Sub frmMain_Shown(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Shown
GameLoop()
End Sub
Private Sub GameLoop()
timer.Start()
Do While (Me.Created)
startTick = timer.ElapsedMilliseconds
GameLogic()
Render()
Application.DoEvents()
'Allows game to run at constant speed on different machines
Do While timer.ElapsedMilliseconds - startTick < interval
Loop
Loop
End Sub
Private Sub GameLogic()
'Spawning, movement & collision
Dim keyPressed As Boolean = False
'playerShip movement & shooting
'Checks for no collision with form's right wall
If Not playerShip.Location.X + playerSpeed > clientWidth - playerShip.Width - playerSpeed Then
'Move playerShip right (right arrow)
If keys(39) Then
playerLoc = New Point(playerShip.Location.X + playerSpeed, playerShip.Location.Y)
playerShip.Location = playerLoc
keyPressed = True
End If
End If
'Checks for no collision with form's left wall
If Not playerShip.Location.X - playerSpeed < 0 Then
'Move playerShip left (left arrow)
If keys(37) Then
playerLoc = New Point(playerShip.Location.X - playerSpeed, playerShip.Location.Y)
playerShip.Location = playerLoc
keyPressed = True
End If
End If
'Launch bullet (space-bar)
If keys(32) Then
playerShoot = True
keyPressed = True
PlayerShipShoot()
End If
End Sub
Private Sub PlayerShipShoot()
'Add bullet activity here... maybe
End Sub
Private Sub Render()
'Drawing playerShip & playerBullet
bbg.DrawImage(playerSpr, playerShip)
If playerShoot = True Then
bbg.DrawImage(playerBulletSpr, playerBullet)
End If
'Drawing backBuff to the form
g.DrawImage(backBuff, 0, 0)
bbg.Clear(Color.Silver)
End Sub
End Class
Thanks.

how to maintain the location of a picturebox in the panel

i want to maintain the location of picturebox2 which is inside a panel. in my case, the image looks like this.. https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ8SUu7ZXBJVXrhic-Xou9OsW4h7QDd8yH5xhYtV3DlnJ0Q1UVJiw (there's a map /picturebox1/ and the color green locator or pointer is another picturebox /picturebox2/)
is it possible to zoom in and zoom out the image without losing the right coordinates? Because i want to maintain the location of the locator(picturebox2) in the map (picturebox1)
so far, i can now zoom in and zoom out the image in the scrollable panel using trackbar. but my only problem is that, the picturebox2 (another image above the picturebox1) needs to move its location as picturebox1 is zooming.
Public ClassForm1
Private img original As Image
Private m_PanStartPoint As New Point
Private n_PanStartPoint As New Point
Private Sub Form1_Load(ByVal sender AsSystem.Object, ByVal e AsSystem.EventArgs) Handles MyBase.Load
imgoriginal = Image.FromFile("C:\New Folder\picture1.jpg")
PictureBox1.BackgroundImageLayout = ImageLayout.Stretch
zoomSlider.Minimum = 1
zoomSlider.Maximum = 5
zoomSlider.SmallChange = 1
zoomSlider.LargeChange = 1
zoomSlider.UseWaitCursor = False
Me.DoubleBuffered = True
Panel1.AutoScroll = True
PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
PictureBox1.Parent = PictureBox1
PictureBox2.Parent = PictureBox1
PictureBox1.BackColor = Color.Transparent
Dim mstream As NewSystem.IO.MemoryStream()
PictureBox1.Image = Image.FromStream(mstream)
PictureBox2.Location = NewSystem.Drawing.Point(100, 100)
End Sub
Public Function pictureboxzoom(ByValimgAsImage, ByVal size AsSize) AsImage
Dim bm As Bitmap = New Bitmap(img, Convert.ToInt32(img.Width * size.Width), Convert.ToInt32(img.Height * size.Height))
Dim grap As Graphics = Graphics.FromImage(bm)
grap.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
Return bm
End Function
Private Sub zoomSlider_Scroll(ByVal sender AsSystem.Object, ByVal e AsSystem.EventArgs) Handles zoomSlider.Scroll
If zoomSlider.Value> 0 Then
PictureBox1.Image = Nothing
PictureBox1.Image = pictureboxzoom(imgoriginal, New Size(zoomSlider.Value, zoomSlider.Value))
End If
End Sub
Private Sub PictureBox1_MouseDown(ByVal sender AsObject, ByVal e AsSystem.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
m_PanStartPoint = NewPoint(e.X, e.Y)
End Sub
Private Sub PictureBox1_MouseMove(ByVal sender AsObject, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
If e.Button = Windows.Forms.MouseButtons.Left Then
Dim DeltaX As Integer = (m_PanStartPoint.X - e.X)
Dim DeltaY As Integer = (m_PanStartPoint.Y - e.Y)
Panel1.AutoScrollPosition = _
New Drawing.Point((DeltaX - Panel1.AutoScrollPosition.X), _
(DeltaY - Panel1.AutoScrollPosition.Y))
Button1.Location = New System.Drawing.Point(0, 0)
End If
End Sub
End Class

How to draw points when user clicks on a map

Dim HaveToDraw As New Boolean
Dim xMouse As Integer
Dim yMouse As Integer
Private Sub foo(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
If HaveToDraw = True Then
e.Graphics.FillEllipse(Brushes.Green, xMouse, yMouse, 10, 10)
End If
HaveToDraw = False
End Sub
Sub PictureBox1_MouseClick(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseClick
If RadioButton1.Checked = True Then
xMouse = e.X
yMouse = e.Y
HaveToDraw = True
PictureBox1.Refresh()
End If
End Sub
This code lets the user draw an elipse when he clicks on any point on the map, but there are 2 problems with it: 1 - The user is able to draw only one elipse; 2 - The user is not able to erase a previously created elipse.
So, how can I do that solving these two problems?
As #Idle_Mind suggested, you could use a list to store your points, and a right-click event to remove the points:
Dim radius as Integer = 5
Private points As New List(Of Point)()
Private Sub pictureBox1_MouseClick(sender As Object, e As MouseEventArgs)
If e.Button = System.Windows.Forms.MouseButtons.Left Then
points.Add(e.Location) ' add point on left click
ElseIf e.Button = System.Windows.Forms.MouseButtons.Right Then
For i As Integer = 0 To points.Count - 1 ' remove points on right-click
If distance(points(i).X, points(i).Y, e.Location) < radius Then
points.RemoveAt(i)
End If
Next
End If
pictureBox1.Refresh()
End Sub
'helper function
Private Function distance(x__1 As Integer, y__2 As Integer, mousep As Point) As Integer
Dim X__3 As Integer = CInt(Math.Pow(CDbl(x__1 - mousep.X), 2))
Dim Y__4 As Integer = CInt(Math.Pow(CDbl(y__2 - mousep.Y), 2))
Return CInt(Math.Sqrt(CDbl(X__3 + Y__4)))
End Function
Private Sub pictureBox1_Paint(sender As Object, e As PaintEventArgs)
For i As Integer = 0 To points.Count - 1
e.Graphics.FillEllipse(Brushes.Green, points(i).X - radius, points(i).Y - radius, radius * 2, radius * 2)
Next
End Sub
I also changed the paint code to draw the circles so that they are centered under the mouse-click.

Highlighting Listbox Item when Mouseover on Item

I manage to customize the normal list box with an image, change text and background color when item is selected in ownerdrawn, what I want to achieve now is to drawn a custom highlight color on the item when mouse is hover on the listbox item, is that possible or not..., I provided my sample code below on what I come so far..
If e.Index = -1 Then Exit Sub
Dim listBox As ListBox = CType(sender, ListBox)
e.DrawBackground()
Dim isItemSelected As Boolean = ((e.State And DrawItemState.Selected) = DrawItemState.Selected)
If e.Index >= 0 AndAlso e.Index < listBox.Items.Count Then
Dim textSize As SizeF = e.Graphics.MeasureString(listBox.Items(e.Index).ToString(), listBox.Font)
Dim itemImage As Image = My.Resources.FolderHorizontal
'set background and text color
Dim backgroundColorBrush As New SolidBrush(If((isItemSelected), Color.CornflowerBlue, Color.White))
Dim itemTextColorBrush As Color = If((isItemSelected), Color.White, Color.Black)
e.Graphics.FillRectangle(backgroundColorBrush, e.Bounds)
'draw the item image
e.Graphics.SmoothingMode = SmoothingMode.HighQuality
e.Graphics.DrawImage(itemImage, e.Bounds.X + 2, _
e.Bounds.Y + (e.Bounds.Height - textSize.Height) / 2, _
itemImage.Width, itemImage.Height)
'draw the item text
Dim x, y As Single
Dim h As Single = textSize.Height
Dim rect As Rectangle = e.Bounds
rect.X += listBox.ItemHeight
rect.Width -= listBox.ItemHeight
x = rect.X - 3
y = rect.Y + (rect.Height - h) / 2
Dim itemText As String = listBox.Items(e.Index).ToString()
TextRenderer.DrawText(e.Graphics, itemText, e.Font, _
New Rectangle(x, y, ClientRectangle.Width, ClientRectangle.Height), _
itemTextColorBrush, TextFormatFlags.Default)
'clean up
backgroundColorBrush.Dispose()
End If
e.DrawFocusRectangle()
You can use the IndexFromPoint to do something like that:
Dim mouseIndex As Integer = -1
Private Sub ListBox1_MouseMove(sender As Object, e As MouseEventArgs) _
Handles ListBox1.MouseMove
Dim index As Integer = ListBox1.IndexFromPoint(e.Location)
If index <> mouseIndex Then
If mouseIndex > -1 Then
Dim oldIndex As Integer = mouseIndex
mouseIndex = -1
If oldIndex <= ListBox1.Items.Count - 1 Then
ListBox1.Invalidate(ListBox1.GetItemRectangle(oldIndex))
End If
End If
mouseIndex = index
If mouseIndex > -1 Then
ListBox1.Invalidate(ListBox1.GetItemRectangle(mouseIndex))
End If
End If
End Sub
Then in your drawing code:
If mouseIndex > -1 AndAlso mouseIndex = e.Index Then
backgroundColorBrush = New SolidBrush(Color.DarkMagenta)
End If
I will show you how to do this. All experts say its complicated and cannot be done with a listbox... I was able to do that in 5 minutes
the name of the listbox I created is listPOSSIBILITIES
1) create a variable which is global to your form
Dim HOVERTIME As Boolean = True
2) create MouseEnter event
Private Sub listPOSSIBILITIES_MouseEnter(sender As Object, e As system.EventArgs) Handles listPOSSIBILITIES.MouseEnter
HOVERTIME = True
End Sub
3) create MouseLeave event
Private Sub listPOSSIBILITIES_MouseLeave(sender As Object, e As System.EventArgs) Handles listPOSSIBILITIES.MouseLeave
HOVERTIME = False
End Sub
4) create MouseMove event
Private Sub listPOSSIBILITIES_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles listPOSSIBILITIES.MouseMove
Dim mypoint As Point
mypoint = listPOSSIBILITIES.PointToClient(Cursor.Position)
Dim myindex As Integer = listPOSSIBILITIES.IndexFromPoint(mypoint)
If myindex < 0 Then Exit Sub
listPOSSIBILITIES.SelectedIndex = myindex
End Sub
5) create MouseClick event
Private Sub listPOSSIBILITIES_MouseClick(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles listPOSSIBILITIES.MouseClick
HOVERTIME = False
End Sub
6) create SelectedIndexChanged event
Private Sub listPOSSIBILITIES_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles listPOSSIBILITIES.SelectedIndexChanged
If HOVERTIME Then Exit Sub
'put the rest of your code after this above If statement
End Sub
This works because the MouseClick event is triggered before the SelectIndexChanged event

How to drag and drop row within the same datagridview

In a Windows App (Visual Studio)(VB) how do you drag and drop a single row to another postition to allow for the user to re-order the row? I haven't found any worthy examples for this yet.
Here is a vb version from this C# answer: How could I Drag and Drop DataGridView Rows under each other?
The form class variables:
Private fromIndex As Integer
Private dragIndex As Integer
Private dragRect As Rectangle
The drag events:
Private Sub DataGridView1_DragDrop(ByVal sender As Object, _
ByVal e As DragEventArgs) _
Handles DataGridView1.DragDrop
Dim p As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))
dragIndex = DataGridView1.HitTest(p.X, p.Y).RowIndex
If (e.Effect = DragDropEffects.Move) Then
Dim dragRow As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))
DataGridView1.Rows.RemoveAt(fromIndex)
DataGridView1.Rows.Insert(dragIndex, dragRow)
End If
End Sub
Private Sub DataGridView1_DragOver(ByVal sender As Object, _
ByVal e As DragEventArgs) _
Handles DataGridView1.DragOver
e.Effect = DragDropEffects.Move
End Sub
The mouse events:
Private Sub DataGridView1_MouseDown(ByVal sender As Object, _
ByVal e As MouseEventArgs) _
Handles DataGridView1.MouseDown
fromIndex = DataGridView1.HitTest(e.X, e.Y).RowIndex
If fromIndex > -1 Then
Dim dragSize As Size = SystemInformation.DragSize
dragRect = New Rectangle(New Point(e.X - (dragSize.Width / 2), _
e.Y - (dragSize.Height / 2)), _
dragSize)
Else
dragRect = Rectangle.Empty
End If
End Sub
Private Sub DataGridView1_MouseMove(ByVal sender As Object, _
ByVal e As MouseEventArgs) _
Handles DataGridView1.MouseMove
If (e.Button And MouseButtons.Left) = MouseButtons.Left Then
If (dragRect <> Rectangle.Empty _
AndAlso Not dragRect.Contains(e.X, e.Y)) Then
DataGridView1.DoDragDrop(DataGridView1.Rows(fromIndex), _
DragDropEffects.Move)
End If
End If
End Sub
Make sure you have the grids AllowDrop property set to true.
UPDATE:
Instead of
If dragIndex < 0 Then dragIndex = DataGridView1.RowCount - 1
change to
If dragIndex > -1 Then
'action if not selected in the row header and blank space
else
'return error if selected in the column header and blank space
end if
then a error occurs when you drag a row to the "blank zone", if you don't believe me, you must to try it.
the final code (Only for the part "The drag events") is this:
Private Sub DataGridView1_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragDrop
Dim p As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))
dragIndex = DataGridView1.HitTest(p.X, p.Y).RowIndex
'Determine if dragindex is valid row index
If dragIndex > -1 Then
If (e.Effect = DragDropEffects.Move) Then
Dim dragRow As DataGridViewRow = CType(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
DataGridView1.Rows.RemoveAt(fromIndex)
DataGridView1.Rows.Insert(dragIndex, dragRow)
'Add this line of code if you want to put selected rows to the rows that change
DataGridView1.Rows(dragIndex).Selected = True
End If
Else 'Do any message here if selected in column header and blank space.
End If
End Sub
Here's a Control without the mentioned bug.
Set AllowUserToOrderRows and AllowDrop to True in the Windows Forms Designer and drag the row headers, not the content.
Imports System.ComponentModel
Public Class BetterDataGridView
Inherits DataGridView
<Category("Behavior"), DefaultValue(False)>
Public Property AllowUserToOrderRows As Boolean = False
Protected Overrides Sub OnMouseDown(e As MouseEventArgs)
MyBase.OnMouseDown(e)
Dim hitInfo As HitTestInfo = HitTest(e.X, e.Y)
If AllowUserToOrderRows AndAlso
e.Button = MouseButtons.Left AndAlso
hitInfo.ColumnIndex = -1 AndAlso
ValidRow(hitInfo.RowIndex) Then
DoDragDrop(Rows(hitInfo.RowIndex), DragDropEffects.Move)
End If
End Sub
Protected Overrides Sub OnDragOver(e As DragEventArgs)
MyBase.OnDragOver(e)
Dim dragRow As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))
Dim targetIndex As Integer = GetRowIndex(e)
e.Effect = If(ValidRowDragDrop(dragRow, targetIndex),
DragDropEffects.Move,
DragDropEffects.None)
End Sub
Protected Overrides Sub OnDragDrop(e As DragEventArgs)
MyBase.OnDragDrop(e)
Dim dragRow As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))
Dim targetIndex As Integer = GetRowIndex(e)
If e.Effect = DragDropEffects.Move AndAlso ValidRowDragDrop(dragRow, targetIndex) Then
EndEdit()
Rows.Remove(dragRow)
Rows.Insert(targetIndex, dragRow)
ClearSelection()
dragRow.Selected = True
End If
End Sub
Protected Function ValidRow(rowIndex As Integer) As Boolean
Return rowIndex >= 0 AndAlso
rowIndex < Rows.Count - If(AllowUserToAddRows, 1, 0)
End Function
Protected Function GetRowIndex(e As DragEventArgs) As Integer
Dim clientPos As Point = PointToClient(New Point(e.X, e.Y))
Return HitTest(clientPos.X, clientPos.Y).RowIndex
End Function
Protected Function ValidRowDragDrop(dragRow As DataGridViewRow, targetIndex As Integer) As Boolean
Return dragRow IsNot Nothing AndAlso
ValidRow(targetIndex) AndAlso
targetIndex <> dragRow.Index AndAlso
Rows.Contains(dragRow)
End Function
End Class
1.5 improvements for the event GridView.DragDrop:
The first 50% improvement, To avoid the descriped error you can also use
Private Sub DgvSearchFieldCurrent_DragDrop( _
ByVal sender As Object, ByVal e As DragEventArgs) _
Handles DgvSearchFieldCurrent.DragDrop
Dim LclDgv As DataGridView = CType(sender, DataGridView)
If dragIndex > -1 AndAlso dragIndex < LclDgv.RowCount -1 Then
Second is to set focus to the current row and the first cell:
LclDgv.Rows.Insert(dragIndex, dragRow)
LclDgv.Rows(fromIndex).Selected = False
LclDgv.Rows(dragIndex).Selected = True
For Each C As DataGridViewColumn In LclDgv.Columns
LclDgv(C.Index, fromIndex).Selected = False
Next
LclDgv(0, dragIndex).Selected = True
Thank you for everything, code working. I was getting only one error. I solved it.
if the datagridview "Enable Editing" is set, you receive an error when you throw line spacing. You can try. I solved it as follows:
Private Sub DataGridView1(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragDrop
Dim p As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))
dragIndex = DataGridView1.HitTest(p.X, p.Y).RowIndex
If (e.Effect = DragDropEffects.Move) Then
Dim dragRow As DataGridViewRow = CType(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
If dragIndex = DataGridView1.RowCount - 1 Then '**ADD THIS AREA**
DataGridView1.Rows.RemoveAt(fromIndex)
DataGridView1.Rows.Insert(DataGridView1.RowCount - 1, dragRow)
Else
If dragIndex < 0 Then dragIndex = DataGridView1.RowCount - 2 '**this is important**
DataGridView1.Rows.RemoveAt(fromIndex)
DataGridView1.Rows.Insert(dragIndex, dragRow)
End If
End If
End Sub
Thanks for all other information