I am trying to make a form fade away from the taskbar if the user is not currently hovering over the form with the mouse. (The form contains hyperlinks). On the adverse, i want the form to reset to its original position if the mouse comes back to the form. However, for whatever reason, it seems as thought the enter and leave evens fire in sync when either even occurs. If i leave the form with my mouse, both events fire. If i enter the form, both events fire. What is wrong?
Sub FormLeave()
MouseForm = False
Do Until y = Screen.PrimaryScreen.WorkingArea.height + 50
Sleep(10)
y = y + 1
Me.Location = New Point(x, y)
If MouseForm = True Then
Exit Sub
End If
Loop
End Sub
Sub FormEnter()
MouseForm = True
Me.Visible = True
x = Screen.PrimaryScreen.WorkingArea.Width - Me.Width
y = Screen.PrimaryScreen.WorkingArea.Height - Me.Height
Me.Location = New Point(x, y)
End Sub
Animation is best done with a timer (i.e. event driven) rather than sleeping.
You have to make sure the cursor has left the boundary of the form, as a leave event is fired on the form when the mouse enters a control within the form.
I added a longer delay when the mouse initially leaves the form as otherwise it is really annoying.
To demonstrate, use this code with a new Windows Forms project. Add controls to the form if you want to confirm it does not misbehave when you put the mouse over a control on the form.
Public Class Form1
Dim x As Integer
Dim y As Integer
Dim tim As Timer
Sub MoveFormAway(sender As Object, e As EventArgs)
' called on timer tick event
Dim destY = Screen.PrimaryScreen.WorkingArea.Height - 100
If y >= destY Then
tim.Enabled = False
Exit Sub
End If
' interval for form position changes
tim.Interval = 10
y += 2
Me.Location = New Point(x, y)
End Sub
Sub FormBodyLeave(sender As Object, e As EventArgs)
' If the mouse has not left the outside of the form,
' i.e. it has entered a control on the form, then exit:
If Me.ClientRectangle.Contains(PointToClient(Cursor.Position)) Then
Exit Sub
End If
If tim Is Nothing OrElse Not tim.Enabled Then
x = Me.Location.X
y = Me.Location.Y
tim = New Timer
' initial interval until form starts running away
tim.Interval = 1000
AddHandler tim.Tick, AddressOf MoveFormAway
tim.Enabled = True
End If
End Sub
Sub FormBodyEnter(sender As Object, e As EventArgs)
If tim IsNot Nothing Then
tim.Enabled = False
End If
MoveToDefaultPosition()
End Sub
Sub AddFormDisappearingHandlers()
AddHandler Me.MouseEnter, AddressOf FormBodyEnter
AddHandler Me.MouseLeave, AddressOf FormBodyLeave
End Sub
Sub MoveToDefaultPosition()
x = Screen.PrimaryScreen.WorkingArea.Width - Me.Width
y = Screen.PrimaryScreen.WorkingArea.Height - Me.Height
Me.Location = New Point(x, y)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MoveToDefaultPosition()
AddFormDisappearingHandlers()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
' Tidy up.
If tim IsNot Nothing Then
tim.Dispose()
End If
End Sub
End Class
Related
I'm trying to create a simple game where my character has deal with a maze, in visual basic 2019
I cannot stop my character(picturebox) from passing through a wall(picturebox).
I have to say that I am far away from an expert and it's just an important project for school.
I tried this
Dim colliding As Boolean = False
For Each PictureBox In Me.Controls
If PictureBox1.Bounds.IntersectsWith(PictureBox.Bounds) Then
colliding = True
Else
colliding = False
End If
Next
and this
Dim colliding As Boolean = False
For Each PictureBox In Me.Controls
If PictureBox IsNot PictureBox1 AndAlso PictureBox21.Bounds.IntersectsWith(PictureBox.Bounds) Then
colliding = True
Else
colliding = False
End If
Next
in both attends I failed hard, and my character (picturebox1) can still pass through a wall
Code assumes that all PictureBoxes are DIRECTLY contained by the Form itself (they are not inside another container like a Panel), and that anything besides PictureBox1 is a wall:
Dim colliding As Boolean = False
For Each PB As PictureBox In Me.Controls.OfType(Of PictureBox)
If PB IsNot PictureBox1 Then
If PB.Bounds.IntersectsWith(PictureBox1.Bounds) Then
colliding = True
Exit For
End If
End If
Next
An alternate approach using a bit of LINQ:
Public Class Form1
Private Walls As New List(Of PictureBox)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Walls = Me.Controls.OfType(Of PictureBox).Where(Function(pb) pb IsNot PictureBox1).ToList
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim colliding As Boolean = Walls.Any(Function(pb) pb.Bounds.IntersectsWith(PictureBox1.Bounds))
End Sub
End Class
Here is another option for dealing with the collision.
This assumes 4 buttons to move the 'character'
Private Enum MoveDirection
Left
Down
Right
Up
End Enum
Private Sub RightButton_Click(sender As Object, e As EventArgs) Handles RightButton.Click
MovePicBox(CharacterPicBox, MoveDirection.Right)
End Sub
Private Sub LeftButton_Click(sender As Object, e As EventArgs) Handles LeftButton.Click
MovePicBox(CharacterPicBox, MoveDirection.Left)
End Sub
Private Sub UpButton_Click(sender As Object, e As EventArgs) Handles UpButton.Click
MovePicBox(CharacterPicBox, MoveDirection.Up)
End Sub
Private Sub DownButton_Click(sender As Object, e As EventArgs) Handles DownButton.Click
MovePicBox(CharacterPicBox, MoveDirection.Down)
End Sub
Private Sub MovePicBox(PicBox As PictureBox, movement As MoveDirection)
'save the old location to move the pic box back if a clash occurs
Dim oldLocation As Point = PicBox.Location
Dim newLocation As Point
Dim stepSize As Integer = 50
'calculate new position
Select Case movement
Case MoveDirection.Down
newLocation.X = oldLocation.X
newLocation.Y = oldLocation.Y + stepSize
Case MoveDirection.Left
newLocation.X = oldLocation.X - stepSize
newLocation.Y = oldLocation.Y
Case MoveDirection.Up
newLocation.X = oldLocation.X
newLocation.Y = oldLocation.Y - stepSize
Case MoveDirection.Right
newLocation.X = oldLocation.X + stepSize
newLocation.Y = oldLocation.Y
End Select
'move the picture box
PicBox.Location = newLocation
'check if it has collided
For Each wallPicBox As PictureBox In Me.Controls.OfType(Of PictureBox)
If wallPicBox Is PicBox Then
Continue For
End If
If PicBox.Bounds.IntersectsWith(wallPicBox.Bounds) Then
'move it back
PicBox.Location = oldLocation
End If
Next
End Sub
Making a joke VB program that requires a button click to make a PictureBox very quickly switch between two pictures. I tried using the sleep command but nothing changes on screen. Here's what I've tried so far.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TransClass2.Image = My.Resources._21
System.Threading.Thread.Sleep(100)
TransClass2.Image = My.Resources._11
System.Threading.Thread.Sleep(100)
End Sub
TransClass2 is a class that inherits PictureBox. It's used to add transparent functionalities to PictureBoxes.
Public Class TransClass
Inherits PictureBox
Protected Overrides Sub OnPaintBackground(e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaintBackground(e)
If Parent IsNot Nothing Then
Dim index As Integer = Parent.Controls.GetChildIndex(Me)
For i As Integer = Parent.Controls.Count - 1 To index + 1 Step -1
Dim c As Control = Parent.Controls(i)
If c.Bounds.IntersectsWith(Bounds) AndAlso c.Visible = True Then
Dim bmp As New Bitmap(c.Width, c.Height, e.Graphics)
c.DrawToBitmap(bmp, c.ClientRectangle)
e.Graphics.TranslateTransform(c.Left - Left, c.Top - Top)
e.Graphics.DrawImageUnscaled(bmp, Point.Empty)
e.Graphics.TranslateTransform(Left - c.Left, Top - c.Top)
bmp.Dispose()
End If
Next
End If
End Sub
End Class
Mark the click handler as Async, then use Await Task.Delay():
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TransClass2.Image = My.Resources._21
Await Task.Delay(100)
TransClass2.Image = My.Resources._11
Await Task.Delay(100)
End Sub
I think 100 might be too fast!
When I click the button to maximize my form, it covers the entire screen including the taskbar. I managed to find a solution and it works, I used my code in form load event but I cannot return the form into normal state.
Private Sub frmDashboard_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Top = Screen.PrimaryScreen.WorkingArea.Top
Me.Left = Screen.PrimaryScreen.WorkingArea.Left
Me.Height = Screen.PrimaryScreen.WorkingArea.Height
Me.Width = Screen.PrimaryScreen.WorkingArea.Width
End Sub
Private Sub btnMaximizeMin_Click(sender As Object, e As EventArgs) Handles btnMaxMin.Click
If Me.WindowState = FormWindowState.Normal Then
'maximize but dont cover taskbar
Me.Top = Screen.PrimaryScreen.WorkingArea.Top
Me.Left = Screen.PrimaryScreen.WorkingArea.Left
Me.Height = Screen.PrimaryScreen.WorkingArea.Height
Me.Width = Screen.PrimaryScreen.WorkingArea.Width
Else
Me.WindowState = FormWindowState.Normal
End If
End Sub
The problem is that you aren't maximising the form. You specifically DON'T want to maximise the form because that covers the Windows Task Bar. You can't set the WindowState "back" to Normal because it's already in that state, because it never leaves that state. It's up to you to remember the state for yourself and also the previous bounds, e.g.
Private isMaximised As Boolean = False
Private normalBounds As Rectangle
Private Sub MaximiseOrRestore()
isMaximised = Not isMaximised
If isMaximised Then
normalBounds = Bounds
Bounds = Screen.PrimaryScreen.WorkingArea
Else
Bounds = normalBounds
End If
End Sub
I have a panel with 50 button and 1 label in my form
Private Sub flp_table_paint(sender As Object, e As PaintEventArgs) Handles flp_table.Paint
Dim i As Integer
For i = 1 To 50
Dim btn_tableNo As New Button
btn_tableNo.Width = 40
btn_tableNo.Height = 40
btn_tableNo.Text = i
AddHandler btn_tableNo.Click, AddressOf TableButtonClicked
Dim timer As New Timer
timer.Tag = i
Me.flp_table.Controls.Add(btn_tableNo)
Next
End Sub
What I try to do is, for every single button that i clicked they will start their own timer and show on the label.
Example:
11:00:00PM - Clicked on Button1 , lb_timer will show 1,2,3,4...
11:00:30PM - Clicked on Button2 , lb_timer will show 1,2,3,4...
11:00:45PM - Clicked on Button1 again, lb_timer will show 45,46,47,48...
11:00:50PM - Clicked on Button2 again, lb_timer will show 20,21,22,23...
Here is what i try so far, but fail...
Private Sub TableButtonClicked(ByVal sender As Object, ByVal e As EventArgs)
Dim currButton As Button = sender
selectedTable = currButton
For Each timer As Timer In Me.Controls
If timer.Tag = selectedTable.Text Then
timer.Start()
lb_timer.Text = timer.ToString
End If
Next
End Sub
I have no idea how to make it work, please help me...
Here asynchronous approach without Timer and one eventhandler for all buttons
' In constructor
AddHandler button1.Click, AddressOf Button_Click
AddHandler button2.Click, AddressOf Button_Click
AddHandler button3.Click, AddressOf Button_Click
' ... rest of buttons
' variable will keep name of the button which number is showing
Private _selectedButtonName As String
Private Async Sub Button_Click(sender As object, e As EventArgs)
Dim button As Button = DirectCast(sender, Button)
_selectedButtonName = button.Name
Dim isRunning As Boolean = (button.Tag IsNot Nothing)
If isRunning = True Then return
await StartCounterAsync(button)
End Sub
Private Async Function StartCounterAsync(Button button) As Task
button.Tag = new object()
Dim number As Integer = 0
While True
await Task.Delay(1000)
number += 1
If _selectedButtonName.Equals(button.Name)
lb_timer.Text = $"{button.Name}: {number}"
End If
End While
End Function
You can add CancellationToken in case you want reset counters.
Sadly the timer doesn't show elapsed time. An easier control to use for your purpose would be the StopWatch control. Its quite similar and will show a running time.
Dim StopWatchTest As New Stopwatch
StopWatchTest.Start()
Dim EllapsedTime As String = StopWatchTest.ElapsedMilliseconds / 1000
However since you want to continually update the label with the ellapsed time, you would need a timer to update the label at every tick.
Dim UpdateLabelTimer As New Timer()
UpdateLabelTimer.Interval = 1000 'How often to update your label (in milliseconds)
AddHandler UpdateLabelTimer.Tick, AddressOf Tick
UpdateLabelTimer.Start()
----------
Private Sub Tick()
lblLabel.text = StopWatchTest.ElapsedMilliseconds
End Sub
In VB.NET I'm looking to build a "Time" grid very similar to the Time Restriction grid of the Parental Section of Windows: http://www.thinkbroadband.com/images/guides/time-restrictions.png
It needs to toggle between 2 colors on cell-click
I've played around with One-Cell = One-Label and it kinda works but, like the Windows Time Restriction grid, I'd like to have the labels change colors if I move over the label whilst having the left button pressed (and not only on label click).
Here is what I currently have:
Private Sub ColorToggle(sender As Object, e As MouseEventArgs) Handles Label1.Click, Label2.Click, Label3.Click 'etc..
If e.Button = Windows.Forms.MouseButtons.Left Then
sender.backcolor = If(sender.backcolor = SystemColors.Control, Color.LightGreen, SystemColors.Control)
End If
End Sub
Since the sender stays the same when I hover the labels (sender = label I've originally clicked on), this code doesn't work for my purpose.
I'm looking for suggestions!
Thanks :)
When you click on a control and you hold the mouse button down, this control captures the following mouse events, so that you won't get events from the other lables when moving the mouse over them.
The trick is to set label.Capture = False.
Lets define colors:
Private ReadOnly selectedColor As Color = Color.Blue
Private ReadOnly unselectedColor As Color = Color.White
And Booleans storing the current state of our operations
Private isSelecting As Boolean = False
Private isUnselecting As Boolean = False
(All four as fields of the form class)
Now lets write these three event handlers:
Private Sub Label_MouseDown(sender As Object, e As EventArgs)
'This event starts selecting/unselecting
Dim label = DirectCast(sender, Label)
label.Capture = False '<=== THIS IS IMPORTANT!
If label.BackColor = selectedColor Then
isUnselecting = True
Else
isSelecting = True
End If
SelectLabel(label)
End Sub
Private Sub Label_MouseUp(sender As Object, e As EventArgs)
'This event stops selecting/unselecting
isSelecting = False
isUnselecting = False
End Sub
Private Sub Label_MouseEnter(sender As Object, e As EventArgs)
SelectLabel(DirectCast(sender, Label))
End Sub
And we need this procedure that selects or unselects the labels:
Private Sub SelectLabel(label As Label)
If isSelecting Then
label.BackColor = selectedColor
ElseIf isUnselecting Then
label.BackColor = unselectedColor
End If
End Sub
That's it!
Footnote: I have created the lables like this:
Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Const w As Integer = 50, h As Integer = 50
For x = 1 To 10
For y = 1 To 10
Dim lbl As New Label() With {
.Location = New Point(x * w, y * h),
.Size = New Size(w, h),
.BorderStyle = BorderStyle.FixedSingle,
.BackColor = unselectedColor
}
AddHandler lbl.MouseDown, AddressOf Label_MouseDown
AddHandler lbl.MouseUp, AddressOf Label_MouseUp
AddHandler lbl.MouseEnter, AddressOf Label_MouseEnter
Controls.Add(lbl)
Next
Next
End Sub
I hope this isn't homework...
Private Sub Button8_Click(sender As Object, e As EventArgs) Handles Button8.Click
Dim i As Integer
With dgv
.ColumnCount = 0
.DataSource = Nothing
.Columns.Add("Day", "Day")
For i = 0 To 23
.Columns.Add(i, i)
.Columns(.Columns.Count - 1).Width = 30
Next
For i = 1 To 7
.Rows.Add({i})
Next
End With
End Sub
Private Sub dgv_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellClick
dgv.CurrentCell.Style.BackColor = Color.Blue
End Sub
Here is a drag version:
Private Sub dgv_MouseUp(sender As Object, e As MouseEventArgs) Handles dgv.MouseUp
For Each cell As DataGridViewCell In dgv.SelectedCells
If cell.Style.BackColor = Color.Blue Then
cell.Style.BackColor = Color.White
Else
cell.Style.BackColor = Color.Blue
End If
Next
dgv.ClearSelection()
End Sub