WPF, like most other UI frameworks, requires that you modify UI controls from the same thread on which they were created. This thread, often referred to as the "UI thread", is usually the application's main thread, i.e. the one where program execution started.
If you happen to subscribe to the SerialPort.DataReceived
event, you are facing a problem, because that event might be triggered on any (background) thread:
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread, attempting to modify some elements in the main thread, such as UI elements, could raise a threading exception. If it is necessary to modify elements in the main Form or Control, post change requests back using Invoke, which will do the work on the proper thread.
Meaning that inside your event handler, you may not directly manipulate any UI controls.
That is where WPF's Dispatcher.BeginInvoke
comes in: It helps you to get some code back on the UI thread. You pass it a delegate, and that delegate will be invoked on the UI thread. Therefore you are allowed to manipulate the UI inside that delegate.
Data binding is no real alternative to BeginInvoke
: Data binding also manipulates the UI, based on changes to some data object (or vice versa), but it does not automatically switch to the UI thread. This means that if you're using data binding, you must change the data context only if you're on the UI thread... so you need BeginInvoke
anyway.
That all being said, if the use of data binding makes your code simpler and easier to understand, by all means use it! But you might still need Dispatcher.BeginInvoke
:
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(..., () => dataContext.SomeProperty = serialPort...;
}