views:

78

answers:

1

the user has the option of dragging several pictureboxes around the form. when he lets go of the mouse, the picturebox will take on a new position on the form. this i have done succesfully thanks to the stackoverflow community.

i would like to implement the following:

on mouseup, if the picturebox position is within some amount maybe 50 or 100 (i dont know what units VB.net) uses, i would like it to be dropped exactly in a defined position. sort of like if you play checkers on yahoo games, you dont have to place the piece exactly on the square only approximately.

please help me with a solution in vb.net

+1  A: 

This would work well, I think (cellSize is the "resolution" to which the control will "snap", assuming that each cell is square).

Prerequisites: you will need to have a PictureBox (or some other control) that you want to move around on your Form. Put the sample code below in your form's code and attach the MouseDown, MouseMove and MouseUp events of that control to these event handlers. You can attach the events using the property grid (click the events button, select the event, and use the combo box to select the corresponding event handler).

VB.NET:

Private Sub SetControlPosition(ByVal control As Control, ByVal targetPoint As Point, ByVal cellSize As Integer)
    Dim roundedLocation As New Point(CInt((Math.Round(CSng(targetPoint.X) / cellSize) * cellSize)), CInt((Math.Round(CSng(targetPoint.Y) / cellSize) * cellSize)))
    control.Location = roundedLocation
End Sub

If you want the control location to snap to some specific pre-defined locations, you can do like this instead (_allowedLocations defines two allowed locations; x=50, y=50 and x=500, y=500):

Private _allowedLocations As Point() = {New Point(50, 50), New Point(500, 500), New Point(700, 100)}
Private Sub SetControlPosition(ByVal control As Control, ByVal targetPoint As Point, ByVal cellSize As Integer)
    Dim shortestDistance As Integer = Integer.MaxValue
    Dim nearestLocationIndex As Integer = -1
    
    For i As Integer = 0 To _allowedLocations.Length - 1
        Dim width As Integer = targetPoint.X - _allowedLocations(i).X
        Dim height As Integer = targetPoint.Y - _allowedLocations(i).Y
        Dim distance As Integer = CInt(Math.Sqrt(Math.Pow(width, 2) + Math.Pow(height, 2)))
        If distance < shortestDistance Then
            shortestDistance = distance
            nearestLocationIndex = i
        End If
    Next
    control.Location = _allowedLocations(nearestLocationIndex)
End Sub

Example code calling the method (including moving the control with the mouse):

Private _mouseDownLocation As Point = Point.Empty
Private Sub PictureBox_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
    If (e.Button And MouseButtons.Left) = System.Windows.Forms.MouseButtons.Left Then
        _mouseDownLocation = e.Location
    End If
End Sub

Private Sub PictureBox_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
    If (e.Button And MouseButtons.Left) = System.Windows.Forms.MouseButtons.Left Then
        Dim target As Control = DirectCast(sender, Control)
        target.Location = New Point(target.Location.X + e.Location.X - _mouseDownLocation.X, target.Location.Y + e.Location.Y - _mouseDownLocation.Y)
    End If
End Sub

Private Sub PictureBox_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
    If (e.Button And MouseButtons.Left) = System.Windows.Forms.MouseButtons.Left Then
        Dim target As Control = DirectCast(sender, Control)
        ' Snap the control in place, to nearest 100x100 corner '
        SetControlPosition(target, target.Location, 100)
    End If
End Sub

The SetControlPosition method in C# (as an extra bonus):

private void SetControlPosition(Control control, Point targetPoint, int cellSize)
{
    Point roundedLocation = new Point(
        (int)(Math.Round((float)targetPoint.X / cellSize) * cellSize),
        (int)(Math.Round((float)targetPoint.Y / cellSize) * cellSize)
        );
    control.Location = roundedLocation;
}
Fredrik Mörk
@avrohom: Sorry about that ;o) I think the code is OK now (don't have a VB.NET environment around, converted the code here: http://www.developerfusion.com/tools/convert/csharp-to-vb/)
Fredrik Mörk
There, full example posted :o)
Fredrik Mörk
That's strange, I tested the code (the C# variant) before posting it. You did hook up the mouse events from your picture box to the event handlers in my sample?
Fredrik Mörk
Select your control in the forms designer and then show the property grid (I think F4 is the default key for this). In the toolbar of the property grid there is a tool button with an icon looking like a little flash; that's the events button. If you click that the property grid will display a list of events for the control. Select MouseDown in that list to connect the event to an event handler in your form (if you pasted my sample code into the form you should find PictureBox_MouseDown in the list).
Fredrik Mörk
In my sample it will make the control snap to the nearest position where x and y are multiples of 100. I guess the SetControlPosition could be reworked to compare the control's position to a list of "allowed" positions, finding the nearest one.
Fredrik Mörk
The answer is already updated ;o)
Fredrik Mörk
its beautiful! but in this line: Private _allowedLocations As Point() = New () {New Point(50, 50), New Point(500, 500), New Point(700, 100) i am getting the error "type expected" for "new ()"
I__
Oops, bad conversion from C# to VB.NET; simply remove the New() part (I have updated the code in the answer). Thanks for pointing it out.
Fredrik Mörk