Link to home
Start Free TrialLog in
Avatar of sgaggerj
sgaggerjFlag for United States of America

asked on

VB.Net Ensure a form is displayed before continuing

Hi Experts,

I am creating an app that will have a splash screen (a dialog) that displays loading information to the user while the rest of the app is loading in the background.

When the rest of the app is done loading, the splash screen will close and the main form will be displayed.

I'm having trouble getting that working....

I have set the startup form to my splash dialog, and shutdown mode to when last form closes.

Problem:  The splash screen is never 'visible' until AFTER the main window is displayed. It should be visible from the moment the app executes, displaying the animation of 1 image fading to another (and later progress bar animation and status text updates)

Any suggestions?

Here is the code for my splash dialog

Option Strict On
Option Explicit On

Imports System.Drawing.Imaging

Public Class Splash
#Region "Delegates "
    Private Delegate Sub CloseWindowDelegate()
#End Region
#Region "Variables "
    Private m_Alpha As Single = 0 ' Alpha on a 0-1 scale.
    Private m_DAlpha As Single = 0.05
    Private image1 As Bitmap = My.Resources.image_1
    Private image2 As Bitmap = My.Resources.image_2
    Private animating As Boolean
#End Region
    Public Sub New()

        InitializeComponent()

        ' Start the animation
        tmrDisplayFrame.Enabled = True
        tmrDisplayFrame.Start()

        Dim MainWindow As New MainForm(AddressOf CloseWindow)
        Dim t As New System.Threading.Thread(AddressOf MainWindow.Initialize)
        t.Start()

        While t.IsAlive
            System.Windows.Forms.Application.DoEvents()
        End While

        tmrDisplayFrame.Enabled = False
        tmrDisplayFrame.Stop()
        tmrDisplayFrame = Nothing

        While animating
            System.Windows.Forms.Application.DoEvents()
        End While

        MainWindow.Show()

    End Sub
#Region "Subs "
    Private Sub CloseWindow()
        If Me.InvokeRequired Then
            Dim shd As New CloseWindowDelegate(AddressOf CloseWindow)
            Me.Invoke(shd)
        Else
            Me.Close()
        End If

    End Sub
#End Region
#Region "Handlers "
    Private Sub tmrDisplayFrame_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrDisplayFrame.Tick
        animating = True

        System.Windows.Forms.Application.DoEvents()

        Dim image_attr As New ImageAttributes
        Dim cm As ColorMatrix

        Dim bm As New Bitmap(image1.Width, image1.Height)
        Dim gr As Graphics = Graphics.FromImage(bm)
        Dim rect As Rectangle = Rectangle.Round(image1.GetBounds(GraphicsUnit.Pixel))

        cm = New ColorMatrix(New Single()() { _
            New Single() {1.0, 0.0, 0.0, 0.0, 0.0}, _
            New Single() {0.0, 1.0, 0.0, 0.0, 0.0}, _
            New Single() {0.0, 0.0, 1.0, 0.0, 0.0}, _
            New Single() {0.0, 0.0, 0.0, 0.0, 0.0}, _
            New Single() {0.0, 0.0, 0.0, m_Alpha, 1.0}})
        image_attr.SetColorMatrix(cm)
        gr.DrawImage(image1, rect, 0, 0, image1.Width, image2.Width, GraphicsUnit.Pixel, image_attr)

        cm = New ColorMatrix(New Single()() { _
            New Single() {1.0, 0.0, 0.0, 0.0, 0.0}, _
            New Single() {0.0, 1.0, 0.0, 0.0, 0.0}, _
            New Single() {0.0, 0.0, 1.0, 0.0, 0.0}, _
            New Single() {0.0, 0.0, 0.0, 0.0, 0.0}, _
            New Single() {0.0, 0.0, 0.0, 1 - m_Alpha, 1.0}})
        image_attr.SetColorMatrix(cm)
        gr.DrawImage(image2, rect, 0, 0, image1.Width, image2.Width, GraphicsUnit.Pixel, image_attr)

        picResult.Width = image1.Width
        picResult.Height = image1.Height
        picResult.BackgroundImage = bm
        picResult.Refresh()

        m_Alpha += m_DAlpha
        If m_Alpha > 1 Then
            m_Alpha = 1
            m_DAlpha *= -1
        ElseIf m_Alpha < 0 Then
            m_Alpha = 0
            m_DAlpha *= -1
        End If
        System.Windows.Forms.Application.DoEvents()
        animating = False
    End Sub
#End Region

End Class

Open in new window


and the code for my main form

Option Strict On
Option Explicit On

Public Class MainForm
#Region "Delegates "
    Public Delegate Sub CloseSplashWindowDelegate()
#End Region
#Region "Variables "
    Private CloseSplashWindow As CloseSplashWindowDelegate
    Private WithEvents CloseSplashTimer As Timers.Timer

#End Region

    Public Sub New(ByVal _csw As CloseSplashWindowDelegate)
        InitializeComponent()

        CloseSplashWindow = _csw
    End Sub

#Region "Public Subs "
    Public Sub Initialize()
        Dim s As DateTime = Now.AddSeconds(15)
        While Now < s
            System.Windows.Forms.Application.DoEvents()
        End While
        CloseSplashTimer = New Timers.Timer
        With CloseSplashTimer
            .AutoReset = False
            .Interval = 1000
            .Start()
        End With
    End Sub
#End Region
#Region "Private Subs "
    Private Sub LoadExistingServers()

    End Sub
#End Region
#Region "Handlers "
    Private Sub AddNewServerToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles AddNewServerToolStripMenuItem.Click
       
    End Sub
    Private Sub RemoveCurrentServerToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles RemoveCurrentServerToolStripMenuItem.Click

    End Sub
    Private Sub ExitToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles ExitToolStripMenuItem.Click
        Application.Exit()
    End Sub
    Private Sub CloseSplashTimer_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles CloseSplashTimer.Elapsed
        CloseSplashWindow.Invoke()
    End Sub
#End Region

End Class

Open in new window

SOLUTION
Avatar of Nasir Razzaq
Nasir Razzaq
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of sgaggerj

ASKER

Ok. I'll try that. The code I'm using is exactly what I posted. I didn't strip anything out.
"I have set the startup form to my splash dialog, and shutdown mode to when last form closes."

You've over complicated this in a big way!...

This bit of code:
        Dim s As DateTime = Now.AddSeconds(15)
        While Now < s
            System.Windows.Forms.Application.DoEvents()
        End While

Open in new window

*implies that you have no "real work" in the loading of your main form.  You simply want your splash form to appear for 15 seconds?!  That's a long time to wait for nothing...

Get rid of all that manual threading code.

In Project --> Properties, leave your main form as the "startup object/form", and set your splash dialog as the "splash screen".

Now, to control how long your splash is displayed, override the MinimumSplashScreenDisplayTime property:
http://msdn.microsoft.com/en-us/library/ms234874(VS.80).aspx

    "Determines the minimum length of time, in milliseconds, for which the splash screen is displayed."

To do this, click on Project --> Properties, then click on the "View Application Events" button in the bottom right.  You should see this 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

    End Class


End Namespace

Open in new window


Inside the "MyApplication" class portion is where you'd place the sample overrided from the MSDN link:
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 15 seconds:
            Me.MinimumSplashScreenDisplayTime = TimeSpan.FromSeconds(15).TotalMilliseconds
            Return MyBase.OnInitialize(commandLineArgs)
        End Function

    End Class

End Namespace

Open in new window


This will cause the splash screen to automatically close after the minimum time and your main startup form will then be loaded for you.

If your main form is doing actual work, and you want it to communicate with your splash screen, then see my article here:
https://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/A_3671-Updating-a-Splash-Screen-with-Loading-Progress-in-a-VB-Net-WinForms-Application.html
The reason I set the 15 sec delay there was to simulate work being done in the main form that I haven't added yet.  The main form will be loading several tables that are 28k and 92k rows in length as well as using that data to populate combo boxes etc.

The 15 sec was to get a good idea of how the splash screen was working, but as you said a long time.

I'll look at your post tomorrow and see what I can do with it. The main form will be communicating with the splash screen to report progress.

I'm not sure I could calculate how long the loading process would take as every pc is slower/faster than another and not all users will have the db local to the app. This is why I was figuring to do the loading the way I did so that as soon as it was done it would display, whether it took 5 sec or 5 min to load everything.

I'll give those posts a read and see what I can learn.

Thanks!
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That's exactly what I was needing!

Just needed to make the calls to the picturebox (picResult) thread friendly and it's working like I wanted!