C # обновление и добавление значения текстового поля с использованием процесса backgroundworker - PullRequest
14 голосов
/ 21 июля 2010

У меня есть приложение ac # windows form, которое я скинул вместе.Это довольно просто: \

входные данные:

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

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

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

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, чтобы он обновлял пользовательский интерфейс во время его работы.

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

Ответы [ 4 ]

14 голосов
/ 21 июля 2010

Вы на правильном пути с фоновым работником.Вот пример, который я собрал, чтобы показать вам, как это сделать.Создайте новое приложение для Windows с Form1.Добавьте к нему 4 элемента управления: label1, backgroundWorker1, button1 и button2.Тогда используйте этот код позади.Затем вы можете использовать пользовательское состояние 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();
    }
}
7 голосов
/ 21 июля 2010

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

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

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

2 голосов
/ 21 июля 2010

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

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

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

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

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

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

1 голос
/ 21 июля 2010

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

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