XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Collections.ObjectModel;
using System.Windows;
using XFEToolBox.Client.Models;

namespace XFEToolBox.Client.Utilities;

public static class ActivityCenterService
{
    private const int MaximumCompletedItems = 8;
    private static readonly ObservableCollection<ActivityItem> MutableItems = [];

    public static ReadOnlyObservableCollection<ActivityItem> Items { get; } = new(MutableItems);

    public static event EventHandler? Changed;

    public static int ActiveCount => MutableItems.Count(item => item.State is ActivityState.Pending or ActivityState.Running);

    public static ActivityHandle Start(string title, ActivityKind kind = ActivityKind.General, bool canCancel = false)
    {
        var item = new ActivityItem(Guid.NewGuid().ToString("N"), kind, title, canCancel)
        {
            State = ActivityState.Running
        };
        Dispatch(() =>
        {
            MutableItems.Insert(0, item);
            TrimCompletedItems();
            Changed?.Invoke(null, EventArgs.Empty);
        });
        return new ActivityHandle(item);
    }

    public static IReadOnlyList<ActivityItem> GetSnapshot(int maximumCount = 8) =>
        MutableItems.Take(Math.Max(0, maximumCount)).ToArray();

    public static string DescribeActiveItems() => string.Join(",\n", MutableItems
        .Where(item => item.State is ActivityState.Pending or ActivityState.Running)
        .Select(item => $"{item.Title} · {item.ProgressText}"));

    public static void ClearCompleted()
    {
        Dispatch(() =>
        {
            for (var index = MutableItems.Count - 1; index >= 0; index--)
                if (MutableItems[index].State is not (ActivityState.Pending or ActivityState.Running))
                    MutableItems.RemoveAt(index);
            Changed?.Invoke(null, EventArgs.Empty);
        });
    }

    internal static void NotifyChanged() => Dispatch(() =>
    {
        TrimCompletedItems();
        Changed?.Invoke(null, EventArgs.Empty);
    });

    internal static void Dispatch(Action action)
    {
        var dispatcher = Application.Current?.Dispatcher;
        if (dispatcher is null || dispatcher.CheckAccess()) action();
        else dispatcher.Invoke(action);
    }

    private static void TrimCompletedItems()
    {
        var completed = 0;
        for (var index = 0; index < MutableItems.Count; index++)
        {
            if (MutableItems[index].State is ActivityState.Pending or ActivityState.Running) continue;
            completed++;
            if (completed <= MaximumCompletedItems) continue;
            MutableItems.RemoveAt(index--);
        }
    }
}

public sealed class ActivityHandle : IDisposable
{
    private readonly ActivityItem item;
    private bool completed;

    internal ActivityHandle(ActivityItem item) => this.item = item;

    public ActivityItem Item => item;

    public CancellationToken CancellationToken => item.Token;

    public void Report(double? progress, string? message = null) => ActivityCenterService.Dispatch(() =>
    {
        if (completed) return;
        item.Progress = progress;
        if (message is not null) item.Message = message;
        ActivityCenterService.NotifyChanged();
    });

    public void Succeed(string? message = null) => Finish(ActivityState.Succeeded, message);

    public void Fail(string message) => Finish(ActivityState.Failed, message);

    public void Cancel(string? message = null) => Finish(ActivityState.Cancelled, message ?? "任务已取消");

    public void Dispose()
    {
        if (!completed) Succeed();
    }

    private void Finish(ActivityState state, string? message)
    {
        ActivityCenterService.Dispatch(() =>
        {
            if (completed) return;
            completed = true;
            item.State = state;
            item.Progress = state == ActivityState.Succeeded ? 100 : item.Progress;
            if (message is not null) item.Message = message;
            item.CompletedAt = DateTimeOffset.Now;
            item.CanCancel = false;
            ActivityCenterService.NotifyChanged();
        });
    }
}