Принудительно заставить многопоточный класс VB.NET отображать результаты в одной форме. - PullRequest
0 голосов
/ 08 ноября 2008

У меня есть приложение Windows Form, которое использует класс Shared для размещения всех общих объектов приложения. Класс settings имеет коллекцию объектов, которые периодически что-то делают, а затем есть кое-что интересное, им нужно предупредить основную форму и обновить ее.

В настоящее время я делаю это через События на объектах, и когда каждый объект создается, я добавляю EventHandler, чтобы отобразить событие обратно в форму. Тем не менее, я сталкиваюсь с некоторыми проблемами, из-за которых эти запросы не всегда попадают в основную копию моей формы. Например, моя форма имеет значок в области уведомлений, но когда форма захватывает и событие и пытается отобразить пузырь, пузырь не появляется. Однако, если я изменю этот код, чтобы сделать значок видимым (хотя он уже есть), а затем отобразить пузырь, появится второй значок и правильно отобразит пузырь.

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

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ: В настоящее время я использую Me.InvokeRequired в обработчике событий в моей форме, и в этом случае он всегда возвращает FALSE. Кроме того, второй значок в трее, созданный, когда я делаю его видимым из этой формы, не имеет контекстного меню, в то время как "реальный" значок делает - кто-нибудь в этом подсказывает?

Я собираюсь вырвать мои волосы! Это не может быть так сложно!

РЕШЕНИЕ : Спасибо nobugz за подсказку, и это привело меня к коду, который я сейчас использую (который прекрасно работает, хотя я не могу не думать, что есть лучший способ сделать это) , Я добавил частную логическую переменную в форму с именем «IsPrimary» и добавил следующий код в конструктор формы:

    Public Sub New()
        If My.Application.OpenForms(0).Equals(Me) Then
            Me.IsFirstForm = True
        End If
    End Sub

Как только эта переменная установлена ​​и конструктор завершает работу, она направляется прямо к обработчику событий, и я имею дело с этим следующим образом (CAVEAT: поскольку искомая форма является основной формой приложения, My.Application .OpenForms (0) получает то, что мне нужно. Если бы я искал первый экземпляр не запускаемой формы, мне пришлось бы перебирать, пока я ее не нашел):

    Public Sub EventHandler()
        If Not IsFirstForm Then
            Dim f As Form1 = My.Application.OpenForms(0)
            f.EventHandler()
            Me.Close()
        ElseIf InvokeRequired Then
            Me.Invoke(New HandlerDelegate(AddressOf EventHandler))
        Else
            ' Do your event handling code '
        End If
    End Sub

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

Опять же, спасибо за помощь - это сводило меня с ума!

Ответы [ 4 ]

3 голосов
/ 08 ноября 2008

Я думаю, что это тоже проблема с многопоточностью. Вы используете Control.Invoke () в вашем обработчике событий? .NET обычно обнаруживает нарушения при отладке приложения, но есть случаи, когда это невозможно. NotifyIcon является одним из них, нет дескриптора окна для проверки соответствия потоков.

Редактировать после изменения ОП вопрос:

Классическая ловушка VB.NET - ссылаться на экземпляр Form по имени его типа. Как Form1.NotifyIcon1.Something. Это не работает, как ожидалось, когда вы используете многопоточность. Он создаст новый экземпляр класса Form1, но не будет использовать существующий экземпляр. Этот экземпляр не виден (Show () никогда не вызывался) и в противном случае считается мертвым как doornail, так как он работает в потоке, который не качает цикл сообщений. Наблюдение за появлением второго значка - это мертвая распродажа. Так же получается InvokeRequired = False, когда вы знаете, что используете его из потока.

Вы должны использовать ссылку на существующий экземпляр формы. Если это трудно найти (обычно вы передаете «Я» в качестве аргумента конструктору класса), вы можете использовать Application.OpenForms:

  Dim main As Form1 = CType(Application.OpenForms(0), Form1)
  if (main.InvokeRequired)
    ' etc...
0 голосов
/ 03 декабря 2008

в c # это выглядит так:

private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.Invoke(StatusHandler, sender, e);
        }
        else
        {
          //do work
        }
    }
0 голосов
/ 08 ноября 2008

Вы должны посмотреть документацию по методу Invoke в форме. Это позволит вам заставить код, который обновляет форму, запускаться в потоке, который владеет формой (что он должен делать, формы Windows не являются потокобезопасными). Что-то вроде Частный делегат Sub UpdateStatusDelegate (ByVal newStatus as String)

Public sub UpdateStatus (ByVal newStatus as String) Если Me.InvokeRequired то Dim d As New UpdateStatusDelegate (AddressOf UpdateStatus) Me.Invoke (d, new Object () {newStatus}) еще Обновить статус формы Конец, если

Если вы предоставите некоторый пример кода, я был бы рад предоставить более специализированный пример.

Редактировать после того, как OP сказал, что они используют InvokeRequired.

Перед вызовом InvokeRequired убедитесь, что дескриптор формы создан, есть свойство HandleCreated, которое я считаю. InvokeRequired всегда возвращает false, если элемент управления в настоящее время не имеет дескриптора, это будет означать, что код не является потокобезопасным, даже если вы сделали правильную вещь, чтобы сделать это так. Обновите свой вопрос, когда узнаете. Некоторый пример кода тоже будет полезен.

0 голосов
/ 08 ноября 2008

Используйте Control.InvokeRequired, чтобы определить, что вы находитесь в нужном потоке, а затем используйте Control.Invoke, если вы не.

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