diff --git a/docs/concepts/Splashscreen.md b/docs/concepts/Splashscreen.md
index 33c8228..965239f 100644
--- a/docs/concepts/Splashscreen.md
+++ b/docs/concepts/Splashscreen.md
@@ -1,7 +1,73 @@
## Splash Screen
+WinUIEx provides two kinds of splash screens:
+ - `SimpleSplashScreen`: A simple splash screen that shows just an image and can be launched before any UI loads.
+ - `SplashScreen`: A more advanced splash screen that can show any XAML including a progress bar and status text.
+
+
+### SimpleSplashScreen
+
+`SimpleSplashScreen` will show an image while the app is loading. To use it, create a new `SimpleSplashScreen` in App.xaml.cs:
+
+```cs
+private SimpleSplashScreen vss { get; set; }
+
+public App()
+{
+ fss = SimpleSplashScreen.ShowDefaultSplashScreen(); // Shows the splash screen you already defined in your app manifest. For unpackaged apps use .ShowSplashScreenImage(imagepath):
+ // fss = SimpleSplashScreen.ShowSplashScreenImage(full_path_to_image_); // Shows a custom splash screen image. Must be a full-path (no relative paths)
+ this.InitializeComponent();
+}
+```
+
+Once your window is activated, you can remove the splash screen by either calling `.Dispose()` or `Hide()`.
+
+```cs
+protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
+{
+ m_window = new MainWindow();
+ m_window.Activate();
+ Window_Activated += Window_Activated;
+
+}
+
+private void Window_Activated(object sender, WindowActivatedEventArgs args)
+{
+ ((Window)sender).Activated -= Window_Activated;
+ fss?.Hide();
+ fss = null;
+}
+```
+
+For even faster splash screen, disable the XAML generated main method by defining the `DISABLE_XAML_GENERATED_MAIN` preprocessor directive
+and instead defining your own start method. You'll then be able to display the splash screen as the very first thing before the application is created. For example:
+
+```cs
+#if DISABLE_XAML_GENERATED_MAIN
+ public static class Program
+ {
+ [System.STAThreadAttribute]
+ static void Main(string[] args)
+ {
+ // If you're using the WebAuthenticator, make sure you call this method first before the splashscreen shows
+ if (WebAuthenticator.CheckOAuthRedirectionActivation(true))
+ return;
+ var fss = SimpleSplashScreen.ShowDefaultSplashScreen();
+ WinRT.ComWrappersSupport.InitializeComWrappers();
+ Microsoft.UI.Xaml.Application.Start((p) => {
+ var context = new Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext(Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread());
+ System.Threading.SynchronizationContext.SetSynchronizationContext(context);
+ new App(fss); // Pass the splash screen to your app so it can close it on activation
+ });
+ }
+ }
+#endif
+```
+
+### SplashScreen
+
To create a new splash screen, first create a new WinUI Window.
-Next change the baseclass from "Window" to SplashScreen:
+Next change the baseclass from `Window` to `SplashScreen`:
Before:
```xml
@@ -21,52 +87,52 @@ Before:
```cs
public sealed partial class SplashScreen : Window
{
- public SplashScreen()
- {
- this.InitializeComponent();
- }
+ public SplashScreen()
+ {
+ this.InitializeComponent();
+ }
}
```
After:
```cs
public sealed partial class SplashScreen : WinUIEx.SplashScreen
{
- public SplashScreen(Type window) : base(window)
- {
- this.InitializeComponent();
- }
+ public SplashScreen(Type window) : base(window)
+ {
+ this.InitializeComponent();
+ }
}
```
Next in App.xaml.cs, change OnLaunched from:
```cs
- protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
- {
- m_window = new MainWindow();
- m_window.Activate();
- }
+protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
+{
+ m_window = new MainWindow();
+ m_window.Activate();
+}
```
To:
```cs
- protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
- {
- var splash = new SplashScreen(typeof(MainWindow));
- splash.Completed += (s, e) => m_window = e;
- }
+protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
+{
+ var splash = new SplashScreen(typeof(MainWindow));
+ splash.Completed += (s, e) => m_window = e;
+}
```
Lastly, override OnLoading, to create some long-running setup work - once this method completes, the splash screen will close and the window in the type parameter will launch.
Example:
-```
- protected override async Task OnLoading()
- {
- //TODO: Do some actual work
- for (int i = 0; i < 100; i+=5)
- {
- statusText.Text = $"Loading {i}%...";
- progressBar.Value = i;
- await Task.Delay(50);
- }
- }
+```cs
+protected override async Task OnLoading()
+{
+ //TODO: Do some actual work
+ for (int i = 0; i < 100; i+=5)
+ {
+ statusText.Text = $"Loading {i}%...";
+ progressBar.Value = i;
+ await Task.Delay(50);
+ }
+}
```
\ No newline at end of file
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index cc23f14..9a1a617 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -17,7 +17,7 @@
Morten Nielsen - https://xaml.dev
Morten Nielsen - https://xaml.dev
logo.png
- 2.4.2
+ 2.5.0
diff --git a/src/WinUIEx/NativeMethods.txt b/src/WinUIEx/NativeMethods.txt
index c2b8722..39ed06f 100644
--- a/src/WinUIEx/NativeMethods.txt
+++ b/src/WinUIEx/NativeMethods.txt
@@ -54,4 +54,15 @@ DwmExtendFrameIntoClientArea
CreateRectRgn
CreateSolidBrush
FillRect
-GetDC
\ No newline at end of file
+GetDC
+ReleaseDC
+GdiplusStartup
+GdiplusShutdown
+GetObject
+GdipCreateBitmapFromFile
+GdipCreateHBITMAPFromBitmap
+WNDCLASSEXW
+RegisterClassEx
+CreateCompatibleDC
+SelectObject
+MonitorFromPoint
diff --git a/src/WinUIEx/SimpleSplashScreen.cs b/src/WinUIEx/SimpleSplashScreen.cs
new file mode 100644
index 0000000..148a8e9
--- /dev/null
+++ b/src/WinUIEx/SimpleSplashScreen.cs
@@ -0,0 +1,381 @@
+// Based on an implementation by Castorix: https://github.com/castorix/WinUI3_SplashScreen
+
+//#define MEDIAPLAYER
+using Microsoft.UI.Xaml;
+using System;
+using System.Runtime.InteropServices;
+using System.Xml.Linq;
+using System.Xml.XPath;
+
+#if MEDIAPLAYER
+using MFPlay;
+#endif
+using Windows.Win32;
+using Windows.Win32.UI.WindowsAndMessaging;
+
+namespace WinUIEx
+{
+ ///
+ /// Simple and fast splash screen for display images during application startup.
+ ///
+ ///
+ /// Once your application window has launched/loaded, The splashscreen should be removed by either disposing this instance or calling .
+ ///
+ ///
+ public sealed class SimpleSplashScreen : IDisposable
+ {
+ private const nint COLOR_BACKGROUND = 1;
+
+ private DispatcherTimer? dTimer;
+ private TimeSpan tsFadeoutDuration;
+ private DateTime tsFadeoutEnd;
+ Windows.Win32.Foundation.HWND hWndSplash = Windows.Win32.Foundation.HWND.Null;
+ private Windows.Win32.Graphics.Gdi.HGDIOBJ hBitmap = Windows.Win32.Graphics.Gdi.HGDIOBJ.Null;
+ private nuint initToken = 0;
+#if MEDIAPLAYER
+ private MFPlayer? mediaPlayer;
+#endif
+
+ ///
+ /// Shows the splashscreen image specified in the application manifest.
+ ///
+ ///
+ ///
+ public static SimpleSplashScreen ShowDefaultSplashScreen()
+ {
+ var image = GetManifestSplashScreen();
+ if (image is null)
+ throw new InvalidOperationException("SplashScreen section not found in AppxManifest.xml");
+ var manager = new Microsoft.Windows.ApplicationModel.Resources.ResourceManager();
+ var context = manager.CreateResourceContext();
+
+ uint dpi = 96;
+ var monitor = PInvoke.MonitorFromPoint(new System.Drawing.Point(0, 0), Windows.Win32.Graphics.Gdi.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
+
+ if(monitor != IntPtr.Zero)
+ {
+ PInvoke.GetDpiForMonitor(monitor, Windows.Win32.UI.HiDpi.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out var dpiy);
+ dpi = dpiX;
+ }
+ var scale = (int)(dpi / 96d * 100);
+ if (scale == 0) scale = 100;
+ context.QualifierValues["Scale"] = scale.ToString();
+ var splashScreenImageResource = manager.MainResourceMap.TryGetValue("Files/" + image.Replace('\\','/'), context);
+ if (splashScreenImageResource is not null && splashScreenImageResource.Kind == Microsoft.Windows.ApplicationModel.Resources.ResourceCandidateKind.FilePath)
+ {
+ return SimpleSplashScreen.ShowSplashScreenImage(splashScreenImageResource.ValueAsString);
+ }
+ throw new InvalidOperationException("Splash screen image not found in resources");
+ }
+
+ private static string? GetManifestSplashScreen()
+ {
+ var rootFolder = Windows.ApplicationModel.Package.Current?.InstalledLocation.Path ?? AppContext.BaseDirectory;
+ var docPath = System.IO.Path.Combine(rootFolder, "AppxManifest.xml");
+ if (!System.IO.File.Exists(docPath))
+ throw new System.IO.FileNotFoundException("AppxManifest.xml not found");
+
+ var doc = XDocument.Load(docPath, LoadOptions.None);
+ var reader = doc.CreateReader();
+ var namespaceManager = new System.Xml.XmlNamespaceManager(reader.NameTable);
+ namespaceManager.AddNamespace("x", "http://schemas.microsoft.com/appx/manifest/foundation/windows10");
+ namespaceManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");
+
+ var element = doc.Root?.XPathSelectElement("/x:Package/x:Applications/x:Application/uap:VisualElements/uap:SplashScreen", namespaceManager);
+ return element?.Attribute(XName.Get("Image"))?.Value;
+ }
+
+ ///
+ /// Shows a splash screen image at the center of the screen
+ ///
+ ///
+ ///
+ public static SimpleSplashScreen ShowSplashScreenImage(string image)
+ {
+ var s = new SimpleSplashScreen();
+ s.Initialize();
+ var hBitmap = s.GetBitmap(image);
+ s.DisplaySplash(Windows.Win32.Foundation.HWND.Null, hBitmap, null);
+ return s;
+ }
+#if MEDIAPLAYER
+ public static SimpleSplashScreen ShowSplashScreenVideo(string video)
+ {
+ var s = new SimpleSplashScreen();
+ s.Initialize();
+ s.DisplaySplash(Windows.Win32.Foundation.HWND.Null, Windows.Win32.Graphics.Gdi.HBITMAP.Null, video);
+ return s;
+ }
+#endif
+ private void Initialize()
+ {
+ var input = new Windows.Win32.Graphics.GdiPlus.GdiplusStartupInput()
+ {
+ GdiplusVersion = 1,
+ SuppressBackgroundThread = false,
+ SuppressExternalCodecs = false
+ };
+ var output = new Windows.Win32.Graphics.GdiPlus.GdiplusStartupOutput();
+ var nStatus = PInvoke.GdiplusStartup(ref initToken, input, ref output);
+ }
+
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ CleanUp();
+ if (disposing)
+ {
+#if MEDIAPLAYER
+ mediaPlayer.Dispose();
+#endif
+ }
+ }
+
+ private void CleanUp()
+ {
+ if (hWndSplash != IntPtr.Zero)
+ {
+ PInvoke.DestroyWindow(hWndSplash);
+ }
+ if (hBitmap != IntPtr.Zero)
+ {
+ PInvoke.DeleteObject(new Windows.Win32.Graphics.Gdi.HGDIOBJ(hBitmap));
+ hBitmap = Windows.Win32.Graphics.Gdi.HGDIOBJ.Null;
+ }
+ PInvoke.GdiplusShutdown(initToken);
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~SimpleSplashScreen() => Dispose(false);
+
+ private unsafe void DisplaySplash(Windows.Win32.Foundation.HWND hWnd, Windows.Win32.Graphics.Gdi.HBITMAP bitmap, string? sVideo)
+ {
+ Windows.Win32.UI.WindowsAndMessaging.WNDCLASSEXW wcex;
+ this.hBitmap = bitmap;
+ wcex.cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEXW));
+ wcex.style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW | WNDCLASS_STYLES.CS_DBLCLKS;
+ wcex.hbrBackground = new Windows.Win32.Graphics.Gdi.HBRUSH(COLOR_BACKGROUND + 1);
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = new Windows.Win32.Foundation.HINSTANCE(Marshal.GetHINSTANCE(this.GetType().Module));
+ wcex.hIcon = HICON.Null;
+ wcex.hCursor = HCURSOR.Null;
+ wcex.lpszMenuName = null;
+ string sClassName = "Win32Class";
+ fixed (char* name = sClassName)
+ wcex.lpszClassName = name;
+ wcex.lpfnWndProc = &Win32WndProc;
+
+ wcex.hIconSm = HICON.Null;
+ ushort nRet = PInvoke.RegisterClassEx(wcex);
+ if (nRet == 0)
+ {
+ int nError = Marshal.GetLastWin32Error();
+ if (nError != 1410) //0x582 ERROR_CLASS_ALREADY_EXISTS
+ return;
+ }
+ int nWidth = 0, nHeight = 0;
+ if (hBitmap != IntPtr.Zero)
+ {
+ BITMAP bm;
+ PInvoke.GetObject(new DeleteObjectSafeHandle(hBitmap), Marshal.SizeOf(typeof(BITMAP)), &bm);
+ nWidth = bm.bmWidth;
+ nHeight = bm.bmHeight;
+ }
+
+ hWndSplash = PInvoke.CreateWindowEx(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW | WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_TRANSPARENT | WINDOW_EX_STYLE.WS_EX_TOPMOST,
+ sClassName, "Win32 window", WINDOW_STYLE.WS_POPUP | WINDOW_STYLE.WS_VISIBLE, 400, 400, nWidth, nHeight, hWnd, null,
+ new DestroyIconSafeHandle(wcex.hInstance), null);
+ if (hBitmap != IntPtr.Zero)
+ {
+ CenterToScreen(hWndSplash);
+ SetPictureToLayeredWindow(hWndSplash, hBitmap);
+ }
+#if MEDIAPLAYER
+ if (sVideo != null)
+ {
+ mediaPlayer = new MFPlayer(this, hWndSplash, sVideo);
+ }
+#endif
+ }
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ private struct BITMAP
+ {
+ public int bmType;
+ public int bmWidth;
+ public int bmHeight;
+ public int bmWidthBytes;
+ public short bmPlanes;
+ public short bmBitsPixel;
+ public IntPtr bmBits;
+ }
+
+#if MEDIAPLAYER
+ private class MFPlayer : IMFPMediaPlayerCallback, IDisposable
+ {
+ private readonly SimpleSplashScreen m_ss;
+ private readonly Windows.Win32.Foundation.HWND m_hWndParent;
+ private readonly IMFPMediaPlayer m_pMediaPlayer;
+
+ internal unsafe MFPlayer(SimpleSplashScreen ss, Windows.Win32.Foundation.HWND hWnd, string sVideo)
+ {
+ GlobalStructures.HRESULT hr = MFPlayTools.MFPCreateMediaPlayer(sVideo, false, MFPlay.MFPlayTools.MFP_CREATION_OPTIONS.MFP_OPTION_NONE, this, hWnd, out m_pMediaPlayer);
+ m_hWndParent = hWnd;
+ m_ss = ss;
+ }
+
+ void IMFPMediaPlayerCallback.OnMediaPlayerEvent(MFP_EVENT_HEADER pEventHeader)
+ {
+ switch (pEventHeader.eEventType)
+ {
+ case MFP_EVENT_TYPE.MFP_EVENT_TYPE_MEDIAITEM_SET:
+ {
+ GlobalStructures.SIZE szVideo, szARVideo;
+ GlobalStructures.HRESULT hr = m_pMediaPlayer.GetNativeVideoSize(out szVideo, out szARVideo);
+ if (hr == GlobalStructures.HRESULT.S_OK)
+ {
+ PInvoke.SetWindowPos(m_hWndParent, Windows.Win32.Foundation.HWND.Null, 0, 0, szVideo.cx, szVideo.cy, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
+ m_ss.CenterToScreen(m_hWndParent);
+ hr = m_pMediaPlayer.Play();
+ }
+ }
+ break;
+ case MFP_EVENT_TYPE.MFP_EVENT_TYPE_PLAYBACK_ENDED:
+ {
+ PlaybackEnded?.Invoke(this, EventArgs.Empty);
+ }
+ break;
+ }
+ return;
+ }
+
+ ~MFPlayer() => Dispose(false);
+
+ public void Dispose() => Dispose(true);
+
+ private void Dispose(bool disposing) => m_pMediaPlayer.Shutdown();
+
+ public event EventHandler PlaybackEnded;
+ }
+#endif
+
+ ///
+ /// Hides the splashscreen
+ ///
+ /// Time spend fading out the splash screen (defaults to no fade)
+ public void Hide(TimeSpan fadeTimout = default)
+ {
+ if (fadeTimout.Ticks <= 0)
+ CleanUp();
+ else
+ {
+ dTimer = new DispatcherTimer();
+ dTimer.Interval = TimeSpan.FromMilliseconds(16);
+ tsFadeoutDuration = fadeTimout;
+ tsFadeoutEnd = DateTime.UtcNow + tsFadeoutDuration;
+ dTimer.Tick += FadeTimer_Tick;
+ dTimer.Start();
+ }
+ }
+
+ private unsafe Windows.Win32.Graphics.Gdi.HBITMAP GetBitmap(string sBitmapFile)
+ {
+ var bitmap = new Windows.Win32.Graphics.GdiPlus.GpBitmap();
+ var pBitmap = &bitmap;
+
+ var nStatus = PInvoke.GdipCreateBitmapFromFile(sBitmapFile, ref pBitmap);
+
+ if (nStatus == Windows.Win32.Graphics.GdiPlus.Status.Ok)
+ {
+ var hBitmap = new Windows.Win32.Graphics.Gdi.HBITMAP();
+ PInvoke.GdipCreateHBITMAPFromBitmap(pBitmap, &hBitmap, (uint)System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.FromArgb(0)));
+ return hBitmap;
+ }
+ throw new InvalidOperationException("Failed to open bitmap: " + nStatus.ToString());
+ }
+
+ private void FadeTimer_Tick(object? sender, object e)
+ {
+ DateTime dtNow = DateTime.UtcNow;
+ if (dtNow >= tsFadeoutEnd)
+ {
+ if (dTimer != null)
+ {
+ dTimer.Stop();
+ dTimer = null;
+ }
+ CleanUp();
+ }
+ else
+ {
+ double nProgress = (tsFadeoutEnd - dtNow).TotalMilliseconds / tsFadeoutDuration.TotalMilliseconds;
+ var bf = new Windows.Win32.Graphics.Gdi.BLENDFUNCTION()
+ {
+ BlendOp = AC_SRC_OVER,
+ SourceConstantAlpha = (byte)(255 * nProgress),
+ AlphaFormat = AC_SRC_ALPHA
+ };
+
+ PInvoke.UpdateLayeredWindow(hWndSplash,
+ new Windows.Win32.Graphics.Gdi.HDC(0), null, null, new Windows.Win32.Graphics.Gdi.HDC(0), (System.Drawing.Point?)null, new Windows.Win32.Foundation.COLORREF(0), bf, UPDATE_LAYERED_WINDOW_FLAGS.ULW_ALPHA);
+ }
+ }
+
+ private const byte AC_SRC_OVER = 0x00;
+ private const byte AC_SRC_ALPHA = 0x01;
+
+ private unsafe void SetPictureToLayeredWindow(Windows.Win32.Foundation.HWND hWnd, IntPtr hBitmap)
+ {
+ BITMAP bm;
+ Windows.Win32.Graphics.Gdi.HBITMAP bitmap = new Windows.Win32.Graphics.Gdi.HBITMAP(hBitmap);
+ PInvoke.GetObject(new Windows.Win32.DeleteObjectSafeHandle(hBitmap), Marshal.SizeOf(typeof(BITMAP)), &bm);
+ System.Drawing.Size sizeBitmap = new System.Drawing.Size(bm.bmWidth, bm.bmHeight);
+
+ var hDCScreen = PInvoke.GetDC(Windows.Win32.Foundation.HWND.Null);
+ var hDCMem = PInvoke.CreateCompatibleDC(hDCScreen);
+ using var hBitmapOld = PInvoke.SelectObject(hDCMem, new DestroyIconSafeHandle(hBitmap));
+
+ var bf = new Windows.Win32.Graphics.Gdi.BLENDFUNCTION()
+ {
+ BlendOp = AC_SRC_OVER,
+ SourceConstantAlpha = 255,
+ AlphaFormat = AC_SRC_ALPHA
+ };
+ PInvoke.GetWindowRect(hWnd, out var rectWnd);
+
+ System.Drawing.Point ptSrc = new System.Drawing.Point();
+ System.Drawing.Point ptDest = new System.Drawing.Point(rectWnd.left, rectWnd.top);
+
+ bool bRet = PInvoke.UpdateLayeredWindow(hWnd,
+ hDCScreen, ptDest, new Windows.Win32.Foundation.SIZE(sizeBitmap.Width, sizeBitmap.Height),
+ hDCMem, ptSrc, new Windows.Win32.Foundation.COLORREF(0), bf, UPDATE_LAYERED_WINDOW_FLAGS.ULW_ALPHA);
+
+ PInvoke.ReleaseDC(Windows.Win32.Foundation.HWND.Null, hDCScreen);
+ }
+
+ private unsafe void CenterToScreen(IntPtr hWnd)
+ {
+ var rcWorkArea = new Windows.Win32.Foundation.RECT();
+ PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETWORKAREA, 0, &rcWorkArea, 0);
+ var h = new Windows.Win32.Foundation.HWND(hWnd);
+ PInvoke.GetWindowRect(h, out Windows.Win32.Foundation.RECT rc);
+ int nX = System.Convert.ToInt32((rcWorkArea.left + rcWorkArea.right) / (double)2 - (rc.right - rc.left) / (double)2);
+ int nY = System.Convert.ToInt32((rcWorkArea.top + rcWorkArea.bottom) / (double)2 - (rc.bottom - rc.top) / (double)2);
+ PInvoke.SetWindowPos(h, Windows.Win32.Foundation.HWND.Null, nX, nY, -1, -1, SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_FRAMECHANGED | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
+ private static Windows.Win32.Foundation.LRESULT Win32WndProc(Windows.Win32.Foundation.HWND hwnd,
+ uint msg, Windows.Win32.Foundation.WPARAM wParam, Windows.Win32.Foundation.LPARAM lParam)
+ {
+ return PInvoke.DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ }
+}
diff --git a/src/WinUIEx/SplashScreen.cs b/src/WinUIEx/SplashScreen.cs
index 3cf1b6d..5caa33a 100644
--- a/src/WinUIEx/SplashScreen.cs
+++ b/src/WinUIEx/SplashScreen.cs
@@ -7,9 +7,10 @@
namespace WinUIEx
{
///
- /// A splash screen window that shows with no chrome, and once has completed,
- /// opens a new window
+ /// A splash screen window for rendering XAML that shows with no chrome, and once has completed,
+ /// opens a new window.
///
+ ///
public class SplashScreen : Window
{
private Window? _window;
diff --git a/src/WinUIEx/WinUIEx.csproj b/src/WinUIEx/WinUIEx.csproj
index 28e6d66..51350d9 100644
--- a/src/WinUIEx/WinUIEx.csproj
+++ b/src/WinUIEx/WinUIEx.csproj
@@ -25,7 +25,7 @@
WinUIEx
WinUI Extensions
- - Workaround package bug in WebView2 nuget package. (Issue #188)
+ - Added `SimpleSplashScreen` for display application launch splash screens
@@ -37,7 +37,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/WinUIExSample/App.xaml.cs b/src/WinUIExSample/App.xaml.cs
index 5ba3758..db5a56b 100644
--- a/src/WinUIExSample/App.xaml.cs
+++ b/src/WinUIExSample/App.xaml.cs
@@ -23,8 +23,11 @@ public partial class App : Application
///
public App()
{
+#if !DISABLE_XAML_GENERATED_MAIN // With custom main, we'd rather want to do this code in main
if (WebAuthenticator.CheckOAuthRedirectionActivation())
return;
+ fss = SimpleSplashScreen.ShowDefaultSplashScreen();
+#endif
this.InitializeComponent();
int length = 0;
var sb = new System.Text.StringBuilder(0);
@@ -34,8 +37,9 @@ public App()
// Not a packaged app. Configure file-based persistence instead
WinUIEx.WindowManager.PersistenceStorage = new FilePersistence("WinUIExPersistence.json");
}
+
}
-
+ internal SimpleSplashScreen fss { get; set; }
///
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
@@ -45,7 +49,18 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
{
var window = new MainWindow();
var splash = new SplashScreen(window);
- splash.Completed += (s, e) => m_window = (WindowEx)e;
+ splash.Completed += (s, e) =>
+ {
+ m_window = (WindowEx)e;
+ };
+ splash.Activated += Splash_Activated;
+ }
+
+ private void Splash_Activated(object sender, WindowActivatedEventArgs args)
+ {
+ ((Window)sender).Activated -= Splash_Activated;
+ fss?.Hide(TimeSpan.FromSeconds(1));
+ fss = null;
}
private WindowEx m_window;
@@ -129,4 +144,26 @@ public void Clear()
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); // TODO
}
}
+
+#if DISABLE_XAML_GENERATED_MAIN
+ ///
+ /// Program class
+ ///
+ public static class Program
+ {
+ [global::System.STAThreadAttribute]
+ static void Main(string[] args)
+ {
+ if (WebAuthenticator.CheckOAuthRedirectionActivation(true))
+ return;
+ var fss = SimpleSplashScreen.ShowDefaultSplashScreen();
+ global::WinRT.ComWrappersSupport.InitializeComWrappers();
+ global::Microsoft.UI.Xaml.Application.Start((p) => {
+ var context = new global::Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext(global::Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread());
+ global::System.Threading.SynchronizationContext.SetSynchronizationContext(context);
+ new App() { fss = fss };
+ });
+ }
+ }
+#endif
}
diff --git a/src/WinUIExSample/WinUIExSample.csproj b/src/WinUIExSample/WinUIExSample.csproj
index 5b2bd76..258cacb 100644
--- a/src/WinUIExSample/WinUIExSample.csproj
+++ b/src/WinUIExSample/WinUIExSample.csproj
@@ -17,6 +17,7 @@
10.0.22621.38
+ DISABLE_XAML_GENERATED_MAIN;$(DefineConstants)
true