Raise Events in .NET on the main UI thread
Your library could check the Target of each delegate in the event's invocation list, and marshal the call to the target thread if that target is ISynchronizeInvoke:
private void RaiseEventOnUIThread(Delegate theEvent, object[] args)
{
foreach (Delegate d in theEvent.GetInvocationList())
{
ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(args);
}
else
{
syncer.BeginInvoke(d, args); // cleanup omitted
}
}
}
Another approach, which makes the threading contract more explicit, is to require clients of your library to pass in an ISynchronizeInvoke or SynchronizationContext for the thread on which they want you to raise events. This gives users of your library a bit more visibility and control than the "secretly check the delegate target" approach.
In regard to your second question, I would place the thread marshalling stuff within your OnXxx or whatever API the user code calls that could result in an event being raised.
.NET raise event on UI thread
Here's a quick example using SynchronizationContext.
Note that it assumes you are creating the class from the main UI thread, and you store that current context from the constructor:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim d As New Duck("Bob")
AddHandler d.Quack, AddressOf duck_Quack
Label1.Text = "Waiting for Quack..."
d.RaiseThreadedQuack(3)
End Sub
Private Sub duck_Quack(source As Duck)
Label1.Text = "Quack received from: " & source.Name
End Sub
End Class
Public Class Duck
Public ReadOnly Property Name() As String
Private sc As WindowsFormsSynchronizationContext
Public Sub New(ByVal name As String)
Me.Name = name
sc = WindowsFormsSynchronizationContext.Current
End Sub
Public Event Quack(ByVal source As Duck)
Public Sub RaiseThreadedQuack(ByVal delayInSeconds As Integer)
Dim T As New Threading.Thread(AddressOf ThreadedQuack)
T.Start(delayInSeconds)
End Sub
Private Sub ThreadedQuack(ByVal O As Object)
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(O).TotalMilliseconds)
sc.Post(AddressOf RaiseUIQuack, Me)
End Sub
Private Sub RaiseUIQuack(ByVal O As Object)
RaiseEvent Quack(O)
End Sub
End Class
How can I raise an event on the Main UI thread from background task in UWP APP (vb.net)
I've not done any UWP development myself but I believe the correct way to do as you want is to use the SynchronizationContext
class, as in WPF. The principle is that the SynchronizationContext.Current
property will yield a thread-specific instance of the class, so you can get the value of that property on the UI thread and then use that object elsewhere to marshal a call to the owning thread. The property value is usually retrieved in the constructor of an object that is itself created on the UI thread, e.g.
Imports System.Threading
Public Class SomeClass
'Get the context for the thread on which the current instance is created.
Private uiContext As SynchronizationContext = SynchronizationContext.Current
Private Sub ThisMethodIsCalledOnASecondaryThread()
uiContext.Post(AddressOf ThisMethodIsCalledOnTheUIThread, Nothing)
End Sub
Private Sub ThisMethodIsCalledOnTheUIThread(data As Object)
'Execute UI thread logic here, e.g. raise an event.
End Sub
End Class
raise event in UI thread
I use MethodInvoker
, which has to be declared (because it does not exist under the compact framework), then include a link to the parent control (usually the main form):
class Publisher {
#if PocketPC // MethodInvoker delegate is not declared in CF!
public delegate void MethodInvoker();
#endif
private Form _parent;
public Publisher(Form parent) {
_parent = parent;
}
public event EventHandler OnEventHandler;
private void OnEvent() {
if (OnEventHandler != null) {
MethodInvoker mi = delegate { OnEventHandler(this, EventArgs.Empty); };
try {
_parent.BeginInvoke(methInvoker);
} catch (ObjectDisposedException) {
} catch (NullReferenceException err) {
LogError("OnEvent NullReference", err);
} catch (InvalidOperationException err) {
LogError("OnEvent InvalidOperation", err);
}
}
}
}
Hopefully, you are able to do something with that. ;)
~Joe
C# Properly raise event from background thread and handle it in the UI thread
Try to use BackgroundWorker. You'll be able to update your UI in RunWorkerCompleted
event.
Is this the proper way to inform the main thread of an event from a Background thread?
You write in the comments:
I was under the impression that because the instance was created on the main thread, the event would be called there too.
Objects in C# and .NET are not thread-affine by default. You need to implement this behavior manually. Creating a generic object on a thread doesn't cause that object to raise events on the thread the object was created on. Events are raised on the caller thread, if you don't provide a custom implementation that changes this.
With your current code, the StatusMessageEvent
events will be raised on the caller thread (the one that runs the StartListening
method).
If you have a GUI application (e.g. WPF, WinForms), you can manually marshal to the main thread when necessary.
class Program
{
static SynchronizationContext mainThreadContext;
static void Main()
{
// app initialization code here
// you could also do this somewhere in your main window
mainThreadContext = SynchronizationContext.Current;
}
static void MessengerEvent(object sender, EventArgs<string> e)
{
// do additional stuff here that can be done on a background thread
// The UpdateUI method will be executed on the UI thread
mainThreadContext.Post(_ => UpdateUI(), null);
}
static void UpdateUI() { }
}
Instead of the SynchronizationContext
, you can use Control.BeginInvoke
(WinForms) or Dispatcher.BeginInvoke
(WPF).
If you have a console app and for some reason need to marshal to the main thread... Well, then you need to implement your own SynchronizationContext
and something like a task scheduler or a dispatcher or a main loop. That's not easy.
Related Topics
Cannot Convert Lambda Expression to Type 'String' Because It Is Not a Delegate Type
Are Empty Interfaces Code Smell
Current Possibilities for Tracing Program Flow in C#
Convert List to Dictionary Using Linq and Not Worrying About Duplicates
Index of Currently Selected Row in Datagridview
Converting an Int[] to Byte[] in C#
How Better to Resolve Dependencies in Object Created by Factory
Dynamically Updating Tabcontrol Content at Runtime
Xml File Creation Using Xdocument in C#
Wpf Listview: Attaching a Double-Click (On an Item) Event
How to Create an Audit Trail with Entity Framework 5 and MVC 4
Forms' Does Not Exist in the Namespace System.Windows
Finding Property Differences Between Two C# Objects
Micro Optimization of a 4-Bucket Histogram of a Large Array or List