Большие файлы Microsoft Project MPP и Primavera XER могут занимать от нескольких секунд до нескольких минут для разбора. Пока это происходит, простое приложение выглядит замороженным: нет индикатора прогресса, нет текста статуса, пользователь ничего не может сделать. Любому, кто загружает большие расписания, нужен способ показать, насколько продвинулась загрузка, и возможность остановить её, если она длится слишком долго.

Это руководство охватывает оба случая с Aspose.Tasks for .NET. Вы создадите обратный вызов прогресса, который будет сообщать текущую фазу и процент выполнения, подключите его к загрузке, обеспечите отзывчивость настольного пользовательского интерфейса, пока разбор выполняется в фоновом потоке, и отмените загрузку, если она превысит установленный временной бюджет.

Ключевые выводы

  • IProgressNotificationCallback.Notify(ProgressNotificationArgs) — единственный метод, который вы реализуете для получения прогресса загрузки из Aspose.Tasks for .NET.
  • ProgressNotificationArgs предоставляет CurrentStepName, CurrentStepProgress (0–100) и EstimatedTotalProgress (0–100).
  • Привяжите обратный вызов с помощью LoadOptions.ProjectLoadingCallback; он поддерживается для форматов MPP и XER.
  • Notify выполняется в потоке загрузки, поэтому необходимо передать значения в UI‑поток перед обновлением элемента управления.
  • Отмените медленную загрузку, установив LoadOptions.CancellationToken и вызвав Cancel() у его CancellationTokenSource из другого потока.
  • API прогресса работает в режиме оценки; лицензия лишь удаляет ограничения на размер файла и вывод.

Впервые работаете с библиотекой? Страница продукта Aspose.Tasks for .NET и документация полностью охватывают настройку и лицензирование.

Как отслеживать прогресс загрузки проекта в C#?

Реализуйте IProgressNotificationCallback, установите его в LoadOptions.ProjectLoadingCallback и загрузите файл через конструктор Project, принимающий LoadOptions. Минимальный рабочий код состоит из трёх частей: класса обратного вызова, экземпляра 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 и настроить проект?

Добавьте пакет NuGet Aspose.Tasks в проект .NET 6 или более поздней версии.

dotnet add package Aspense.Tasks

Или из консоли диспетчера пакетов Visual Studio:

Install-Package Aspose.Tasks

Обратный вызов прогресса работает без лицензии. Если вам нужно загружать файлы, превышающие ограничение размера в оценочной версии, или обрабатывать их без ограничений оценки, установите лицензию один раз при запуске:

using Aspose.Tasks;

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

Вы можете запросить бесплатную временную лицензию на Aspose temporary license page, а также узнать больше о библиотеке на странице продукта Aspose.Tasks for .NET product page.

Что предоставляет API IProgressNotificationCallback?

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 объявлен как sealed и наследуется от EventArgs. Все три его свойства только для чтения.

Как реализовать обратный вызов уведомления о прогрессе?

Создайте класс, реализующий IProgressNotificationCallback, и сделайте что‑то полезное с ProgressNotificationArgs внутри Notify. Держите метод быстрым, потому что он выполняется в потоке загрузки.

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 сообщают один и тот же общий результат.

Как загрузить проект с отслеживанием прогресса?

Установите ProjectLoadingCallback в объект LoadOptions, затем передайте его конструктору 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‑поток внутри обратного вызова, потому что 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.Invoke или Dispatcher.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 и читайте CurrentStepName, CurrentStepProgress и EstimatedTotalProgress внутри Notify. Добавьте CancellationToken, когда загрузки могут длиться достаточно долго, чтобы пользователь захотел их остановить.

Изучите документацию Aspose.Tasks for .NET и справочник API, чтобы узнать больше, а также скачайте бесплатную временную лицензию для неограниченной оценки.

Часто задаваемые вопросы

Нужна ли платная лицензия для использования API уведомления о прогрессе?
Нет. IProgressNotificationCallback работает в режиме оценки. Временная или платная лицензия только снимает ограничения оценки на размер файла и вывод; она не разблокирует API прогресса.

Работает ли тот же обратный вызов для файлов XER так же, как и для MPP?
Да. LoadOptions.ProjectLoadingCallback в настоящее время поддерживается для форматов MPP и XER. Та же реализация IProgressNotificationCallback обрабатывает оба случая.

Потокобезопасен ли обратный вызов прогресса?
Notify вызывается в потоке, который выполняет загрузку, а не в фоновом потоке. В настольном приложении перед обновлением индикатора прогресса передайте значения в UI‑поток с помощью Control.Invoke или Dispatcher.Invoke.

В чем разница между CurrentStepProgress и EstimatedTotalProgress? CurrentStepProgress — это процент завершения текущей фазы загрузки, например чтения задач или чтения ресурсов. EstimatedTotalProgress — это оценочный процент завершения всей операции. Оба являются целыми числами от 0 до 100.

Можно ли отменить загрузку изнутри обратного вызова? Нельзя сделать это непосредственно из Notify. Вместо этого задайте LoadOptions.CancellationToken токеном из CancellationTokenSource и вызовите Cancel() для этого источника из другого потока. Конструктор Project тогда останавливается и генерирует исключение, поэтому оберните его в try/catch.

Добавление обратного вызова замедляет загрузку?
Накладные расходы на обратный вызов незначительны, потому что Notify вызывается лишь в небольшом числе контрольных точек, а не для каждой записи. Тем не менее, держите метод легковесным, поскольку любая работа, выполненная внутри него, выполняется в потоке загрузки.

Читать далее