I've got a new Problem here. I've got a MainWindow in which is a ContentGrid and in this is a Frame. Now I've created different Pages which can be shown in this "content-area". In One of these pages there is a Datagrid bound to a CollectionViewSource which Source is a Database (via EntityFramework). Now, when a Change on this Database-Table happens (solved via ServiceBroker and SQLDependency, firing works fine) The Datagrid have to update.
Now the Problem:
The "Dependency_OnChange"-Event is working in the MainWindow-Thread. When i try to access the CollectionViewSource of the Page to Update it (cvs.View.Refresh) i get an Exception that this is not possible because of another Thread which own this CVS.
I've tried it with different Dispatching like:
Application.Current.Dispatcher.Invoke((Action)(()=>
{
cvs.Source = _db.Table.OrderByDescending(nr => nr.Date).Take(200);
cvs.View.Refresh();
}));
This Codeblock doesn't brings an Exception but i wont update the UI too... It seems that it does nothing.
Can anyone help me?
You data grid will update if your LINQ query evaluates. Right now it just specifies the LINQ IEnumerable but is not evaluating it.
cvs.Source = _db.Table.OrderByDescending(nr => nr.Date).Take(200).ToList();
should do the evaluation of the LINQ for you...
Although I must say your cvs.View.Refresh() call is very expensive as it causes entire grid to refresh. You may have to consider a better design here.
Why dont you set the dataGrid.ItemsSource = _db.Table.DefaultView as the item source to the datagrid directly. I guess if your table updates (and peforms _db.Table.AcceptChanges();) the view would automatically notify the changes to the grid and grid would possibly update itself faster!
But thats just my opinion as I am not aware of your threading context here. But still do try and let me know.
Related
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.
My model has a couple properties one is a string and the other is an observablecolletion. When the model is created it fires off a backgroundworker thread to basically poll a .dll for data. Based on the data it receives it will either set the string and/or add and item to observable collection. My string property seems to fire its Onproperty change just fine and the view updates. However, my observable Collection throws a cross thread exception. I have tried moving code where i set the ObesrvableCollection to the worker.ReportProgress and get the same error. I have moved the logic into the view model and still get the same threading error. I'm unsure why my string property works for one. I have read about Dispatcher.Invoke, but i'm pretty sure that my model should not be aware of this. Can anyone explain the correct way to go about this please.
Just fyi - my view is not tied directly to my model. I have a property for my model in my viewModel and the model gets passed through constructor injection. Just want to put that out there before anyone thinks my model is talking directly to the the view.
Hard to give specifics without code. However, WPF automatically marshals property change notifications for scalar properties but not collections. Hence, you must be modifying the collection from a non-UI thread.
There is no reason why your VM can't use Dispatcher, or perhaps the more generic SynchronizationContext if you prefer. It can make things more tricky to test, however.
If you post code there may be a way you can simplify things.
As Kent said, if you're not on the UI you need to use the Dispatcher to update your collection:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
// update your ObservableCollection here
}));
I'm trying to use the code from the following sample:
http://msdn.microsoft.com/en-us/magazine/dd569761.aspx
to dynamically create FlowDocuments with a table bound to xml.
The problem is that I first load the template and only then set the Databinding (because I use different data each time).
Anybody knows how to modify the code (maybe react to another event?) to make the code
from this example trigger code generation after the "dynamic" datacontext is set?
Thank you in advance
Ah, so you are using the 'BindableRun' class and are never getting bound? I recently encountered the same problem. You can trigger the data binding by updating the layout on any 'UIElement' object tied to the same dispatcher. The object does not even need to be in the visual tree - it is just invoking the context layout manager shared by all objects using the same dispatcher.
Simple example:
Button b = new Button();
b.UpdateLayout();
To get an accurate page count from the document paginator, you should update the layout first. Be careful though, as updating the layout again later will also invalidate any document paginators tied to the same dispatcher.
I want to do something that I thought would be very simple. I want to bind a generated Entity Framework EntityCollection to a WPF DataGrid. I also want this grid to be sortable.
I have tried all kinds of things to make this happen, including using a CollectionViewSource. However, nothing seems to work. Using a normal CollectionViewSource around the EntityCollection gives me:
'System.Windows.Data.BindingListCollectionView' view does not support sorting.
Ok...strange. I would have thought this would work. Next on the CollectionViewSource, I try setting:
CollectionViewType="ListCollectionView"
Great, sorting now works. But wait, I can't add or remove entities using the grid now, presumably because ListCollectionView doesn't support this with an entity framework context.
So, I guess I need to capture events coming out of the datagrid in order to add or remove entities manually from my context. Now I can't find an event to capture to detect an add...!
Why is this so hard? This should be the standard "demo" case that Microsoft should have designed around.
Any ideas?
BindingListCollectionView is not directly the problem. See 'System.Windows.Data.BindingListCollectionView' view does not support sorting on Microsoft Connect for details why it does not support sorting.
On the other hand ListCollectionView supports sorting obviously using a different technique.
I have also tried the following code and it worked beautifully. I have basically implemented your XAML from the other post in code.
DatabaseContext.ObjectStateManager.ObjectStateManagerChanged += (o, args) => Debug.WriteLine(args.Element.ToString());
var collectionViewSource = new CollectionViewSource();
((ISupportInitialize)collectionViewSource).BeginInit();
collectionViewSource.CollectionViewType = typeof (ListCollectionView);
collectionViewSource.Source = ((IListSource) DatabaseContext.Survey).GetList();
collectionViewSource.SortDescriptions.Add(new SortDescription {PropertyName = "Name"});
((ISupportInitialize)collectionViewSource).EndInit();
var editableCollectionView = (IEditableCollectionView)collectionViewSource.View;
var survey = editableCollectionView.AddNew();
// Before this point ObjectStateManager event has occurred and Debug Output is written to.
editableCollectionView.CommitNew();
DatabaseContext.SaveChanges(); // THIS WORKS TOO!
My DatabaseContext.Survey is an ObjectQuery<Survey>. Are you showing an ObjectQuery or a Linq-to-EF query? The former obviously works for me. The latter is where I see a problem. That is not supposed to work.
It seems like, the View just doesn't get notified, when chages occur. So, I just do
myCollectionViewSource.View.Refresh(); //refresh CollectionViewSource of CollectionViewType="ListCollectionView"
after add/remove of list items.
But then the whole state gets refreshed (e.g. you have to reset preselected sorting again). You need to check, if this fits your needs.
Is there a way to get the wpf toolkit DataGrid to show new rows when its bound to a DataSet? In other words, I have a DataGrid, I've set its ItemsSource to a DataTable, and everything seems to work fine, except I can't get the grid to show rows that I add to the DataTable programatically.
You can set the datagrid.ItemsSource to an ObservableCollection<T>.
ObservableCollection<YourItem> items = new ObservableCollection<YourItem>();
yourDataGrid.ItemsSource = items;
Then you should be able to just add to the collection to get the new rows to appear:
Edit: Based on updated info.
if (Dispatcher.CheckAcces())
{
// already on thread UI control was created on
items.Add(<your item>);
}
else
{
// update on same thread UI control was created on
// BeginInvoke would invoke a delegate which would call items.Add(<your item>)
Dispatcher.BeginInvoke(...);
}
See Dispatcher. All System.Windows.UserControl objects have a Dispatcher property.
I'm not sure how anyone would have figured this out, especially since I didn't mention it, but the problem was that I was updating the dataset from a thread other than the form's main thread, i.e. the thread that the grid was created on. You can do updates from another thread, but you can't do inserts, although I don't know why. If someone could shed some light on that, I'd appreciate it. My guess would be that the grid checks to see if the insert is coming in on another thread, and if so, it ignores it because that would cause an exception.