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

StackOverflow https://stackoverflow.com/questions/273639

Вопрос

У меня есть приложение Windows form, которое использует общий класс для размещения всех общих объектов для приложения.У класса 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

Как только эта переменная установлена и конструктор завершает работу, он направляется прямо к обработчику событий, и я разбираюсь с этим следующим образом (ПРЕДОСТЕРЕЖЕНИЕ:Поскольку форма, которую я ищу, является основной формой для приложения, 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

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

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

Это было полезно?

Решение

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

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

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

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

  Dim main As Form1 = CType(Application.OpenForms(0), Form1)
  if (main.InvokeRequired)
    ' etc...

Другие советы

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

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

Public sub UpdateStatus(обычно вводит newStatus как строку) Если требуется Me.invoker, то Затемните d Как New UpdateStatusDelegate(адрес UpdateStatus) Me.Invoke(d,новый объект() {newStatus}) Ещё 'Обновить статус формы Завершить, если

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

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

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

в c # это выглядит примерно так:

private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.Invoke(StatusHandler, sender, e);
        }
        else
        {
          //do work
        }
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top