我尝试从Link to original为UWP调整异步集合。但是如果我尝试构建文件,我会收到以下错误:
严重级代码描述项目文件行抑制状态 错误无法确定集合类型'Logic.Model.AsyncObservableCollection`1 [DataAccess.Core.ch.Phex.API.Customer]'的项类型,因为它具有多个Add方法或ICollection实现。要使此集合类型在XAML中可用,请添加公共Add(对象)方法,实现System.Collections.IList或单个System.Collections.Generic.ICollection。 Ui.Windows
有人可以给我一个提示吗?
祝你好运 Kaffi
源代码
public delegate void OnMtCollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs args);
[DataContract]
public class AsyncObservableCollection<T> : ICollection<T>, IReadOnlyList<T>
{
// ******************************************************************
private List<T> _recordedNew = new List<T>();
private List<T> _recordedRemoved = new List<T>();
private bool _isRecording = false;
private readonly object _syncRoot = new object();
protected List<T> List = new List<T>();
private readonly ObservableCollection<T> _obsColl = new ObservableCollection<T>();
private readonly ConcurrentQueue<NotifyCollectionChangedEventArgs> _uiItemQueue = new ConcurrentQueue<NotifyCollectionChangedEventArgs>();
public event OnMtCollectionChangedHandler OnMtCollectionChanged;
public CoreDispatcher Dispatcher { get; set; }
// ******************************************************************
/// <summary>
/// You should never add any item directly in the collection.
/// It should only serve as a readonly collection for the UI.
/// If you ever decide to do so, it would be preferable to use directly the ObsCollection
/// without ever using this class (kind of detach)
/// </summary>
public ObservableCollection<T> ObsColl
{
get { return _obsColl; }
}
// ******************************************************************
public AsyncObservableCollection()
{
//Dispatcher = Application.Current;
Dispatcher = Window.Current.Dispatcher;
}
public AsyncObservableCollection(Collection<T> collection)
{
//Dispatcher = Application.Current;
Dispatcher = Window.Current.Dispatcher;
this.Add(collection.ToList<T>());
}
// ******************************************************************
public bool IsRecording
{
get { return _isRecording; }
set { _isRecording = value; }
}
// ******************************************************************
/// <summary>
/// Return tuple of new and removed items
/// </summary>
/// <returns></returns>
public Tuple<List<T>, List<T>> ResetRecordedItems()
{
Tuple<List<T>, List<T>> changes;
lock (_syncRoot)
{
changes = new Tuple<List<T>, List<T>>(_recordedNew, _recordedRemoved);
_recordedNew = new List<T>();
_recordedRemoved = new List<T>();
}
return changes;
}
// ******************************************************************
public T[] GetCopyOfRecordedItemsNew()
{
T[] changes;
lock (_syncRoot)
{
changes = _recordedNew.ToArray();
}
return changes;
}
// ******************************************************************
public T[] GetCopyOfRecordedItemsRemoved()
{
T[] changes;
lock (_syncRoot)
{
changes = _recordedRemoved.ToArray();
}
return changes;
}
// ******************************************************************
private async void AddTask(NotifyCollectionChangedEventArgs args)
{
_uiItemQueue.Enqueue(args);
// Dispatcher.BeginInvoke(new Action(this.ProcessQueue));
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
this.ProcessQueue();
});
}
// ******************************************************************
private void ProcessQueue()
{
// This Method should always be invoked only by the UI thread only.
if (!this.Dispatcher.HasThreadAccess)
{
throw new Exception("Can't be called from any thread than the dispatcher one");
}
NotifyCollectionChangedEventArgs args;
while (this._uiItemQueue.TryDequeue(out args))
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
int offset = 0;
foreach (T item in args.NewItems)
{
ObsColl.Insert(args.NewStartingIndex + offset, item);
offset++;
}
break;
case NotifyCollectionChangedAction.Remove:
if (args.NewStartingIndex >= 0)
{
ObsColl.RemoveAt(args.NewStartingIndex);
}
else
{
foreach (T item in args.OldItems)
{
ObsColl.Remove(item);
}
}
break;
case NotifyCollectionChangedAction.Replace:
// Replace is used for the [] operator. 'Insert' raise an 'Add' event.
if (args.NewStartingIndex >= 0 && args.OldStartingIndex < 0)
{
throw new ArgumentException(String.Format("Replace action expect NewStartingIndex and OldStartingIndex as: 0 <= {0} <= {1}, {2} <= 0.", args.NewStartingIndex, ObsColl.Count, args.OldStartingIndex));
}
IList listOld = args.OldItems as IList;
IList listNew = args.NewItems as IList;
if (listOld == null || listNew == null)
{
throw new ArgumentException("Both argument Old and New item should be IList in a replace action.");
}
ObsColl[args.NewStartingIndex] = (T)listNew[0];
break;
case NotifyCollectionChangedAction.Reset:
ObsColl.Clear();
break;
case NotifyCollectionChangedAction.Move:
ObsColl.Move(args.OldStartingIndex, args.NewStartingIndex);
break;
default:
throw new Exception("Unsupported NotifyCollectionChangedEventArgs.Action");
}
}
}
// ******************************************************************
public List<T> GetSnapshot()
{
List<T> listCopy = null;
lock (_syncRoot)
{
listCopy = new List<T>(List);
}
return listCopy;
}
// ******************************************************************
public void GetSnapshot(IList list)
{
lock (_syncRoot)
{
List.ApplyForEachItem((path) => list.Add(path));
}
}
// ******************************************************************
public virtual IEnumerator<T> GetEnumerator()
{
return GetSnapshot().GetEnumerator();
}
// ******************************************************************
public virtual IEnumerator<T> GetBlockingEnumerator()
{
return new BlockingIterator<T>(List.GetEnumerator(), _syncRoot);
}
// ******************************************************************
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetSnapshot().GetEnumerator();
}
// ******************************************************************
public void InsertAsFirst(T item)
{
NotifyCollectionChangedEventArgs args;
lock (_syncRoot)
{
List.Insert(0, item);
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, 0);
AddTask(args);
}
RaiseEventCollectionChanged(args);
}
// ******************************************************************
public void Add(T item)
{
NotifyCollectionChangedEventArgs args;
lock (_syncRoot)
{
List.Add(item);
if (_isRecording)
{
_recordedNew.Add(item);
}
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, List.Count - 1);
AddTask(args);
}
RaiseEventCollectionChanged(args);
}
public void Add(IList<T> items)
{
NotifyCollectionChangedEventArgs args;
lock (_syncRoot)
{
int insertIndex = List.Count;
List.AddRange(items);
if (_isRecording)
{
_recordedNew.AddRange(items);
}
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items as IList, insertIndex);
AddTask(args);
}
RaiseEventCollectionChanged(args);
}
// ******************************************************************
public bool Remove(T item)
{
bool isRemoved = false;
NotifyCollectionChangedEventArgs args;
lock (_syncRoot)
{
isRemoved = List.Remove(item);
if (_isRecording)
{
_recordedNew.Add(item);
}
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item);
AddTask(args);
}
RaiseEventCollectionChanged(args);
return isRemoved;
}
// ******************************************************************
public void Replace(T itemOld, T itemNew)
{
NotifyCollectionChangedEventArgs args = null;
lock (_syncRoot)
{
int index = List.IndexOf(itemOld);
if (index < 0 || index >= List.Count)
{
throw new ArgumentException("Invalid old value");
}
if (_isRecording)
{
_recordedNew.Add(itemNew);
_recordedRemoved.Add(itemOld);
}
List[index] = itemNew;
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, itemNew, itemOld, index);
AddTask(args);
}
RaiseEventCollectionChanged(args);
}
// ******************************************************************
private void RaiseEventCollectionChanged(NotifyCollectionChangedEventArgs args)
{
if (OnMtCollectionChanged != null && args != null)
{
OnMtCollectionChanged(this, args);
}
}
// ******************************************************************
/// <summary>
/// To use this function and all 'Unsafe' ones in a MT context,
/// you should have a lock on the collection prior to call it.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T UnsafeGetAt(int index)
{
return List[index];
}
// ******************************************************************
/// <summary>
/// To use this function and all 'Unsafe' ones in a MT context,
/// you should have a lock on the collection prior to call it.
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
/// <returns></returns>
public T UnsafeSetAt(int index, T itemNew)
{
T itemOld = List[index];
if (_isRecording)
{
_recordedNew.Add(itemNew);
_recordedRemoved.Add(itemOld);
}
List[index] = itemNew;
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, itemNew, itemOld, index);
AddTask(args);
RaiseEventCollectionChanged(args);
return itemOld;
}
// ******************************************************************
public void UnsafeInsertAt(int index, T itemNew)
{
if (_isRecording)
{
_recordedNew.Add(itemNew);
}
List.Insert(index, itemNew);
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, itemNew, index);
AddTask(args);
RaiseEventCollectionChanged(args);
}
// ******************************************************************
/// <summary>
/// To use this function and all 'Unsafe' ones in a MT context,
/// you should have a lock on the collection prior to call it.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T UnsafeRemoveAt(int index)
{
T itemOld = List[index];
if (_isRecording)
{
_recordedRemoved.Add(itemOld);
}
List.RemoveAt(index);
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemOld, index);
AddTask(args);
RaiseEventCollectionChanged(args);
return itemOld;
}
// ******************************************************************
public virtual void Clear()
{
NotifyCollectionChangedEventArgs args = null;
lock (_syncRoot)
{
if (_isRecording)
{
_recordedRemoved.AddRange(List);
}
List.Clear();
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
AddTask(args);
}
RaiseEventCollectionChanged(args);
}
// ******************************************************************
public bool Contains(T item)
{
bool result;
lock (_syncRoot)
{
result = List.Contains(item);
}
return result;
}
// ******************************************************************
public void CopyTo(T[] array, int arrayIndex)
{
lock (_syncRoot)
{
List.CopyTo(array, arrayIndex);
}
}
// ******************************************************************
public int Count
{
get
{
lock (_syncRoot)
{
return List.Count;
}
}
}
// ******************************************************************
public void Remove(object item)
{
Remove((T)item);
}
// ******************************************************************
public int IndexOf(object value)
{
return IndexOf((T)value);
}
// ******************************************************************
public object SyncRoot
{
get { return _syncRoot; }
}
// ******************************************************************
public bool IsEqual(IEnumerable<T> iEnumerable)
{
if (this.Count != iEnumerable.Count())
{
return false;
}
lock (_syncRoot)
{
var thisEnumerator = this.GetEnumerator();
thisEnumerator.Reset();
foreach (var t in iEnumerable)
{
thisEnumerator.MoveNext();
if (thisEnumerator.Current.Equals(t))
{
return false;
}
}
Disposal.Dispose(thisEnumerator);
}
return true;
}
// ******************************************************************
private void IsEqualToObsColl()
{
if (!IsEqual(this.ObsColl))
{
Dump();
}
}
// ******************************************************************
/// <summary>
/// This function dumps to the ouput window formated lines of the content of both collections...
/// The list which is thread safe and the obs coll that is used as a readonly list.
/// Its main purpose is to debug to validate that both list contains the same values in the same order.
/// </summary>
private void Dump()
{
Debug.WriteLine("=============== Start");
lock (_syncRoot)
{
IEnumerator enum1 = List.GetEnumerator();
IEnumerator enum2 = ObsColl.GetEnumerator();
enum1.Reset();
enum2.Reset();
bool ok1 = enum1.MoveNext();
bool ok2 = enum2.MoveNext();
while (ok1 || ok2)
{
Debug.WriteLine(String.Format("{0,20} - {0,-20}", ok1 == true ? enum1.Current : "-", ok2 == true ? enum2.Current : "-"));
if (ok1)
ok1 = enum1.MoveNext();
if (ok2)
ok2 = enum2.MoveNext();
}
Disposal.Dispose(enum1);
Disposal.Dispose(enum2);
}
Debug.WriteLine("=============== End");
}
// ******************************************************************
[OnSerializing]
void OnSerializing(StreamingContext ctx)
{
Monitor.Enter(this._syncRoot);
}
// ******************************************************************
[OnSerialized]
void OnSerialized(StreamingContext ctx)
{
Monitor.Exit(this._syncRoot);
}
// ******************************************************************
[OnDeserializing]
void OnDeserializing(StreamingContext ctx)
{
}
// ******************************************************************
[OnDeserialized]
void OnDeserialized(StreamingContext ctx)
{
}
// ******************************************************************
/// <summary>
/// ATTENTION : This method is not MT safe
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this[int index]
{
get { return this.List[index]; }
}
// ******************************************************************
/// <summary>
/// Add stack functionnality to use the list as a queue
/// </summary>
/// <param name="item"></param>
public void Push(T item)
{
Add(item);
}
// ******************************************************************
/// <summary>
/// Add stack functionnality to use the list as a queue
/// </summary>
/// <returns></returns>
public bool TryPop(out T item)
{
lock (_syncRoot)
{
int count = List.Count;
if (count > 0)
{
item = UnsafeRemoveAt(count - 1);
return true;
}
}
item = default(T);
return false;
}
// ******************************************************************
/// <summary>
/// Add queue functionnality to use the list as a queue. Item are added at the end of the list
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
Add(item);
}
// ******************************************************************
/// <summary>
/// Add queue functionnality to use the list as a queue. Item are removed at position 0 (cost a lot due to move all array item left from one position)
/// </summary>
/// <returns></returns>
public bool TryDequeue(out T item)
{
lock (_syncRoot)
{
int count = List.Count;
if (count > 0)
{
item = UnsafeRemoveAt(0);
return true;
}
}
item = default(T);
return false;
}
// ******************************************************************
public bool IsReadOnly
{
get { return false; }
}
// ******************************************************************
bool ICollection<T>.Remove(T item)
{
return Remove(item);
}
// ******************************************************************
public void CopyTo(Array array, int index)
{
lock (_syncRoot)
{
foreach (var t in List)
{
array.SetValue(t, index++);
}
}
}
// ******************************************************************
public bool IsSynchronized
{
get { return Dispatcher.HasThreadAccess; }
}
// ******************************************************************
}
答案 0 :(得分:0)
你错过了代码示例中的一些内容,所以我无法编译它(Disposal
和ApplyForEachItem
),但我可以看到你有一个public void Add(T item)
和一个public void Add(IList<T> items)
方法。 ICollection<T>
接口实现只需要第一个 - 对于第二个,通常的命名约定是AddRange
。