Метка не меняет значение внутри цикла while - PullRequest
5 голосов
/ 07 августа 2010
private void button1_Click(object sender, RoutedEventArgs e)
{
        int i = 0;

        while (i < 500)
        {
            label1.Content = i.ToString();
        //  System.Threading.Thread.Sleep(2000);
            ++i;
        }
 }

Я пытаюсь обновлять содержимое метки каждый раз, когда переменная увеличивается, но происходит то, что содержимое метки изменяется только один раз и только после завершения цикла while.Я думал, что приращение переменной counter было настолько быстрым, что поток пользовательского интерфейса не мог его догнать, поэтому я хотел, чтобы поток простаивал в течение 2 секунд, надеясь, что значение label1 изменится в 500 раз.Это тоже не сработало.Почему?

Ответы [ 5 ]

7 голосов
/ 07 августа 2010

Как уже говорили другие, проблема в том, что вы блокируете поток пользовательского интерфейса.

Вместо того, чтобы использовать предложения с Application.DoEvents, я бы предложил использовать DispatcherTimer чтобы запланировать обновление.Пример кода:

DispatcherTimer timer = new DispatcherTimer();
timer.Tick += delegate
{
    label.Content = counter.ToString();
    counter++;
    if (counter == 500)
    {
        timer.Stop();
    }
};
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Start();

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

Вы могли бы использовать Timer (либо System.Threading.Timer, либо System.Timers.Timer), но в обоих случаях вам все равно придется вернуться обратно к ветке диспетчера.DispatcherTimer делает это проще - событие Tick всегда вызывается в потоке Dispatcher.

EDIT: Если вы действительно хотите получить эквивалент DoEvents, вот кодкоторый я проверял, работает, и основан на документах MSDN для Dispatcher.PushFrame, объясняющих, как сделать эквивалент Application.DoEvents, но из WPF:

public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrames), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrames(object f)
{
    ((DispatcherFrame)f).Continue = false;

    return null;
}

private void ButtonClick(object sender, RoutedEventArgs e)
{
    for (int i = 0; i < 500; i++)
    {
        label.Content = i.ToString();
        DoEvents();
    }
}

Я все ещехотя я бы не рекомендовал этого - WPF (например, Windows Forms) разработан на основе метода работы, основанного на событиях.

0 голосов
/ 03 апреля 2014

Я думаю, что вам нужно

Label.Update();
0 голосов
/ 07 августа 2010

Обновите свой метод так:

private void button1_Click(object sender, RoutedEventArgs e)
{
        int i = 0;

        while (i < 500)
        {
            label1.Content = i.ToString();
            //Update the UI
            Application.DoEvents();
        //  System.Threading.Thread.Sleep(2000);
            ++i;
        }
 }

Это самый простой способ сделать это. Но не предпочтительный. Предпочтительным будет использовать backgroundworker для такой необходимости.

0 голосов
/ 07 августа 2010

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

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

0 голосов
/ 07 августа 2010

Обработчик прерывает цикл обработки событий.

Один из вариантов - использовать метод Application.DoEvents, чтобы разрешить выполнение операций рисования. Обратите внимание, что это считается плохой практикой, как и Thread.Sleep, из-за которого пользовательский интерфейс не отвечает.

private void button1_Click(object sender, RoutedEventArgs e)
{
        int i = 0;

        while (i < 500)
        {
            label1.Content = i.ToString();
            System.Threading.Thread.Sleep(2000);
            Application.DoEvents();
            ++i;
        }
 }

Другой вариант - использовать Control.BeginInvoke для каждой операции label1.Content = ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...