VB.Net and splash screens

Posted Tue, Jan 20 2009 10:27 by bill

One of the nice things about the My application framework in VB.NET is the ability to easily show splash screens.  The splash screen is displayed using a separate thread and by default will close when your main form’s Load event is called. There is however a quirk with it sometimes.

Whilst testing, if I launched the application from Explorer, the splash screen would appear in front of the explorer window but the main form behind it.  When the splash screen closed, the main form didn’t come to the foreground.  This behaviour would depend on the system being tested and seems to only consistently show up when run from a VPC.  The fix for this is reasonably simple: you just need to call Activate in your main form’s Load event

 

   Private Sub Me_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load

      Me.Activate()

   End Sub

 

Kudos to the VB team for the fix :)

When the VB team suggested this fix to me, I could have sworn I had already tried that.  I had also looked into the my application framework code and saw it called Activate in it’s handler of the Load. So my first thoughts on it was it was a race condition, and hence I set up a single shot timer to call Activate, the timer being set to trigger a couple of hundred milliseconds after the load event.  At first I thought that fixed it, but that was the “sometimes” effect tricking me <g>

Kevin from the VB team suggested I try Activate in the Load event, and sure enough it seems to work *ALL* the time.  But this was even more puzzling as to why didn’t my timer approach work.  So I tried overriding the OnLoad method :

 

   Protected Overrides Sub OnLoad(ByVal e As EventArgs)

      MyBase.OnLoad(e)

      Me.Activate()

   End Sub

 

That actually fails ( yes “sometimes”).  But if you change that to:

 

  Protected Overrides Sub OnLoad(ByVal e As EventArgs)

      Me.Activate()

      MyBase.OnLoad(e)

   End Sub

 

Then it works. So the key is that Activate has to be called before the call to OnLoad completes.   Suddenly the pieces of the puzzle all fit together. 

What I think is happening is this: The my application framework hooks into the Load event and uses that to dispose of the splash screen. The problem occurs when the splash screen which has the UI input is closed before the call to Activate is made. Because the calling thread doesn’t have the UI Input, SetForeGround window doesn’t work.  Based on this hypothesis, I suggested a fix to the WindowsFormsApplicationBase.  Currently is has this method in it:

 

 

        '''**************************************************************************

        ''' ;HideSplashScreen

        ''' <summary>

        ''' Hide the splash screeen.  The splash screen was created on another thread so marshal

        ''' the call over to it.

        ''' </summary>

        ''' <remarks></remarks>

        <EditorBrowsable(EditorBrowsableState.Advanced)> Protected Sub HideSplashScreen()

            SyncLock m_SplashLock 'We can get called from two threads at once

               If m_SplashScreen IsNot Nothing AndAlso Not m_SplashScreen.IsDisposed Then

                    Dim TheBigGoodbye As New DisposeDelegate(AddressOf m_SplashScreen.Dispose)

                    m_SplashScreen.Invoke(TheBigGoodbye)

                    m_SplashScreen = Nothing

               End If

               If Me.MainForm IsNot Nothing Then

                    Call New System.Security.Permissions.UIPermission(UIPermissionWindow.AllWindows).Assert()

                    Me.MainForm.Activate()

                    System.Security.PermissionSet.RevertAssert() 'CLR also reverts if we throw or when we return from this function

               End If

            End SyncLock

        End Sub

 

 

My suggestion is to activate the main form before disposing of the splash screen :

 

          <EditorBrowsable(EditorBrowsableState.Advanced)> Protected Sub HideSplashScreen()
            SyncLock m_SplashLock 'We can get called from two threads at once

                 If Me.MainForm IsNot Nothing Then
                    Call New System.Security.Permissions.UIPermission(UIPermissionWindow.AllWindows).Assert()

                    Me.MainForm.Activate()

                    System.Security.PermissionSet.RevertAssert() 'CLR also reverts if we throw or when we return from this function

               End If

               If m_SplashScreen IsNot Nothing AndAlso Not m_SplashScreen.IsDisposed Then

                    Dim TheBigGoodbye As New DisposeDelegate(AddressOf m_SplashScreen.Dispose)

                    m_SplashScreen.Invoke(TheBigGoodbye)

                    m_SplashScreen = Nothing

               End If

            End SyncLock

        End Sub

 

 

Hopefully that will fix this issue completely.

 

In the meanwhile, simply call Activate in your main form’s Load event :)

Filed under: , , , , ,

Comments

# Pages tagged "puzzle"

Monday, January 19, 2009 10:03 PM by Pages tagged "puzzle"

Pingback from  Pages tagged "puzzle"

# re: VB.Net and splash screens

Wednesday, January 28, 2009 11:19 AM by Chris B.

Thanks for posting this. It turned out to be extremely timely information, as I had just implemented ApplicationServices for my project, with the same problematic behavior.

Your solution works like a charm!

# re: VB.Net and splash screens

Friday, February 13, 2009 1:30 AM by Takdir

We are facing the same problem in our project.

I have overrided onLoad as per your statement.

It works fine on my W2K professional machine(single cpu)but on my Vista machine (dual core)  it sometimes fails.