简体   繁体   中英

Backgroundworker doesn't update progress bar in separate window

I got this to work in Indeterminate mode, but wanted to show actual progress.

When I try to update from within the BackgroundWorker, I get the error "The calling thread cannot access this object because a different thread owns it." However, the examples I see seem to be doing what I'm trying to do. Most of the tutorials use a progress bar in the main window, I want a separate window that appears while the action is happening. This is.Net 4

Progress Bar Window

<Window x:Class="WpfTest.ProgressBarPopup"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="ProgressBarPopup" Height="100" Width="300">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Height="20" x:Name="UpdateProgress" Value="10" IsIndeterminate="False" />
        <!-- {Binding Path=PercentDone}-->
    </Grid>
</Window>

Code behind:

namespace WpfTest
{
    /// <summary>
    /// Interaction logic for ProgressBarPopup.xaml
    /// </summary>
    public partial class ProgressBarPopup : Window
    {
        public ProgressBarPopup()
        {
            InitializeComponent();
            UpdateProgress.Value = 60;
        }
    }
}

Main window: (I deleted some stuff about an ObservableCollection to unclutter the code.)

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel; // for background task
using System.Threading;    // for thread.sleep

namespace WpfTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {    
        ProgressBarPopup bar = new ProgressBarPopup();

        bool barExists = true;

        public MainWindow()
        {
            InitializeComponent();    
        }

        private void Progress_Click(object sender, RoutedEventArgs e)
        {
            if (!barExists)
            {
                bar = new ProgressBarPopup();
                barExists = false;
            }

            bar.Show();
#if true
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunCompleted);

            worker.RunWorkerAsync();
#else       
            // this simple method of doing work in foreground doesn't work with progress bars
            for (int i = 0; i < 50; i++)
            {
                Thread.Sleep(100);
            }
            bar.Hide();
            bar.Close();  // need to clean up
#endif
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            for (int i = 0; i < 50; i++)
            {
                bar.UpdateProgress.Value = i*2;       //  <<<=== gives Error here 
                Thread.Sleep(100);
            }
        }

        void worker_RunCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bar.Hide();
            bar.Close();
            barExists = false;
        }
    }
}

You can't access the UI thread from the DoWork() method since the code in that method is running on a separate thread. You must use the BackgroundWorker.ProgressChanged Event and BackgroundWorker.ReportProgress Method for that...

You will have to invoke the controls like so. Form controls run on there own thread. Since the background worker runs separately it does not have the sufficient privileges to access that control. Please Look at the MethodInvoker to separate your controls from your background worker.

void worker_RunCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
           _RunCompleted();
        }


private void _RunCompleted(){
MethodInvoker action = delegate
            {
           bar.Hide();
            bar.Close();
            barExists = false;
};
}

Only the Progress Changed of the backgroundworker is thread safe so you should do any changes to the UI from the progress changed but not limited to UI only.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel; // for background task
using System.Threading;    // for thread.sleep

namespace WpfTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {    
        ProgressBarPopup bar = new ProgressBarPopup();

        bool barExists = true;

        public MainWindow()
        {
            InitializeComponent();    
        }

        private void Progress_Click(object sender, RoutedEventArgs e)
        {
            if (!barExists)
            {
                bar = new ProgressBarPopup();
                barExists = false;
            }

            bar.Show();
#if true
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunCompleted);

            worker.RunWorkerAsync();
#else       
            // this simple method of doing work in foreground doesn't work with progress bars
            for (int i = 0; i < 50; i++)
            {
                Thread.Sleep(100);
            }
            bar.Hide();
            bar.Close();  // need to clean up
#endif
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            for (int i = 0; i < 50; i++)
            {
                worker.reportprogress(i)
                Thread.Sleep(100);
            }
        }

       void worker_ProgressChanged(object sender, DoWorkEventArgs e)

{
    int i=e.progresschanged
     bar.UpdateProgress.Value = i*2;       //  <<<=== gives Error here 
}

        void worker_RunCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bar.Hide();
            bar.Close();
            barExists = false;
        }
    }
}

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