强制多线程VB.NET类以单一形式显示结果 [英] Force multi-threaded VB.NET class to display results on a single form

查看:68
本文介绍了强制多线程VB.NET类以单一形式显示结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Windows窗体应用程序,该应用程序使用Shared类来容纳该应用程序的所有常见对象.设置类具有一组定期执行操作的对象,然后有一些有趣的事情,它们需要提醒主表单并对其进行更新.

I have a windows form application that uses a Shared class to house all of the common objects for the application. The settings class has a collection of objects that do things periodically, and then there's something of interest, they need to alert the main form and have it update.

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

I'm currently doing this through Events on the objects, and when each object is created, I add an EventHandler to maps the event back to the form. However, I'm running into some trouble that suggests that these requests aren't always ending up on the main copy of my form. For example, my form has a notification tray icon, but when the form captures and event and attempts to display a bubble, no bubble appears. However, if I modify that code to make the icon visible (though it already is), and then display the bubble, a second icon appears and displays the bubble properly.

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

Has anybody run into this before? Is there a way that I can force all of my events to be captured by the single instance of the form, or is there a completely different way to handle this? I can post code samples if necessary, but I'm thinking it's a common threading problem.

更多信息::我当前在表单的事件处理程序中使用Me.InvokeRequired,在这种情况下,它始终返回FALSE.另外,当我从此表单中看到它时创建的第二个托盘图标上没有上下文菜单,而真实"图标却包含了-有人在里面吗?

MORE INFORMATION: I'm currently using Me.InvokeRequired in the event handler on my form, and it always returns FALSE in this case. Also, the second tray icon created when I make it visible from this form doesn't have a context menu on it, whereas the "real" icon does - does that clue anybody in?

我要拔头发!这没那么难!

I'm going to pull my hair out! This can't be that hard!

解决方案:感谢nobugz的提示,它使我进入了我现在正在使用的代码(效果很好,尽管我不禁想到有一种更好的方法可以执行此操作).我在名为"IsPrimary"的表单中添加了一个私有布尔变量,并在表单构造函数中添加了以下代码:

SOLUTION: Thanks to nobugz for the clue, and it lead me to the code I'm now using (which works beautifully, though I can't help thinking there's a better way to do this). I added a private boolean variable to the form called "IsPrimary", and added the following code to the form constructor:

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

一旦设置了此变量并且构造函数完成,它将直接转到事件处理程序,我将以这种方式进行处理(注意:由于我要查找的表单是应用程序My.Application的主要表单, .OpenForms(0)满足了我的需要.如果我正在寻找非启动表单的第一个实例,则必须遍历直到找到它为止):

Once this variable is set and the constructor finishes, it heads right to the event handler, and I deal with it this way (CAVEAT: Since the form I'm looking for is the primary form for the application, My.Application.OpenForms(0) gets what I need. If I was looking for the first instance of a non-startup form, I'd have to iterate through until I found it):

    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线程.然后,它运行事件代码.我不喜欢这可能要打三个电话,但我想不出另一种方法来做.尽管有点麻烦,但它似乎工作得很好.如果有人有更好的方法,我很想听听!

First, it checks to see if it's running on the correct form - if it's not, then call the right form. Then it checks to see if the thread is correct, and calls the UI thread if it's not. Then it runs the event code. I don't like that it's potentially three calls, but I can't think of another way to do it. It seems to work well, though it's a little cumbersome. If anybody has a better way to do it, I'd love to hear it!

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

Again, thanks for all the help - this was going to drive me nuts!

推荐答案

我认为这也是一个线程问题.您是否在事件处理程序中使用Control.Invoke()?在调试应用程序时,.NET通常会捕获违规行为,但在某些情况下无法. NotifyIcon是其中之一,没有用于检查线程相似性的窗口句柄.

I think it is a threading problem too. Are you using Control.Invoke() in your event handler? .NET usually catches violations when you debug the app but there are cases it can't. NotifyIcon is one of them, there is no window handle to check thread affinity.

在OP更改问题后进行

经典的VB.NET陷阱是通过其类型名称引用Form实例.像Form1.NotifyIcon1.Something.使用线程时,这不能按预期方式工作.它将创建Form1类的 new 实例,而不使用现有实例.该实例是不可见的(从未调用Show()),否则将作为门钉而死,因为它运行在不会泵送消息循环的线程上.看到第二个图标出现是一个死掉的礼物.当您从线程中使用InvokeRequired = False时也会如此.

A classic VB.NET trap is to reference a Form instance by its type name. Like Form1.NotifyIcon1.Something. That doesn't work as expected when you use threading. It will create a new instance of the Form1 class, not use the existing instance. That instance isn't visible (Show() was never called) and is otherwise dead as a doornail since it is running on thread that doesn't pump a message loop. Seeing a second icon appear is a dead give-away. So is getting InvokeRequired = False when you know you are using it from a thread.

您必须使用对现有表单实例的引用.如果很难做到这一点(通常将"Me"作为参数传递给类构造函数),则可以使用Application.OpenForms:

You must use a reference to the existing form instance. If that is hard to come by (you usually pass "Me" as an argument to the class constructor), you can use Application.OpenForms:

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

这篇关于强制多线程VB.NET类以单一形式显示结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆