I have a list of objects which among other properties contain a Brush (property name "Color").
In a Backgroundworker I want to write the contents of these objects to a file.
Everything works fine until I try to read the color of the brush property.
//this works fine
var brush = myObject.Color;
//this fails with InvalidOperationException
var c= ((SolidColorBrush)myObject.Color).Color;
It seems as if my code is somehow modifying the color but I don't see where/how.
Is there a way to read-only access the color of the brush?
Regards,
tabina
Do i sense that you're accessing UI objects on a thread that didn't create them. You cannot access UI objects from a BGW thread. That's the golden rule not to forget using BGW class
If there is no need to modify the objects after they have been added to that list, you might try to Freeze them before they get accessed from a different thread.
Make sure that the Freeze call happens in the UI thread, where you created those objects. You might also check if freezing is possible at all by calling CanFreeze beforehand.
Related
In my project I have to use WPF to place a big set of similar user controls(around 2000) on Canvas object. Basically, it's just a set of rectangles, that can change visibility, can be selected and store data object inside.
I add new controls with help of attached property like this:
public static readonly DependencyProperty VisualStaticBlocksProperty =
DependencyProperty.RegisterAttached("VisualStaticBlocks", typeof(ObservableCollection<VisualBlockViewModel>), typeof(BindableBlocksBehaviour),
new UIPropertyMetadata(null, VisualStaticBlocksPropertyChanged));
private static void VisualStaticBlocksPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
ClickSightView clickSight = source as ClickSightView;
ObservableCollection<VisualBlockViewModel> visualBlocks = e.NewValue as ObservableCollection<VisualBlockViewModel>;
if (clickSight != null && visualBlocks != null)
{
foreach (VisualBlockViewModel visualBlock in visualBlocks)
{
clickSight.StaticBlocksCanvas.Children.Add(new VisualBlockView(visualBlock));
}
}
}
However, it takes a lot of time to build all of them(around 2 seconds). I used a profiler to check that main problem is in LoadBaml() method, which is called in InitializeComponent() method.
As I understand, LoadBaml() is used to parse xaml markup. Is it possible somehow to cache the LoadBaml() result for component and reuse it instead of parse xaml each time I create new control instance?
EDIT:
To represent this set of objects visually I have created user control with Canvas on it, and created attached property VisualStaticBlocks to attachblock view models(type VisualBlockViewModel) to this control and insert visual block instances(type VisualBlockView) directly to Canvas.
EDIT2:
I've solved the problem by giving up using user controls for this purpose at all.
As my controls are quite simple, I used Rectangle() class instead with 3 manually added bindings and 3 manually added events. Of course, there were no InitializeComponent() calls at all.It allowed me to build the set of 2000 rectangles in 200 miliseconds, which is 10 times faster.
Anyway, still will be grateful for information if I can clone similar objects without loading BAML each time.
It sounds like you have an issue with the time it takes to create visual elements. I can see why you think you need to call InitializeComponent, but that is not how WPF works.
As noted here:
The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).
Which leads me to suspect you do not understand how (or why) InitializeComponent works; it is impossible to call it once to build multiple elements and externally, no less.
You are using ObservableCollection, which neither works well with large data sets nor complex views. Consider using a thread-safe ObservableCollection and add the data objects on a background thread. This shouldn't be an issue because you're adding data objects (view models) versus visual objects (views); visual objects should be added on the same thread (UI) they are created.
It would help to provide additional information as you have not explained how you represent these objects visually. Is the collection bound to an ItemsControl and does it define a DataTemplate to visually represent each data object?
A common exception one can get when working with multiple threads in WPF is:
The calling thread cannot access this object because a different thread owns it
What are the options to deal with this properly?
Depending on the situation there are various options:
Accessing a control from another thread
e.g. updating a TextBlock with progress information.
Data Binding:
In this case the easiest thing you can do is avoiding the direct interaction with the control. You can just bind the property you want to access or modify to an object whose class implements INotifyPropertyChanged and then set the property on that object instead. The framework will handle the rest for you. (In general you rarely should need to interact with UI-elements directly, you can almost always bind the respective properties and work with the binding source instead; one case where direct control access may be necessary is control authoring.)
There are some cases where data binding alone is not enough, for example when trying to modify a bound ObservableCollection<T>, for this you need...
Dispatching:
You can dispatch your accessing code to the thread owning the object, this can be done by calling Invoke or BeginInvoke on the Dispatcher owning the object being accessed (getting this Dispatcher is possible on another thread).
e.g.
new Thread(ThisThreadStart).Start();
void ThisThreadStart()
{
textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test"));
}
If it is not clear on which thread a method is executed you can use Dispatcher.CheckAccess to either dispatch or execute an action directly.
e.g.
void Update()
{
Action action = () => myTextBlock.Text = "Test";
var dispatcher = myTextBlock.Dispatcher;
if (dispatcher.CheckAccess())
action();
else
dispatcher.Invoke(action);
}
If an object is not a DispatcherObject and you still need the associated Dispatcher you can use Dispatcher.CurrentDispatcher in the thread creating the object (so doing this in the method being executed by a thread will not do you any good). For convenience as you usually create objects on the application's main UI thread; you can get that thread's Dispatcher from anywhere using Application.Current.Dispatcher.
Special cases:
BackgroundWorker
Move any control access to ProgressChanged as it occurs on the thread that created the instance (which should of course be the UI-thread)
Timers
In WPF you can use the DispatcherTimer for convenience, it does the dispatching for you so any code in Tick is invoked on the associated dispatcher. If you can delegate the dispatching to the data binding system you of course can use a normal timer as well.
You can read more about how the Dispatcher queue works and WPF threading in general on MSDN.
Accessing an object created on another thread
e.g. loading an image in the background.
If the object in question is not Freezable you should in general simply avoid creating it on another thread or restricting access to the creating thread. If it is Freezable you just need to call Freeze to make it accessible to other threads.
Accessing a data object from another thread
That is, the type whose instance is being updated is user-code. If an exception is thrown this situation probably came about by someone using DependencyObject as base type for a data class.
This situation is the same as accessing a control and the same approaches can be applied but usually it should be avoided in the first place. Granted, this allows for simple property change notifications via dependency properties and those properties can also be bound but often enough this is just not worth giving up thread-independency. You can get change notifications from INotifyPropertyChanged and the binding system in WPF is inherently asymmetrical, there always is a property that is bound (target) and something that is the source for this binding. Usually the UI is the target and the data is the source, meaning that only UI components should need dependency properties.
That would be several hundred lines of code, for something I "figured out".
But the summary is:
App_OnStartup
generate a background thread
in the callback,
Call
Application.Current.MainWindow.Dispatcher.CheckAccess() - gets the exception
Application.Current.Dispatcher.CheckAccess() does not
I have a udp listener object that communicates through events where the method/callbacks are +='ed in my mainWindow wpf .cs file.
The event handler functions are called with parameters, one being the message I want displayed in a listbox in the mainWindow.cs
Using the information in this thread by H.B. above;
I have added, tested and handled the crossthread in wpf in my eventhandler callback using the following code, but I use a real message not a hard coded one:
listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));
UPDATE:
This is better because you can put more things in the anonymous function.
listBox1.Dispatcher.Invoke((Action)delegate
{
listBox1.Items.Add(e.ReaderMessage);
});
I have this:
Shows a waiting animation to 'block' the UI while performs a loading operation in the background.
At the end of the loading I call a method that instances a User Control and displays some data by using Bindings (and ObservableCollection among others)
This User Control gets displayed and user can interact with it, however the ObservableCollection seems to be stuck in another thread as it doesn't allow to add new items to it.
I've tried to update the UI at the Completed event of a BackgroundWorker, using Dispatcher, using DispatchTimer... all of this displays the User Control, but the ObservableCollection stays of out reach for adding.
The code that tries to add items to the collection is inside the UserControl.
The exact error is: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread"
This does not happen if I don't do the loading in the background.
Thank you for any workaround for this.
By the way, trying to add the items using Dispatcher doesn't work either.
In other words, what I would like to do is to create an object in the UI Thread while being in the background... I know this may sounds silly.
You may have to check which Dispatcher you are using? In your case you could be referring to two different dispatchers.
Also why not use thread safe observable collection?
Usually I will create the objects on my UI thread, then populate them with data obtained from a background thread.
For example,
void async LoadButton_Clicked(object sender, EventArgs e)
{
MyCollection = new ObservableCollection<SomeItem>();
// Can also use a BackgroundWorker
var collectionData = await GetCollectionData();
foreach(var item in collectionData)
{
MyCollection.Add(item);
}
}
I'm using C# 5.0 async and await keywords for asynchronous operations, but you can also use a BackgroundWorker that does your background work.
You can also use Dispatcher.BeginInvoke() for some lighter background work (such as copying data into MyCollection), although for heavy work I find it still locks up the UI so I prefer to use background threads.
It is not possible to modify the contents of an ObservableCollection on a separate thread if a view is bound to this collection, instead you can override ObservableCollection and provide support for it and use it across your application.
This sample contains exactly what you want - http://tomlev2.wordpress.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/
When it comes to threads and ui-elements one of the most important rules to follow which may safe you a lot of trouble in the long run is to keep ui-element instantiation on the ui-thread. Surely you can manage that. And if you need to change those objects from another thread you can use the Dispatcher.
(The threading model reference may also be of interest)
Thank you everyone for your help... a guy from MS visited the company (sorry for the commercial annotation) to do other things, I stoled him and show this behavior. In a matter of 2 minutes founds the source of the problem... which I'm not sure to really understand.
It happens that I'm using an ICollectionView to display a sorted/filtered version of my problematic ObservableCollection. I was creating this ICollectionView in the constructor of my class, so at the moment of deserialization it was created in another thread. He suggested to move this creation to a further time in code (when the related property gets read). This solved the problem.
However the ObservableCollection, created in that other thread, now lets me add new item. Not sure why, but now it works.
Sorry for being this late and thank you again.
Well lets say i have an object that i databind to, it implements INotifyPropertyChanged to tell the GUI when a value has changed...
if i trigger this from a different thread than the GUI thread how would wpf behave?
and will it make sure that it gets the value of the property from memory and not the cpu cache?
more or less im asking if wpf does lock() on the object containing the property...
Value changes fired by INotifyPropertyChanged are automatically marshalled back onto the dispatcher. (http://blog.lab49.com/archives/1166)
Fire this event on any thread you like...
Value changes fired by INotifyCollectionChanged are NOT reliably marshalled onto the dispatcher. (http://csharplive.wordpress.com/2008/09/11/wpf-data-binding-observablecollection-cross-thread-binding-support/)
If you need to update an observable collection from a different thread, follow the advice in this link
In addition to #Rob Fonseca-Ensor's answer, there is some good news for those lucky enough to use WPF4.5:
WPF enables you to access and modify data collections on threads other than the one that created the collection. This enables you to use a background thread to receive data from an external source, such as a database, and display the data on the UI thread. By using another thread to modify the collection, your user interface remains responsive to user interaction. (https://msdn.microsoft.com/en-us/library/bb613588(v=vs.110).aspx#xthread_access)
There's a helpful summary by Jonathan Antoine: http://www.jonathanantoine.com/2011/09/24/wpf-4-5-part-7-accessing-collections-on-non-ui-threads/
In practice it seems to work as expected and seems to be thread-safe (haven't seen anything odd happen or exceptions as a result of updating on background thread). I believe it invokes on to the UI thread when needed, but I'm not too familiar with the internals.
I'm trying to write a background worker that processes a flowdocument. I can't access the properties of flowdocument objects because of the thread verification. I tried to serialize the document and loaded it on the worker thread which actually solved the thread verfication issue. However, once the processing is complete I also need to use things like TextPointer objects. Those objects now point to a objects in the copy not the original.
Can anyone suggest the best way to approach such background processing in WPF?
You can't, WPF objects can only be accessed from the thread that created them so by definition you can't do any background processing on them.
But, as you already discovered you can use serialization techniques to create a copy in another thread and you can serialize the result back.
XamlWriter/XamlReader can serialize almost every WPF object but can be slow on large object graphs.
And for TextPointer maybe you can use GetOffsetToPosition/GetPositionAtOffset to recreate an equivalent TextPointer back in the main thread.
Another options is to use freezables, objects that inherit from Freezeable can be used from other threads (after the Freeze method is called), documents are not freezable but drawing and geometries are - so you may be able to transfer document "fragments" between threads as drawings.