using System.Diagnostics; using System.IO.Packaging; using System.Windows.Threading; namespace Melskin.Controls; /// /// 提供显示和关闭启动画面的方法。 /// public static class Splash { private static STAThread? Current { get; set; } static Splash() { if (!UriParser.IsKnownScheme("pack")) { // 确保 PackUriHelper 的 UriSchemePack 已经注册 _ = PackUriHelper.UriSchemePack; } } /// /// 异步显示启动画面。 /// /// 启动画面图像的URI字符串。 /// 启动画面的不透明度,默认为1.0(完全不透明)。 /// 可选的完成回调,当启动画面关闭后调用。 public static void ShowAsync(string imageUriString, double opacity = 1d, Action? completed = null) { var imageUri = new Uri(imageUriString); Current = new STAThread(sta => { if (string.IsNullOrWhiteSpace(sta.Value.Name)) { sta.Value.Name = "Splash Thread"; } sta.Result = new SplashWindow(imageUri) { Opacity = opacity, }; sta.Result.Show(); }); Current.Start(); if (completed != null) { Task.Run(() => { try { Current.Value.Join(); completed?.Invoke(); } catch (Exception ex) { Debug.WriteLine(ex); } }); } } /// /// 异步关闭启动画面。 /// /// 拥有启动画面的窗口,默认为null。 /// 是否强制关闭启动画面,默认为false。 public static void CloseAsync(Window? owner = null, bool forced = false) { try { var current = Current?.Result; if (current == null) { return; } current.Closing += (_, _) => { owner?.Dispatcher.Invoke(() => { nint hwnd = new WindowInteropHelper(owner).Handle; //User32.SetForegroundWindow(hwnd); //User32.BringWindowToTop(hwnd); }); }; if (forced) { current.Shutdown(); } else { if (!current.AutoEnd) { current.StartEnd(); } } } catch (Exception ex) { Debug.WriteLine(ex); } } /// /// 在指定窗口加载完成后关闭启动画面。 /// /// 拥有启动画面的窗口。 /// 启动画面显示的最小毫秒数,如果未达到该时间则延迟关闭,默认为null。 /// 是否强制关闭启动画面,默认为false。 /// 为null时抛出。 public static void CloseOnLoaded(Window? owner = null, int? minimumMilliseconds = null, bool forced = false) { if (owner == null) { throw new ArgumentNullException(nameof(owner)); } owner.Loaded += OnLoaded; async void OnLoaded(object? sender, RoutedEventArgs e) { owner.Loaded -= OnLoaded; if (minimumMilliseconds != null) { var current = Current?.Result; if (current != null) { var survivalMilliseconds = (DateTime.Now - current.TimeOfCtor).TotalMilliseconds; if (survivalMilliseconds < minimumMilliseconds) { await Task.Delay((int)(minimumMilliseconds.Value - survivalMilliseconds)); } } } CloseAsync(owner, forced); } } private class STAThread : STADispatcherObject, IDisposable where T : class { public Thread Value { get; set; } public T Result { get; set; } = null!; public STAThread(Action> start) { Value = new Thread(() => { Dispatcher = Dispatcher.CurrentDispatcher; start?.Invoke(this); Dispatcher.Run(); }) { IsBackground = true, Name = $"STAThread<{typeof(T)}>", }; Value.SetApartmentState(ApartmentState.STA); } public void Start() { Value.Start(); } public void Forget() { Dispose(); } public void Dispose() { if (typeof(IDisposable).IsAssignableFrom(typeof(T))) { try { ((IDisposable?)Result)?.Dispose(); } catch { // ignored } } Dispatcher?.InvokeShutdown(); } } private class STADispatcherObject(Dispatcher dispatcher = null!) { public Dispatcher Dispatcher { get; set; } = dispatcher; } }