chimcham Ответов: 2

Шатер индикатора выполнения с запущенным процессом


Привет, пожалуйста, помогите мне с моей проблемой.

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

Поскольку я не хочу сообщать о прогрессе, именно поэтому я использую Marquee и for loop ниже-это просто пример процесса.

Я столкнулся с приведенной ниже ошибкой в
this.label1.Text += string.Format("{0}. This is a test.", i);


An exception 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException' occurred

Что я уже пробовал:

private void button1_Click(object sender, EventArgs e)
       {
           button1.Enabled = false;
           progressBar1.Style = ProgressBarStyle.Marquee;
           progressBar1.MarqueeAnimationSpeed = 50;

           BackgroundWorker bw = new BackgroundWorker();
           bw.DoWork += bw_DoWork;
           bw.RunWorkerCompleted += bw_RunWorkerCompleted;
           bw.RunWorkerAsync();
       }

       void bw_DoWork(object sender, DoWorkEventArgs e)
       {
           // INSERT TIME CONSUMING OPERATIONS HERE
           // THAT DON'T REPORT PROGRESS
           for (int i = 1; i <= 100; i++)
           {
               this.label1.Text += string.Format("{0}. This is a test.", i);

           }
           Thread.Sleep(100);
       }

       void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
       {
           progressBar1.MarqueeAnimationSpeed = 0;
           progressBar1.Style = ProgressBarStyle.Blocks;
           progressBar1.Value = progressBar1.Minimum;

           button1.Enabled = true;
           MessageBox.Show("Done!");
       }

Philippe Mori

Вы никогда не должны использовать стиль шатра, если можете вычислить процент... так как это дает меньше информации пользователю о прогрессе.

2 Ответов

Рейтинг:
1

Philippe Mori

Вы должны прочитать документацию по BackgroundWorker, прежде чем использовать его, чтобы правильно понять, как он работает и должен использоваться.

Класс BackgroundWorker (System.ComponentModel)[^]

Как сделать Потокобезопасные вызовы элементов управления Windows Forms[^]

Вот несколько подобных вопросов :
c# - CrossThreadMessagingException при использовании backgroundworker windows forms - переполнение стека[^]
многопоточность - проблема многопоточности C# - переполнение стека[^]

В вашем случае обычно требуется включить отчет о ходе выполнения, а затем реализовать обработчик для отчета о ходе выполнения.
BackgroundWorker.Метод ReportProgress (Int32) (System.ComponentModel)[^]

Обычно я вычислял процент внутри DSoWork обработчик и сообщает о прогрессе только в том случае, если процент (в виде целого числа) изменился. Таким образом, вы гарантируете, что не обновляете пользовательский интерфейс более 100 раз, что достаточно при отображении индикатора выполнения.

Кстати, есть версия, которая позволяет передавать UserState, который можно использовать, если вы хотите сообщить что-то еще, кроме процента. Это несколько проще и понятнее, чем использование Invoke непосредственно.

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


Рейтинг:
0

Garth J Lancaster

Я не уверен, почему вы устанавливаете progressBar1. Value = progressBar1. Maximum там - это может быть что-то, чего я не знаю о стиле шатра ..

progressBar1.BeginInvoke(
  new Action( () =>
    {
      progressBar1.Value = i;
    }
  ));


внутри вашего "цикла for" - я не вижу, где еще вы обновляете значение индикатора выполнения

[редактировать] .. почему бы не взять это

for (int i = 1; i <= 100; i++)
{
  this.label1.Text += string.Format("{0}. This is a test.", i);
  Thread.Sleep(10000);
}


и сделать это так

for (int i = 1; i <= 100; i++)
{
  this.label1.Text += string.Format("{0}. This is a test.", i);
  progressBar1.Value = i; 
  Thread.Sleep(10000);  // Not sure you need to delay for this long 
}


если только каким-то образом я не упустил то, что вы пытаетесь сделать/в чем ваша проблема

[/редактировать]


chimcham

Привет, сэр Гарт, я уже обновил свой код.. Пожалуйста помочь..

Garth J Lancaster

вы все еще не обновляете progressBar1. Value в цикле ! смотрите обновленное решение (через минуту после того, как я его отредактировал)

chimcham

Да, сэр, я не обновлял значение индикатора выполнения, потому что просто хочу использовать стиль шатра..

chimcham

Я столкнулся с новой ошибкой, сэр..

Garth J Lancaster

вы можете справиться с этой ошибкой с помощью BeginInvoke-see the strike through text и применить то же самое к label1. text

chimcham

Я попробовал это, Сэр, по вашему совету..
этикетка 1.BeginInvoke(
новое действие (() =>
{
for (int i = 1; i <= 1000; i++)
{
этот.этикетка 1.Текстовая строка.Формат ("{0}. Это тест.\n", я);

}
}
));
Нить.Сон(10000);

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

Philippe Mori

Очевидно, что если вы отправите 1000 обновлений подряд, пользовательский интерфейс замерзнет в течение этого времени, так как эти обновления будут поступать гораздо быстрее, чем пользовательский интерфейс будет обновляться!

Чтобы он работал, очевидно, что задержка должна быть внутри цикла (10 МС на каждой итерации вместо 10 секунд сразу в конце).

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

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