0

I have a windows form applications, and a class.

The class fires events in different threads, so that I need to do code of Control.InvokeRequired and Control.Invoke() in the functions in the form that receive the events.

Is it possible to make all the thread handling happen on the class, so that I do not need to do Invoke() in the form, and assume all coming events are already on the same thread ?

The solution I am considering, which I do not like, is to initiate a dummy control in the constructor of the class, or pass a reference to the form in the class, and check InvokeRequired against.

Any other possible solution ?

sharp12345
  • 4,420
  • 3
  • 22
  • 38
  • 1
    *"The solution I am considering, which I do not like, is to initiate a dummy control in the constructor of the class, or pass a reference to the form in the class, and check InvokeRequired against."* Agreed, I don't like that either, and I don't see why it would even be considered. What's wrong with checking `InvokeRequired` or just always using `Invoke`? Why is this a "problem" you feel the need to "solve"? – Ed S. Feb 23 '13 at 01:50
  • By default everything should run on the UI thread unless you explicitly create a new thread, task, or queue an item on the thread pool. Can you show the minimal amount of code to demonstrate this? – Despertar Feb 23 '13 at 01:51
  • @EdS. I want to wrap all the code in the class file, and leave the implementation as cleanly as possible in the form., just the way normal windows.forms events are handled. – sharp12345 Feb 23 '13 at 01:52
  • @Despertar `Thread t = new Thread(()=>{ raiseEvent(); }); t.Start();` – sharp12345 Feb 23 '13 at 01:54
  • Yeah if you are firing the event explicitly in a separate thread you have to use control.Invoke() to get back onto the UI thread when needed. Without telling it you want the work to be performed on the UI thread it will run in on the separate thread and you will get an exception. This doesn't change how anything is implemented, just how it is called. – Despertar Feb 23 '13 at 02:01
  • 1
    Maybe you can take advantage of some aspect oriented programming. I posted an answer on enforcing method calls to happen on the UI/main thread here: http://stackoverflow.com/questions/11183026/how-to-write-a-postsharp-invoke-aspect-to-simplify-cross-thread-control-updates Maybe it's something you can apply for this too? If you delegate the UI alterations to methods adorned with the special attribute, perhaps it can simplify usage that way? – Chris Sinclair Feb 23 '13 at 02:12

1 Answers1

1

In general a class should not get involved with UI implementation details. It may well be used in a scenario where it doesn't matter that the event is raised on another thread. In which case you certainly don't want to marshal. Nor would you know what to marshal to.

There's an excellent way to de-couple that in Winforms through the ISynchronizeInvoke interface, the FileSystemWatcher.SynchronizingObject is a good example of that. That property permits an application to choose whether to take care of the marshaling itself or to leave it up to FSW to do it. You make that look like this in an example class with a Foo event (VS2010 syntax):

Imports System.ComponentModel

Public Class Example
    Public Event Foo As EventHandler

    Public Property SynchronizingObject As ISynchronizeInvoke

    Protected Sub OnFoo(e As EventArgs)
        If SynchronizingObject IsNot Nothing AndAlso SynchronizingObject.InvokeRequired Then
            SynchronizingObject.BeginInvoke(Sub() RaiseEvent Foo(Me, e), Nothing)
        Else
            RaiseEvent Foo(Me, e)
        End If
    End Sub
End Class

A Form class can now simply assign itself to the SynchronizingObject property, like this:

Public Class Form1
    Private WithEvents obj As Example

    Public Sub New()
        InitializeComponent()
        obj.SynchronizingObject = Me
    End Sub

    Private Sub obj_Foo(sender As Object, e As EventArgs) Handles obj.Foo
        '' No marshaling required
        ''...
    End Sub
End Class
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536