A better WPF splash screen

by StefanOlson 27. November 2008 10:07

Further Update: there was a bug relating to the splash screen start application not correctly closing. This has now been fixed and you can download it here

Update: Thanks to all of those who have pointed out that it didn't work correctly on windows XP, I have now fixed this and you can download it here

WPF 3.5 Service Pack 1 introduced a new splash screen feature which was supposed to provide a splash screen whilst the program loads.  Unfortunately it has a number of faults:

  1. Although the splash screen class uses native code within it, it is actually part of the .net framework so a large part of the framework has to load before the splash screen can be displayed.  This still provides a substantial period of time, particularly on cold boots where the user doesn't have any indication that your application is loading.
  2. The splash screen will display images stored in resources in either an application or a dll.  In my application I need to load the splash screen from an image at runtime.  I have submitted a suggestion on connect that this feature should be added.
  3. There is a bug in the splash screen code, that in certain cases where a user may shift focus away from the splash screen, the splash screen will throw an exception causing the application to crash.  I have submitted this issue as a bug on connect, but Microsoft seem to have ignored my attempts to reopen it, so it is anyone's guess it will be fixed in the update to the service pack due early next year
  4. Because it is not set to be on top, the splash screen can disappear below your application.
The last three issues could have been avoided, had the splash screen been part of the beta of the service pack.  It was not available until the final release, so we were unable to comment on these issues or even discover them prior to that.

Recently, Bradley Grainger on the Logos Bible Software Code Blog posted a series on how to build a splash screen in native code.  His solution is to build an executable which displays the splash screen using native code and then runs your WPF application.  This resolves problem number one because it starts much more quickly than the .net framework and makes it clear to the user what is going on.  Of course it also resolves problem number 3 because it doesn't have the same code is .net is using.  However it does not address problem number 2, and his code was posted without a compilable version, he just posted snippets of code in his blog.

Since I needed to solve my splash screen problems, I built Bradley’s code into a new Visual C++ application and made a few improvements including being able to display images not in the resources, being always on top, and fading out the splash screen like WPF does when it no longer needs to be displayed.

The source code can be downloaded here

The code is available under the same licence as the original.

To test it for yourself, after you have downloaded the code, run splashscreenstarter.exe in the root folder.  If you want to see how it works in a real world application, this video shows you how we use it in our virtual tour viewer.

So, if you wish to use it in your own application, what do you do?  You can modify the splashscreenstarter project.  Generally you should only need to change this line of code in splashscreenstarter.cpp:

CSplashScreen splash(
        hInstance, 
        // Length of time in milliseconds to display splashscreen fading 
        3000, 
        // Specifies the way to load the image for the splashscreen 
        // To load from the resources, as we have done here, provide the 
        // resource name and the resource type name.  We have used a jpg.
        // If you use a PNG, its opacity should be honored to allow you 
        // to display partially transparent splashscreens.
        // To edit the resources in a C++ application go to the resource 
        // view tab
        // To load from a file use this line instead, where filename is the file
        // you wish to load:
        // new CFileImageLoader(filename), 
        new CResourceImageLoader(MAKEINTRESOURCE(IDR_SPLASH), _T("JPG")),
        // Application prefix.  This will be added to the event name so there are no 
        // conflicts between applications.
        _T("SplashScreenStarter"), 
        // File name of your executable to run.  The extension does not need to be .exe.  
        // If you want to stop your users from starting your application without displaying
        // the splashscreen you could use a different extension.
        // Is assumed it is in the same folder as this program 
        // If it is not you can call splash.SetFullPath
        _T("SplashScreenTester.exe") 
        ) ;

You will need to replace the image used for the splashscreen which is stored in startuppic.jpg.  You'll also want to replace the icon with the application icon you are using.

Then in your WPF application class (app.xaml.cs):

protected override void OnStartup(StartupEventArgs e)
{
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded,
        (DispatcherOperationCallback)delegate { CloseSplashScreen(); return null; },
        this);
    base.OnStartup(e);
}

private void CloseSplashScreen()
{
    // signal the native process (that launched us) to close the splash screen
    using (var closeSplashEvent = new EventWaitHandle(false,
        EventResetMode.ManualReset, "CloseSplashScreenEventSplashScreenStarter"))
    {
        closeSplashEvent.Set();
    } 
}

Notes that the event name, CloseSplashScreenEventSplashScreenStarter, includes the prefix from above at the end of it.  If you use a different prefix you will need to modify this.

If you wish to close the splash screen at any each time without fading it you can call this code:

static protected void CloseSplashScreenWithoutFade()
{
    // signal the native process (that launched us) to close the splash screen
    using (var closeSplashEvent = new EventWaitHandle(false, 
        EventResetMode.ManualReset, "CloseSplashScreenWithoutFadeEventSplashScreenStarter"))
    {
        closeSplashEvent.Set();
    }
}

Hopefully this code will help you to create a more responsive application. If you have any questions or comments, please feel free to contact me.

…Stefan

Tags:

WPF

Single Sourcing WPF and Silverlight

by StefanOlson 27. November 2008 07:32

If you are like me, when you first heard about Silverlight, you thought this is fantastic, the WPF application you just developed can be taken, and with a few changes to support asynchronous loading, you've got an Internet accessible application! Well, think again!  That's not how it works.  Although it is supposed to be a subset of WPF, there is a lot of core WPF functionality that is either not there or operates fundamentally differently under Silverlight.

Most Microsoft people recommend that if you are starting a new application that you develop the Silverlight version first, and later move that to WPF.  Unfortunately, in our case we had already developed the Virtual Tour Viewer software for WPF, well before Silverlight was on the scene.  To some extent I still think it's better start with WPF because ultimately, hopefully, Silverlight will pick up more WPF functionality, and in my opinion the WPF functionality, such as triggers, is generally easier to code with than Silverlight equivalents.

To prove that it is possible, below are images of our virtual tour viewer running in WPF and Silverlight:

WPF

Silverlight

wpftour sltour

So, having built your application how do you convert it to Silverlight?  Well, the first thing is to create a new solution.  I have one solution for the Silverlight version and another one for the WPF version.  So I ended up with a folder structure like this:

 tourfolders

The Virtual Tour Viewer folder is the WPF version.  Within each solution the projects are replicated, with Silverlight versions and WPF versions of the project (where appropriate, there is some projects that are only in WPF in the Virtual Tour Viewer).  This can be seen below:

WPF

Silverlight

wpfprojects slprojects

So, what code can you share between Silverlight and WPF?  Initially, I was hopeful of being able to share both the xaml and .cs files.  However it became clear very early on that it was not going to be possible due to the number of differences in the xaml capabilities between Silverlight and WPF.  In the end there was not a single XAML file that I was able to share.  A number of people have also reported difficulties in dealing with linked XAML files in Visual Studio, although in my testing I did manage to get it to work, but I didn't do any serious testing so that may have been a fluke.

So in the end I share the code behind .cs files but not the XAML files.  I am also able to share all the data access layer .cs files.

The one important point is that you need to make sure that each assembly has exactly the same name on WPF and Silverlight.  This is important for xaml references

e.g:

xmlns:Controls1="clr-namespace:OlsonSoftware.Windows.Controls;assembly=OlsClassLibUI"

This reference can then be used across both WPF and Silverlight if the assembly name is kept the same. This can be done by right clicking on the project and selecting properties:

assemblyname

There are two ways you can manage each project:

Both projects in a single folder

This is the simplest way.  All you do is create a second project (in my case a Silverlight class library), in the same folder.  Visual Studio automatically sees all the files in that folder and you can simply exclude or include any files to get the files you require. 

incinprj

A problem with this approach is that it doesn't work where you have custom controls.  The xaml for a custom control is stored in a file called themes/generic.xaml.  Because your xaml will almost inevitably be different between WPF and Silverlight, it isn't possible to share that file between the two projects when they are both in the same folder, as you need two separate generic.xaml files. 

Another problem is that both the WPF and Silverlight solution want to build the same folder, e.g. Debug.  The work around for this to set the output path in the project properties:

outputfolder

Projects in separate folders using linked files

This is the way that I ended up choosing for almost every project in the solution.  It gives much more flexibility the previous option, because I can now have a clear separation between files that are just used for Silverlight and the WPF project.

So, how do you do this?  Well this time you create a new class library project in a new folder.  This leaves you with an empty project.  Now you have two projects, one for Silverlight and one for WPF, each in their own folder.

For any files that you need to share, you can use Visual Studio's Add Existing Item.  The difference is that instead of clicking Add, you click the drop down menu beside it and Add as Link:

addaslink

Normally, Visual Studio will copy the file to your project folder.  Using this feature it does not do this and simply references the file in the other folder.  This way any changes you make for the other project are automatically reflected in this one.

This opens up another problem, there are a number of features available only in WPF, or in Silverlight that you may wish to use.  With this linked file, if you use any of these features it may not compile in both projects.  The solution to this is to use the conditional compiling feature.  Any code that does not compile in Silverlight can be surrounded by these commands:

#if !SILVERLIGHT
[code goes here]
#endif

Of course it is the opposite situation where the code does not compile in WPF:

#if SILVERLIGHT
[code goes here]
#endif  

Unfortunately you'll find yourself using these far more than you would wish because there are so many differences between WPF and Silverlight.  I have resolved some of these issues by developing a WPF compatibility library to provide WPF functionality on Silverlight such as routed commands, routed events, FlowDocumentViewer, navigation etc.  We will be making it available as an open source library in the near future.

With this project setup or you can add files specific to Silverlight and WPF in their respective project folders as normal.

Sharing user controls

To share the code behind for your xaml files, create your user controls as normal in WPF.  e.g:

wpfctl

Then create the same control in Silverlight and delete the .cs file from the project.  Then you can add the existing CS file from the other project.  Unfortunately, when you are using linked files as code behind, Visual Studio doesn't display them as a child of the xaml file, even though it is correctly indicated in the project. So this is what you will end up with:

slctl

To share the xaml code between both projects you just have to copy and paste, unfortunately.

Sharing custom controls

Custom controls have a single xaml file themes/generic.xaml.  You need a separate file in both the WPF and Silverlight projects.  The code for each control can be linked to as described above.

 

So that is an overview of how we solved project management with Silverlight and WPF.  We hope that it is helpful and adequately explains how we have done this.

If you want to check out the WPF and Silverlight versions of the Virtual Tour Viewer, I'll be posting a new blog entry when I make them publicly available in the next week or so.

…Stefan

Tags:

WPF | Silverlight

Some Silverlight 3 features announced

by StefanOlson 17. November 2008 07:14

Scott Guthrie today announced a number of new features that will be available in Silverlight 3, available next year.  In addition to the previously announced H.264 video support, there is a substantial surprise in that they have announced 3D and GPU hardware acceleration support.  I hadn't expected this until after version 3.

He also announced richer data binding support which frankly could mean anything. Hopefully it means DependencyProperties and element binding work the same as in WPF.

He also includes in his post a screen shot of the new visual data binding editor that will be available in Visual Studio 2010.  It will be interesting to see how this works in practice as most of the time my data context is set in code and I'm not sure how that will be reflected in the editor.  If you want to see how this new visual data binding works, it was demonstrated in this session at PDC: http://channel9.msdn.com/pdc2008/PC45/.

...Stefan

Tags:

Targeted commands in WPF and Silverlight

by StefanOlson 2. November 2008 12:11

Whilst building the WPF version of our virtual tour software we came across a situation where routed commands, the built in command infrastructure in WPF would not actually achieve what we wanted.  The way routed commands work is that they work their way up the visual tree trying to find a CommandBinding from someone who was interested in processing that command.  Unfortunately in a number of situations in the viewer that is not the behaviour that we want.  Sometimes you could be in a different part of visual tree but still want a command to execute when you press a keystroke for example.

Confused yet?  Let's get on to a concrete example.  Below is a screen shot of the virtual tour viewer running in WPF:

Virtual Tour Viewer

If the keyboard focus happened to be in the index for example you would still want the navigation buttons beside the word Introduction to be operational via the keyboard shortcut (Ctrl+Right):

 navbuttons  

Of course with a routed command it would simply try and find a binding from the current keyboard focus (index) up to the main window.  As the command is handled by the TourContentControl on the right it would never know about the request.

Therefore we have come up with a new concept borrowed, in a way, from Expression Blend, called Targeted Commands.  The way Targeted Commands work is that each window registers the commands they want to receive with the TargetedCommandManager.  So when a command is executed the TargetedCommandManager goes through the list of registered bindings and finds out which window is interested in the current command.  It can then use that given command binding to execute the command. 

So, what do we want to achieve with this:

  • Be able to send keyboard and button commands to the place that needs to be able to handle it
  • Have the command automatically assign a tooltip to buttons which gives a description of the command and the keystrokes to execute it. Ultimately it would be good if the system could automatically set up the content of the button including an icon, which could be associated with the command (not done yet).
  • Same code under Silverlight and WPF.

So let's look at some code. First off, how do we create a command?

public static readonly ICommand NextObjectCommand = new TargetedCommand("NextObject", typeof(MainWindow), new KeyGesture(Key.Right, ModifierKeys.Control));
 
As you can see it's very similar to a normal routed command.  We create all commands in the main window.  This allows us to ensure that all the possible keyboard shortcuts are a registered in one place which makes it easier to ensure that but we don't end up with duplicates.  In a more line of business application it would allow you to give the user control over customizing the keyboard shortcuts because all the commands are created in one place, but it's not strictly required to do it that way.
 
In the constructor for the main window we create the TargetedCommandManager:
public MainWindow()
{
    InitializeComponent();

    _commandManager = new TargetedCommandManager(this, null);
The second parameter, which is currently null allows you to provide a class implementing the interface ICommandReporter, which is an interface we have developed to provide support for reporting back to you the usage of your application in the real world.
 
Then in the control where you wish to handle the command, you declare a TargetedCommandBindingCollection:
private readonly TargetedCommandBindingCollection _bindings = new TargetedCommandBindingCollection();
 
Then in the constructor you can associate the commands with the binding collection:
public ViewerTitleControl()
{
    InitializeComponent();

    _bindings.Add(new TargetedCommandBinding(MainWindow.NextObjectCommand,NextObjectExecuted));
    _bindings.Add(new TargetedCommandBinding(MainWindow.PrevObjectCommand,PrevObjectExecuted));
    _bindings.Add(new TargetedCommandBinding(MainWindow.ParentObjectCommand,ParentObjectExecuted));

Of course there is support for CanExecute, which ties into the existing CommandManager system of WPF, or in Silverlight with the CommandManager that is part of our Silverlight WPF compatibility library, which contains the Targeted Command System.

So the final part is to ensure that our bindings are registered with the TargetedCommandManager when the window is displayed.  So we will create functions to add and remove the bindings:

internal void AddCommands()
{
    TargetedCommandManager.Current.AddCommandBindings(_bindings);
}
internal void RemoveCommands()
{
    TargetedCommandManager.Current.RemoveCommandBindings(_bindings);
}

So in this case when the TourContentControl (which is the larger window containing the title control) is displayed we call TitleControl.AddCommands().  We call TitleControl.RemoveCommands() when a different type of window appears in the content area of the MainWindow.

So now, in both WPF and Silverlight you automatically get the correct handling of the keyboard so that the command is routed to the correct place that actually wants to do something with it!  Unfortunately, in Silverlight you don't actually get any ALT key commands (e.g: ALT+S) coming through to the keyboard handler as they are eaten by the browser (I’ll blog about that soon).

You assign the command to a button in exactly the same way as you always have, using the Command attribute.  If you want the tooltip to automatically be created with a description pulled from the application resources using the name of the command, you can do it via the provided attached property (CommandHelpers.Command) which we have also created for Silverlight.

Here's an example of automatic tooltip creation, running under Silverlight:

prevpic

So there we have one possible solution for handling commands in an application.  Because of the design the TargetedCommandBinding could equally be put in the ViewModel rather than the View as we have done here.

The source code for the Targeted Command system and our routed command implementation for Silverlight and other associated classes will be available in the near future as part of the open source Silverlight WPF compatibility library we are creating.  Keep watching for more news!

…Stefan

Tags:

About the author

Stefan Olson is the Managing Director of Olson Software.  He has been developing software using Microsoft Technologies for nearly 20 years.

He is currently working on building the next generation Virtual Tour software in WPF and Silverlight for www.palacevirtualtours.com.

Tag cloud