Почему не контролируется обновление / обновление в середине процесса

У меня есть форма Windows (C # .NET) с меткой состояния, которую я не могу обновить в середине процесса в методах обработчика событий. Мой код выглядит так ...

    void Process_Completed(object sender, EventArgs e)
    {

        string t = "Process is finished!";
        this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
    }

    void Process_Started(object sender, EventArgs e)
    {
        string t = "Process has begun";
        this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
    }

    private delegate void StatusLabelUpdator(string text);
    private void updateStatusLabel(string text)
    {
        StatusLabel1.Text = text;
        statusStrip1.Invalidate();
        statusStrip1.Refresh();
        statusStrip1.Update();
    }

Когда я запускаю код, после запуска процесса запускается метод Process_Started, а через пару секунд запускается метод Process_Completed. По какой-то причине я не могу заставить метку статуса когда-либо отображать «Процесс начался». Он всегда отображает только «Процесс завершен!». Как видите, я попытался сделать недействительным, обновить и обновить полосу состояния, содержащую метку состояния, но безуспешно. Я не могу вызвать update / refresh / invalidate для самой метки состояния, потому что эти методы для нее недоступны. Что я делаю неправильно?

ДОБАВЛЕННАЯ ИНФОРМАЦИЯ:

«Процесс» запускается нажатием кнопки на форме, которая вызывает метод в отдельном классе, который выглядит следующим образом:

public void DoSomeProcess()
{
    TriggerProcessStarted();
    System.Threading.Thread.Sleep(2000);   // For testing..
    TriggerProcessComplete();
}

и внутри методов TriggerProcessxxxx я инициирую события, используя этот код ...

var EventListeners = EH.GetInvocationList();    //EH is the appropriate EventHandler
if (EventListeners != null)
{
    for (int index = 0; index < EventListeners.Count(); index++)
    {
        var methodToInvoke = (EventHandler)EventListeners[index];
        methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, new object[] { });
    }
}

Наконец, я добавил Application.DoEvents() к методу updateStatusLabel, но это не помогло. Я по-прежнему получаю тот же результат. Вот мой способ обновления.

private void updateStatusLabel(string text)
{
    StatusLabel1.Text = text;
    statusStrip1.Refresh();
    Application.DoEvents(); 
}

Итак, я предполагаю, что «обработка» происходит в потоке пользовательского интерфейса, но обработчик событий вызывается в собственном потоке, который затем вызывает обновление элемента управления обратно в поток пользовательского интерфейса. Это глупый способ делать что-то? Примечание. Класс, содержащий метод DoSomeProcess (), находится в отдельной .NET ClassLibrary, на которую я ссылаюсь.


person PICyourBrain    schedule 26.02.2010    source источник


Ответы (1)


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

Ответ - выполнять длительную обработку в отдельном потоке . Хак (IMHO) заключается в использовании _1 _, чтобы поток пользовательского интерфейса выполнял некоторые действия пользовательского интерфейса во время обработки. Если вы поместите один из них после обновления этикетки и до того, как начнете обработку, шансы довольно высоки, что этикетка будет перекрашена. Но затем, во время обработки, никакие другие события рисования не могут быть обработаны (что приводит к полуотрисованным окнам, когда кто-то перемещает другое окно приложения поверх вашего приложения и обратно, и т. Д.). Поэтому я называю это хакерством (хотя, э-э, я, как известно, делал это :-)).

Изменить. Обновите на основе внесенных вами изменений:

Re

Итак, я предполагаю, что «обработка» происходит в потоке пользовательского интерфейса, но обработчик событий вызывается в собственном потоке ...

Я предполагаю, что DoSomeProcess запускается из потока пользовательского интерфейса (например, в прямом ответе на нажатие кнопки или подобное). Если да, то да, ваша обработка определенно выполняется в потоке пользовательского интерфейса. Поскольку TriggerProcessStarted запускает ваш обратный вызов асинхронно через BeginInvoke, вы не знаете, когда он будет запущен, но в любом случае ваш код немедленно запускается в обработку, никогда не уступая, поэтому никто другой не сможет возьми эту нить. Поскольку это поток пользовательского интерфейса, вызов делегата будет заблокирован для Invoke вызова, устанавливающего текст метки, после чего он должен ждать потока пользовательского интерфейса (который занят обработкой). (И это при условии, что это запланировано в другом потоке; я не мог на 100% убедить себя в любом случае, потому что у Microsoft есть два разных BeginInvoke, которые, как признал один из дизайнеров IIRC, были действительно глупой идеей, и это было давно так как я боролся с этим материалом.)

Если вы делаете TriggerProcessStarted вызовы обратных вызовов синхронными, все будет в порядке. Но в идеале следует запланировать обработку (если она не выполняет пользовательский интерфейс) вместо этого в отдельном потоке.

person T.J. Crowder    schedule 26.02.2010
comment
Я пробовал твой взлом, но это не помогло. Я добавил больше деталей к вопросу выше. - person PICyourBrain; 26.02.2010
comment
ПОСЛЕ РЕДАКТИРОВАНИЯ: Вы правы. Я оставил BeginInvoke в покое, но изменил событие нажатия кнопки, чтобы запустить DoSomeWork в собственном потоке. Это исправило! - person PICyourBrain; 26.02.2010