简体   繁体   中英

Why doesn't the WPF Progressbar Animation Start On Function Call?

I'm working on my first WPF project and I'm trying to add a progress bar for a lengthy function. I have added message boxes to notify me on function success/error. I need to use IsIndeterminate type progress bar. The RunWorkerAsync() line also gets called properly but then when the DoWork function call the lengthy function inside it, the animation doesn't work.When the function is over and the message-box pops up the animation works fine.

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;
}

BackgroundWorker is a tool to help abstract the handling of threads, but the code you have posted uses it in an odd way:

The event attached to it at DoWork ( worker_DoWorkPipeline_Click ) will run in a different thread than the UI, however since you have the entire method wrapped in an invoke to Dispatcher, all of aLongFunction will run on the same thread as the UI.

From your description of the error, the problem is that the progress bar stops animating, right? The reason for this is that the UI is not responsive whenever code is run on its thread using Dispatcher.Invoke.

The solution to this issue will be to remove the Invoke method from the worker_DoWorkPipeline_Click .

However if the code in SynchronizationSession accesses any UI objects in your project, you can expect an exception to occur for this since WPF UI objects generally can only be accessed on the same thread. You haven't provided the code for the function so we cannot tell from what you have provided.

If the SynchronizationSession, or anything else running within the Backgroundworker needs to update the UI, use Dispatcher.Invoke only for the part of the code that needs to update the UI . This will avoid blocking the UI for the entirety of the operation but only do so when necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM