VB.NET TableLayout MouseEnter and leave event - vb.net

I'm trying to do something that actually looks "simple" since several hours, but I cannot understand how to do it...and lurking over SO or different sites it seems that maybe is not that obvious.
The question is simple: i have a tableLayoutPanel with multiple rows, each one of them contains a panel, that contains several other controls.
I just want that when the mouse enters a row, the row background changes and when the mouse leave that row, it comes back to original color.
These are the simple event trappers, where pnlLayoutRow is the name of the panel containing the other controls:
Private Sub devRowMouseEnter(sender As System.Object, e As EventArgs) Handles pnlLayoutrow.MouseEnter
pnlLayoutrow.BackColor = Drawing.Color.FromArgb(&HFFFFEEAA)
End Sub
Private Sub devRowMouseLeave(sender As System.Object, e As EventArgs) Handles pnlLayoutrow.MouseLeave
pnlLayoutrow.BackColor = Drawing.Color.FromArgb(&HFFE7DEBD)
End Sub
The problem is: mouseEnter is correctly fired each time I enter the row, but Mouseleave is fired as soon as the mouse reach one of the controls inside the panel..that drives me crazy.
In other environment, I would solve this placing a transparent object all over the panel and trapping the mouseEnter and leave for that object..but it seems in VB trasparent objects do not exist.
Hope I have been clear in my explanation..it is pretty late in the night and I'm a bit tired.
thank you in advance
hope someone can help me
Cristiano

This version of your mouse leave event checks that the mouse is still within the bounds of your TableLayoutPanel and if it is then it exits without changing the color
Private Sub devRowMouseLeave(sender As System.Object, e As EventArgs) Handles pnlLayoutRow.MouseLeave
Dim p As Point = Me.PointToClient(MousePosition)
If p.Y > pnlLayoutRow.Top And p.Y < (pnlLayoutRow.Top + pnlLayoutRow.Height) And p.X > pnlLayoutRow.Left And p.X < (pnlLayoutRow.Left + pnlLayoutRow.Width) Then
Exit Sub
Else
pnlLayoutRow.BackColor = Drawing.Color.FromArgb(&HFFE7DEBD)
End If
End Sub
It seems to work ok for me,so I hope it is the same for you.
I've had a Google about mouse polling rates and by default, in windows, it's 125hz which might seem OK. However, if you move the mouse quickly, the mouse will enter and leave the panel more quickly that windows can detect it. Because of this, sometimes the .MouseEnter and .MouseLeave events don't fire. So I have here an alternative which will at least detect when the mouse leaves the panel. Add a Timer you your form called tmrPanelLeave
Private Sub devRowMouseEnter(sender As System.Object, e As EventArgs) Handles pnlLayoutRow.MouseEnter
pnlLayoutRow.BackColor = Drawing.Color.FromArgb(&HFFFFEEAA)
tmrPanelLeave.Start()
End Sub
Private Sub tmrPanelLeave_Tick(sender As Object, e As EventArgs) Handles tmrPanelLeave.Tick
Dim p As Point = Me.PointToClient(MousePosition)
If p.Y > pnlLayoutRow.Top And p.Y < (pnlLayoutRow.Top + pnlLayoutRow.Height) And p.X > pnlLayoutRow.Left And p.X < (pnlLayoutRow.Left + pnlLayoutRow.Width) Then
Exit Sub
Else
pnlLayoutRow.BackColor = Drawing.Color.FromArgb(&HFFE7DEBD)
tmrPanelLeave.Stop()
End If
End Sub

Related

Button collision event vb.net

I'm trying to make some buttons move around in a rapid manner. Each click of a specific button should "reward" the player with a certain quantity of points (both positive or negative). This is a similar idea to some of the aspects in some of the "idiot test games" you see online.
How can I perform collision checks with the buttons?
I know it's possible with picture boxes to perform event collisions, with the following code picObject1.bounds.intersectsWith(picObject2.bounds).
However, when I tried using that function for buttons, they didn't register as a collision. I do not know if that is because buttons don't have bounds (though that doesn't sound right) or due to some other hidden detail that I have missed.
Any pointers in the right direction would be extremely useful!
Handle the Click event for the buttons. The code might look something like this:
Public Class Form1
Private PlayerScore As Integer = 0
Private Buttons() As Button
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Buttons = Enumerable.Range(0,10).
Select(Function(i)
i = New Button()
AddHandler i.Click, AddressOf ScoreClick
Me.Controls.Add(i)
Return i
End Function)
End Sub
Dim rnd As New Random()
Private Sub ScoreClick(ByVal sender As Object, ByVal e As EventArgs)
Dim button As Button = DirectCast(sender, Button)
PlayerScore += rnd.Next(-10, 10)
End Sub
End Class
Now just add a timer to to the form to move the buttons around.

Getting a panel within a panel with mouse position

I've been looking for this thing... It should be working yet it is not. There must be something I don't get understand or that I'm missing. It's quite a simple problem but I can't seem to solve it.
I got Panel1 and Panel2 as shown in this picture.
I want to catch when mouse is over Panel2 within Panel1 MouseLeave event. My code goes like this :
Private Sub Panel1_MouseLeave(sender As Object, e As EventArgs) Handles Panel1.MouseLeave
If sender.ClientRectangle.Contains(PointToClient(Control.MousePosition)) Then
For Each ctrl As Object In sender.controls
If ctrl.ClientRectangle.Contains(PointToClient(Control.MousePosition)) Then Exit Sub
Next
If Not IsNothing(sender.BackgroundImage) Then sender.BackgroundImage = Nothing
End If
End Sub
Private Sub Panel2_MouseLeave(sender As Object, e As EventArgs) Handles Panel2.MouseLeave
If Not sender.ClientRectangle.Contains(PointToClient(Control.MousePosition)) Then
If Not IsNothing(sender.BackgroundImage) Then sender.BackgroundImage = Nothing
End If
End Sub
I'm successfully getting into the first if, but the 2nd one within the For Each just never equals true. So I thought maybe there was a problem with the 2nd panel, so I tried placing the same code for Panel2 MouseLeave, but it's working just fine.
I really need this code to work for a big control flickering problem I'm having.
Thanks to Hans Passant for the hint. I just had to call the PointToClient with the right control :
Private Sub Panel1_MouseLeave(sender As Object, e As EventArgs) Handles Panel1.MouseLeave
If sender.ClientRectangle.Contains(Panel1.PointToClient(Control.MousePosition)) Then
For Each ctrl As Object In sender.controls
If ctrl.ClientRectangle.Contains(ctrl.PointToClient(Control.MousePosition)) Then Exit Sub
Next
If Not IsNothing(sender.BackgroundImage) Then sender.BackgroundImage = Nothing
End If
End Sub

How to change progress bar value by dragging mouse in vb.net

I am developing a media player and I'm trying to change value of Progress Bar using mouse cursor.
what i want is to set the value of progress bar where mouse cursor points after clicking + Dragging.
A progress bar is not the correct control to use for this. You should use a TrackBar instead.
But it can be made to work, with about -10 elegance points. The trickiest problem with ProgressBar is that it animates progress. That makes it slow to respond to your mouse moves. That animation can be disabled, but not perfectly. Closest you can get is:
Private Shared Sub ChangeProgress(bar As ProgressBar, e As MouseEventArgs)
If e.Button = Windows.Forms.MouseButtons.Left Then
Dim mousepos = Math.Min(Math.Max(e.X, 0), bar.ClientSize.Width)
Dim value = CInt(bar.Minimum + (bar.Maximum - bar.Minimum) * mousepos / bar.ClientSize.Width)
'' Disable animation, if possible
If value > bar.Value And value < bar.Maximum Then
bar.Value = value + 1
bar.Value = value
Else
bar.Value = value
End If
End If
End Sub
And call it from MouseDown and MouseMove event handlers:
Private Sub ProgressBar1_MouseMove(sender As Object, e As MouseEventArgs) Handles ProgressBar1.MouseMove
ChangeProgress(ProgressBar1, e)
End Sub
Private Sub ProgressBar1_MouseDown(sender As Object, e As MouseEventArgs) Handles ProgressBar1.MouseDown
ChangeProgress(ProgressBar1, e)
End Sub
It is workable, you'll notice that getting to 100% is a bit awkward. But, really, use a TrackBar instead. It was made to do this.

.NET TrackBar MouseDown follow cursor?

I currently use this code to fix the bug where if you click somewhere on the Horizontal TrackBar it jumps to the middle then to the end of the TrackBar. So this code fixes that bug, which now jumps to the location you click.
But still a problem remains when I keep my mouse down and move it around the TrackBar the slider should follow but it just resets to beginning position, how do I make it follow right on top of cursor? would I need a timer control for that?
Private Sub tbTest_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles tbTest.MouseDown
Dim dblValue As Double
'Jump to the clicked location, bug FIX.
dblValue = (Convert.ToDouble(e.X) / Convert.ToDouble(tbTest.Width)) * (tbTest.Maximum - tbTest.Minimum)
tbTest.Value = Convert.ToInt32(dblValue)
End Sub
Make the method handle both the MouseDown() and MouseMove() events like this:
Private Sub tbTest_MovePointer(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles tbTest.MouseDown, tbTest.MouseMove
If e.Button = Windows.Forms.MouseButtons.Left Then
Dim dblValue As Double
'Jump to the clicked location, bug FIX.
dblValue = (Convert.ToDouble(e.X) / Convert.ToDouble(tbTest.Width)) * (tbTest.Maximum - tbTest.Minimum)
tbTest.Value = Convert.ToInt32(dblValue)
End If
End Sub
*Note the multiple events listed after the Handles keyword at the end of the first line. I also added a check to ensure the left mouse button is down.

Moving .NET controls at runtime

I am attempting to move all controls on a form down or up by the height of a menubar depending on whether it is visible or not. I have code which I think ought to work well for this, however it seems that Me.Controls is empty at runtime, so my for each loop is never entered. Could someone please offer a suggestion as to how I can move the controls?
Private Sub uxMenuStrip_VisibleChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles uxMenuStrip.VisibleChanged
For Each control As Control In Me.Controls
If control.Name <> "uxMenuStrip" Then
Dim temp As AnchorStyles = control.Anchor
control.Anchor = AnchorStyles.None
control.Top -= ((CInt(uxMenuStrip.Visible) * 2 - 1) * uxMenuStrip.Height)
control.Anchor = temp
End If
Next
Me.Height += ((CInt(uxMenuStrip.Visible) * 2 - 1) * uxMenuStrip.Height)
End Sub
Add a new Handler with Control and change location in address
Public Sub ChngPostion(ByVal sender As System.Object, ByVal e As System.EventArgs)
For Each cntrl As Control In Me.Controls
If cntrl.Name = sender.Name Then
cntrl.Location = New System.Drawing.Point(sender.Location.X,sender.Location.Y)
End If
Next
End Sub
As Michael Todd points out, Me.Controls can't be empty. Also, this might not work as well as you're thinking. Controls on WinForms apps are hierarchical. The only way to do this 100% cleanly is to make the move code recursive. IE, perform the same operation on every control in each control's controls collection. (Now I'm sounding like Dr. Seuss...) If your form is simple, this wouldn't be an issue, obviously.
At the end of the day, though, you'll probably be better off just putting everything on the form inside a Panel and just moving the Panel control explicitly by name. It would make what you're trying to do more clear.
Try this:
Private Sub uxMenuStrip_VisibleChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles uxMenuStrip.VisibleChanged
Dim menu As Control = sender
Dim dh As Integer = IIf(menu.Visible, 1, -1) * menu.Height
For Each control As Control In Controls
If control.Parent Is Me And Not control Is menu Then
control.Top += dh
End If
Next
Height += dh
End Sub
Update:
Anyway, i strongly recomment using container, in case with MenuStrip - ToolStripContainer.