Accessing UI (Main) Thread Safely in Wpf

Accessing UI (Main) Thread safely in WPF

You can use

Dispatcher.Invoke(Delegate, object[])

on the Application's (or any UIElement's) dispatcher.

You can use it for example like this:

Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

or

someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

WPF Access window created on different UI thread

I simulated your issue using two WPF Window objects and a timer to ensure that the Second Window was created before calling operations on it. Below is my code sample and it updates the second Windows TextBox every five seconds:

    private Timer _timer;
private SecondWindow _secondWindow;

public MainWindow()
{
InitializeComponent();
CreateVisualisationWindow();
_timer = new Timer(Callback);
_timer.Change(5000, 5000);
}

private void Callback(object state)
{
UpdateSecondWindowText();
}

private void CreateVisualisationWindow()
{
Thread VisualisationWIndowThread = new Thread(ThreadStartingPoint);
VisualisationWIndowThread.SetApartmentState(ApartmentState.STA);
VisualisationWIndowThread.IsBackground = true;
VisualisationWIndowThread.Start();
}

private void ThreadStartingPoint()
{
_secondWindow = new SecondWindow();
_secondWindow.SecondWindowTextBlock.Text = "Hello";
_secondWindow.Show();
Dispatcher.Run();
}

private void UpdateSecondWindowText()
{
_secondWindow.Dispatcher.BeginInvoke(new Action(() =>
{
_secondWindow.SecondWindowTextBlock.Text = _secondWindow.SecondWindowTextBlock.Text + " World";
}));
}

So the trick is, you need to call the Dispatcher on the second Window in order to gain access to it.

How to access c# WPF control in thread safe way?

You simply want to use the Dispatcher.Invoke method (or the asynchronous equivalent Dispatcher.BeginInvoke), which will marshal the call to the main WPF UI thread.

The DependencyObject class contains a Dispatcher property, which means all controls and other objects which inherit from this class also provide this property, in a way similar to WinForms. In addition, the Application object provides access to the dispatcher.

An example usage might be the following (in code-behind of a Window/UserControl):

this.Dispatcher.Invoke((Action)(() =>
{
...
}));

Accessing UI from outside thread in Winforms

You MUST access your UI from your UI Thread only. But you can use the Control.Invoke method - a Form IS a Control - to ensure your code is run from the UI Thread.

Also, you have a static BindingList, but I assume you want to display the contents of that list in an specific form. You should make the BindingList an instance member instead... or get a reference to a valid Form. The Control.Invoke method is not static.

There are several ways to do so. I would do it like so:

First, create a method in your Form class that adds the record to the list.

public void AddRecord(Record r) {
if(this.InvokeRequired) {
this.Invoke(new MethodInvoker(() => this.AddRecord(r)));
} else {
this.record_list.Add(r);
}
}

Second, you need to have a reference to the form (in the next step, that is the theForm variable).

Then, in your listener method, invoke AddRecord method instead of adding the record in your BindingList directly.

public static void listen(IPEndPoint server_ip)
{
Console.WriteLine("In listen");
while (true)
{
try
{
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
Record record = JsonConvert.DeserializeObject<Record>(received_data);
theForm.AddRecord(record); // You need a Form instance.
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}

Ensuring that things run on the UI thread in WPF

Going over each of your questions, one by one:

  1. Not quite; you should only invoke onto the UI thread when necessary. See #2.
  2. Yes, it does matter. You should not just automatically Invoke everything. The key is to only invoke onto the UI thread if necessary. To do this, you can use the Dispatcher.CheckAccess method.
  3. That is correct.
  4. Also correct, and yes, you do run the risk of less responsive programs. Most of the time, you are not going to be looking at a severe performance hit (we're talking about milliseconds for a context switch), but you should only Invoke if necessary. That being said, at some points it is unavoidable, so no, I would not say it is bad practice at all. It is just one solution to a problem that you will encounter every now and then.
  5. In every case I have seen, I have made due with Dispatcher.CurrentDispatcher. For complex scenarios, this may not be sufficient, but I (personally) have not seen them.
  6. Not entirely correct, but this line of thinking will not do any harm. Let me put it this way: the Dispatcher can be used to gain access to the UI thread for the application. But it is not in and of itself the UI thread.
  7. BackgroundWorker is generally used when you have a time-consuming operation and want to maintain a responsive UI while running that operation in the background. Normally you do not use BackgroundWorker instead of Invoke, rather, you use BackgroundWorker in conjunction with Invoke. That is, if you need to update some UI object in your BackgroundWorker, you can Invoke onto the UI thread, perform the update, and then return to the original operation.
  8. Yes. The UI thread of a WPF application, by definition, must be running in a single-threaded apartment.

There's a lot to be said about BackgroundWorker, I'm sure many questions are already devoted to it, so I won't go into too much depth. If you're curious, check out the MSDN page for BackgroundWorker class.

Detecting whether on UI thread in WPF and Winforms

Don't use

if(Dispatcher.CurrentDispatcher.Thread == Thread.CurrentThread)
{
// Do something
}

Dispatcher.CurrentDispatcher will, if the current thread do not have a dispatcher, create and return a new Dispatcher associated with the current thread.

Instead do like this

Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
if (dispatcher != null)
{
// We know the thread have a dispatcher that we can use.
}

To be sure you have the correct dispatcher or are on the correct thread you have the following options

Dispatcher _myDispatcher;

public void UnknownThreadCalling()
{
if (_myDispatcher.CheckAccess())
{
// Calling thread is associated with the Dispatcher
}

try
{
_myDispatcher.VerifyAccess();

// Calling thread is associated with the Dispatcher
}
catch (InvalidOperationException)
{
// Thread can't use dispatcher
}
}

CheckAccess() and VerifyAccess() do not show up in intellisense.

Also, if you have to resort to these kinds of things its likely due to bad design. You should know which threads run what code in your program.



Related Topics



Leave a reply



Submit