wpf 如何向 ObservableCollection 添加项目?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42702266/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add items to ObservableCollection?
提问by Marvin Law
That might sound like a trivial question, but I couldn't find anything that works online. I'm using PRISMand I'm one step before I walk away and never go back to this framework. Here's why:
这听起来像是一个微不足道的问题,但我在网上找不到任何有用的东西。我正在使用PRISM并且在我走开之前迈出了一步,永远不会回到这个框架。原因如下:
I have pretty ObservableCollectionthat 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:
我有漂亮ObservableCollection,如果我给你一个列表,并忘掉它基本的工作原理。但这不是 的目标ObservableCollection,对吧?它改变了..所以,这是集合:
<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.
PresentationFramework.dll 中出现类型为“System.InvalidOperationException”的未处理异常 附加信息:ItemsControl 与其项目源不一致。
AddRange()? Forget it..
AddRange()? 算了吧..
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 :(
呃..我做错了什么?我正在改变ObservableCollection。所以,每次我想添加新项目时,我都必须从旧的和新的集合中创建新的集合并分配给 ObservableCollection?因为只有赋值运算符对我有用:(
Thanks for any help!
谢谢你的帮助!
回答by MikeT
An ItemsControl is inconsistent with its items source
ItemsControl 与其项目源不一致
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
或者如果您没有使用 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
这很重要,因为您在创建类时需要 CurrentDispatcher 而不是当前调用它的那个
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
使用以下 xaml
<DockPanel >
<Button DockPanel.Dock="Top" Content="Add" Command="{Binding AddItems, Mode=OneWay}" />
<ListView ItemsSource="{Binding Items}"/>
</DockPanel>
回答by Nikhil Agrawal
Couple of problems in your code.
您的代码中有几个问题。
a) When working with ObservableCollection, never initialize it again. Create a single instance and add or remove items from it. So you code becomes.
a) 使用 时ObservableCollection,切勿再次初始化。创建单个实例并从中添加或删除项目。所以你的代码就变成了。
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
}
OR this (if your VS supports)
或者这个(如果你的 VS 支持)
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
and for adding items
和添加项目
foreach (var item in InitializeItems()) Items.Add(item);
Items.Add(new Item() { ItemId = 1 });
for (int i = 1; i < 10; i++)
{
Items.Add(new Item() { ItemId = i });
}
b) You said
b) 你说
Everything is done in separate thread:
一切都在单独的线程中完成:
Never update UI bound properties from Non-UI Threads. For Data fetching you can use Non-UI Threads, but once data is retrieved, add/update data in property on UI Threads only.
永远不要从非 UI 线程更新 UI 绑定属性。对于数据获取,您可以使用非 UI 线程,但是一旦检索到数据,只能在 UI 线程的属性中添加/更新数据。

