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");
});
}
Related
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
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);
}
I have a worker thread which calculates data for DataGrid after every change user have made. In some cases user make changes too fast so on GUI thread i call
Thread.Abort();
in the meantime on the worker thread i use such a construction
while (true)
{
try
{
_calculateEvent.WaitOne();
...
Application.Current.MainWindow.Dispatcher.BeginInvoke((Action)delegate()
{
_viewModel.UpdateInterfaceFromAssigningInfo(assigningInfo);
});
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
}
Don't know if it will work at all, but for now my main problem is i can't call code on the GUI thread to update interface. At Invoke row i have exception: InvalidOperationException with message
The calling thread cannot access this object because a different
thread owns it.
I'm usually use slightly different way:
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action( ()=>
{
_viewModel.UpdateInterfaceFromAssigningInfo(assigningInfo);
}));
Try, may be it's a reason.
After some researches i have found info that accurately fills my needs, because in my case i need to update UI only after all calculation in Task have been done.
Option 1.
For the case of WPF application we can benefit from synchronization context task scheduler which runs task right on GUI thread. So, one can employ such scenario to update GUI after task has finished:
Task t = Task.Run(() => foo());
t.ContinueWith((task) =>
{
// Update observable properties
}, TaskScheduler.FromCurrentSynchronizationContext());
Continuation task will be executed on GUI thread and so will be able to update GUI.
Option 2
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
// Since we asynchronously wait, the UI thread is not blocked by our code.
await foo();
// Since we resume on the UI context, we can directly access UI elements.
UpdateObservableProperties();
}
I have a windows form with a datagridview and some buttons. One of the buttons when clicked will call a method called loadMyData() that reads some data from a csv and puts them in three datagridviews in the form.
The code is something like this:
public partial class NewForm : Form
{
private void loadData_Click_1(object sender, EventArgs e) // load market data, create a base copy and update gridview
{
ThreadStart thread1Start = new ThreadStart(loadMyData);
Thread t1 = new Thread(thread1Start);
t1.Start();
}
public void loadMyData()
{
dataMap = dataLoader.newLoadTheData(dataMap, grid1, grid2)
}
}
where dataLoader.newLoadTheData is a static method that takes as input my datagridviews (grid1, grid2) and a dictionary (dataMap). The method simply reads some data from a csv and put the numbers in the 2 datagridviews. These are updated from this method and an updated dictionary (dataMap) is also returned by the method. It all works fine when the method loadMyData() is executed normally but I get this error when I execute it as thread:
Cross-thread operation not valid: Control 'grid1' accessed from a
thread other than the thread it was created on.
I realize that I might be using something like "invoke" but I really can't find a clear example that shows how to do this in my case. Can anyone help with tjis situation? How should I change the code to make it work?
When working with your grid from the other thread, you should do something like this:
if (grid1.InvokeRequired)
grid1.Invoke(new Action(() => { /*do my stuff here*/ })
else
{
/*do my stuff here*/
}
You have to marshal the call back to the UI thread.
Are you using WinForms or WPF?
In WPF you can use the Dispatcher.
In WinForms:
Try
// Get the UI thread's context in the constructor.
var context = TaskScheduler.FromCurrentSynchronizationContext();
// Then its possible to start a task directly on the UI thread
var token = Task.Factory.CancellationToken;
Task.Factory.StartNew(() =>
{
this.label1.Text = "Task past first work section...";
}, token, TaskCreationOptions.None, context);
EDIT:
The reason you are getting this error is because you are trying to access the Grid control from another thread. In general, most UI applications are in a STA(Single Threaded Affinity) model, where any interactions with the UI must be done on the "UI" thread which is usually the main/first thread the application starts on.
As you are loading the data on a background thread, after it is finished, you need a way to Marshal(invoke/run) the code which update the Grid on the Main/UI thread.
To achieve this, you create a TaskScheduler on the main thread by using its current SynchronizationContext (as in the constructor of the window/control the current context will be the UI thread) and than later you can pass that context into the Task.Factory.StartNew method as a parameter, so that it knows to "Marshal"(Invoke/Run) the code on the given "Context" which is the UI Thread
System.Threading.Tasks allows you to easily create a child task and run a completion block when all is complete. If you specify the UI context then the completion block will run in the UI thread, no need for Invoke().
Code:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(
() =>
{
// this runs in worker thread
loadMyData();
DoSomeLengthyJob();
DoSomethingElse();
})
.ContinueWith(t =>
{
// now we are in UI thread
// now update the UI with whatever you want
// with the results from your worker thread
dataGridView1.Rows.Add();
}, ui);
Tell me more
Check out this article by Stephen Cleary
In WinForms application I start worker thread that adds data to root a XElement.
Then in main thread I need to wait while worker thread finishes it's work (to get complete XElement), and output this XElement to a textarea.
If I call .Join() on the main thread - it freezes until another thread stops (and user can't click any button on the main form).
Is it possible to unblock main thread while waiting for another thread to finish it's work??
I've tried:
1.
BeginInvoke(new Action(() => {
XmlManager.whReady.WaitOne();
xDocString = xResultDoc.ToString();
}));
2.
string xDocString = String.Empty;
new Thread(() => { xDocString = XelementToString(); }).Start();
txtXmlTree.Text = xDocString;
public string XelementToString() {
XmlManager.whReady.WaitOne();
return xResultDoc.ToString();
}
But it had no effect.
EventWaitHandle XmlManager.whReady.WaitOne(); is being .Set() in the worker thread just before it closes.
Yes, you can use async/await
string docString = await Task.Run(() => {
XmlManager.whReady.WaitOne();
return xResultDoc.ToString();
}).ConfigureAwait(true);
//Execution flow will resume here once the thread is done.
....
//Now do something here with the text (e.g. display it).
...
For example, if you want to run this on a button click, you would have (note the async modifier):
private async void button1_Click(object sender, EventArgs e)
{
...The code above goes here...
}
As to why your code is not working as expected (both of your attempts):
Your code is blocking, because it causes the action to be executed on the thread on which the main form's handle was created (so the UI thread). You typically call BeginInvoke from another (non UI) thread to tell the UI to do something.
You start a thread and then immediately expect to have xDocString ready to use. It doesn't work that way. By the time this line is executed txtXmlTree.Text = xDocString; the thread may or may not have finished executing (most likely not finished).
I hope now you see why await is a way better option. You don't have to synchronize your workers with the main thread, you don't have worry about context switching and making sure UI code executes on the UI thread.
You can use BackgroundWorker class since it's a WinForm application.
The BackgroundWorker will let the sub-task to be run in the background, and notify the main form on it's completion (as well as progress, if needed), so the main form will be able to display the output in the text area once the sub-task is complete.