My main form is closing immediately - vb.net

I have the situation that when I run my app my main form is closing immediately. I don’t know why, here is the code I’m using:
Public Sub Main()
...
form.Visible = True
form.Show()
...
End Sub
This code is from an app from vb6.0 upgraded to vb2013, obviously there is a lot of code in this app for to make a new app, please the problem is when I execute the program and the main form initialize it closes immediately.
Any help would be appreciated.

If you set the Startup Object of your application to a Sub Main and inside this Main you want to launch your form then your Main should be something like this
Public Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New Form1)
End Sub
Actually your code terminates immediately because you show the form non modally (form.Show()) and this means that the call exits immediately. Of course this also means that you program terminates because you exit from the Sub Main.

Related

Form.Show vs Form.ShowDialog Cross Threading

I have the following code which closes a loginform and opens the mainform
Sub Loadform(ByVal formName As Form)
Dim Thisform As Form = DirectCast(formName, frmLogIn1)
Thisform.Hide()
Dim frm As New frmMain
frm.ShowDialog() <- Problem
Thisform.Close()
End Sub
The mainform has a DevExpress SplashScreenManager which loads and unloads the splash screen automatically.
When I call frm.Show I get a cross thread exception when the SplashScreen closes.
If I call it using frm.ShowDialog it works OK. This inst new code. The application is approx 12 months old and this code was added at the start, however I am now having problems.
The original code (taken from a backup) is:
Me.Hide()
Dim main As New frmMain
main.Show()
Me.Dispose()
But that seems to be closing the mainform now.
Any ideas?
I do not quite understand your question. Do you want to use the old code? The new? What problem are you trying to solve?
Well, an explanation for you calling frm.Show() and Thisform.Close() is simple.
When you call frm.Show() the new form is opened on a second thread and the current form continues to execute its code normally, logically reaching a line of code Thisform.Close () in which it closes. Because it is the main form, when it is closed it tries to terminate all other forms, so it tries to close the form you just opened, so the cross-thread exception occurs (remember that the new form is Running on another thread ;) )
But when you call frm.ShowDialog () the operation is different. It does not start a new thread, it just directs to the new form, so any line of code after ShowDialog only runs when the new forum is closed;

VB.NET: How to control the Form that is displayed when the program starts from Sub Main

VB.NET 2012
My Startup Object is set to (Sub Main). The app needs to collect a few different sets of data before the primary form is loaded
This article http://msdn.microsoft.com/en-us/library/ms235406(v=vs.110).aspx mentions
In Main, you can determine which form is to be loaded first when the program starts
But it never explains how to show the form
If I use ShowDialog the application terminates when mainView’s Visible property is set to False or when mainView is Hidden
Module Module1
Public mainView As New Form1
Public Sub Main()
' initialization code
mainView.ShowDialog() ' this works until I need to hide mainView, ShowDialog returns and the app terminates
End Sub
End Module
If I use Show the application immediately falls out of Sub Main and terminates
Module Module1
Public mainView As New Form1
Public Sub Main()
' initialization code
mainView.Show() ' this doesn't work at all, the app terminates as soon as Main is executed
End Sub
End Module
The primary form needs to exist the entire time the app is running.
I need sections of code to run before the primary form is displayed.
I need to be able to hide the primary at times and show it at others.
What is the best approach to achieve these requirements?
This seems to work perfectly. I had read about the messaging loop but it didn’t seem to work until I tried it like below, thanks LarsTech
Module Module1
Public mainView As Form1
Public Sub Main()
' initialization code
''...
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
mainView = New Form1
Application.Run(mainView) ' I can reference 'mainView' from anywhere in my app, toggle its Visible property etc.
End Sub
End Module

How to make a loader in a separate thread?

I have a main form wich is expected to perfom some long operations. In parallel, I'm trying to display the percentage of the executed actions.
So I created a second form like this:
Private Delegate Sub DoubleFunction(ByVal D as Double)
Private Delegate Sub EmptyFunction()
Public Class LoaderClass
Inherits Form
'Some properties here
Public Sub DisplayPercentage(Value as Double)
If Me.InvokeRequired then
dim TempFunction as New DoubleFunction(addressof DisplayPercentage)
Me.Invoke(TempFunction, Value)
Else
Me.PercentageLabel.text = Value
End if
End sub
Public Sub CloseForm()
If Me.InvokeRequired Then
Dim CloseFunction As New EmptyFunction(AddressOf CloseForm)
Me.Invoke(CloseFunction)
Else
Me.Close()
End If
FormClosed = True
End Sub
End class
My main sub, the one which is expected to perform the long operations is in another form as follows:
Private Sub InitApplication
Dim Loader as new LoaderClass
Dim LoaderThread as new thread(Sub()
Loader.ShowDialog()
End sub)
LoaderThread.start()
Loader.DisplayPercentage(1/10)
LoadLocalConfiguration()
Loader.DisplayPercentage(2/10)
ConnectToDataBase()
Loader.DisplayPercentage(3/10)
LoadInterfaceObjects()
Loader.DisplayPercentage(4/10)
LoadClients()
...
Loader.CloseForm()
End sub
The code works almost 95% of the time but sometimes I'm getting a thread exception somewhere in the sub DisplayPercentage. I change absolutely nothing, I just hit the start button again and the debugger continues the execution without any problem.
The exception I get is: Cross-thread operation not valid: Control 'LoaderClass' accessed from a thread other than the thread it was created on event though I'm using : if InvokeRequired
Does anyone know what is wrong with that code please ?
Thank you.
This is a standard threading bug, called a "race condition". The fundamental problem with your code is that the InvokeRequired property can only be accurate after the native window for the dialog is created. The problem is that you don't wait for that. The thread you started needs time to create the dialog. It blows up when InvokeRequired still returns false but a fraction of a second later the window is created and Invoke() now objects loudly against being called on a worker thread.
This requires interlocking, you must use an AutoResetEvent. Call its Set() method in the Load event handler for the dialog. Call its WaitOne() method in InitApplication().
This is not the only problem with this code. Your dialog also doesn't have a Z-order relationship with the rest of the windows in your app. Non-zero odds that it will show behind another window.
And an especially nasty kind of problem caused by the SystemEvents class. Which needs to fire events on the UI thread. It doesn't know what thread is the UI thread, it guesses that the first one that subscribes an event is that UI thread. That turns out very poorly if that's your dialog when it uses, say, a ProgressBar. Which uses SystemEvents to know when to repaint itself. Your program will crash and burn long after the dialog is closed when one of the SystemEvents now is raised on the wrong thread.
Scared you enough? Don't do it. Only display UI on the UI thread, only execute slow non-UI code on worker threads.
Thank you for your proposal. How to do that please ? Where should I
add Invoke ?
Assuming you've opted to leave the "loading" code of the main form in the main UI thread (probably called from the Load() event), AND you've set LoaderClass() as the "Splash screen" in Project --> Properties...
Here is what LoaderClass() would look like:
Public Class LoaderClass
Private Delegate Sub DoubleFunction(ByVal D As Double)
Public Sub DisplayPercentage(Value As Double)
If Me.InvokeRequired Then
Dim TempFunction As New DoubleFunction(AddressOf DisplayPercentage)
Me.Invoke(TempFunction, Value)
Else
Me.PercentageLabel.text = Value
End If
End Sub
End Class
*This is the same as what you had but I moved the delegate into the class.
*Note that you do NOT need the CloseForm() method as the framework will automatically close your splash screen once the main form is completely loaded.
Now, over in the main form, you can grab the displayed instance of the splash screen with My.Application.SplashScreen and cast it back to LoaderClass(). Then simply call your DisplayPercentage() method at the appropriate times with appropriate values:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitApplication()
End Sub
Private Sub InitApplication()
Dim Loader As LoaderClass = DirectCast(My.Application.SplashScreen, LoaderClass)
Loader.DisplayPercentage(1 / 10)
LoadLocalConfiguration()
Loader.DisplayPercentage(2 / 10)
ConnectToDataBase()
Loader.DisplayPercentage(3 / 10)
LoadInterfaceObjects()
Loader.DisplayPercentage(4 / 10)
LoadClients()
' Loader.CloseForm() <-- This is no longer needed..."Loader" will be closed automatically!
End Sub
Private Sub LoadLocalConfiguration()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
Private Sub ConnectToDataBase()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
Private Sub LoadInterfaceObjects()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
Private Sub LoadClients()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
End Class
If all goes well, your splash screen should automatically display, update with progress, then automatically close when your main form has finished loading and displayed itself.
Me.Invoke(TempFunction, Value)
Should be:
Me.Invoke(TempFunction, new Object(){Value})
because the overload with parameters takes an array of parameters.
Value is on the stack of the function in the current thread. You need to allocate memory on the GC heap and copy the value to that memory so that it is available to the other thread even after the local stack has been destroyed.

Delegates not working in another module

Running into an odd issue with tasks and delegates. Code in question is running under dotNET 4.5.1, VS2013. On the form's code I have a sub that updates a grid, it checks to see if an invoke is required, and if it is it calls a delegate. When a task runs that's called in the same module, it works as expected, no problems. Threaded or not, the grid updates properly.
However, if the same thing is called from another module, the delegate never gets called and the visual component doesn't get updated. Just a watered down bit of pseudocode to clarify..
In the form's module:
Private Delegate Sub DoWhateverDelegate(ByVal _____)
Public Sub DoWhatever(ByVal _____)
If MyComponent.InvokeReqired
Dim Delegated As New DoWhateverDelegate(AddressOf DoWhatever)
Debug.Print("The delegate fired")
Invoke(Delegated, _____)
Else
' .. carry on as usual ..
End If
End Sub
Elsewhere....
Task.Run(Sub()
' .. various things I'd rather not block the UI thread with ..
DoWhatever()
End Sub)
Works fine. I can do Task.Run__ that calls DoWhatever and it's all happy and good. However if I create a task in another module and call DoWhatever, it doesn't fire the delegate and that visual component doesn't update. The code is identical, in the same module it works, in another module it does not.
I'm probably missing something blatantly obvious.. anyone care to point out my mistake? Thanks.
Edit -- just to clarify, that other module is just code, there's only one form in the entire solution. It's created at program startup automatically, there is no other form creation going on.
Should be a thread-specific issue. Check this:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
foo.DoSomething()
End Sub
End Class
The class with the delegate:
Public Class foo
Public Shared Sub DoSomething()
Task.Run(Sub() UpdateText())
End Sub
Public Delegate Sub UpdateTextDelegate()
Public Shared Sub UpdateText()
Dim f = Form1
'Dim f As Form1 = Application.OpenForms("Form1")
If f.InvokeRequired Then
Dim d As UpdateTextDelegate = AddressOf UpdateText
f.Invoke(d)
Else
f.TextBox1.Text = "Hi"
End If
End Sub
End Class
Run the code and the textbox will not be updated. Use the second f=.... (that one that take a reference from OpenForms) and it will be updated.
If you just try to access the default instance and you are outside the UI-thread, a new instance of the form will be created. That means, the content IS updated, but because that form is not shown, you will not see it.
NOTE I do NOT advise to solve your problem, by using OpenForms. I'd advise to correctly instantiate forms!
Add a new module/class to your code:
Module Startup
Public MyForm1 As Form1
Public Sub main()
MyForm1 = New Form1
Application.Run(MyForm1)
End Sub
End Module
Go to project properties -> application. Disable application framework and choose Sub Main as your start object. In the app, access your form via MyForm1 - or whatever you want to name it. Problem should be gone then.

FormName.ButtonName.Visible = True - not working (vb.net)

I am going to start by giving a simplified example of what I am dealing with
I have a windows form - lets call it 'formA' and on formA I have a button that in the properties for the button i set visible = false.
I have a different class called MainLoop
Public class MainLoop
sub new()
end sub
public sub run()
If someCondition then
formA.ButtonName.Visible = True
End if
end sub
end class
I have more than one thread running in this application and one thread just keeps running through this 'public sub run' looping through it until certain conditions are met. By debugging and stepping through the application I am sure that it is running the line that sets the visiblity = true. But the buttons are just not showing up on my form. I have confirmed that its not a simple spelling mistake or anything - it seems I am missing a fundamental piece of logic here.
I have tried doing
dim TempForm as new formA
then in the IF statement I tried
TempForm.ButtonName.Visible = true
but that is creating a new instance of the form - and its not actually setting the current form that I'm using's button.
Any help is appreciated.
If you are setting the visibility on a thread other than the UI thread, you will have to invoke it in order to make it visible. You can't touch anything on the UI thread from another thread.
To anyone who is wondering the answer ended up being,
Control.Invoke Method (Delegate, Object())
Tutorial can be found here