Обновление С# и добавление значения текстового поля с использованием процесса backgroundworker

У меня есть приложение с окнами windows С#, которое я сбрасывал вместе. Это довольно просто:\

входы:

  • текстовая строка
  • путь к исходной папке
  • путь к папке назначения
  • целочисленный счет

Приложение ищет текстовые файлы в исходной папке для введенной текстовой строки; если он найдет строку, он копирует этот файл и файл с тем же именем в папку назначения. Он делает это, однако, много раз на основе целочисленного ввода.

Итак, у меня есть кнопка, и в событии нажатия кнопки я вызываю

ProcessImages(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString());

который:

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages)
        {         
            int ImageCounter = 0;
            int MaxImages = Convert.ToInt32(strNumImages);

            DirectoryInfo di = new DirectoryInfo(SourceFolder);

            foreach (FileInfo fi in di.GetFiles("*.txt"))
            {
                if (fi.OpenText().ReadToEnd().Contains(DID))
                {
                    //found one!
                    FileInfo fi2 = new FileInfo(fi.FullName.Replace(".txt", ".tif"));
                    if (fi2.Exists)
                    {
                        try
                        {
                            tbOutput.Text += "Copying " + fi2.FullName + " to " + tbDest.Text + "\r\n";
                            fi2.CopyTo(tbDest.Text + @"\" + fi2.Name, true);
                            tbOutput.Text += "Copying " + fi.FullName + " to " + tbDest.Text + "\r\n";
                            fi.CopyTo(tbDest.Text + @"\" + fi.Name, true);

                            ImageCounter++;
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    }
                }

                if (ImageCounter >= MaxImages)
                    break;

            }

        }

Что происходит, так это то, что процесс выполняется нормально, но я хочу обновить текстовое поле в форме с прогрессом при копировании файлов. В основном форма затухает во время ее запуска, а после ее завершения вывод находится в текстовом поле. Я хотел бы реализовать BackgroundWorker, чтобы он обновил интерфейс во время его работы.

Я просмотрел примеры, но я действительно не слежу за ними. У меня нет процентного значения, я просто хочу обновить .Text изменяет каждую итерацию и отображает ее. Я даже не думаю, что мне обязательно нужно поставить фактическое действие копирования в разных потоках, просто звучит так, что нужно запускать отдельно от основного потока пользовательского интерфейса. Может быть, я слишком усложняю это... может кто-то подтолкнет меня в правильном направлении? Спасибо!

Ответ 1

Если вы используете фонового работника, вы можете использовать метод ReportProgress для возврата целого числа, такого как количество обработанных записей. Это не должен быть процент. Затем в обработчике ProgressChanged вы можете обновить свое текстовое поле. Например.

int count = e.ProgressPercentage;
textBox1.Text = string.Format("{0} images processed.", count);

Если вы не хотите использовать фонового работника, вы можете вызвать Application.DoEvents() внутри вашего цикла. Это даст UI возможность обновиться и ответить на действия пользователя. Но будьте осторожны - это сильно замедлит вашу программу, поэтому вы можете называть ее только на каждой 100-й итерации.

Ответ 2

Вы находитесь на правильном пути с фоновым работником. Вот пример, который я собрал, чтобы показать вам, как это сделать. Создайте новое приложение Windows с Form1. Добавьте к нему 4 элемента управления: label1, backgroundWorker1, button1 и button2. Затем используйте этот код. Затем вы можете использовать UserState ReportProgress, чтобы сообщать в основной поток все, что захотите. В этом примере я передаю строку. Обработчик события ProgressChanged затем находится в потоке пользовательского интерфейса и обновляет текстовое поле.

    public partial class Form1 : Form
{
    int backgroundInt;
    public Form1()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        label1.Text = e.UserState as string;
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        backgroundInt = 1;
        while (backgroundWorker1.CancellationPending == false)
        {
            System.Threading.Thread.Sleep(500);
            backgroundWorker1.ReportProgress(0, 
                String.Format("I found file # {0}!", backgroundInt));
            backgroundInt++;
        }
    }


    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        backgroundWorker1.CancelAsync();
    }
}

Ответ 3

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

Самое простое решение - принудительно обновить пользовательский интерфейс: попробуйте вызвать Update() в своей форме после изменения текстового поля внутри вашего цикла.

Ваше приложение по-прежнему будет заморожено в пользовательском интерфейсе (не реагирует на щелчки мыши и т.д.), но это должно хотя бы получить сообщения о ходе работы, отображаемые на экране. Если обновление дисплея - это все, что вам действительно нужно, остановитесь здесь.

Следующим уровнем решения было бы позволить вашему приложению обрабатывать ожидающие сообщения окна в вашем цикле обработки файлов. Вызовите Application.DoEvents() в своем цикле (вместо form.Update). Это позволит форме перерисовать себя с вашими обновлениями на выходе текста и устранит ваш замораживание UI - приложение может реагировать на действия с мышью и клавиатурой.

Однако будьте осторожны: пользователь может нажать кнопку, которая начала текущую активность, пока текущая активность продолжается - повторное включение. Вы должны как минимум отключить меню или кнопку, которая запускает длительную обработку файлов, чтобы предотвратить повторное размещение.

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

Ответ 4

Просто создайте (1) делегат, чтобы обернуть вызов метода ProcessImages, (2) отключить вызов с помощью делегата и (3), когда вы хотите обновить текстовое поле из вашего метода ProcessImages, проверьте наличие cross-thead и убедитесь, что вы сделали обновление из основного потока:

delegate void ProcessImagesDelegate(string did, string sourceFolder, string destFolder, string strNumImages);

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages)
{
    // do work

    // update textbox in form
    if (this.textBox1.InvokeRequired)
    {
        this.textBox1.Invoke(new MethodInvoker(delegate() { this.textBox1.Text = "delegate update"; }));
    }
    else
    {
        this.textBox1.Text = "regular update";
    }

    // do some more work
}

public void MyMethod()
{
    new ProcessImagesDelegate(ProcessImages).BeginInvoke(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString(), null, null);
}