我有一个Windows窗体应用程序,它使用Shared类来容纳应用程序的所有公共对象。设置类有一组定期执行操作的对象,然后有一些感兴趣的东西,他们需要提醒主窗体并让它更新。

我目前正在通过对象上的事件来做这件事,并且当创建每个对象时,我添加一个EventHandler来将事件映射回到表单。但是,我遇到了一些麻烦,表明这些请求并不总是在我的表单的主副本上结束。例如,我的表单有一个通知托盘图标,但当表单捕获并发生事件并尝试显示气泡时,不会出现气泡。但是,如果我修改该代码以使图标可见(尽管已经显示),然后显示气泡,则会出现第二个图标并正确显示气泡。

以前有人碰到过吗?有没有办法可以强制我的所有事件都被表单的单个实例捕获,还是有一种完全不同的方式来处理它?我可以在必要时发布代码示例,但我认为这是一个常见的线程问题。

更多信息:我目前在我的表单上的事件处理程序中使用Me.InvokeRequired,在这种情况下它总是返回FALSE。此外,当我从该表单中看到它时创建的第二个托盘图标上没有上下文菜单,而“真实”图标则是“真实”。 icon确实 - 这对任何人都有疑问吗?

我要把头发拉出来!这不是那么难!

解决方案:感谢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

首先,它检查它是否在正确的表格上运行 - 如果不是,则调用正确的表格。然后它检查线程是否正确,如果不是则调用UI线程。然后它运行事件代码。我不喜欢它可能有三个电话,但我想不出另一种方法。它似乎运作良好,虽然它有点麻烦。如果有人有更好的方法,我很乐意听到它!

再次感谢所有的帮助 - 这会让我疯狂!

有帮助吗?

解决方案

我认为这也是一个线程问题。你在事件处理程序中使用Control.Invoke()吗? .NET通常会在您调试应用程序时捕获违规,但有些情况却无法解决。 NotifyIcon就是其中之一,没有窗口句柄可以检查线程关联。

OP改变问题后编辑:

经典的VB.NET陷阱是通过其类型名称引用Form实例。像Form1.NotifyIcon1.Something。当您使用线程时,这不会按预期工作。它将创建Form1类的 new 实例,而不是使用现有实例。该实例是不可见的(Show()从未被调用过)并且因为它在不抽取消息循环的线程上运行而作为一个doornail死了。看到第二个图标出现是一个死的赠品。当您知道从线程中使用它时,获取InvokeRequired = False就是这样。

您必须使用对现有表单实例的引用。如果很难得到(你通常将“我”作为参数传递给类构造函数),你可以使用Application.OpenForms:

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

其他提示

使用Control.InvokeRequired确定您是否在正确的线程上,如果不是,请使用Control.Invoke。

您应该查看表单上的Invoke方法的文档。它将允许您创建更新表单的代码在拥有该表单的线程上运行(它必须这样做,Windows表单不是线程安全的)。 就像是 Private Delegate 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,这意味着代码不是线程安全的,即使你做了正确的事情也是如此。当你发现问题时更新你的问题。一些示例代码也会有所帮助。

在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