Почему анимация WPF Progressbar не запускается при вызове функции? - PullRequest
0 голосов
/ 21 января 2020

Я работаю над своим первым проектом WPF и пытаюсь добавить индикатор выполнения для длинной функции. Я добавил сообщения, чтобы уведомить меня об успехе / ошибке функции. Мне нужно использовать индикатор прогресса типа IsIndeterminate. Строка RunWorkerAsyn c () также вызывается должным образом, но затем, когда функция DoWork вызывает длинную функцию внутри нее, анимация не работает. Когда функция завершена и всплывающее окно сообщения, анимация работает нормально.

private void ButtonPipeline_Click(object sender, RoutedEventArgs e)
{

    pbStatus.IsIndeterminate = true;
    BackgroundWorker worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += worker_DoWorkPipeline_Click;
    worker.RunWorkerCompleted += worker_RunWorkerCompleted;
    worker.RunWorkerAsync();

}


private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        MessageBox.Show("Sync Interrupted.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
    }
    else
    {
        pbStatus.IsIndeterminate = false;
        MessageBox.Show("Synced Completed.", "Sync Complete", MessageBoxButton.OK, MessageBoxImage.None);
        pbStatus.Value = 0;
    }
}

void worker_DoWorkPipeline_Click(object sender, DoWorkEventArgs e)
{
        this.Dispatcher.Invoke(() =>
        {
            var worker = sender as BackgroundWorker;

            try
            {
                var pipeline = GetPipeline(); //THis function throws "The calling thread cannot access this object because a different thread owns it"

                if (pipeline.Name.Equals("<None>"))
                {
                    MessageBox.Show("Please select a pipeline.", "Missing Data", MessageBoxButton.OK, MessageBoxImage.Warning);
                    worker.CancelAsync();
                }
                else
                {
                    aLongFunction(pipeline);
MessageBox.Show("Pipeline: " + pipeline + Environment.NewLine + "Successfully Synced.", "Sync Complete", MessageBoxButton.OK, MessageBoxImage.None);
                }

                if (worker.CancellationPending == true)
                {
                    pbStatus.IsIndeterminate = false;
                    pbStatus.Value = 0;
                    e.Cancel = true;
                    return;
                }

            }
            catch (Exception ex)
            {
                worker.CancelAsync();
                MessageBox.Show(ex.InnerException.ToString(), "Exception Occoured!", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        });
}

private void aLongFunction(Pipeline pipeline)
{
    var session = new SynchronizationSession(pipeline);

    session.Run();
    MessageBox.Show("Successfully Synced.", "Sync Complete", MessageBoxButton.OK, MessageBoxImage.None);

}

public void Run()
{
    anotherFunction();
}

private Pipeline GetPipeline()
{
            var pipeline = (Pipeline)DropdownSyncPipeline.SelectedItem; //This throws and error since trying to access another UI Object.
            if (null != pipeline)
            {
                if (0 == pipeline.Id)
                {
                    var p = PerfOtherPipeline.Text;
                    if (!string.IsNullOrEmpty(p)) pipeline = BrokerDataCache.Pipelines.Find(p_ => p.Equals(p_.Name));
                }
            }
   return pipeline;
}

1 Ответ

0 голосов
/ 21 января 2020

BackgroundWorker - это инструмент, помогающий абстрагировать обработку потоков, но опубликованный вами код использует его странным образом:

Событие, прикрепленное к нему в DoWork ( worker_DoWorkPipeline_Click ) будет работать в потоке, отличном от пользовательского интерфейса, однако, так как весь метод включен в вызов Dispatcher, все aLongFunction будут выполняться в том же потоке, что и пользовательский интерфейс.

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

Решение этой проблемы заключается в удалении метода Invoke из worker_DoWorkPipeline_Click .

Однако если код в SynchronizationSession обращается к каким-либо объектам пользовательского интерфейса в вашем проекте, вы можете ожидать исключения для этого, поскольку объекты пользовательского интерфейса WPF обычно доступны только в одном потоке. Вы не предоставили код для функции, поэтому мы не можем определить, из чего вы ее предоставили.

Если SynchronizationSession или что-либо еще, работающее в Backgroundworker, нуждается в обновлении пользовательского интерфейса, используйте Dispatcher.Invoke только для той части кода, которая требует обновления интерфейса пользователя . Это позволит избежать блокировки пользовательского интерфейса для всей операции, но делать это только при необходимости.

...