Read more on MSDN: http://msdn.microsoft.com/en-us/library/cc656886.aspx
Unfortunately current implementation has some limitations. Since only static images are supported (BMP, GIF, JPEG, PNG, or TIFF format):
- It’s not possible to use XAML to define splash screen design
- It’s not possible to display dynamic informations like:
- List of available plugins
- Licensee information
- Plugin being currently loaded.
Solution overview
Solution for those issues is:
- Show built-in WPF splash screen
- Load regular WPF window (dynamic splash screen) which is pixel-perfect same as previously displayed splash screen but displays also dynamic information
- When all plugins and main window is loaded hide dynamic splash screen and show main window.
Implementation : DynamicSplashScreen class
DynamicSplashScreen is reusable class which can be used as base class for your custom splash screen. Check inline comments for details.
public class DynamicSplashScreen:Window { public DynamicSplashScreen() { //Defaults for splash screen this.ShowInTaskbar = false; this.WindowStartupLocation = WindowStartupLocation.Manual; this.ResizeMode = ResizeMode.NoResize; this.WindowStyle = WindowStyle.None; this.Topmost = true; this.Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { //calculate it manually since CenterScreen substracts //taskbar's height from available area this.Left = (SystemParameters.PrimaryScreenWidth - this.Width) / 2; this.Top = (SystemParameters.PrimaryScreenHeight - this.Height) / 2; } }
Creating pixel-perfect static splash screen image
Creating pixel-perfect static image can be error prone, maintenance hell if you start using external applications like Photoshop – each changed in dynamic version would have to be reflected in static image manually.
DynamicSplashScreen has another feature: capturing static splash screen image. It used build-in WPF functionality to get Control’s “screen shot” dynamically.
You can use this method to generate image and add it to Visual Studio project with build action set to SplashScreen. This still requires some manual work, but it’s not possible (and reasonably) to generate splash screen at runtime.
public class DynamicSplashScreen:Window { //.... see implementation above .. public void Capture(string filePath) { this.Capture(filePath, new PngBitmapEncoder()); } public void Capture(string filePath, BitmapEncoder encoder) { RenderTargetBitmap bmp = new RenderTargetBitmap( (int)this.Width, (int)this.Height, 96, 96, PixelFormats.Pbgra32); bmp.Render(this); encoder.Frames.Add(BitmapFrame.Create(bmp)); using (Stream stm = File.Create(filePath)) { encoder.Save(stm); } } }
Usage
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { // MyDynamicSplashScreen derives from DynamicSplashScreen and // defines layout and custom properties (like Licensee, Plugins, Message) MyDynamicSplashScreen splashScreen = new MyDynamicSplashScreen(); splashScreen.Show(); //set basic dynamic data on splash screen splashScreen.AvailablePlugins = new[] {"Plugin 1", "Plugin 2"}; splashScreen.Licensee = "Mirosław Jedynak"; //use during development to generate image and embed it in application //splashScreen.Capture(@"c:\StaticSplashScreen.png"); var startupTask=new Task(() => { //Load plugins in non-UI thread - may be time consuming for (int i = 0; i < 3; i++) { //set custom message on screen splashScreen.Dispatcher.BeginInvoke( (Action) (()=>splashScreen.Message = "Loading: Plugin " + i)); Thread.Sleep(100); } }); //when plugin loading finished, show main window startupTask.ContinueWith(t => { MainWindow mainWindow = new MainWindow(); //when main windows is loaded close splash screen mainWindow.Loaded += (sender, args) => splashScreen.Close(); //set application main window; this.MainWindow = mainWindow; //and finally show it mainWindow.Show(); }, TaskScheduler.FromCurrentSynchronizationContext()); startupTask.Start(); } }
Sample Dynamic splash screen
<Startup:DynamicSplashScreen x:Class="DynamicSplashScreenDemo.Startup.SplashScreen" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Startup="clr-namespace:DynamicSplashScreenDemo.Startup" Title="My application" x:Name="spashScreen" Width="500" Height="300"> <Border BorderThickness="1" BorderBrush="Black"> <Grid > <Rectangle> <Rectangle.Fill> <LinearGradientBrush EndPoint="0,0" StartPoint="0.5,1.3"> <GradientStop Color="#FF07254E" Offset="1"/> <GradientStop Color="White" Offset="0.369"/> <GradientStop Color="White"/> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> <TextBlock FontFamily="Verdana" FontWeight="Bold" FontSize="50" VerticalAlignment="Top" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,40,0,0" > <Run Text="Dynamic" Foreground="#FF006AB3" /> <LineBreak/> <Run Text="splash screen" Foreground="#FF006AB3"/> </TextBlock> <TextBlock Text="{Binding ElementName=spashScreen, Path=Message}" VerticalAlignment="Bottom" Margin="10"/> <StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10"> <ItemsControl Margin="0,0,0,10" HorizontalAlignment="Right" ItemsSource="{Binding ElementName=spashScreen, Path=AvailablePlugins}"/> <StackPanel Orientation="Horizontal"> <TextBlock Text="Licensed to: "/> <TextBlock Text="{Binding ElementName=spashScreen, Path=Licensee}"/> </StackPanel> </StackPanel> </Grid> </Border> </Startup:DynamicSplashScreen>
Source: DynamicSplashScreenDemo_source.zip
7 comments:
thanks , it was very useful for me
Hi,
Thank you very much for this.
I'd found this solution, http://szymonrozga.net/blog/?p=136
but it requires modifying Main(). I was looking for a splashscreen that enhanced the VS2010 feature.
thanks, I've learnt a lot from your example.
John
Good idea. However, this method requires loading WPF environment (unlike in the native NET 3.5 SP1) that affects the rate of appearance of a splashscreen.
i can't agree with you.
actually it uses native NET 3.5 SP1 splash screen to give first impression with static image. Then, when environment is loaded, dynamic splash is shown instead of static one
This is very nice.
I must say thank you for this. It has to be about the best implementation I've seen yet.
Malc
I tried placing a label "Close" on the splash screen window. Then tried to close the application on LeftMouseButtonDown event. But that event was never fired. Can you help me with this.
Post a Comment