Files
ShrlAlgoToolkit/NeoUI/Melskin/Controls/SplashWindow/Splash.cs

196 lines
5.6 KiB
C#
Raw Normal View History

2025-08-20 12:10:13 +08:00
using System.Diagnostics;
2025-08-12 23:08:54 +08:00
using System.IO.Packaging;
using System.Windows.Threading;
2026-01-02 17:30:30 +08:00
namespace VariaStudio.Controls;
2025-08-12 23:08:54 +08:00
2025-08-20 12:10:13 +08:00
/// <summary>
/// 提供显示和关闭启动画面的方法。
/// </summary>
2025-08-12 23:08:54 +08:00
public static class Splash
{
private static STAThread<SplashWindow>? Current { get; set; }
static Splash()
{
if (!UriParser.IsKnownScheme("pack"))
{
2025-08-20 12:10:13 +08:00
// 确保 PackUriHelper 的 UriSchemePack 已经注册
2025-08-12 23:08:54 +08:00
_ = PackUriHelper.UriSchemePack;
}
}
2025-08-20 12:10:13 +08:00
/// <summary>
/// 异步显示启动画面。
/// </summary>
/// <param name="imageUriString">启动画面图像的URI字符串。</param>
/// <param name="opacity">启动画面的不透明度默认为1.0(完全不透明)。</param>
/// <param name="completed">可选的完成回调,当启动画面关闭后调用。</param>
2025-08-12 23:08:54 +08:00
public static void ShowAsync(string imageUriString, double opacity = 1d, Action? completed = null)
{
var imageUri = new Uri(imageUriString);
2025-08-20 12:10:13 +08:00
Current = new STAThread<SplashWindow>(sta =>
2025-08-12 23:08:54 +08:00
{
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);
}
});
}
}
2025-08-20 12:10:13 +08:00
/// <summary>
/// 异步关闭启动画面。
/// </summary>
/// <param name="owner">拥有启动画面的窗口默认为null。</param>
/// <param name="forced">是否强制关闭启动画面默认为false。</param>
2025-08-12 23:08:54 +08:00
public static void CloseAsync(Window? owner = null, bool forced = false)
{
try
{
2025-08-20 12:10:13 +08:00
var current = Current?.Result;
2025-08-12 23:08:54 +08:00
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);
}
}
2025-08-20 12:10:13 +08:00
/// <summary>
/// 在指定窗口加载完成后关闭启动画面。
/// </summary>
/// <param name="owner">拥有启动画面的窗口。</param>
/// <param name="minimumMilliseconds">启动画面显示的最小毫秒数如果未达到该时间则延迟关闭默认为null。</param>
/// <param name="forced">是否强制关闭启动画面默认为false。</param>
/// <exception cref="ArgumentNullException">当<paramref name="owner"/>为null时抛出。</exception>
2025-08-12 23:08:54 +08:00
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)
{
2025-08-20 12:10:13 +08:00
var current = Current?.Result;
2025-08-12 23:08:54 +08:00
if (current != null)
{
2025-08-20 12:10:13 +08:00
var survivalMilliseconds = (DateTime.Now - current.TimeOfCtor).TotalMilliseconds;
2025-08-12 23:08:54 +08:00
if (survivalMilliseconds < minimumMilliseconds)
{
await Task.Delay((int)(minimumMilliseconds.Value - survivalMilliseconds));
}
}
}
CloseAsync(owner, forced);
}
}
private class STAThread<T> : STADispatcherObject, IDisposable where T : class
{
2025-08-20 12:10:13 +08:00
public Thread Value { get; set; }
2025-08-12 23:08:54 +08:00
public T Result { get; set; } = null!;
public STAThread(Action<STAThread<T>> start)
{
2025-08-20 12:10:13 +08:00
Value = new Thread(() =>
2025-08-12 23:08:54 +08:00
{
Dispatcher = Dispatcher.CurrentDispatcher;
start?.Invoke(this);
Dispatcher.Run();
})
{
IsBackground = true,
Name = $"STAThread<{typeof(T)}>",
};
Value.SetApartmentState(ApartmentState.STA);
}
public void Start()
{
2025-08-20 12:10:13 +08:00
Value.Start();
2025-08-12 23:08:54 +08:00
}
public void Forget()
{
Dispose();
}
public void Dispose()
{
if (typeof(IDisposable).IsAssignableFrom(typeof(T)))
{
try
{
((IDisposable?)Result)?.Dispose();
}
catch
{
2025-08-20 12:10:13 +08:00
// ignored
2025-08-12 23:08:54 +08:00
}
}
Dispatcher?.InvokeShutdown();
}
}
private class STADispatcherObject(Dispatcher dispatcher = null!)
{
public Dispatcher Dispatcher { get; set; } = dispatcher;
}
}