Reputation: 2935
In .NET 4.0 I'm dealing with an app which has a long loading time (about 20 seconds), so I wanted to display a swish scrolling marquee on a form that comes on top of the application whilst it is loading.
Since the main UI thread is doing all the loading of data UI elements, I couldn't get that to execute on a separate thread, so I ended up trying to run the form on a new thread. I ended up sticking this code in the form itself, to have it show itself on a new thread, like this:
Public Class frmWait
Public Property Message As String
Get
Return Me.lblMessage.Text
End Get
Set(value As String)
If Not String.IsNullOrEmpty(value) Then
Me.lblMessage.Text = value
Else
Me.lblMessage.Text = DefaultMessage
End If
End Set
End Property
Private OwnerThread As Thread
Private OwnerForm As Form
Private Const DefaultMessage As String = "しばらくお待ちください..."
Public Sub New(ByVal ParentForm As Form)
InitializeComponent()
Me.Message = DefaultMessage
Me.OwnerForm = ParentForm
End Sub
Public Sub New(ByVal Message As String, ByVal ParentForm As Form)
Call InitializeComponent()
Me.Message = Message
Me.OwnerForm = ParentForm
End Sub
Public Sub ShowOnThread()
' Position the form in the center of the owner
With Me.OwnerForm
Dim ownerCenter As New Point(.Location.X + CInt(.Width / 2), .Location.Y + CInt(.Height / 2))
Me.Location = New Point(ownerCenter.X - CInt(Me.Width / 2), ownerCenter.Y - CInt(Me.Height / 2))
End With
Me.OwnerThread = New Thread(New ThreadStart(AddressOf Me.ShowDialog))
Call Me.OwnerThread.Start()
End Sub
Public Shadows Sub Close()
If Me.OwnerThread IsNot Nothing AndAlso Me.OwnerThread.IsAlive Then
Call Me.OwnerThread.Abort()
Else
Call MyBase.Close()
End If
End Sub
End Class
This is probably quite clumsy, but I am showing it in different places in the application, so this seemed the most code-efficient way of doing this...
It actually works quite well, but I am encountering problems from time to time with this and need some help on how to address these issues.
Sometimes when the form gets closed I get an error about the thread being aborted in an unsafe manner.
At the moment I position the form manually in the centre of the form I want it to cover form. Ideally I'd like to be able to call .ShowDialog(ParentForm)
on it, but of course that raises an exception because of cross-thread access from one form to the other.
Any suggestions on how to resolve this would be most appreciated. Because I know virtually nothing about threading I probably coded this like a monkey, so if there is a better method to get this done, I would very much like to know about it.
The code I list is in VB.NET, but answer code in C# is fine too (for any overzealous retaggers)
UPDATE:
I realise now that I should have given a lot more details in my question... The wait form is actually not the first form I am displaying the app. There first is a login screen. When the user is authenticated, the login form launches the main interface of the app, which is the form which actually takes a long time to load.
I am displaying the wait form in between the login form and the main interface. I also use this form to cover for any long running tasks launched on the main interface by the user.
Upvotes: 0
Views: 2309
Reputation: 10855
You could use the ApplicationContext
class to allow the splash screen to be run first and then swap in the Main form when it is ready. You use it in place of your main form in the call to Application.Run:
Application.Run(new MyApplicationCtx())
Just googled up a article for you as well:
http://www.codeproject.com/Articles/5756/Use-the-ApplicationContext-Class-to-Fully-Encapsul
Upvotes: 0
Reputation: 1388
Have a look at TaskFactory.New
Run your threaded code of a different thread to keep the UI responsive
http://msdn.microsoft.com/en-us/library/ee782519.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-3
Upvotes: 0
Reputation: 31393
VisualStudio can do this for you. You can certainly write your own, of course, but if you look at your project options under Application
there is a setting for Splash Screen
. You can assign any form (other than the startup form) to be your splash screen. This runs on its own thread and you can marshall calls to it during startup to advance a progress bar, etc.
Upvotes: 1
Reputation: 27322
Instead of trying to show a dialog on a separate thread you should be moving your loading code to a separate thread / background worker.
Then just start your threads and show the progressbar on form_load and hide the progressbar when the thread completes:
Dim _progressForm As frmLoading
Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load
'start loading on a separate thread
BackgroundWorker1.RunWorkerAsync()
'show a marquee animation while loading
_progressForm = New frmLoading
_progressForm.ShowDialog(Me)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'simulate a long load
For i As Integer = 1 To 10
System.Threading.Thread.Sleep(1000)
Next
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
_progressForm.Close()
End Sub
Upvotes: 1