如何向 ObservableCollection 添加项目?

Mar*_*Law 6 c# wpf prism observablecollection mvvm

这听起来像是一个微不足道的问题,但我在网上找不到任何有效的东西。我正在使用PRISM并且在我走开之前迈出了一步,永远不会回到这个框架。原因如下:

我有漂亮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); }
    }
Run Code Online (Sandbox Code Playgroud)

所以,这里是:

        Items = InitializeItems(); // Works great!
        Items.Add(new Item() { ItemId = 1 }); // Also works
Run Code Online (Sandbox Code Playgroud)

但是之后..

        for (int i = 1; i < 10; i++)
        {
            Items.Add(new Item() { ItemId = i });
        }
Run Code Online (Sandbox Code Playgroud)

失败.. 有时,除了:

PresentationFramework.dll 中出现类型为“System.InvalidOperationException”的未处理异常 附加信息:ItemsControl 与其项目源不一致。

AddRange()? 算了吧..

一切都在单独的线程中完成:

        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 });
            }
        });
Run Code Online (Sandbox Code Playgroud)

我什至创建了扩展方法:

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?!

    }
}
Run Code Online (Sandbox Code Playgroud)

呃..我做错了什么?我正在改变ObservableCollection。所以,每次我想添加新项目时,我都必须从旧的和新的集合中创建新的集合并分配给 ObservableCollection?因为只有分配运算符对我有用:(

谢谢你的帮助!

Mik*_*keT 7

ItemsControl 与其项目源不一致

意味着数据网格检测到它所持有的项目与源上的项目不匹配,当您将源更改为新集合而不强制刷新项目控件时会发生这种情况

解决此问题的最简单方法是更改

private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
    get { return _items; }
    set { SetProperty(ref _items, value); }
}
Run Code Online (Sandbox Code Playgroud)

public ObservableCollection<Item> Items{get;}= new ObservableCollection<Item>();
Run Code Online (Sandbox Code Playgroud)

或者如果您没有使用 c#6

private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
    get { return _items; }
}
Run Code Online (Sandbox Code Playgroud)

这意味着您不能再仅更改集合的内容

如果你真的需要多线程,那么我会添加以下代码

private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
Run Code Online (Sandbox Code Playgroud)

这很重要,因为您在创建类时需要 CurrentDispatcher 而不是当前调用它的那个

然后打电话

dispatcher.Invoke(()=>Items.Add(item));
Run Code Online (Sandbox Code Playgroud)

因为这将确保只有创建集合的线程才能更改它

这是一个完整的工作示例

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; }
}
Run Code Online (Sandbox Code Playgroud)

使用以下 xaml

<DockPanel >
    <Button DockPanel.Dock="Top" Content="Add" Command="{Binding AddItems, Mode=OneWay}"  />
    <ListView ItemsSource="{Binding Items}"/>

</DockPanel>
Run Code Online (Sandbox Code Playgroud)


Nik*_*wal 5

您的代码中有几个问题。

a) 使用 时ObservableCollection,切勿再次对其进行初始化。创建单个实例并向其中添加或删除项目。所以你的代码就变成了。

private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
    get { return _items; }
}
Run Code Online (Sandbox Code Playgroud)

或者这个(如果你的VS支持)

public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
Run Code Online (Sandbox Code Playgroud)

以及添加项目

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 });
}
Run Code Online (Sandbox Code Playgroud)

b) 你说

一切都在单独的线程中完成:

切勿从非 UI 线程更新 UI 绑定属性。对于数据获取,您可以使用非 UI 线程,但是一旦检索到数据,就只能在 UI 线程上的属性中添加/更新数据。