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.
Related
I know there are a lot of questions about this already, but I seem to have a fundamental misunderstanding about how BindingOperations.EnableCollectionSynchronization(observableC, padlock); works.
I have a WPF app using mvvm and in my viewmodel I want to update my observablecollection.
After some googling I landed on this solution that imo should work: Calling it the first time works fine, but after sleeping for 1 minute it gives me this:
System.NotSupportedException: 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'
public MainViewModel()
{
MainOc= new ObservableCollection<DataModel>();
MainView= CollectionViewSource.GetDefaultView(MainOc);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
BindingOperations.EnableCollectionSynchronization(MainOc, padlock);
BindingOperations.EnableCollectionSynchronization(MainView, padlock);
}));
Task.Run(() => GetData());
}
private async void GetData()
{
while (true)
{
lock (padlock)
{
MainOc.Clear();
}
foreach (DataRow row in tempTable.Rows)
{
lock (padlock) {
MainOc.Add(new DataModel());
}
}
lock (padlock)
{
MainView= CollectionViewSource.GetDefaultView(MainOc);
}
Thread.Sleep(TimeSpan.FromMinutes(1));
}
}
I would suspect Application.Current.Dispatcher.BeginInvoke, since this will be run at some later time on the UI thread. Presumably the constructor is already running on the UI thread, so this code will be placed last in the queue of tasks the UI thread is scheduled to perform. The documentation for EnableCollectionSynchronization states
The call must occur on the UI thread.
The call must occur before using the collection on a different thread or before attaching the collection to the ItemsControl, whichever is later.
I'm not confident the later point is fulfilled. I would expect the order to be something like:
MainViewModel() is run
GetData() start updating the collection (on background thread)
The viewmodel is attached to the view
BindingOperations.EnableCollectionSynchronization is called
So I would at least try to call BindingOperations.EnableCollectionSynchronization directly, without the BeginInvoke. Or at the very least, place some breakpoints or logging to verify that the EnableCollectionSynchronization is called before the viewmodel is attached.
In any case I would probably not recommend using that kind of synchronization mechanism. My recommendation would be to:
Use a dispatch timer to invoke a method on the UI thread every minute
If method do any slow operation, like fetching data from the database, make the method async and either do a async database call, or a synchronous database call on a background thread that returns the result. In either case the call should be awaited.
Once you have the data, update your collections to update the UI. This code will be run on the UI thread, even if you awaited work on a background thread, so there is no need for synchronization.
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 a WinForm application and want to convert to WPF, after the 200 errors was clean, I'm trying to get works and always have one errors I'm not able to resolve.
At my BackgroundWorker1_DoWork I'm trying to retrieve how many files in a directory by using this:
//Set the progress bar
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render,
new Action(() => {
Directory.GetFiles((textBox_UpdatedBuildPath.Text, "*",
SearchOption.AllDirectories).Length;
})
); //<-----------------HERE
and get following error:
The calling thread cannot access this object because a different
thread owns it.
After quick search, it's seemed to be related when user want to update UI, but it seems that it is not my case, I need informations from Textbox and recursively find how many files folder can contains.
Thank's for future answer.
Edit: Here the code with full comment -> http://codepaste.net/r1qejd
Almost every WPF element has thread affinity. This means that access to such an element should be made only from the thread that created the element. In order to do so, every element that requires thread affinity is derived, eventually, from DispatcherObject class. This class provides a property named Dispatcher that returns the Dispatcher object associated with the WPF element.
The Dispatcher class is used to perform work on his attached thread. It has a queue of work items and it is in charge of executing the work items on the dispatcher thread.
You can find on the following link some more details on the subject.
As MSDN says:
Only one thread can modify the UI thread. But how do background
threads interact with the user? A background thread can ask the UI
thread to perform an operation on its behalf. It does this by
registering a work item with the Dispatcher of the UI thread. The
Dispatcher class provides two methods for registering work items:
Invoke and BeginInvoke. Both methods schedule a delegate for
execution. Invoke is a synchronous call – that is, it doesn’t return
until the UI thread actually finishes executing the delegate.
BeginInvoke is asynchronous and returns immediately.
So try to use Dispatcher. For example:
int CountFileToCheck;
textBox_UpdatedBuildPath.Dispatcher.Invoke(() => {
CountFileToCheck=Directory.GetFiles(textBox_UpdatedBuildPath.Text, "*", SearchOption.AllDirectories).Length;
});
Update:
I've made an axample for you and tested it. It works perfectly.
XAML:
<StackPanel>
<Button Name="btn" Click="btn_Click" Content="Foo Button"/>
<TextBox Name="textBox" Text="123"/>
</StackPanel>
Code-behind:
public partial class MainWindow : Window
{
BackgroundWorker bw;
public MainWindow()
{
InitializeComponent();
bw = new BackgroundWorker();
}
private void btn_Click(object sender, RoutedEventArgs e)
{
bw.DoWork += (o, a) =>
{
try
{
string str = "";
textBox.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
str = textBox.Text;
MessageBox.Show(str);
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
//e.Result = LoadXmlDoc();
};
bw.RunWorkerAsync();
}
}
I know I have seen an article or SO on this before, but cannot seem to find it now. I ran into a problem helping a coworker write a test that was checking UI actions that were occurring across multiple threads (I realize the problems here...that is not what I want to focus on at the moment :)). The code looks similar to this kind of pseudocode:
[RequiresSTA]
Test
{
var tb = new Textbox();
tb.DoSomethingAsyncAndThenUpdateTB() //This is done via tb.SetValue being called
}
...
DoSomethingAsyncAndThenUpdateTB()
{
var bw = new BackgroundWorker();
bw.DoWork += ...Do Stuff...
bw.RunWorkerCompleted += { tb.Text = "foo";}
}
The problem I am running into is that the OnComplete is throwing a cross thread exception. However, everything should have been created on the STA thread. I BELIEVE the problem is that UI elements get attached to their thread not on creation, but at a later point....and my textbox ends up attaching to a thread that isn't the STA? Or maybe the backgroundworker?
The question:
When does a UI element actually get attached to a thread?
So first off, there isn't just one STA thread. Any thread can be created as an STA thread when it's created. The UI thread needs to be an STA thread, but not all STA threads are UI threads.
Next, you have the issue that BackgroundWorker needs to have some way of knowing what the UI thread is. It's not magic that it's able to marshal certain events to the UI thread. What it does is it looks at the value of SynchronizationContext.Current in its constructor. It then captures the value of that context, and uses it later as it's definition of the context to post all non-work events to. If you create the BGW outside of the UI thread then it's not able to marshal back to the UI thread later.
If you have an STA thread that you are using as your UI thread, but you haven't set a value of SynchronizationContext.Current, then the BGW won't be able to do its thing.
As for the question of:
When does a UI element actually get attached to a thread?
In its constructor.
Each ui element implements DependencyObject, which implements abstract class DispatcherObject.. When DispatcherObject is created it assigns current dispatcher to the DispatcherObject..
Later on, whenever you access any property of the UI element, there is a validation that checks if you are accessing it from UI thread.
This si done in VerifyAccess
/// <summary>Enforces that the calling thread has access to this <see cref="T:System.Windows.Threading.DispatcherObject" />.</summary>
/// <exception cref="T:System.InvalidOperationException">the calling thread does not have access to this <see cref="T:System.Windows.Threading.DispatcherObject" />.</exception>
[EditorBrowsable(EditorBrowsableState.Never)]
public void VerifyAccess()
{
Dispatcher dispatcher = this._dispatcher;
if (dispatcher != null)
{
dispatcher.VerifyAccess();
}
}
If a calling thread is not the one associated with dispatcher, then exception is thrown
public void VerifyAccess()
{
if (!this.CheckAccess())
{
throw new InvalidOperationException(SR.Get("VerifyAccess"));
}
}
This is called Thread Affinity
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;
}
));