creating a UI in background thread WPF? - c#

I tried following:
var task = new Task(() =>
{
for (int i=0; i<10; i++) {
//Create new Grid HERE
// Add Table with some dynamic data here..
// print the above Grid here.
}
});
task.ContinueWith((previousTask) =>
{
label.Content = printerStatus(); // will return "Out of Paper", "printing", "Paper jam", etc.
},
TaskScheduler.FromCurrentSynchronizationContext());
label.Content = "Sending to printer";
It returns following error: The calling thread must be STA, because many UI components require this..
The error occurs when it tries to create a new UI object Grid.
How can i fix this? Let me know if there is any other way arround!

Tasks use thread pool threads, which are in a MTA.
If you want a background STA thread, you will have to create one yourself and explicitly set the ApartmentState.
Thread t = new Thread( ... );
t.SetApartmentState( ApartmentState.STA );
t.Start();

You cannot create UI objects on different thread than the main UI thread because as soon as you add them to the UI, it tries to set the Parent property, and a thread in WPF cannot modify objects that were created on a different thread.
Instead, I'd recommend creating a list or collection of the Grid's Data on the 2nd thread, and binding it to the UI using something like an ItemsControl. This will keep all UI objects on the main UI thread, while background processing can be done on a background thread.
To update a UI object from a background thread, such as your status label, I'd recommend using the Dispatcher like lawrencealan's answer suggests. The Dispatcher is WPF's internal message queue for the main UI thread

Using the Dispatcher for the label and Invoke might help:
label.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
label.Content = printerStatus();
}
));

Related

How to remove SynchronizationContext until my BackgroundWorker has finished?

I'm trying to download some movie data in a BackgroundWorker thread, but when the background thread tries to modify ObservableCollection fields for the selected movie there is an error stating "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread".
First I tried removing any UI elements such as TextBoxes before running the BackgroundWorker, which strangely didn't seem to work even though there were no objects left to synchronize changes.
I can get around this by using the answer here to send the changes to the UI thread but it means I'd have to flood my background thread with many lines like uiContext.Send(x => _matchObsCollection.Add(match), null); which would make my code a little messier than I'd like.
Ideally I'd like to remove the SynchronizationContext so that the UI thread would not try to interfere with the background thread, but again this isn't making any difference. Is there something wrong with what I'm trying to do here:
// Prevent synchronization with the UI thread.
var uiContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
// Prepare the background worker for data download tasks.
BackgroundThread = new BackgroundWorker();
BackgroundThread.WorkerReportsProgress = true;
BackgroundThread.ProgressChanged += BackgroundThread_ProgressChanged;
BackgroundThread.DoWork += (f, arg) =>
{
DownloadMovieData(movie, uiContext);
};
You could try to store the result of the DownloadMovieData function on a temporary variable and add a RunWorkerCompleted event to your background worker to set the actual ObservableCollection to the value of the temporary variable you created.

C# WPF Update UI Application freeze

In C# we know there is multiple Background thread ... so like that can we create multiple UI thread?
and Multiple UI thread helps to update data in observation collection without freeez?
if No. what is the best way to get data from webservice and update to observation collection..
Code:-
Thread lthrThread = new Thread((ThreadStart)delegate
{
string Data = DataFromServer()
this.Dispatcher.BeginInvoke(new Action(() =>
{
UI freeze here for 5 -10 seconds
}));
});
lthrThread.SetApartmentState(ApartmentState.STA);
lthrThread.Start();
UI thread is main thread.You cannot copy of it.If you call your data on ui thread,your application user interface will not respond until it ends.So,you need to create background thread or task that handles data call from webservices.When you get data from webservice pass it to the main thread (UI). You can use Dispatcher for it.
Application.Current.Dispatcher?.BeginInvoke(new Action(() => {
// Pass data to UI here.
}));
or you can use async Task which will be more convenient.

STA call from MTA

I am just starting to deal with STA/MTA issues, so apologies for the simplicity of the question. I couldn't find an answer I could actually understand at the bottom of the ladder here.
I am writing a plugin for another piece of software, and come to a point in a worker thread that I need to create some UI elements. I understand that I cannot do that from inside the worker thread since it is not an STA thread, and that I need to get back to the Main (or just another?) STA thread to create the UI elements. Some clarifications would help greatly.
Do all STA threads have the same 'rights', i.e. if the main thread is STA and creates a Window, adds some UI elements to it. Then spawns off another STA thread, and that second thread likewise creates some UI elements, are they doing it in the same 'space' (poor word choice, but I don't know what else to use) and can access each other's UI elements without causing death and destruction? Or do I need to explicitly jump back to the Main/Original STA thread and ONLY ever create UI elements from THAT (not just ANY) STA thread?
If that is the case (only 1 STA thread is allowed to make UI elements) how do I do that correctly? I have seen many posts that related to this but for some reason I can't quite catch what's going on, and would love a REAL simple answer.
Please no 'Here's a cool slick way of doing...' I just need the simple way of at the point of execution where I need some UI elements jumping back over to the main STA thread if that's what's necessary.
If it is not necessary, then I will just make that worker thread an STA thread and continue on my way, is that fair? Or am I courting disaster?
if the main thread is STA and creates a Window, adds some UI elements to it. Then spawns off another STA thread, and that second thread likewise creates some UI elements, are they doing it in the same 'space' [snip...] and can access each other's UI elements without causing death and destruction?
If Thread A and B are both STA, then they can each create and update their own UI elements, but not eachothers. Any other threads that want to affect the UI have to use one of the BeginInvoke style methods to ask the appropriate thread to do the update.
If it is not necessary, then I will just make that worker thread an STA thread and continue on my way, is that fair? Or am I courting disaster?
You may not be able to make the worker thread an STA thread if it's been set to MTA and initialized. You may have to make a new thread.
How do you do it? It seems like you want to use WPF (System.Windows.*) for your UI - so If the app that you are "plugging into" is also using WPF, you should be able to access it and re use it's UI thread. If not, you can make a new thread, and create a new Application on it and call Run. This should set up a dispatcher for you.
Something like this (pseudo code sort-of copied from some working code I have elsewhere)
Dispatcher dispatcher = null; // we 'get to' the UI thread via the dispatcher
if(Application.Current) { // re use an existing application's UI thread
dispatcher = Application.Current.Dispatcher;
} else {
var threadReadyEvent = new ManualResetEvent(false);
var uiThread = new Thread(() => {
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
var application = new Application();
application.Startup += (sender, args) => {
dispatcher = application.Dispatcher;
threadReadyEvent.Set();
};
// apps have to have a "main window" - but we don't want one, so make a stub
var stubWindow = new Window {
Width = 1, Height = 1,
ShowInTaskbar = false, AllowsTransparency = true,
Background = Brushes.Transparent, WindowStyle = WindowStyle.None
};
application.Run(stubWindow);
}){ IsBackground = true };
uiThread.Start();
threadReadyEvent.WaitOne();
threadReadyEvent.Dispose();
}
dispatcher.Invoke(() => {
// ask the UI thread to do something and block until it finishes
});
dispatcher.BeginInvoke(() => {
// ask the UI thread to do something asynchronously
});
and so forth
If a thread creates a control. Only this specific thread can interact with it, even if there are other STA threads.
In WinForms you would invoke a method on the control: Control.Invoke.In WPF you have the dispatcher to do it: Dispatcher.Invoke.
WinForms:
form1.Invoke(/* a delegate for your operation */)
WPF:
window1.Dispatcher.Invoke(/* a delegate for your operation */)
What you do is instead of changing an object in a "single apartment" you ask (invoke) the STA thread in control of it to do it (the delegate you invoke) for you. You also have BeginInvoke for doing it asynchronously.

Creating WPF control from a separate thread

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;
}
));

Dispatcher.Dispatch on the UI thread

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.

Categories