That might sound like a trivial question, but I couldn't find anything that works online. I'm using PRISM
and I'm one step before I walk away and never go back to this framework. Here's why:
I have pretty ObservableCollection
that basically works if I assign a list to it and forget about it. But that's not the goal of ObservableCollection
, right? It changes.. So, here's the collection:
<DataGrid ItemsSource="{Binding Items, Mode=TwoWay}" AutoGenerateColumns="True" />
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
set { SetProperty(ref _items, value); }
}
So, here goes:
Items = InitializeItems(); // Works great!
Items.Add(new Item() { ItemId = 1 }); // Also works
but then..
for (int i = 1; i < 10; i++)
{
Items.Add(new Item() { ItemId = i });
}
failed.. sometimes, with exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll Additional information: An ItemsControl is inconsistent with its items source.
AddRange()
? Forget it..
Everything is done in separate thread:
Task.Factory.StartNew(() =>
{
Items = InitializeItems(); // Works great!
Items.Add(new Item() { ItemId = 1 }); // Also works
for (int i = 1; i < 10; i++)
{
Items.Add(new Item() { ItemId = i });
}
});
I even created extension method:
public static class ObservableCollectionExtensions
{
public static void AddRange<T>(this ObservableCollection<T> data, List<T> range)
{
if (range == null) throw new ArgumentNullException("range");
foreach (var i in range) data.Add(i);
// How can I force ObservableCollection to update?!
}
}
Ehh.. what am I doing wrong? I'm changing ObservableCollection
. So, everytime I want to add new items, I have to create new collection from old and new ones and assign to ObservableCollection? Because only assign operator works for me :(
Thanks for any help!
An ItemsControl is inconsistent with its items source
means the the datagrid has detected that the items it is holding don't match those on the source, this happens when you change the source to a new collection with out forcing a refresh on the items control
the easiest way to fix this is to change
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
set { SetProperty(ref _items, value); }
}
to
public ObservableCollection<Item> Items{get;}= new ObservableCollection<Item>();
or if you are not using c#6
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
}
this means that you can't change the collection anymore only its content
if you truly require Multithreading then i would add the following code
private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
this is vital as you need the CurrentDispatcher at the time the class was created not the one currently calling it
then call
dispatcher.Invoke(()=>Items.Add(item));
as this will ensure that only the thread that created the collection changes it
here is a complete working example
public class VM
{
public VM()
{
AddItems = new DelegateCommand(() => Task.Run(()=>
Parallel.ForEach(
Enumerable.Range(1,1000),
(item) => dispatcher.Invoke(() => Items.Add(item))
))
);
}
public ObservableCollection<int> Items { get; } = new ObservableCollection<int>();
private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
public DelegateCommand AddItems { get; }
}
with the following xaml
<DockPanel >
<Button DockPanel.Dock="Top" Content="Add" Command="{Binding AddItems, Mode=OneWay}" />
<ListView ItemsSource="{Binding Items}"/>
</DockPanel>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With