自定义 Observable 不更新 UWP ListView
Custom Observable not updating UWP ListView
我实现了自己的 ObservableDictionary
,因为 A) 我对看到其他人发布的代码不满意,并且 B) 想要实践。据我所知,一切正常,包括通知听众,但由于某种原因,我试图用来显示内容的 ListView
没有更新。
我的理解是它使用INotifyCollectionChanged
监听变化。就像完整性检查一样,我在页面初始化时为 CollectionChanged
事件附加了一个侦听器,当我添加项目时它会正确触发,即使 ListView
没有显示任何内容。我知道我已经正确设置了 XAML——如果我在将所有内容添加到字典后在代码隐藏中重新设置 ItemsSource
,所有内容都会正确显示;同样,将 ObservableDictionary
替换为内置 ObservableCollection
(不触及 XAML)即使没有手动干预也能很好地工作,只要我更改二元字典的参数 .Add(...)
适合单元素集合one.
与 my source code to that for the ObservableCollection
相比,我能看到的唯一主要区别是它们包含添加对象的索引,但即使将其添加到我的代码中也不会改变任何内容。
为了提取相关代码,我的(诚然有点过度设计的)事件触发系统:
Tuple<bool, TValue> SendAddEvents(Func<bool> preTest, Func<Tuple<bool, TValue>> action, KeyValuePair<TKey, TValue> item) {
if (preTest.Invoke()) {
#if (SUPPORT_PROPERTYCHANGING_EVENT)
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Keys)));
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Values)));
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Count)));
#endif
OnAdding(item);
}
var result = action.Invoke();
if (result.Item1) {
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
item
));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Keys)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Values)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
OnAdd(item);
}
return result;
}
而且(据我所知),官方系统:
protected override void InsertItem(int index, T item)
{
CheckReentrancy();
base.InsertItem(index, item);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
using (BlockReentrancy())
{
CollectionChanged(this, e);
}
}
}
就像我说的,我看不出两者之间有什么本质区别。我是不是遗漏了一些明显的东西,或者是否有关于 ListView
如何处理任何人都知道如何解决的可观察集合的特殊之处?
编辑:作为参考,XAML 中的元素与以下行绑定;零索引行包含一个 TextBlock
,我用它来指示集合何时已完全填充。不过,正如我上面所说,只需将 "Library" 属性 的类型从 ObservableDictionary<string, StorageFile>
更改为 ObservableCollection<string>
(并且 ObservableCollection<StorageFile>
没有什么不同)就可以使一切正常工作而无需我触摸 XAML.
<ListView ItemsSource="{Binding Library, ElementName=page}" Grid.Row="1" />
查看您的源代码,我注意到您的 ObservableDictionaryBase<TKey, TValue>
没有实现 IList<TValue>
。您需要有一个可用于 ListView
的索引器。
绑定到 ListView.ItemsSource
的任何内容都需要同时实现 INotifyCollectionChanged
和 IList
。当它收到字典中更改的通知时,它将尝试通过索引器(即 page.Library[n]
)检索更改。
这最终会引导您实现类似于 KeyedCollection<TKey, TValue>
的实现,其中您有一个有序的键控元素列表。
我实现了自己的 ObservableDictionary
,因为 A) 我对看到其他人发布的代码不满意,并且 B) 想要实践。据我所知,一切正常,包括通知听众,但由于某种原因,我试图用来显示内容的 ListView
没有更新。
我的理解是它使用INotifyCollectionChanged
监听变化。就像完整性检查一样,我在页面初始化时为 CollectionChanged
事件附加了一个侦听器,当我添加项目时它会正确触发,即使 ListView
没有显示任何内容。我知道我已经正确设置了 XAML——如果我在将所有内容添加到字典后在代码隐藏中重新设置 ItemsSource
,所有内容都会正确显示;同样,将 ObservableDictionary
替换为内置 ObservableCollection
(不触及 XAML)即使没有手动干预也能很好地工作,只要我更改二元字典的参数 .Add(...)
适合单元素集合one.
与 my source code to that for the ObservableCollection
相比,我能看到的唯一主要区别是它们包含添加对象的索引,但即使将其添加到我的代码中也不会改变任何内容。
为了提取相关代码,我的(诚然有点过度设计的)事件触发系统:
Tuple<bool, TValue> SendAddEvents(Func<bool> preTest, Func<Tuple<bool, TValue>> action, KeyValuePair<TKey, TValue> item) {
if (preTest.Invoke()) {
#if (SUPPORT_PROPERTYCHANGING_EVENT)
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Keys)));
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Values)));
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Count)));
#endif
OnAdding(item);
}
var result = action.Invoke();
if (result.Item1) {
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
item
));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Keys)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Values)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
OnAdd(item);
}
return result;
}
而且(据我所知),官方系统:
protected override void InsertItem(int index, T item)
{
CheckReentrancy();
base.InsertItem(index, item);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
using (BlockReentrancy())
{
CollectionChanged(this, e);
}
}
}
就像我说的,我看不出两者之间有什么本质区别。我是不是遗漏了一些明显的东西,或者是否有关于 ListView
如何处理任何人都知道如何解决的可观察集合的特殊之处?
编辑:作为参考,XAML 中的元素与以下行绑定;零索引行包含一个 TextBlock
,我用它来指示集合何时已完全填充。不过,正如我上面所说,只需将 "Library" 属性 的类型从 ObservableDictionary<string, StorageFile>
更改为 ObservableCollection<string>
(并且 ObservableCollection<StorageFile>
没有什么不同)就可以使一切正常工作而无需我触摸 XAML.
<ListView ItemsSource="{Binding Library, ElementName=page}" Grid.Row="1" />
查看您的源代码,我注意到您的 ObservableDictionaryBase<TKey, TValue>
没有实现 IList<TValue>
。您需要有一个可用于 ListView
的索引器。
绑定到 ListView.ItemsSource
的任何内容都需要同时实现 INotifyCollectionChanged
和 IList
。当它收到字典中更改的通知时,它将尝试通过索引器(即 page.Library[n]
)检索更改。
这最终会引导您实现类似于 KeyedCollection<TKey, TValue>
的实现,其中您有一个有序的键控元素列表。