功能更新
This commit is contained in:
195
Melskin/Controls/SplashWindow/Splash.cs
Normal file
195
Melskin/Controls/SplashWindow/Splash.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user