Basic delay command for Visual Basic - vb.net

I need a wait command in Visual Basic that suits me.
I know:
Declare Sub Sleep Lib "kernel32.dll" (ByVal milliseconds As Long)
sleep 5000
But that makes the program unresponsive.
System.Threading.Thread.Sleep(5000) 'The window doesn't load until the timing is over (useless)
My code:
Imports Microsoft.Win32 'To check if is 64Bit or 32Bit
Public Class Loading
Private Sub Loading_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If Registry.LocalMachine.OpenSubKey("Hardware\Description\System\CentralProcessor\0").GetValue("Identifier").ToString.Contains("x86") Then
My.Settings.Is64 = False
Else
My.Settings.Is64 = True
End If
'I need command here
If My.Settings.Is64 = True Then
Form64.Show()
Me.Close()
Else
MsgBox("No version developed for 32-bit computers.")
End
End If
End Sub
End Class
Errors:
#Idle_Mind
1. function 'OnInitialize' cannot be declared 'Overrides' because it does not override a function in a base class.
2. 'MinimumSplashScreenDisplayTime' is not a member of 'App.Loading.MyApplication'.
3. 'OnInitialize' is not a member of 'Object'.

From the comments:
Go into Project Properties and leave your main form as the Startup form. Set your splash screen form as the splash screen entry down at the bottom. Now click the "View Application Events" button to the right and override OnIntialize so you can set the MinimumSplashScreenDisplayTime() like this:
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Protected Overrides Function OnInitialize(ByVal commandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean
' Set the display time to 5000 milliseconds (5 seconds).
Me.MinimumSplashScreenDisplayTime = 5000
Return MyBase.OnInitialize(commandLineArgs)
End Function
End Class
End Namespace

If you want to execute the rest of your code after 5 seconds, why not create a separate thread/task, which will wait for 5 seconds and then trigger the rest of your code to run via a callback to the main thread? This approach will not hang your UI.
EDIT: If you want a splash screen, drop a Timer control, set the interval to 5 seconds and run the rest of your code inside a Tick event handler.
Assuming you have already set up the Timer, move your loading code into Timer1_Tick handler:
Public Class Loading
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
'part 1
If Registry.LocalMachine.OpenSubKey("Hardware\Description\System\CentralProcessor\0").GetValue("Identifier").ToString.Contains("x86") Then
My.Settings.Is64 = False
Else
My.Settings.Is64 = True
End If
'part 2
If My.Settings.Is64 = True Then
Form64.Show()
Me.Close()
Else
MsgBox("No version developed for 32-bit computers.")
End
End If
End Sub
End Class
Or leave part 1 in Load, and move part 2 into Tick. I would prefer this option for semantics.
Also don't forget to set Timer.Enabled = True.

If you want a place to CANCEL the application, you use the Application.Startup event and set e.Cancel = True from within there. When this is done the main form will not even appear; the application will simply exit. That could look something like:
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_Startup(sender As Object, e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
If someCondition Then
MessageBox.Show("oops")
e.Cancel = True ' <-- main form will NOT show, app will simply exit
End If
End Sub
End Class
End Namespace

just do this:
For i As Integer = 1 To 500
System.Threading.Thread.Sleep(10)
System.Windows.Forms.Application.DoEvents()
Next
Edit: Be careful with DoEvents; it can cause problems if the user clicks on something or an event is processed when it shouldn't. See http://www.codinghorror.com/blog/2004/12/is-doevents-evil.html

Since sleeping and busy waiting are generally frowned upon, you could do something with an AutoResetEvent:
Private ReadOnly _resetEvent As AutoResetEvent = New AutoResetEvent(False)
Sub Pause(ByVal milliseconds As Long)
Dim waitInterval As TimeSpan = TimeSpan.FromMilliseconds(milliseconds)
While Not _resetEvent.WaitOne(waitInterval)
' Waiting!
End While
End Sub
This is not recommended, but here's an example using the System.Diagnostics.Stopwatch class:
Sub Pause(ByVal milliseconds As Long)
If milliseconds <= 0 Then Return
Dim sw As New Stopwatch()
sw.Start()
Dim i As Long = 0
Do
If i Mod 50000 = 0 Then ' Check the timer every 50,000th iteration
sw.Stop()
If sw.ElapsedMilliseconds >= milliseconds Then
Exit Do
Else
sw.Start()
End If
End If
i += 1
Loop
End Sub
And then where you need to pause, just call this Pause() method:
Pause(5000) ' Pause for 5 seconds

Related

(VB.NET) Quick way for a Simple Splashscreen for WinForms [duplicate]

This question already has answers here:
How to create a Splash screen for VB.net program
(3 answers)
Closed 6 days ago.
My program took ~5-10 seconds to load and sometimes people using it would end up trying to open it again, which caused problems. I found a quick and easy way to make a "splashscreen" (in a sense) that pops up for a set amount of time immediately on execution. I found that the first order of events in a WinForm EXE loading was Handle Created. The answer is not a true splashscreen, but for a couple lines of code that can be easily added to a project, I think some people will like it.
The below code will show a MessageBox immediately on running the EXE and closes after 10 seconds.
Imports System.Threading
Private Sub Control1_HandleCreated(ByVal sender As Object, ByVal e As EventArgs) Handles Me.HandleCreated
Dim SplashScreen As New Thread(
Sub()
CreateObject("WScript.Shell").Popup("Program Initializing, Please Wait...",10, "Setup Tool")
End Sub)
SplashScreen.Start()
End Sub
I use Threading so that the MessageBox will not freeze the code and the program will open with or without the OK button being pressed. Doing a regular MessageBox.Show() will prevent any more code from running until the user clicks OK I have found.
The best way I have found to implement a splash screen which keeps the user informed via messages and/or a progress bar or animated wheel is the following.
Have a startup form eg Form1, and have it carry out all the tedious startup procedures which might cause any animated or progress bar graphic to get stalled in the event queue. Add a "BackgroundWorker" object to Form1 from the Toolbox and in my case I just named it BackgroundWorker1.
Before starting these routines, usually in the Form1_Load event, make a call to the BackgroundWorker.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
CallBackgroundWork()
StartRoutines() 'this is the heavy lifting routines to get the app working. Set the LoadingStatusflag (declared as a Global Variable"
to various values to tell the splashscreen to display different messages
Loadingstatus = 10 'triggers splashform to exit
CancelBackgroundWork()
End Sub
These are the other subs to support this
Sub CallBackgroundWork()
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.WorkerReportsProgress = True
' call this method to start your asynchronous Task.
BackgroundWorker1.RunWorkerAsync()
End Sub
Sub CancelBackgroundWork()
' to cancel the task, just call the BackgroundWorker1.CancelAsync method.
BackgroundWorker1.CancelAsync()
End Sub
Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'' The asynchronous task we want to perform goes here
FormSplash.Show()
End Sub
My splashscreen has some label controls and pictureboxes and the FormSplash_Load event runs a stopwatch loop of 40ms and loads a series of images (24 in total) of a spinning wheel. This keeps running while the splashscreen is active. By setting the global variable Loadingstatus to various values within different part of the loading sequence in Form1 it can trigger the loop routine to display different messages example shown. An easy way to communicate between threads as you can't directly access objects between threads The wheel keeps spinning no matter how intensive the load routine in Form1 as it is running in another thread. I used a stopwatch loop as starting a timer doesn't work for me - maybe an event queue issue in splash form.
Private Sub FormSplash_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Show()
Me.Opacity = 1 'show this form
'now start a loop that gets ended by other thread through variable Loadingstatus flag
Dim ggtimer As New Stopwatch, lastvalue As Integer, FProgPosition as integer
ggtimer.Start()
lastvalue = ggtimer.ElapsedMilliseconds
nextimage:
FProgPosition += 1
If FProgPosition = 24 Then FProgPosition = 1 'has 24 frames in the animated image
Do 'loop for 40 ms
If ggtimer.ElapsedMilliseconds - lastvalue > 40 Then
lastvalue = ggtimer.ElapsedMilliseconds
Exit Do
End If
Loop
PictureBoxProgress1.Image = FProgIMG(FProgPosition)
PictureBoxProgress1.Refresh()
If Loadingstatus = 10 Then GoTo endsplash
If Loadingstatus = 1 Then
If CoreTempRunning = False Then
Me.LabelCoreTemp.Text = "CoreTemp is NOT Running"
Me.LabelCoreTemp.ForeColor = Color.White
'insert cross picturebox
PictureBoxCoreTemp.Image = My.Resources.ResourceManager.GetObject("Cross24x24")
loaderrorflag2 = True
Else
Me.LabelCoreTemp.Text = "CoreTemp is Running"
Me.LabelCoreTemp.ForeColor = Color.White
'insert tick picturebox
PictureBoxCoreTemp.Image = My.Resources.ResourceManager.GetObject("Tick24x24")
loaderrorflag2 = False
End If
Me.PictureBoxCoreTemp.Visible = True
Me.PictureBoxCoreTemp.Refresh()
Me.LabelCoreTemp.Left = Me.Width * 2 / 3 - Me.LabelCoreTemp.Width
Me.LabelCoreTemp.Refresh()
GoTo nextimage
endsplash:
ggtimer.Stop()
Me.Opacity = 0.01
Me.Hide()
End Sub

Threading and modal form windows

VB.Net code.
I have a program where I am running a process in a thread and in that thread I need to have a pop up message information box that is non-modal. The main process is in a thread because it has to run in parallel and the user can initiate this process many times at the same time.
I read that the modal message box needs to be a custom form that is also ran from a thread to not block the program from continuing on. such as .Show() stops the program and waits for the user input. And you have to use .ShowDialog() via a thread
My code:
Calling initial thread:
Public Event Report As EventHandler
'In a method
Task.Run(Function() BackgroundThread())
Private Function BackgroundThread() As Task()
RaiseEvent Report(Me, New System.EventArgs)
End Function
In the Report method I have a snippet of code that then calls the form window to pop up the modal window:
Private mDiaplayMessageBox As NonModalPopUp
Private Sub DisplayMessageBox()
mDiaplayMessageBox = New NonModalPopUp()
Task.Run(Sub() mDiaplayMessageBox.ShowDialog())
End Sub
The issue I am having is that when I am finished with the report method I want to close this popup message. But when there is more than one of these pop up windows open at a time, only the last window opened will close and the program loses the handle I think to the other pop up windows and they will not close.
To close the windows I have in the modal form this code
Public Sub CloseMe()
'This will grab the thread that this window is running on, solves Cross-Threading issue.
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf CloseMe))
Exit Sub
End If
Me.BackColor = Color.Red
Me.Close()
End Sub
This first time this code is called its will hit the Me.Invoke and then close the window. However, on any subsequent calls when it gets to Me.InvokeRequired this will then be set to false, not called the Me.Invoke and go to the Me.Close() but it will not close the window.
I tried to do something where I grab the Handle intptr value but when ever I vent just look at that value the program immediately throws a cross-threading exception.
All I want to do is close the other windows which does not seem like a hard task but I do not know what I am missing.
One of approaches you can follow to achieve your goal might be as code below shows:
You can create a custom event which you can use as a “call” to listen to for the closure of your form.
Public Class Form1
Dim frm2 As Form2
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
frm2 = New Form2
Task.Run(Sub()
AddHandler CloseFrm2, Sub()
Dim CloseMe As Action = Sub()
frm2.Close()
frm2.Dispose()
End Sub
If frm2.InvokeRequired Then
frm2.Invoke(Sub() CloseMe())
Else
CloseMe()
End If
End Sub
frm2.ShowDialog()
End Sub)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
RaiseEventCloseFrm2()
End Sub
End Class
Module EventHelper
Public Event CloseFrm2()
Sub RaiseEventCloseFrm2()
RaiseEvent CloseFrm2()
End Sub
End Module

Wait for Single Instance occurrences to complete before proceeding to next line VB.NET

Currently, I'm using the 'make app single instance'(MyApplication_StartupNextInstance event) in VB (.net framework win forms) to pass command line arguments from multiple instances to the main form. I'm adding this to a list of string and then passing this list to the next function/ sub. The list captures all the arguments if I add a message box just before calling the next function but then when there's no msgbox, not all the arguments are captured.
I've tired using timers/delays which is a hit and a miss. Tried using timed msgbox that disapper after couple secs, which is the same.
How can I make it wait till all the instances have run and then proceed to the next line of code?
'ApplicationEvents.vb
Private Sub MyApplication_StartupNextInstance(sender As Object, e As ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim f = Application.MainForm
If f.GetType Is GetType(my_app_name) Then
CType(f, my_app_name).NewArgumentsReceived(e.CommandLine(0))
End If
End Sub
'my app has the below codes
Public Sub NewArgumentsReceived(args As String)
mylist.Add(args)
End Sub
Private Sub SomeForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
mylist.Add(arg) 'arg is for main form 'args' is for instances
'this is where I want to wait until all the other instances have completed
Anotherfunction(mylist)
End Sub
As I mentioned in my comments, the StartupNextInstance event can be raised any time so you should design your app to react to it at any time. The initial instance has no idea how many subsequent instances there will be or when they will start so it should simply react to them one at a time, whenever they occur. Here's an example of a single instance application where the main form is an MDI parent and the commandline arguments are text file paths that each get opened in a child window.
Child form:
Imports System.IO
Public Class ChildForm
Public Property FilePath As String
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.Text = File.ReadAllText(FilePath)
End Sub
End Class
Parent form:
Public Class ParentForm
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim args = Environment.GetCommandLineArgs()
If args.Length > 1 Then
LoadChildForm(args(1))
End If
End Sub
Public Sub LoadChildForm(filePath As String)
Dim child As New ChildForm With {.MdiParent = Me,
.FilePath = filePath}
child.Show()
End Sub
End Class
Application events:
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
' The following events are available for MyApplication:
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_StartupNextInstance(sender As Object, e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim args = e.CommandLine
If args.Count > 0 Then
DirectCast(MainForm, ParentForm).LoadChildForm(args(0))
End If
End Sub
End Class
End Namespace
In this case, the initial instance doesn't wait for anything. It just goes ahead and does what it does with its own commandline argument. It has no idea if there will be any subsequent instances or, if there are, how many there will be and when they will start, so it would have no idea what it was waiting for. Any time another instance is started, the initial instance reacts then, using the commandline argument provided.

Timer which can be called from a class and form both

I have a simple WinForm application. The main entry point of the application is mainForm. I am using a Timer on the form and the timer interval is being set to 2000ms. The Tick event of the Timer is as below,
Public myValue as Integer = 100
Private Sub myTimer_Tick(sender As Object, e As EventArgs) Handles myTimer.Tick
If myValue = 0 Then
myTimer.Enabled = False
Else
myValue = myValue -1
End If
End Sub
The timer is being called at the start of the application when mainForm is loaded. Now myValue is a global variable and here for the purpose of simplicity I have used this otherwise it is replaced by some process count mechanism which is not required to be explained here.
I am able to use this approach as long as I am using Windows.Forms.Timer placed on some specific Form. I have two more scenarios in which this approach fails.
1 - I have to use the same functionality on some other form and for this currently I am using a separate Timer on another Form and it has its own Tick event.
2 - I have to use the same functionality from another module/class and I am unable to achieve this because for this to work I require a Form.
Now for a start I have looked into Threading.Timer. The problem I am facing is that I don't know how to wait for Threading.Timer to finish as the control goes to next line after Threading.Timer is called. I am not sure whether this can be done with the help of WaitHandle or not. Also I have read that Threading.Timer creates a separate Thread for each of its Tick. This seems like an overkill in my simple scenario.
I just want to use the Timer functionality without the need of Form. Also I could create the similar functionality using a Do Loop with Thread.Sleep inside it but unless I am sure that my Timer functionality is not going to work in other situations I am going to stick to my Timer approach.
I see ... If thats the case, you should really create a second thread that runs a loop. That thread has some exiting parameters that indicates that operation is completed and the Thread itself is set to Isbackground = false.
However, you could also do this ...
Imports System.Timers
Public Class Main
Private Shared WithEvents m_oTimer As Timers.Timer = Nothing
Private Shared m_oWaitHandle_TimerHasCompleted As System.Threading.AutoResetEvent = Nothing
Public Shared Sub Main()
Try
'Application Entry point ...
'Create the global timer
m_oTimer = New Timers.Timer
With m_oTimer
.AutoReset = True
.Interval = 2000
.Start()
End With
'Create the WaitHandle
m_oWaitHandle_TimerHasCompleted = New System.Threading.AutoResetEvent(False)
'Show your form
Dim oFrm As New Form1
Application.Run(oFrm)
'Wait for the timer to also indicate that it has finished before exiting
m_oWaitHandle_TimerHasCompleted.WaitOne()
Catch ex As Exception
'Error Handling here ...
End Try
End Sub
Private Shared Sub m_oTimer_Elapsed(sender As Object, e As ElapsedEventArgs) Handles m_oTimer.Elapsed
'Timer will fire here ...
Try
If 1 = 2 Then
m_oWaitHandle_TimerHasCompleted.Set()
End If
Catch ex As Exception
'Error Handling ...
End Try
End Sub
End Class
Please note that 'm_oWaitHandle_TimerHasCompleted.Set()' will never run, you'll have to add a condition ... however, once run, the WaitOne will complete and the application will exit as required.
Hows zat?
Sounds to me like you want to create a single instance of a timer, that does not need to be instantiated via a form?
If so ... Create a new class called 'Main' and copy the following into it.
Imports System.Timers
Public Class Main
Private Shared WithEvents m_oTimer As Timers.Timer = Nothing
Public Shared Sub Main()
Try
'Application Entry point ...
'Create the global timer
m_oTimer = New Timers.Timer
With m_oTimer
.AutoReset = True
.Interval = 2000
.Start()
End With
'Show your form
Dim oFrm As New Form1
Application.Run(oFrm)
Catch ex As Exception
'Error Handling here ...
End Try
End Sub
Private Shared Sub m_oTimer_Elapsed(sender As Object, e As ElapsedEventArgs) Handles m_oTimer.Elapsed
'Timer will fire here ...
Try
Catch ex As Exception
'Error Handling ...
End Try
End Sub
End Class
Once done, right click on your project and select 'Properties'. In the Application tab you'll see a checkbox called 'Enable Application framework'. Uncheck this box. Now, in the dropdown called 'Startup Object' you should now see 'Sub Main' .... Select that.
When the application runs, Sub Main will now run instead of your form.
This will create the Timer that will fire outside of your form. Please note, as you're not syncing it, I believe it'll run inside a thread so be a little careful there :)

How to run a console application without showing the console window

I have written an application with the following sub main:
Public Sub Main()
Dim Value As String() = Environment.GetCommandLineArgs
Dim F As Form
Select Case Value.Last.ToLower
Case "-character"
F = New frmCharacterSheet
Case "-viewer"
F = New frmClient
Case Else
F = New frmCombat
End Select
Application.Run(F)
End Sub
This is because I want to be able to install my app with three different startup modes based on the command line. I did have a form that did this, but this has made error trapping very hard because the main form just reports the error.
This console seems to work well but I don't want the user to see the black console screen at startup.
I have searched for the answer but most solutions are 'switch back to a windows forms application'. I don't want to do this though for the above reason. (I cannot use application.run(f) in a winforms start situation because I get a threading error.
I need to know either how to hide the console window, or alternatively how to code a main menu that will launch one of the other three forms (but making them the startup form).
Any help would be appreciated....
Try:
Private Declare Auto Function ShowWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
Private Declare Auto Function GetConsoleWindow Lib "kernel32.dll" () As IntPtr
Private Const SW_HIDE As Integer = 0
Sub Main()
Dim hWndConsole As IntPtr
hWndConsole = GetConsoleWindow()
ShowWindow(hWndConsole, SW_HIDE)
'continue your code
End Sub
It has a side effect that the window will be shown and then immediately hidden
valter
"or alternatively how to code a main menu that will launch one of the other three forms (but making them the startup form)."
Start with a standard WinForms Project and use the Application.Startup() event. From there you can check your startup parameters and then dynamically change the Startup form by assigning your desired instance to "My.Application.MainForm". This will cause that form to load as if it was the one originally assigned to the "Startup Form" entry.
Click on Project --> Properties --> Application Tab --> "View Application Events" Button (bottom right; scroll down).
Change the Left dropdown from "(General)" to "(MyApplication Events)".
Change the Right dropdown from "Declarations" to "Startup".
Simplified code:
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
If True Then
My.Application.MainForm = New Form1 ' <-- pass your desired instance to MainForm
End If
End Sub
End Class
End Namespace
Just go to Project Properties> Application> Application Type> and select Windows Forms Application
At this point your ConsoleApplication turns totally invisible, with no User-Interface.
I just want to add another solution although Idle_Mind has already provided an excellent one. This demonstrates that you can use Application.Run(Form) inside a WinForms app.
Public Class Form1
Private Shared applicationThread As New Threading.Thread(AddressOf Main)
Private Shared Sub Main()
Dim myForm As Form
Dim config = 2 ' if 3, will run Form3
Select Case config
Case 2
myForm = New Form2
Case 3
myForm = New Form3
Case Else
MessageBox.Show("Bad config!")
Exit Sub
End Select
Application.Run(myForm)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
applicationThread.Start()
' immediately dispose Form1 so it's not even shown
Dispose()
End Sub
End Class