在我的UWP应用程序中有一个ListBox,列出最近使用的文件。我将该信息存储在ObservableCollection中,并将其绑定到ListBox的ItemsSource属性。更新按预期工作。
但是,如果我从ObservableCollection切换到SmartList(这是一个实现INotifyCollectionChanged的自定义列表),绑定将不再起作用。我经常在WPF应用程序中使用这个类,它总是运行良好。我还有测试CollecionChanged事件的单元测试。
在UpdateTriggers方面x:Bind和Binding之间是否存在差异,或者有人知道我在这里缺少什么?
//更新
以下是SmartList<T>
的代码:
public class SmartList<T> : SmartCollectionBase<T>, IList<T>, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public override bool IsReadOnly => false;
public override int Count => list.Count;
private List<T> list;
public SmartList()
{
list = new List<T>();
}
public SmartList(IEnumerable<T> collection)
{
list = new List<T>(collection);
}
public SmartList(int capacity)
{
list = new List<T>(capacity);
}
public override IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
public override void Add(T item)
{
list.Add(item);
OnItemsAdded(new SmartListChangedEventArgs(new SmartChange(item, list.Count - 1, ChangeType.Added)));
NotifyCountChange();
}
public void AddRange(IEnumerable<T> items)
{
if (!items.Any())
return;
var changes = new List<SmartChange>();
var idx = list.Count;
foreach (var item in items)
{
list.Add(item);
changes.Add(new SmartChange(item, idx, ChangeType.Added));
idx++;
}
OnItemsAdded(new SmartListChangedEventArgs(changes));
NotifyCountChange();
}
public void AddRange(params T[] items)
{
AddRange((IEnumerable<T>) items);
NotifyCountChange();
}
public override void Clear()
{
if (list.Count == 0)
return;
var old = list;
list = new List<T>(list.Capacity);
OnItemsRemoved(new SmartListChangedEventArgs(old.Select((c, i) => new SmartChange(c, i, ChangeType.Removed))));
NotifyCountChange();
}
public override bool Contains(T item)
{
return list.Contains(item);
}
public override void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
public override bool Remove(T item)
{
int idx = IndexOf(item);
if (idx == -1)
return false;
RemoveAt(idx);
NotifyCountChange();
return true;
}
public int IndexOf(T item)
{
return list.IndexOf(item);
}
public void Insert(int index, T item)
{
list.Insert(index, item);
OnItemsAdded(new SmartListChangedEventArgs(new SmartChange(item, index, ChangeType.Added)));
NotifyCountChange();
}
public void InsertRange(int index, IEnumerable<T> items)
{
if (index > list.Count || index < 0)
throw new IndexOutOfRangeException();
if (!items.Any())
return;
var changes = items.Select((item, i) => new SmartChange(item, index + i, ChangeType.Added));
list.InsertRange(index, items);
OnItemsAdded(new SmartListChangedEventArgs(changes));
NotifyCountChange();
}
public void InsertRange(int index, params T[] items)
{
InsertRange(index, (IEnumerable<T>) items);
NotifyCountChange();
}
public void RemoveAt(int index)
{
var item = list[index];
list.RemoveAt(index);
OnItemsRemoved(new SmartListChangedEventArgs(new SmartChange(item, index, ChangeType.Removed)));
NotifyCountChange();
}
public T this[int index]
{
get { return list[index]; }
set
{
var old = list[index];
if (ReferenceEquals(old, value))
return;
list[index] = value;
OnItemsReplaced(new SmartListChangedEventArgs(new SmartChange(old, index, ChangeType.Removed), new SmartChange(value, index, ChangeType.Added)));
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void NotifyCountChange()
{
OnPropertyChanged(nameof(Count));
}
及其基类:
public abstract class SmartCollectionBase<T> : ISmartCollection<T>
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event EventHandler<SmartListChangedEventArgs> ItemsAdded;
public event EventHandler<SmartListChangedEventArgs> ItemsRemoved;
public event EventHandler<SmartListChangedEventArgs> ItemsMoved;
public event EventHandler<SmartListChangedEventArgs> ItemsReplaced;
public event EventHandler<SmartListChangedEventArgs> ItemsChanged;
public abstract IEnumerator<T> GetEnumerator();
public abstract void Add(T item);
public abstract void Clear();
public abstract bool Contains(T item);
public abstract void CopyTo(T[] array, int arrayIndex);
public abstract bool Remove(T item);
public abstract int Count { get; }
public abstract bool IsReadOnly { get; }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
protected virtual void OnItemsAdded(SmartListChangedEventArgs e)
{
ItemsAdded?.Invoke(this, e);
OnItemsChanged(e);
if (MustInvokeINotifyCollectionChanged())
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.Changes.Select(c => c.Change).ToList()));
}
protected virtual void OnItemsRemoved(SmartListChangedEventArgs e)
{
ItemsRemoved?.Invoke(this, e);
OnItemsChanged(e);
if (MustInvokeINotifyCollectionChanged())
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, e.Changes.Select(c => c.Change).ToList(), e.Changes[0].Index));
}
protected virtual void OnItemsMoved(SmartListChangedEventArgs e)
{
ItemsMoved?.Invoke(this, e);
OnItemsChanged(e);
if (MustInvokeINotifyCollectionChanged())
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, e.Changes.Select(c => c.Change).ToList(), e.Changes[0].Index, e.Changes[0].OldIndex));
}
protected virtual void OnItemsReplaced(SmartListChangedEventArgs e)
{
ItemsReplaced?.Invoke(this, e);
OnItemsChanged(e);
if (MustInvokeINotifyCollectionChanged())
{
var changes = e.Changes.GroupBy(c => c.ChangeType).OrderBy(g => g.Key).Select(g => g.ToList()).ToList();
if (changes.Count != 2 || changes[0].Count != changes[1].Count)
throw new InvalidOperationException("A replace operation must have an eqaul amount of added and removed items");
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, changes[0], changes[1]));
}
}
protected virtual void OnItemsChanged(SmartListChangedEventArgs e)
{
ItemsChanged?.Invoke(this, e);
}
private bool MustInvokeINotifyCollectionChanged()
{
return CollectionChanged != null;
}
public override string ToString()
{
return ToString(", ", "{0}");
}
public string ToString(string seperator, string format)
{
return this.Stringify(seperator, format);
}
}