大型 Microsoft Project MPP 和 Primavera XER 文件的解析可能需要几秒到几分钟不等。在解析期间,普通应用程序看起来像是卡住了:没有进度条、没有状态文本,用户无法进行任何操作。加载大型进度表的用户需要一种方式来显示加载的进度,并且需要一种方式来停止运行时间过长的加载。

本指南同时涵盖 Aspose.Tasks for .NET。您将构建一个进度回调,用于报告当前阶段和百分比,将其绑定到加载过程,在解析在后台线程运行时保持桌面 UI 的响应性,并在加载超出时间预算时取消加载。

关键要点

  • IProgressNotificationCallback.Notify(ProgressNotificationArgs) 是您实现的唯一方法,用于从 Aspose.Tasks for .NET 接收加载进度。
  • ProgressNotificationArgs 公开 CurrentStepNameCurrentStepProgress(0–100)和 EstimatedTotalProgress(0–100)。
  • 使用 LoadOptions.ProjectLoadingCallback 附加回调;它支持 MPP 和 XER 格式。
  • Notify 在加载线程上运行,因此在更新控件之前需要将值调度到 UI 线程。
  • 通过设置 LoadOptions.CancellationToken 并在另一个线程中调用其 CancellationTokenSourceCancel() 来取消慢速加载。
  • 进度 API 在评估模式下工作;许可证仅移除文件大小和输出限制。

刚接触该库吗?Aspose.Tasks for .NET 产品页面文档 完整地介绍了设置和许可。

如何在 C# 中监视项目加载进度?

实现 IProgressNotificationCallback,将其设置在 LoadOptions.ProjectLoadingCallback 上,并通过接受 LoadOptionsProject 构造函数加载文件。最小可工作代码包括三部分:回调类、一个 LoadOptions 实例以及加载调用。

  • 输入: 一个 MPP 或 XER 文件,例如 BigProject.mpp
  • 输出: 解析期间的进度更新 — CurrentStepName, CurrentStepProgress, EstimatedTotalProgress
  • 库: Aspose.Tasks for .NET
  • 语言: C#
using System;
using Aspose.Tasks;

internal sealed class ConsoleProgressCallback : IProgressNotificationCallback
{
    public void Notify(ProgressNotificationArgs args)
    {
        Console.WriteLine(
            "{0}: step {1}% / total {2}%",
            args.CurrentStepName,
            args.CurrentStepProgress,
            args.EstimatedTotalProgress);
    }
}

internal static class Program
{
    private static void Main()
    {
        // A license is optional here. Uncomment to remove evaluation limits.
        // new License().SetLicense("Aspose.Tasks.lic");

var loadOptions = new LoadOptions
        {
            ProjectLoadingCallback = new ConsoleProgressCallback()
        };

var project = new Project("BigProject.mpp", loadOptions);

Console.WriteLine("Loaded {0} top-level tasks.", project.RootTask.Children.Count);
    }
}

当文件被解析时,Notify 会运行多次,控制台会显示阶段和百分比。构造函数返回后,Project 对象已完全加载并可使用。

为什么加载进度监控对大文件很重要?

它用可见的反馈取代了看似无响应的暂停,这就是一个看起来已损坏的应用程序与一个看起来正在忙碌的应用程序之间的区别。文件越大,解析时间越长,盲目加载对感知质量的影响就越大。

进度值的具体用途:

  • 使用 EstimatedTotalProgress 驱动确定性的进度条,而不是显示不确定的旋转指示器。
  • 记录 CurrentStepName 的转换,以便在分析慢文件时找出哪个阶段占用了大部分加载时间。
  • 通过在后台线程加载并将进度发布到 UI 线程,保持 UI 的响应性。
  • 在批处理工具中显示每个文件的进度,该工具按顺序转换或迁移多个项目。

如何安装 Aspose.Tasks 并设置项目?

将 Aspose.Tasks NuGet 包添加到 .NET 6 或更高版本的项目中。

dotnet add package Aspense.Tasks

或者,从 Visual Studio Package Manager Console:

Install-Package Aspose.Tasks

进度回调在未授权的情况下也能工作。如果需要加载超过评估大小限制的文件或在没有评估限制的情况下处理,请在启动时设置一次许可证:

using Aspose.Tasks;

var license = new License();
license.SetLicense("Aspose.Tasks.lic");

您可以从 Aspose 临时许可证页面 请求免费临时许可证,并在 Aspose.Tasks for .NET 产品页面 上了解更多关于该库的信息。

IProgressNotificationCallback API 提供了什么?

API 是一个接口、一个方法和一种参数类型,全部位于 Aspose.Tasks 命名空间。

成员类型描述
IProgressNotificationCallback.Notify(ProgressNotificationArgs)void在长时间运行的项目操作期间调用,以报告进度。
ProgressNotificationArgs.CurrentStepNamestring (get)操作当前阶段的名称。
ProgressNotificationArgs.CurrentStepProgressint (get)当前阶段的预计完成百分比,0–100。
ProgressNotificationArgs.EstimatedTotalProgressint (get)整个操作的预计完成百分比,0–100。
LoadOptions.ProjectLoadingCallbackIProgressNotificationCallback (get/set)在加载期间调用的回调。支持 MPP 和 XER。
LoadOptions.CancellationTokenSystem.Threading.CancellationToken (get/set)用于取消正在进行的加载的令牌。

ProgressNotificationArgs 是密封的,并且派生自 EventArgs。它的三个属性都是只读的。

如何实现进度通知回调?

创建一个实现 IProgressNotificationCallback 的类,并在 Notify 中对 ProgressNotificationArgs 做一些有用的处理。保持该方法执行快速,因为它在加载线程上运行。

using System;
using Aspose.Tasks;

internal sealed class ConsoleProgressCallback : IProgressNotificationCallback
{
    private int lastTotal = -1;

public void Notify(ProgressNotificationArgs args)
    {
        // Only redraw when the total percentage actually changes.
        if (args.EstimatedTotalProgress == lastTotal)
        {
            return;
        }

lastTotal = args.EstimatedTotalProgress;
        Console.WriteLine("[{0,3}%] {1}", args.EstimatedTotalProgress, args.CurrentStepName);
    }
}

lastTotal 的检查可防止在多个 Notify 调用报告相同总计时,控制台被重复行淹没。

如何在加载项目时进行进度跟踪?

LoadOptions 对象上设置 ProjectLoadingCallback,然后将其传递给 Project 构造函数。加载是同步运行的;当构造函数返回时,解析已完成。

using System;
using Aspose.Tasks;

internal static class Loader
{
    public static Project Load(string path)
    {
        var loadOptions = new LoadOptions
        {
            ProjectLoadingCallback = new ConsoleProgressCallback()
        };

// Same call for MPP and XER; only the file path changes.
        var project = new Project(path, loadOptions);

Console.WriteLine("Done. Root task children: {0}", project.RootTask.Children.Count);
        return project;
    }
}

想在自己的时间表上尝试吗? 下载免费临时许可证 以在文件大小的评估限制之外进行加载。

如何在加载时保持 UI 响应?

在后台线程上加载,并在回调内部将进度值编排到 UI 线程,因为 Notify 在调用 Project 构造函数的线程上运行。

using System.Windows.Forms;
using Aspose.Tasks;

internal sealed class ProgressBarCallback : IProgressNotificationCallback
{
    private readonly ProgressBar bar;

public ProgressBarCallback(ProgressBar bar) => this.bar = bar;

public void Notify(ProgressNotificationArgs args)
    {
        if (bar.InvokeRequired)
        {
            bar.BeginInvoke(() => bar.Value = args.EstimatedTotalProgress);
        }
        else
        {
            bar.Value = args.EstimatedTotalProgress;
        }
    }
}

在 WPF 中,使用 Dispatcher.InvokeDispatcher.BeginInvoke 来替代 Control.BeginInvoke

如何取消长时间运行的加载?

LoadOptions.CancellationToken 设置为来自 CancellationTokenSource 的令牌,然后在另一个线程上调用该源的 Cancel()。Aspose.Tasks 停止解析,并且 Project 构造函数会抛出异常,因此请将其包装在 try/catch 中。

using System;
using System.Threading;
using Aspose.Tasks;

var cts = new CancellationTokenSource();

var loadOptions = new LoadOptions
{
    ProjectLoadingCallback = new ConsoleProgressCallback(),
    CancellationToken = cts.Token
};

// Wire a Cancel button or a timeout to this:
// cts.CancelAfter(TimeSpan.FromSeconds(30));

try
{
    var project = new Project("BigProject.xer", loadOptions);
    Console.WriteLine("Loaded {0} tasks.", project.RootTask.Children.Count);
}
catch (Exception ex)
{
    Console.WriteLine("Load stopped: {0}", ex.Message);
}

结论

在 Aspose.Tasks for .NET 应用程序中添加加载进度是一个小改动,却能显著提升感知的响应性。实现 IProgressNotificationCallback,将其分配给 LoadOptions.ProjectLoadingCallback,并在 Notify 中读取 CurrentStepNameCurrentStepProgressEstimatedTotalProgress。当加载可能持续足够长的时间以至于用户可能想要停止时,添加 CancellationToken

探索 Aspose.Tasks for .NET 文档 并查看 API 参考 以进一步了解,且 下载免费临时许可证 进行无限制评估。

常见问题

我需要付费许可证才能使用进度通知 API 吗? 不。IProgressNotificationCallback 在评估模式下可用。临时或付费许可证仅会移除文件大小和输出的评估限制;它不会解锁进度 API。

相同的回调也适用于 XER 文件和 MPP 吗? 是的。LoadOptions.ProjectLoadingCallback 目前支持 MPP 和 XER 格式。相同的 IProgressNotificationCallback 实现可处理两者。

进度回调是线程安全的吗? Notify 在执行加载的线程上调用,而不是在后台线程上。在桌面应用程序中,在操作进度条之前,使用 Control.InvokeDispatcher.Invoke 将值调度到 UI 线程。

CurrentStepProgress 和 EstimatedTotalProgress 之间有什么区别? CurrentStepProgress 是加载当前阶段的完成百分比,例如读取任务或读取资源。EstimatedTotalProgress 是对整个操作的预计完成百分比。两者都是 0 到 100 的整数。

我可以在回调内部取消加载吗? 不能直接在 Notify 内部进行。相反,需将 LoadOptions.CancellationToken 设置为来自 CancellationTokenSource 的令牌,并在另一个线程中调用该源的 Cancel()。随后 Project 构造函数会停止并抛出异常,因此请将其放在 try/catch 中。

添加回调会减慢加载吗? 回调开销可以忽略不计,因为 Notify 只在少量检查点被调用,而不是对每条记录调用。不过仍需保持该方法轻量,因为在其中执行的任何工作都会在加载线程上运行。

阅读更多