How do I tell if my current thread is the UI thread? - c#

I'm working on a user control for UWP and it updates some of its visuals upon certain calls. However, since the core .NET library has been shifted around and the threading classes have been severely cut back, I don't know how to identify from the method in the user control if the calling thread is the UI thread or not so it knows whether or not to use the dispatcher to change a dependency property.
Anyone know how to do this?
EDIT: The dispatcher functionally can be "invoked" async fine on the UI thread - however, I really don't know if this is a good idea - to try to invoke on the main thread from the main thread via the dispatcher. If anyone has a reason this is fine or bad, that would also contribute to answering the question. If there's no reason to not use the dispatcher on the main thread, then I guess there's no problem.

I have found the solution...
CoreDispatcher.HasThreadAccess returns a bool indicating if you are on the UI thread or not.

My son just encountered this as an Issue so I thought I would add an updated answer.
The Main UI thread can be accessed using the Core Dispatcher CoreWindow.GetForCurrentThread().Dispatcher or Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher
Below is a class that implements the Core Dispatcher and tests whether the calling thread is the Main UI Thread. If so it invokes the action, otherwise calls Dispatcher.RunAsync to execute it on the Main UI thread.
class Threading {
private static CoreDispatcher Dispatcher =>
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
public static async void ThreadSafe(DispatchedHandler action)
{
// Calls Dispatcher.RunAsync to run a method on the Main UI Thread
IAsyncAction UiThread(DispatchedHandler proc) => Dispatcher.RunAsync(CoreDispatcherPriority.Normal, proc);
// Checks to see if this was called from the Main UI thread
// If we are in the Main UI thread then Invoke the action
// Otherwise: Send it to run in the Main Ui Thread.
if (Dispatcher.HasThreadAccess) { action.Invoke(); } else { await UiThread(action); };
}
}
The above class could be used like so:
// Some event handler callback
private void SomeCallback(object sender)(){
void line() => Frame.Navigate(typeof(PageName));
Threading.ThreadSafe(line);
}
// Pass a handle to the control and a string to update it's text property
internal static void ChangeControlText(dynamic ctrl , string v)
{
void line() {ctrl.Text= v;}
ThreadSafe(line);
}

Related

WPF MainWindow freezes even in Asynchronous state

What I'm trying to do is perform a heavy task triggered by a button event on the MainWindow, but still be able to drag the window freely. I've tried both the async/await pattern and creating new threads. However, threads will be nonblocking, MainWindow still freezes. Here's the code:
uiTIN.Click += async (o, e) =>
{
var _ = await Task.Run(() => job());
};
That's the button callback and here is the func:
private int job()
{
this.Dispatcher.Invoke(() =>
{
//Other function calls here omitted
});
return 0;
}
EDIT: The workaround was to use BackgroundWorker and I have also decorated dependent UI code snippets in Dispatcher Invoke function
From Microsoft's doccumentation on Dispatcher (emphasis mine):
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the queue of the Dispatcher at the specified DispatcherPriority.
So basically what you're doing is call an asynchronous method, and then forcing it to run on the UI thread, which accomplishes nothing.
In your //Other function calls here omitted, I'm asuming that you need to access some part of the UI, if that's not the case, all you have to do is remove the Dispatcher.Invoke from your method.
If my assumptions are right, then you must figure out a way of splitting your function, so that the part that isn't UI related run in a Background thread and only what needs to run on the UI Thread actually do.
My suggestion is to use a Background Worker. Here's how it'd look:
uiTIN.Click += (o, e) =>
{
job();
};
... and then ...
private int job()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// Part of other function calls here omitted that don't need to run on the UI thread
Dispatcher.Invoke(() =>
{
// Part of other function calls here omitted that must run on the UI thread
});
};
worker.RunWorkerAsync();
return 0;
}
The normal practice is that you have to return from buttons onClick event callback as soon as you can in order to avoid blocking the main thread(or some refer to UI thread). If the main thread is blocked the application will look like frozen. This is a fundamental design of any GUI application to synchronize UI flow.
You start an async task in callback but you also wait for the task to finish before returning. You should start a BackgroundWorker in the onClick event then return.
It has been explained quite well already why your code was blocking the UI thread (queuing your work on the Dispatcher). But I would not recommend the usage of the BackgroundWorker, I would rather fix your code with Task.Run for several reasons all explained in this article: https://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html

How to get access to the Main Thread

Is there a way to get a reference to the Main UI thread in Windows Forms from another worker thread?
Something like this:
public void FormLoad()//we are in Main UI Thread
{
Thread backThread = new Thread(DoWork);
backThread.Start();
}
public void DoWork()
{
//get Main Thread instance
//do some work
}
UPDATE
I`m interested if there is some static property or class to get reference to main thread like Thread.CurrentThread for getting current thread.
Thread MainThread=null;//for reference
public void FormLoad()//we are in Main UI Thread
{
MainThread=Thread.CurrentThread;//main thread reference
Thread backThread = new Thread(DoWork);
backThread.Start();
}
public void DoWork()
{
//get Main Thread instance
//do some work
}
This should work!!!
Well, you can always use ParameterizedThreadStart to start a thread and send parameters to it, since it's an object, you could pass a reference to the calling thread.
Knowing the Thread instance won't help, as that doesn't allow you to do anything useful. What you presumably want is to be able to get the main thread to do something (like: update itself).
In windows forms, you should have a sync-context; simply access SynchronizationContext.Current, and should should be able to use Post or Send to pass work to the main thread.
Alternatively, if that doesn't work (the ambient sync-context is null): pass any control/form to your worker; it can then call Invoke/BeginInvoke to pass work to the main thread.
An alternative approach is to expose an event that you Invoke, have the UI listen to that event, and have the UI deal with switching back to the main thread, by calling Invoke/BeginInvoke upon itself (when handling the event).

Using the C# Dispatcher in WPF Applications

I'm building a chat client and am not 100% sure on how to use the dispatcher. So the question is I have a method as such:
public void LostConnection()
{
myGUI.chatBox.AppendText("Lost connection to room: "+ myGUI.UsernameText.ToString() + "\r\n");
}
Do i need to surrond the statement within (myGUI.chatBox... ) with a Dispatcher.Invoke? I appreciate any help.
Your app has a main UI thread (usually ManagedThreadId==1). Typically in a chat app your events will come in on other threads (either dedicated socket listen threads or thread pool threads from listening code). If you want to update the UI from an event that gets pull on some other thread you must use the dispatcher. A useful test here is the Dispatcher.CheckAccess() method that returns true if code is on UI thread and false if on some other thread. A typical call looks something like:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
If you're in the main window you can use:
Dispatcher.BeginInvoke(...
If you're in someother context eg a view model then use:
Application.Current.Dispatcher.BeginInvoke(
Invoke vs BeginInvoke
Use Invoke if you want the current thread to wait until the UI thread has processed the dispatch code or BeginInvoke if you want current thread to continue without waiting for operation to complete on UI thread.
MessageBox, Dispatchers and Invoke/BeginInvoke:
Dispatcher.Invoke will block your thread until the MessageBox is dismissed.
Dispatcher.BeginInvoke will allow your thread code to continue to execute while the UI thread will block on the MessageBox call until its dismissed.
CurrentDispatcher vs Current.Dispatcher!
Be ware of Dispatcher.CurrentDispatcher as my understanding of this is that is will return a Dispatcher for the current thread not the UI thread. Generally are you interested in the dispatcher on the UI thread - Application.Current.Dispatcher always returns this.
Additional note:
If you are finding you are having to check dispatcher CheckAccess often then a useful helper method is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
Which can be called as:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});
I had problems with Application.Current.Dispatcher.BeginInvoke and the object.Invoke() methods.
This worked for me:
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
// code...
}));
Something like this (off the top of my head) should work:
public void LostConnection()
{
myGUI.Invoke
((MethodInvoker)delegate
{
myGUI.chatBox.AppendText("Lost connection to room: "+ myGUI.UsernameText.ToString() + "\r\n");
});
}

Windows Client GUI Design Advice - Accessing UI Thread from Long Running Task

Web Developer here and need some advice on how to achieve what must be a common requirement in Windows Forms.
I have a windows client app that calls a business object in a separate project to perform some long running tasks. Difference to other examples is that the process live in another class library i.e. Business.LongRunningTask();
I have a list box in the client that I would like to have logged to by the task. I can run the process on the UI thread passsing in the instance of the textbox and calling Application.DoEvents() when I log to the textbox from within the task. All fine, but not elegant and would prefer not to call Application.DoEvents();
If I run the long running process on a separate thread using delegates I cannot access the textbox or delegates created in the windows client form which rules out BeginInvoke calls.
Surely this is bad design on my part and would appreciate some feedback.
You're looking for the BackgroundWorker class.
To execute a time-consuming operation in the background, create a BackgroundWorker and listen for events that report the progress of your operation and signal when your operation is finished.
You can find a complete example here: http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.100).aspx#Y1351
I can run the process on the UI thread
passsing in the instance of the
textbox and calling
Application.DoEvents() when I log to
the textbox from within the task.
Yes, you could also pass in an instance of ILoggingINnterface that you have used to put in the code to write to the text box FROM WITHIN THE UI and thus have taken care of all the nice BginInvoke stuff ;)
If I run the long running process on a
separate thread using delegates I
cannot access the textbox or delegates
created in the windows client form
which rules out BeginInvoke calls.
Ah. No. You just most invoke back to the dispatcher thread then you can access all the UI elemente you like.
Yeah, avoid Application.DoEvents().
To marshall the call back onto the UI thread, call this.Invoke(YourDelegate)
To access UI elements from a different thread, you can use control.Invoke to call a delegate on the owning thread.
I used this at one point to create a live log screen which was updated from a timer while a different worker thread was running. Heres a simplified version:
public class DifferentClassLibrary
{
public delegate void StringDataDelegate(string data);
public event StringDataDelegate UpdatedData;
public void DoStuff()
{
if (UpdatedData != null)
{
Thread.Sleep(10000);
UpdatedData("data");
}
}
}
And in the winform:
public void UpdateTextBoxCallback(string data)
{
if (uiTextBoxLiveLogView.InvokeRequired)
{
uiTextBoxLiveLogView.Invoke(new DifferentClassLibrary.StringDataDelegate(UpdateTextBoxCallback), data);
}
else
{
uiTextBoxLiveLogView.Text += data;
}
}
void Main()
{
DifferentClassLibrary test = new DifferentClassLibrary();
test.UpdatedData += UpdateTextBoxCallback;
Thread thread = new Thread(new ThreadStart(test.DoStuff));
thread.Start();
}

C# Multi threading- Move objects between threads

i am working with a winforms control that is both a GUI element and also does some internal processing that has not been exposed to the developer. When this component is instantiated it may take between 5 and 15 seconds to become ready so what i want to do is put it on another thread and when its done bring it back to the gui thread and place it on my form. The problem is that this will (and has) cause a cross thread exception.
Normally when i work with worker threads its just with simple data objects i can push back when processing is complete and then use with controls already on the main thread but ive never needed to move an entire control in this fashion.
Does anyone know if this is possible and if so how? If not how does one deal with a problem like this where there is the potential to lock the main gui?
You don't need to lock the GUI, you just need to call invoke:
Controls in Windows Forms are bound to
a specific thread and are not thread
safe. Therefore, if you are calling a
control's method from a different
thread, you must use one of the
control's invoke methods to marshal
the call to the proper thread. This
property can be used to determine if
you must call an invoke method, which
can be useful if you do not know what
thread owns a control. ref
Here is how it looks in code:
public delegate void ComponentReadyDelegate(YourComponent component);
public void LoadComponent(YourComponent component)
{
if (this.InvokeRequired)
{
ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
this.BeginInvoke(e, new object[]{component});
}
else
{
// The component is used by a UI control
component.DoSomething();
component.GetSomething();
}
}
// From the other thread just initialize the component
// and call the LoadComponent method on the GUI.
component.Initialize(); // 5-15 seconds
yourForm.LoadComponent(component);
Normally calling the LoadComponent from another thread will cause a cross-thread exception, but with the above implementation the method will be invoked on the GUI thread.
InvokeRequired tells you if:
the caller must call an invoke method
when making method calls to the
control because the caller is on a
different thread than the one the
control was created on.
ref
Update:
So if I understand you correctly the control object is created on a thread other than the GUI thread, therefore even if you were able to pass it to the GUI thread you still won't be able to use it without causing a cross-thread exception. The solution would be to create the object on the GUI thread, but initialize it on a separate thread:
public partial class MyForm : Form
{
public delegate void ComponentReadyDelegate(YourComponent component);
private YourComponent _component;
public MyForm()
{
InitializeComponent();
// The componet is created on the same thread as the GUI
_component = new YourComponent();
ThreadPool.QueueUserWorkItem(o =>
{
// The initialization takes 5-10 seconds
// so just initialize the component in separate thread
_component.Initialize();
LoadComponent(_component);
});
}
public void LoadComponent(YourComponent component)
{
if (this.InvokeRequired)
{
ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
this.BeginInvoke(e, new object[]{component});
}
else
{
// The component is used by a UI control
component.DoSomething();
component.GetSomething();
}
}
}
Without knowing too much about the object. To avoid cross thread exceptions, you can make the initial thread invoke a call (Even if you are calling from a thread).
Copied and pasted from one of my own applications :
private delegate void UpdateStatusBoxDel(string status);
private void UpdateStatusBox(string status)
{
listBoxStats.Items.Add(status);
listBoxStats.SelectedIndex = listBoxStats.Items.Count - 1;
labelSuccessful.Text = SuccessfulSubmits.ToString();
labelFailed.Text = FailedSubmits.ToString();
}
private void UpdateStatusBoxAsync(string status)
{
if(!areWeStopping)
this.BeginInvoke(new UpdateStatusBoxDel(UpdateStatusBox), status);
}
So essentially the threaded task will call the "Async" method. Which will then tell the main form to begininvoke (Actually async itself).
I believe there is probably a shorter way to do all of this, without the need for creating delegates and two different methods. But this way is just ingrained into me. And it's what the Microsoft books teach to you do :p
The BackgroundWorker class is designed for exactly this situation. It will manage the thread for you, and let you start the thread, as well as cancel the thread. The thread can send events back to the GUI thread for status updates, or completion. The event handlers for these status and completion events are in the main GUI thread, and can update your WinForm controls. And the WinForm doesn't get locked. It's everything you need. (And works equally well in WPF and Silverlight, too.)
The control must be created and modified from the UI thread, there's no way around that.
In order to keep the UI responsive while doing long-running initialization, keep the process on a background thread and invoke any control access. The UI should remain responsive, but if it doesn't, you can add some wait time to the background thread. This is an example, using .Net 4 parallel tools: http://www.lovethedot.net/2009/01/parallel-programming-in-net-40-and_30.html
If interaction with the specific control being initialized can't be allowed until initialization finishes, then hide or disable it until complete.

Categories

Resources