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;
}
}