WPF С# - обновить панель прогресса из другого потока

Я застрял, пытаясь обновить индикатор прогресса из других потоков, запущенных в другом классе. Чтобы объяснить, что я делаю, я думаю, что картина будет лучше. Я хочу обновить индикатор прогресса в точке//ЗДЕСЬ: enter image description here

Я пробовал использовать делегат, пробовал с ReportProgress, и я думаю, что я в основном пытался использовать все, что сообщается google в первых 100 результатах, без успеха. Я все еще изучаю WPF, и это может быть глупым путем, я ищу быстрый и грязный способ выполнить эту работу, но не стесняйтесь сказать мне, что я должен перепроектировать для более чистого приложения.

EDIT: больше кода.

В ExecutorWindow.xaml.cs:

public void RunExecutor()
{
    // CREATE BACKGROUNDWORKER FOR EXECUTOR
    execBackground.DoWork += new DoWorkEventHandler(execBackground_DoWork);
    execBackground.RunWorkerCompleted += new RunWorkerCompletedEventHandler(execBackground_RunWorkerCompleted);
    execBackground.ProgressChanged += new ProgressChangedEventHandler(execBackground_ProgressChanged);
    execBackground.WorkerReportsProgress = true;
    execBackground.WorkerSupportsCancellation = true;
    // RUN BACKGROUNDWORKER
    execBackground.RunWorkerAsync();
}
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
    myExecutor = new Executor(arg1, arg2);
    myExecutor.Run();            
}

private void execBackground_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    MessageBox.Show("RunWorkerCompleted execBackground");
}

private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    ExecutorProgressBar.Value += 1;
}

// TESTING 
private void updateProgressBar(int i)
{
    ExecutorProgressBar.Value += i;
}

public delegate void callback_updateProgressBar(int i);

В Executor.cs:

public void Run()
{
    string[] options = new string[2];
    int i = 0;

    while (LeftToRun > 0)
    {
        if (CurrentRunningThreads < MaxThreadsRunning)
        {
            BackgroundWorker myThread = new BackgroundWorker();
            myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
            myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
            myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
            myThread.WorkerReportsProgress = true;
            myThread.WorkerSupportsCancellation = true;

            myThread.RunWorkerAsync(new string[2] {opt1, opt2});

            // HERE ?
            CurrentRunningThreads++;
            i++;
            LeftToRun--;

        }
    }

    while (CurrentRunningThreads > 0) { }
    logfile.Close();
    MessageBox.Show("All Tasks finished");
}

private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
    string[] options = (string[])e.Argument;
    string machine = options[0];
    string script = options[1];
    // UPDATE HERE PROGRESSBAR ?
    RemoteProcess myRemoteProcess = new RemoteProcess(machine, script);
    string output = myRemoteProcess.TrueExec();
    // UPDATE HERE PROGRESSBAR ?
    this.logfile.WriteLine(output);
}

private void backgroundWorkerRemoteProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    CurrentRunningThreads--;
}

private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //myExecWindow.ExecutorProgressBar.Value = e.ProgressPercentage; // TESTING
    //ExecutorWindow.callback_updateProgressBar(1); // TESTING 
}

EDIT 2: Я понял! Простой на самом деле, но я предполагаю, что искал слишком близко, чтобы узнать.

В моем классе ExecutorWindow:

private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
    myExecutor = new Executor(arg1, arg2);
    myExecutor.Run(sender);
}

private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    ExecutorProgressBar.Value += 1;
}

И в классе Executor:

private BackgroundWorker myExecutorWindow;

[...]

public void Run(object sender)
{
            myExecutorWindow = sender as BackgroundWorker;
            string[] options = new string[2];
            int i = 0;

            while (LeftToRun > 0)
            {
                if (CurrentRunningThreads < MaxThreadsRunning)
                {
                    BackgroundWorker myThread = new BackgroundWorker();
                    myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
                    myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
                    myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
                    myThread.WorkerReportsProgress = true;
                    myThread.WorkerSupportsCancellation = true;

                    myThread.RunWorkerAsync(new string[2] {opt1, opt2});

                    CurrentRunningThreads++;
                    i++;
                    LeftToRun--;      
                }
            }

[...]

private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
            myBackgroundWorker.ReportProgress(1);
            // PROCESSING MY STUFF HERE
            myBackgroundWorker.ReportProgress(1);
        }

        private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            myExecutorWindow.ReportProgress(1);
        }

Спасибо!

Ответ 1

Я понял! Простой на самом деле, но я предполагаю, что искал слишком близко, чтобы узнать.

В моем классе ExecutorWindow:

private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
    myExecutor = new Executor(arg1, arg2);
    myExecutor.Run(sender);
}

private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    ExecutorProgressBar.Value += 1;
}

И в классе Executor:

private BackgroundWorker myExecutorWindow;

[...]

public void Run(object sender)
{
            myExecutorWindow = sender as BackgroundWorker;
            string[] options = new string[2];
            int i = 0;

            while (LeftToRun > 0)
            {
                if (CurrentRunningThreads < MaxThreadsRunning)
                {
                    BackgroundWorker myThread = new BackgroundWorker();
                    myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
                    myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
                    myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
                    myThread.WorkerReportsProgress = true;
                    myThread.WorkerSupportsCancellation = true;

                    myThread.RunWorkerAsync(new string[2] {opt1, opt2});

                    CurrentRunningThreads++;
                    i++;
                    LeftToRun--;      
                }
            }

[...]

private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
            myBackgroundWorker.ReportProgress(1);
            // PROCESSING MY STUFF HERE
            myBackgroundWorker.ReportProgress(1);
        }

        private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            myExecutorWindow.ReportProgress(1);
        }

Ответ 2

Вы можете запустить любой метод в потоке пользовательского интерфейса с помощью этого самого базового образца

this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() 
{       
   this.progressBar.Value= 20; // Do all the ui thread updates here
}));

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

Если вам действительно нужно иметь полный контроль над потоками фоновых потоков и основными (UI) потоковыми обновлениями, вот фантастический учебник по этому вопросу: http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html

Ответ 3

Вы должны использовать метод Dispatcher.Invoke

например.

    Dispatcher.Invoke(
        new System.Action(() => myProgressBar.Value = newValue)
        );

Ответ 4

Я нашел очень простое решение создать поток для запуска любого блока кода, а также обработать Invocation обратно в основной поток, чтобы изменить свойства управления. Он работает из коробки с .NET 4.5, и вызов лямбды на Диспетчере может быть адаптирован для работы с более ранними версиями .NET. Главное преимущество - это просто так бладно, просто и идеально, когда вам просто нужен быстрый поток для некоторого действительно базового кода.

Итак, предположим, что у вас есть индикатор выполнения где-то в вашем диалоговом окне, сделайте следующее:

progBar.Minimum = 0;
progBar.Maximum = theMaxValue;
progBar.Value = 0;

Dispatcher disp = Dispatcher.CurrentDispatcher;

new Thread(() => {
    // Code executing in other thread
    while (progBar.Value < theMaxValue)
    {
        // Your application logic here


        // Invoke Main Thread UI updates
        disp.Invoke(
            () =>
            {

                progBar.Value++;
            }
        );

    }
}).Start();

Вам также необходимо убедиться, что у вас есть ссылка на WindowsBase.dll

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