Here is a simple VB.Net Forms program. The form contains a TabControl, which has 2 pages. On TabPage1 there is a Button, and a PictureBox, which contains a small 'Waiting' image. Initially the PictureBox is hidden. The TabControl starts by showing TabPage1.
What I would like to happen is that when the Button is pressed, the PictureBox is made visible, then, my SlowRoutine() is called, then the PictureBox is hidden, then I swap to TabPage2.
What actually happens is that when the Button is pressed, we wait 2 seconds, then we swap to TabPage2. I never see the PictureBox.
If I uncomment the MessageBox line, just to add a halt to the program-flow, then press the Button, the following occurs: 2 seconds passes, and then the PictureBox and the MessageBox appear. Clicking on the MessageBox closes it, and we go to TabPage2. Flipping back to TabPage1 shows that the PictureBox has been hidden.
The order of events is not happening in a logical way. How can I fix this, please?
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
PictureBox1.Visible = False
PictureBox1.Hide()
PictureBox1.SendToBack()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PictureBox1.Visible = True
PictureBox1.Show()
PictureBox1.BringToFront()
SlowRoutine()
' MessageBox.Show("I am waiting")
PictureBox1.Visible = False
PictureBox1.Hide()
PictureBox1.SendToBack()
TabControl1.SelectTab("TabPage2")
End Sub
Private Sub SlowRoutine()
' My SLOW ROUTINE: wait 2 seconds
Threading.Thread.Sleep(2000)
End Sub
End Class
Thanks to all. Here is the working code based on those comments, in case anyone else needs to do a similar task:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
PictureBox1.Visible = False
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PictureBox1.Visible = True
Await SlowRoutineAsync()
PictureBox1.Visible = False
TabControl1.SelectTab("TabPage2")
End Sub
Private Async Function SlowRoutineAsync() As Task
' My SLOW ROUTINE: wait 2 seconds
Await Task.Delay(2000)
End Function
End Class
Related
I would like to reset my form using vb.net i would try to below code but form is close can't open new form.
Private Sub resert_button_Click(sender As Object, e As EventArgs) Handles resert_button.Click
Dim client = New client_entry
client.Show()
Me.Close()
End Sub
Try Me.Hide and swap the order
Me.Hide()
client.Show()
There could be multiple ways to handle this one but myapproach would be like calling form_shown even where I will load required data and during reset i will call this event.
Private Sub Me_Shown(sender As Object, e As EventArgs) Handles Me.Shown
LoadData() 'This function/sub will load data for this form
End Sub
Private Sub resert_button_Click(sender As Object, e As EventArgs) Handles resert_button.Click
Me_Shown(sender,e)
End Sub
Use Me.Dispose() instead of Me.Close() it will dispose the form then when you call it again Yourform.Show(), It will Generate a new one.
One way of doing this would be to add a property to Form2. I'm assuming that you have two forms Lets call them Form1 and Form2. Somewhere in the code for Form1, you declare an instance of Form2 ..
Dim frm2 As New Form2
and at some point you want to show Form2 as a modal window ..
frm2.ShowDialog()
For the moment lets look at the Form2 code
I'm assuming here that you have a button to reset the form and to close the form, you just click on the form close button in the top right hand corner and maybe you have a button to close the form as well. Consider the following code for Form2
Public Class Form2
Friend Property resetOnClose As Boolean = False
Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click
resetOnClose = True
Me.Hide()
End Sub
Private Sub btnclose_Click(sender As Object, e As EventArgs) Handles btnclose.Click
resetOnClose = False
Me.Hide()
End Sub
End Class
There is a property called resetOnclose which is a Boolean type. If you click on the reset button, this property is set to True If you click on the close button, the resetOnClose property is set to false.
In all these bits of code, frm2 is Hidden - not closed. This means that the form and its resetOnclose property is still available to Form1. OK now to look at the Form1 code ..
Public Class Form1
Dim frm2 As New Form2
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Shown
frm2.ShowDialog()
Do
If frm2.resetOnClose Then
frm2.Close()
frm2 = New Form2
frm2.ShowDialog()
Else
frm2.Close()
End If
Loop Until frm2.resetOnClose = False
End Sub
End Class
For this example, frm2 is opened as soon as Form1 is shown, but you can put the relevant code anywhere you need
In the Form1.Shown code you will see a loop. The loop will continue looping as long as resetOnClose is True. When frm1 is shown modally, execution in this Form1 code waits until frm2 is hidden or closed. Next, the Form1 code checks to see if the resetOnClose property is true or false. If it's false, frm2 is closed and the loop terminates. If the property is true, frm2 is closed and reassigned a new instance of Form2 in its default state.
Voila!
I have come to learn that a module level variable's value will not be altered until a sub routine that changed it exits.
StopBackgroundWorker1 = True
Thread.Sleep(1500)
If BackgroundWorker1Complete = False Then
Exit Sub
End If
in this example, I added a long delay for testing. I'm simply trying to stop and start a background worker safely with vb 2017 new background worker class.
The example above with "StopBackgroundWorker1 = True", I was hoping to stop the worker at a safe place and then continue within that sub with other code.
But what is happening is that the "StopBackgroundWorker1 = True" is not being set "True" until the sub exits.
There must be another way to do what I am trying to do, please help
Ok here is a complete example,
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private flag As Boolean = False
Dim Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
flag = True
'Do
' do loop never see's a true flag
'Loop Until Completed
Thread.Sleep(500)
If Completed = True Then
Label1.BackColor = Color.Red
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(25)
Loop Until flag
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles
Button2.Click
Label1.Text = flag.ToString
End Sub
End Class
Now the concept is if you hit button1 and wait for background worker to complete, it should turn lable1 red. but it doesn't. The do loop looking for a true flag will spin forever locking the form up.
I have determined with this example that the flag is not set to true until you exit the sub. Hit Button1 again and lable1 turns red.
Thanks in advance for any answers.
This doesn't answer your question per se but I want to post a large code snippet so I'll post it as an answer. It demonstrates that what you think is the problem is not the problem, i.e. that a field's value changes as soon as you change it, even if that change is made from a BackgroundWorker.DoWork event handler.
Create a new Windows Forms application project, add a Button, a Label and a BackgroundWorker to your form and then paste in this code over the default code of the form:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private flag As Boolean
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Label1.Text = flag.ToString()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Thread.Sleep(5000)
flag = True
BackgroundWorker1.ReportProgress(0)
Thread.Sleep(5000)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Label1.BackColor = Color.Green
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Label1.BackColor = Color.Red
End Sub
End Class
Run the project and, when the form appears, start clicking the Button at a pace of a few times per second. You'll see that the value of the flag field, as displayed on the Label, changes from False to True as soon as the code to set it is executed in the DoWork event handler. The Label will turn green when that happens, so it's easy to spot. You'll know that it didn't wait until the DoWork event handler completes because the Label will turn red at that point.
EDIT: Now that you have provided all the relevant information, the issue is obvious. As I have already said, the moment you set a variable, that is the value of that variable. There's no waiting because there cannot be any waiting because there's nowhere to store a temporary value for the variable.
The reason that it looks otherwise is that your test code is faulty. If you use the debugger then you will see how. When you use a BackgroundWorker, the DoWork event handler is executed on a secondary thread but the RunWorkerCompleted event handler is executed on the UI thread. That means that your DoWork event handler can execute at the same time as your Click event handler for Button1 because they are on different threads, but the RunWorkerCompleted event handler cannot run at the same time, so it has to wait until the Click event handler completes before it can be executed. That means that the code to set the Completed field doesn't get executed until the Click event handler completes. It's not that the field value doesn't change when it's set but rather that it doesn't actually get set. If you place breakpoints on the two lines that access that Completed field then you'll see that.
The mistake you're making is trying to do something in that Click event handler after the DoWork event handler completes. That's wrong. That's exactly what the RunWorkerCompleted event handler is for. That's where you do UI work after the background work completes.
Also, you can get rid of that flag variable. Cancellation functionality is built into the BackgroundWorker class. Look at the CancelAsync method and the CancellationPending property.
Many thanks to "jmcilhinney" for his insights! I have figured out the code I was looking for!
This code allows me to stop and start a background thread safely by allowing the background thread to finish completely before restarting.
During the time that the background thread is stopped, user actions can perform operations without the worry of cross-threading or with thread.abort garbled code conditions.
Finally, stress-free threading!
I wish I could find the doc on MSDN that I read that stated the await async method was far superior to task.run but that's another argument.
This may not be the best code in the world but it works!
And in light of trying to rewrite all code in my project with async and await I'll stick with this!
Public Class Form1
Private flag As Boolean = False
Dim Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
flag = True
Await Task.Run(Sub()
Do
Loop Until Completed
End Sub)
If Completed = True Then
Label1.BackColor = Color.Red
End If
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(250)
Loop Until flag
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
End Class
For all of the academics out there this code is more appropriate.
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles
Button1.Click
BackgroundWorker1.CancelAsync()
Await Task.Run(Sub()
Do
Loop Until Completed
End Sub)
If Completed = True Then
Label1.BackColor = Color.Red
End If
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(250)
Loop Until BackgroundWorker1.CancellationPending
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
End Class
So here's what I want. When i click on a button it will show 1 PictureBox and 1 RichtextBox (Which is btw Visible=False so that it will not show unless clicked when the program is opened) as you can see here on my screenshot:
enter image description here
But when I click other button it doesn't change it stays with the first button i clicked when i opened the program. I really don't know the code since I'm new with VB.
Here's the code I used:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PictureBox1.Visible = True
RichTextBox1.Visible = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
PictureBox2.Visible = True
RichTextBox2.Visible = True
End Sub
End Class
Thanks!
Since (I guess) both of the images are right on top of each other, you need to set the first image to invisible again. If not controlled which image is on top depends on the order the images where added.
You could do something like this :
Private Sub hideElements()
For i as Integer = 1 to 6
Me.Controls("PictureBox" & i).Visible = False
Me.Controls("RichTextBox" & i).Visible = False
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Call hideElements()
PictureBox1.Visible = True
RichTextBox1.Visible = True
End Sub
This loop sets all PictureBox1 - Picturebox6 and RichtextBox1 - RichTextBox6 to invisible, now you can set the one you want to show to visible.
So just call hideElements at the beginning of all of your button handler.
If u want to change the amount of images/richtextboxes, you only need to adjust the 6 in the loop.
Hope I could help.
I was able to scroll the text up, put a stop on it when it reached a certain location on Y.
However, when I was trying to load the credit form again. It will not be back on the start.
Here's the code I'm working on.
Public Class creditsform
Private Sub creditsform_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
Label1.Location = New Point(Label1.Location.X, Label1.Location.Y = 304) ' put back the label where it is.
End Sub
Private Sub creditsform_Load(sender As Object, e As EventArgs) Handles Me.Load
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Label1.Location = New Point(Label1.Location.X, Label1.Location.Y - 5) ' scroll up the label up
If (Label1.Location.Y = -1506) Then ' closes the form if he reach the end of credit roll
Timer1.Enabled = False ' disable the timer
Me.Close() ' then close the form
End If
End Sub
End Class
EDIT: HERE IS THE FIRST LOAD AND THE SECOND LOAD OF THE FORM.
Also after the 2nd load, it doesn't execute the if else statement to close automatically the form.
1ST LOAD & 2ND LOAD
Here's where creditsform called.
Public Class EndGame
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim Red, Green, Blue As Integer
Dim RandomNumGenerator As New Random
Red = RandomNumGenerator.Next(0, 255)
Green = RandomNumGenerator.Next(0, 255)
Blue = RandomNumGenerator.Next(0, 255)
Me.Label4.ForeColor = Color.FromArgb(Red, Green, Blue)
Me.Label5.ForeColor = Color.FromArgb(Red, Green, Blue)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click '' this button closes the form and shows the creditsform
Me.Close()
creditsform.ShowDialog()
End Sub
End class
If I understood the problem correctly since it's not very clear what you are trying to do.
This is normal your using a class but not creating new instance of it so your variables don't reset. you should try this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click '' this button closes the form and shows the creditsform
Me.Close()
Dim MyCredits As New creditsform
MyCredits.ShowDialog()
End Sub
I want to create a little window which gets visible when I press a button and is attached to the main Form (like shwon below). I want to use this window to show preview of Images (I gonna have a Listbox and depending on which entry is selected, a picture is shown) How can I do this? And how can I be sure that the window will always be attached to the main Form (not depending on resolution). I tried to do it with a second form, but I can't fix it in the correct position.
regards
Assuming your preview form class is frmPreview and you open it this way:
Private mPreviewForm As frmPreview
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
mPreviewForm = New frmPreview
mPreviewForm.Show()
AttachPreviewForm()
End Sub
Then you must reposition it every time the main form is changing size or location:
Private Sub AttachPreviewForm()
If mPreviewForm IsNot Nothing Then
mPreviewForm.AttachForm(Me)
End If
End Sub
Private Sub Form1_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
AttachPreviewForm()
End Sub
Private Sub Form1_LocationChanged(sender As Object, e As EventArgs) Handles Me.LocationChanged
AttachPreviewForm()
End Sub
And in the frmPreview:
Public Sub AttachForm(parent As Form)
Location = New Point(parent.Left + parent.Width, parent.Top)
Size = New Size(200, parent.Height)
End Sub