I have an asynchronously running Task that fires an event when it's completed like this:
task.ContinueWith(() => {
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}
The event handler then should create an instance of a WPF control. But when I try to do so, it causes an exception: The calling thread must be STA, because many UI components require this. Exception occurs in the class constructor, when calling method InitializeComponent().
As far as I know, usually accessing WPF controls from separate threads is handled using the Dispatcher.Invoke, and it always worked for me, so I tried it:
Dispatcher.Invoke(new Action(() =>
{
InitializeComponent();
}));
But in that case exception keeps occurring. How do I create an instance of a WPF control from a separate thread?
Or maybe it will be a better approach to marshal the completion event to the main UI thread. If yes, how can I do that?
You have to use a Dispatcher instance, which was associated with the UI thread. If you are writing something like this:
Dispatcher.Invoke(new Action(() =>
{
InitializeComponent();
}));
In the task body, you're using dispatcher of the calling thread, which can be a background thread from a pool.
Anyway, with tasks you shouldn't use Dispatcher directly. Use an appropriate task scheduler:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
result =>
{
// Put you UI calls here
}, CancellationToken.None, TaskContinuationOptions.None, ui);
where tasks is a sequence of tasks being executed with the default scheduler.
Calling InitializeComponent from the constructor on another thread seems like looking for trouble. The object isn't there yet (we're in the constructor)
Marshaling it back to the UI thread would normally do the trick but during the constructor looks like a bad idea to me.
If you want to initialize the control asynchronously just subscribe to the loaded event, so you know the object is there, spawn a thread that does some calculations/data retrieval and marshals the data back to the UI thread to display it.
I have done this in the past:
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(
delegate()
{
// Access control created by main thread
textBlock.Text = msg;
}
));
Related
I would like to execute some code from a non-main thread inside the main thread (UI thread) in .Net 6 with C#.
I've tried to use this code:
await Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
This doesn't work, since Windows.UI.Core.CoreWindow.GetForCurrentThread() returns null.
My second try was:
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
This fails, because Windows.ApplicationModel.Core.CoreApplication.MainView throws a System.InvalidOperationException.
Another way should be:
await System.Windows.Threading.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
But the System.Windows.Threading namespace is not available for me, since I'm using .Net 6 and it's not longer supported in it.
Any idea, how I can execute some code from a non-main thread inside the main-thread (UI thread)?
I would like to execute some code from a non-main thread inside the main thread (UI thread) in .Net 6 with C#.
I strongly recommend that you don't. It's far cleaner to have your async methods use something like IProgress<T> to indirectly update the UI as necessary. If you structure your code so that the main thread calls the background threads instead of the background threads manipulating the UI through the UI thread, then you'll end up with a much cleaner design where your logic is less tied to your UI controls.
That said, if you really want to, then the solution is to capture the dispatcher on the UI thread before the background work begins, and have the background work use that dispatcher (not the "current dispatcher") when posting work to the UI thread.
SynchronizationContext is a good solution to switch to the main thread. But it's not implemented for all .Net app types.
For example, for a console app, there is no solution implemented.
But for Windows Forms, WindowsFormsSynchronizationContext works fine.
private SynchronizationContext _synchronizationContext;
Initialization inside, called inside the main thread:
_synchronizationContext = new WindowsFormsSynchronizationContext();
After this, you can call from a different thread:
SynchronizationContext.SetSynchronizationContext(_synchronizationContext);
... here we are in a separate thread
_synchronizationContext.Post(
(state) => {
... this will be executed in the main thread
},
null);
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync
(CoreDispatcherPriority.Normal, () =>
{
do something on UI thread
});
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");
});
}
I have a public static method within a certain class. and it uses items ( combo box, datagridviews etc' ) created in the UI thread. I want to run this function from a new thread, but I get this message :
"Additional information: Cross-thread operation not valid: Control 'CompanycheckedListBox' accessed from a thread other than the thread it was created on."
how can I run the function from a new thread and still use these items created in the UI thread?
I used this line of code : new Thread(delegate () { functionName();}).Start();
The short answer: you can't. Those components can only be accessed on the ui thread. But there are ways to dispatch you function call onto the ui thread.
Most ui frameworks are completely single threaded. It is not allowed to access any component from a different thread than the ui thread.
You need to dispatch to current ui thread. Looking it at your control names, it looks either like a winforms or a WPF application.
In WinForms you need the following code to dispatch back to the ui thread:
public void UpdateUI(object parameter)
{
if (this.InvokeRequired)
{
Dispatcher.BeginInvoke(new Action(() => UpdateUI(parameter)));
return;
}
// Update or access here
}
In WPF the following snipped allows you to change the ui form a different thread:
public void UpdateUI(object parameter)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new Action(() => UpdateUI(parameter)));
return;
}
// Do update or access here
}
The important thing to notice here, is that those functions will be executed on the UI thread and not on the calling thread.
Edit: the object parameter is completely optional. It was intended as a example on how to use this method with a function with parameters.
I have the doubt regarding when to use the Dispatcher.Invoke to update something on UI from different Thread.
Here's my code...
public Window4()
{
InitializeComponent();
this.DataContext = this;
Task.Factory.StartNew(() => Test() );
}
private List<string> listOfString = new List<string>();
public List<string> ListOfString
{
get { return listOfString; }
set { listOfString = value; }
}
public void Test()
{
listOfString.Add("abc");
listOfString.Add("abc");
listOfString.Add("abc");
}
<Grid>
<ListView ItemsSource="{Binding ListOfString}" />
</Grid>
I am starting a new Task on the different Thread, do i need to use Dispatcher.BeginInvoke to update the UI.
In this case it is updating the UI, but i've seen some scenarios where people update UI using Dispatcher.Invoke or BeginInvoke from the different Thread.
So my question is when we have to do that and why in this case it is working fine.
Thanks & Regards,
BHavik
I have the doubt regarding when to use the Dispatcher.Invoke to update
something on UI from different Thread.
When you are on a different thread you will always have to use the dispatcher to update a ui component that belongs to another thread.
I am starting a new Task on the different Thread, do i need to use
Dispatcher.BeginInvoke to update the UI.
Tasks allow for multiple operations to be performed without blocking the thread they are called from but that doesn't mean they are on a different thread. However when updating the UI from inside a Task you will need to use the dispatcher.
In this case it is updating the UI, but i've seen some scenarios where
people update UI using Dispatcher.Invoke or BeginInvoke from the
different Thread.
Invoke will block the calling thread while it is performing the action and BeginInvoke will not. BeginInvoke will return control immediately to the caller, Invoke may cause the calling thread to hang if it is performing a heavy operation.
This is from msdn documentation,
In WPF, only the thread that created a DispatcherObject may access
that object. For example, a background thread that is spun off from
the main UI thread cannot update the contents of a Button that was
created 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.
Edit: In response to your comment I ran some tests.
When calling Test() from a task (without using the dispatcher) I got this error "The calling thread cannot access this object because a different thread owns it."
So I created a method called PrintThreadID(). I printed the thread before entering the task then from inside the task and it does report both are running on the same thread ID.
The error is misleading because it says the calling thread is different than the one that owns it which the PrintThreadID() function shows is not true, they are in fact on the same thread. Tasks while on the same thread still cannot update a UI component without using Dispather.Invoke().
So here is a working example which will update the Grid from a task.
public partial class MainWindow : Window
{
public List<string> myList { get; private set; }
public MainWindow()
{
InitializeComponent();
myList = new List<string>();
label1.Content = Thread.CurrentThread.ManagedThreadId.ToString();
Task.Factory.StartNew(PrintThreadID);
Task.Factory.StartNew(Test);
}
private void PrintThreadID()
{
label1.Dispatcher.Invoke(new Action(() =>
label1.Content += "..." + Thread.CurrentThread.ManagedThreadId.ToString()));
}
private void Test()
{
myList.Add("abc");
myList.Add("abc");
myList.Add("abc");
// if you do not use the dispatcher you will get the error "The calling thread cannot access this object because a different thread owns it."
dataGrid1.Dispatcher.Invoke(new Action(() =>
{
dataGrid1.ItemsSource = myList.Select(i => new { Item = i });
}));
}
}
Your test isn't valid as it isn't actually updating your UI. If you want proof, add this sleep call:
public void Test()
{
Thread.Sleep(10000);
listOfString.Add("abc");
listOfString.Add("abc");
listOfString.Add("abc");
}
You'll find that your UI appears and the list is empty. 10 seconds, 30 seconds, 3 months later, the list won't contain your strings.
Instead your test is demonstrating a race condition - your Test() method is completing fast enough that the strings are added to the list before the UI appears on screen and reads the list.
To fix it, change your collection to an ObservableCollection<string>. But then you'll encounter the next problem - you can't update an ObservableCollection on a background thread. So that's where the Dispatcher comes in.
pseudo code:
form1
{
int i;
label1;
Add()
{
i++;
label1 = i.ToString(); //#ErrorLine
}
backgroundworker worker;
worker_DoWork()
{
FileGuard guard = new FileGuard();
guard.FileKilled += guard.KillH(Add);
guard.StarGuarding(); //there is system watcher inside
//this guard and some processing code
//that will fire event FileKilled();
}
}
Afer calling StartGuarding() worker will be compleated
But when there is event FileKilled fired I goth this error on line #ErrorLine
Cross-thread operation not valid: Control 'form1' accessed from a thread other than the thread it was created on.
This has nothing to do with the events themselves, but rather the fact that you are accessing UI controls from another thread. In Windows Forms, you are not allowed to interact with the UI from any other thread than the main UI thread.
You can use InvokeRequired to check whether you are on a thread that has no access to the UI, and then use Invoke to run code on the UI thread if required. It might look something like this:
private void DoStuffWithGUI()
{
if (InvokeRequired)
{
Action work = DoStuffWithGUI;
Invoke(work);
}
else
{
// Your normal logic
}
}
You can use the UI directly from ProgressChanged and RunWorkerCompletedEvents (because they are automatically marshalled to the UI thread). But all work you do within DoWork, (and therefore all events you might raise as part of the work) runs in a separate thread, and must be marshalled to the UI thread using Invoke. From MSDN for BackgroundWorker:
You must be careful not to manipulate
any user-interface objects in your
DoWork event handler. Instead,
communicate to the user interface
through the ProgressChanged and
RunWorkerCompleted events.
It's because a third thread is used when FileKilled is invoked by the system.
As for BackgroundWorker you should use events to handle GUI updates: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
You cannot access Windows Forms or WPF objects from anything other than the form they were created on, hence your issue.
Use a dispatcher to send the update back to your UI Thread.
If you can give detail on whether you're using WinForms or WPF we can give more information.
You cannot change modify the controls from a thread other than on which they were created. You need to use the InvokeRequired property and Invoke method to marshal the calls to the UI thread from the background thread.
private readonly _lockObject = new Object();
Add()
{
lock(_lockObject)
{
i++;
if(label1.InvokeRequired)
Invoke(new Action( () => label1 = i.ToString()));
else
label1 = i.ToString();
}
}
Note that the lock is not necessary to avoid this exception. It added to make the method thread safe.