如何将项目添加到 ObservableCollection?
How to add items to ObservableCollection?
这听起来像是一个微不足道的问题,但我找不到任何可以在线解决的问题。我正在使用 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); }
}
所以,这里是:
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 });
}
失败..有时,有例外:
An unhandled exception of type 'System.InvalidOperationException'
occurred in PresentationFramework.dll Additional information: An
ItemsControl is inconsistent with its items source.
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 });
}
});
我什至创建了扩展方法:
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?!
}
}
呃..我做错了什么?我正在改变 ObservableCollection
。所以,每次我想添加新项目时,我都必须从旧的和新的创建新的集合并分配给 ObservableCollection?因为只有赋值运算符对我有用:(
感谢您的帮助!
An ItemsControl is inconsistent with its items source
表示数据网格检测到它持有的项目与源上的项目不匹配,当您将源更改为新集合而不强制刷新项目控件时会发生这种情况
解决此问题的最简单方法是更改
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
set { SetProperty(ref _items, value); }
}
到
public ObservableCollection<Item> Items{get;}= new ObservableCollection<Item>();
或者如果您没有使用 c#6
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
}
这意味着您无法再仅更改其内容
如果您确实需要多线程,那么我会添加以下代码
private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
这很重要,因为在创建 class 时需要 CurrentDispatcher,而不是当前调用它的那个
然后打电话
dispatcher.Invoke(()=>Items.Add(item));
因为这将确保只有创建集合的线程才能更改它
这是一个完整的工作示例
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; }
}
与以下 xaml
<DockPanel >
<Button DockPanel.Dock="Top" Content="Add" Command="{Binding AddItems, Mode=OneWay}" />
<ListView ItemsSource="{Binding Items}"/>
</DockPanel>
你的代码中有几个问题。
a) 当使用 ObservableCollection
时,永远不要再次初始化它。创建单个实例并从中添加或删除项目。所以你的代码就变成了。
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
}
或者这个(如果你的 VS 支持)
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
并用于添加项目
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) 你说
Everything is done in separate thread:
永远不要从非 UI 线程更新 UI 绑定属性。对于数据获取,您可以使用非 UI 线程,但是一旦检索到数据,add/update 中的数据 属性 仅在 UI 线程上。
这听起来像是一个微不足道的问题,但我找不到任何可以在线解决的问题。我正在使用 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); }
}
所以,这里是:
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 });
}
失败..有时,有例外:
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll Additional information: An ItemsControl is inconsistent with its items source.
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 });
}
});
我什至创建了扩展方法:
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?!
}
}
呃..我做错了什么?我正在改变 ObservableCollection
。所以,每次我想添加新项目时,我都必须从旧的和新的创建新的集合并分配给 ObservableCollection?因为只有赋值运算符对我有用:(
感谢您的帮助!
An ItemsControl is inconsistent with its items source
表示数据网格检测到它持有的项目与源上的项目不匹配,当您将源更改为新集合而不强制刷新项目控件时会发生这种情况
解决此问题的最简单方法是更改
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
set { SetProperty(ref _items, value); }
}
到
public ObservableCollection<Item> Items{get;}= new ObservableCollection<Item>();
或者如果您没有使用 c#6
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
}
这意味着您无法再仅更改其内容
如果您确实需要多线程,那么我会添加以下代码
private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
这很重要,因为在创建 class 时需要 CurrentDispatcher,而不是当前调用它的那个
然后打电话
dispatcher.Invoke(()=>Items.Add(item));
因为这将确保只有创建集合的线程才能更改它
这是一个完整的工作示例
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; }
}
与以下 xaml
<DockPanel >
<Button DockPanel.Dock="Top" Content="Add" Command="{Binding AddItems, Mode=OneWay}" />
<ListView ItemsSource="{Binding Items}"/>
</DockPanel>
你的代码中有几个问题。
a) 当使用 ObservableCollection
时,永远不要再次初始化它。创建单个实例并从中添加或删除项目。所以你的代码就变成了。
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get { return _items; }
}
或者这个(如果你的 VS 支持)
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
并用于添加项目
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) 你说
Everything is done in separate thread:
永远不要从非 UI 线程更新 UI 绑定属性。对于数据获取,您可以使用非 UI 线程,但是一旦检索到数据,add/update 中的数据 属性 仅在 UI 线程上。