Files
ShrlAlgoToolkit/Melskin/Controls/SplashWindow/Splash.cs
2026-02-17 22:17:13 +08:00

196 lines
5.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Diagnostics;
using System.IO.Packaging;
using System.Windows.Threading;
namespace Melskin.Controls;
/// <summary>
/// 提供显示和关闭启动画面的方法。
/// </summary>
public static class Splash
{
private static STAThread<SplashWindow>? Current { get; set; }
static Splash()
{
if (!UriParser.IsKnownScheme("pack"))
{
// 确保 PackUriHelper 的 UriSchemePack 已经注册
_ = PackUriHelper.UriSchemePack;
}
}
/// <summary>
/// 异步显示启动画面。
/// </summary>
/// <param name="imageUriString">启动画面图像的URI字符串。</param>
/// <param name="opacity">启动画面的不透明度默认为1.0(完全不透明)。</param>
/// <param name="completed">可选的完成回调,当启动画面关闭后调用。</param>
public static void ShowAsync(string imageUriString, double opacity = 1d, Action? completed = null)
{
var imageUri = new Uri(imageUriString);
Current = new STAThread<SplashWindow>(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);
}
});
}
}
/// <summary>
/// 异步关闭启动画面。
/// </summary>
/// <param name="owner">拥有启动画面的窗口默认为null。</param>
/// <param name="forced">是否强制关闭启动画面默认为false。</param>
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);
}
}
/// <summary>
/// 在指定窗口加载完成后关闭启动画面。
/// </summary>
/// <param name="owner">拥有启动画面的窗口。</param>
/// <param name="minimumMilliseconds">启动画面显示的最小毫秒数如果未达到该时间则延迟关闭默认为null。</param>
/// <param name="forced">是否强制关闭启动画面默认为false。</param>
/// <exception cref="ArgumentNullException">当<paramref name="owner"/>为null时抛出。</exception>
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<T> : STADispatcherObject, IDisposable where T : class
{
public Thread Value { get; set; }
public T Result { get; set; } = null!;
public STAThread(Action<STAThread<T>> 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;
}
}