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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 14:02:09  来源:igfitidea点击:

How to add items to ObservableCollection?

c#wpfmvvmprismobservablecollection

提问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 线程的属性中添加/更新数据。