Пользовательский интерфейс по-прежнему не отвечает после использования control.begininvoke - PullRequest
2 голосов
/ 19 мая 2009

Я сделал приложение на C # winforms. Теперь у меня есть форма, в которой есть множество кнопок, которые вызывают огромные функции перебора, вывод которых я обновляю в текстовом поле. Я вызываю метод textbox.begininvoke (), которому я передаю делегат функции, которая обновляет текст в текстовом поле, однако, когда текст огромен, форма не отвечает, так как я не могу нажать на кнопку отмены. Нет ли способа, чтобы вся форма оставалась отзывчивой, а также происходило обновление? Я должен показать данные пользователю по мере его поступления, я не могу буферизовать все это и показать в конце. Я также пытался реализовать свой собственный буфер и показывать данные через определенные промежутки времени, что прекрасно работает с небольшим количеством текста, но в огромном количестве пользовательский интерфейс просто не отвечает. любая помощь? Спасибо

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

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

Ответы [ 3 ]

9 голосов
/ 19 мая 2009

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

Рассмотрим следующий код:

private void HardWork(object state)
{
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(500);
        SetText(i.ToString());
    }
}

private void SetText(string text)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new Action<string>(SetText), text);
    }
    else
    {
        textBox1.Text = text;
    }
}
private void Button_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(HardWork);            
}

Метод Button_Click начнет выполнение метода HardWork в отдельном потоке. HardWork выполнит некоторую обработку (смоделированную с помощью вызова Thread.Sleep), а затем вызовет метод для отображения некоторого прогресса. Внутри этого метода нам нужно проверить, находимся ли мы в потоке пользовательского интерфейса или нет. Если это не так, мы вызываем тот же метод, используя Invoke (или BeginInvoke), чтобы заставить его выполняться в потоке пользовательского интерфейса.

Обновление: если объем данных, генерируемых методом перебора чисел, очень велик, это, конечно, может негативно повлиять на скорость отклика пользовательского интерфейса. Например, если вы накапливаете большой объем текста в поточном методе и генерируете этот текст при каждом обновлении, это будет медленнее, чем просто вывод того, что изменилось с момента последнего обновления. То же самое касается текстового поля; вызов TextBox.AppendText только с новым текстом будет быстрее, чем повторное присвоение свойства TextBox.Text.

Трудно дать более подробные идеи о том, как решить вашу конкретную проблему, поскольку мы не видим, что на самом деле делает ваш код.

7 голосов
/ 19 мая 2009

Ты получил это задом наперед.

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

BeginInvoke - это просто «Выполните следующее в потоке, в котором изначально был создан элемент управления», который является вашим потоком пользовательского интерфейса.

Следовательно, BeginInvoke - это то, что вы должны использовать в своей цепочке обработки чисел для отправки обновлений в пользовательский интерфейс.

Надеюсь, это поможет

0 голосов
/ 19 мая 2009

Сделайте так, чтобы функция, которая обновляет текстовое поле, делала это по частям и вызывала DoEvents между каждым обновлением. То есть разбить строку и ...

update piece 1
DoEvents
update piece 2
DoEvents
...
update piece n
...