You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
463 lines
14 KiB
463 lines
14 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Windows;
|
|
using System.Windows.Threading;
|
|
using Microsoft.Research.DynamicDataDisplay.DataSources;
|
|
|
|
namespace CoreTest
|
|
{
|
|
public class QueueCollection<T> : IEnumerable<T>, INotifyCollectionChanged, INotifyPropertyChanged
|
|
{
|
|
Queue<T> _queue;
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return _queue.Count;
|
|
}
|
|
}
|
|
|
|
public QueueCollection()
|
|
{
|
|
this._queue = new Queue<T>();
|
|
}
|
|
|
|
public QueueCollection(Queue<T> queue)
|
|
{
|
|
this._queue = queue;
|
|
}
|
|
|
|
public QueueCollection(int capacity)
|
|
{
|
|
this._queue = new Queue<T>(capacity);
|
|
}
|
|
|
|
public T Dequeue()
|
|
{
|
|
var value = _queue.Dequeue();
|
|
this.OnPropertyChanged("Count");
|
|
this.OnPropertyChanged("Item[]");
|
|
this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, value, 0);
|
|
return value;
|
|
}
|
|
|
|
public void Enqueue(T value)
|
|
{
|
|
_queue.Enqueue(value);
|
|
this.OnPropertyChanged("Count");
|
|
this.OnPropertyChanged("Item[]");
|
|
this.OnCollectionChanged(NotifyCollectionChangedAction.Add, value, _queue.Count);
|
|
}
|
|
|
|
|
|
public void Clear()
|
|
{
|
|
_queue.Clear();
|
|
this.OnPropertyChanged("Count");
|
|
this.OnPropertyChanged("Item[]");
|
|
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
private void OnPropertyChanged(string propertyName)
|
|
{
|
|
if (this.PropertyChanged != null)
|
|
{
|
|
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
|
|
|
|
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (this.CollectionChanged != null)
|
|
{
|
|
try
|
|
{
|
|
// Walk thru invocation list
|
|
foreach (NotifyCollectionChangedEventHandler handler in CollectionChanged.GetInvocationList())
|
|
{
|
|
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
|
|
// If the subscriber is a DispatcherObject and different thread
|
|
if (dispatcherObject != null && !dispatcherObject.CheckAccess())
|
|
{
|
|
// Invoke handler in the target dispatcher's thread
|
|
dispatcherObject.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, handler, this, e);
|
|
}
|
|
else // Execute handler as is
|
|
handler(this, e);
|
|
}
|
|
}
|
|
catch (Exception err) { }
|
|
}
|
|
}
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
|
|
{
|
|
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
|
|
}
|
|
|
|
|
|
#region INotifyCollectionChanged Members
|
|
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
#endregion
|
|
|
|
#region INotifyPropertyChanged Members
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<T> Members
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return _queue.GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable Members
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return _queue.GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class QueueListSource<T> : IPointDataSource
|
|
{
|
|
private class QueueListIterator : IPointEnumerator, IDisposable
|
|
{
|
|
private readonly QueueListSource<T> dataSource;
|
|
private readonly IEnumerator<T> enumerator;
|
|
|
|
public QueueListIterator(QueueListSource<T> dataSource)
|
|
{
|
|
this.dataSource = dataSource;
|
|
this.enumerator = dataSource.collection.GetEnumerator();
|
|
}
|
|
public bool MoveNext()
|
|
{
|
|
return this.enumerator.MoveNext();
|
|
}
|
|
public void GetCurrent(ref Point p)
|
|
{
|
|
this.dataSource.FillPoint(this.enumerator.Current, ref p);
|
|
}
|
|
public void ApplyMappings(DependencyObject target)
|
|
{
|
|
this.dataSource.ApplyMappings(target, this.enumerator.Current);
|
|
}
|
|
public void Dispose()
|
|
{
|
|
this.enumerator.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
private bool collectionChanged;
|
|
private bool updatesEnabled = true;
|
|
private int _capacity;
|
|
private readonly QueueCollection<T> collection;
|
|
private readonly List<Mapping<T>> mappings = new List<Mapping<T>>();
|
|
private Func<T, double> xMapping;
|
|
private Func<T, double> yMapping;
|
|
private Func<T, Point> xyMapping;
|
|
private FIFOHandler handle;
|
|
public event EventHandler DataChanged;
|
|
public QueueCollection<T> Collection
|
|
{
|
|
get
|
|
{
|
|
return this.collection;
|
|
}
|
|
}
|
|
public QueueListSource(int capacity)
|
|
{
|
|
_capacity = capacity;
|
|
collection = new QueueCollection<T>(capacity);
|
|
this.collection.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnCollectionChanged);
|
|
handle = new FIFOHandler(FIFO);
|
|
if (typeof(T) == typeof(Point))
|
|
{
|
|
this.xyMapping = ((T t) => (Point)((object)t));
|
|
}
|
|
}
|
|
public QueueListSource(IEnumerable<T> data)
|
|
: this(data.Count())
|
|
{
|
|
if (data == null)
|
|
{
|
|
throw new ArgumentNullException("data");
|
|
}
|
|
foreach (T current in data)
|
|
{
|
|
this.collection.Enqueue(current);
|
|
}
|
|
}
|
|
public void SuspendUpdate()
|
|
{
|
|
this.updatesEnabled = false;
|
|
}
|
|
public void ResumeUpdate()
|
|
{
|
|
this.updatesEnabled = true;
|
|
if (this.collectionChanged)
|
|
{
|
|
this.collectionChanged = false;
|
|
this.RaiseDataChanged();
|
|
}
|
|
}
|
|
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (this.updatesEnabled)
|
|
{
|
|
this.RaiseDataChanged();
|
|
return;
|
|
}
|
|
this.collectionChanged = true;
|
|
}
|
|
public void AppendMany(IEnumerable<T> data)
|
|
{
|
|
if (data == null)
|
|
{
|
|
throw new ArgumentNullException("data");
|
|
}
|
|
this.updatesEnabled = false;
|
|
foreach (T current in data)
|
|
{
|
|
this.collection.Enqueue(current);
|
|
}
|
|
this.updatesEnabled = true;
|
|
this.RaiseDataChanged();
|
|
}
|
|
//已重写
|
|
public void AppendAsync(Dispatcher dispatcher, T item)
|
|
{
|
|
dispatcher.Invoke(handle, DispatcherPriority.Normal, item);
|
|
}
|
|
|
|
public void SetXMapping(Func<T, double> mapping)
|
|
{
|
|
if (mapping == null)
|
|
{
|
|
throw new ArgumentNullException("mapping");
|
|
}
|
|
this.xMapping = mapping;
|
|
}
|
|
public void SetYMapping(Func<T, double> mapping)
|
|
{
|
|
if (mapping == null)
|
|
{
|
|
throw new ArgumentNullException("mapping");
|
|
}
|
|
this.yMapping = mapping;
|
|
}
|
|
public void SetXYMapping(Func<T, Point> mapping)
|
|
{
|
|
if (mapping == null)
|
|
{
|
|
throw new ArgumentNullException("mapping");
|
|
}
|
|
this.xyMapping = mapping;
|
|
}
|
|
|
|
private void FIFO(T item)
|
|
{
|
|
if (this.collection.Count >= _capacity)
|
|
{
|
|
this.collection.Dequeue();
|
|
}
|
|
this.collection.Enqueue(item);
|
|
this.RaiseDataChanged();
|
|
}
|
|
private void FillPoint(T elem, ref Point point)
|
|
{
|
|
if (this.xyMapping != null)
|
|
{
|
|
point = this.xyMapping(elem);
|
|
return;
|
|
}
|
|
if (this.xMapping != null)
|
|
{
|
|
point.X = this.xMapping(elem);
|
|
}
|
|
if (this.yMapping != null)
|
|
{
|
|
point.Y = this.yMapping(elem);
|
|
}
|
|
}
|
|
private void ApplyMappings(DependencyObject target, T elem)
|
|
{
|
|
if (target != null)
|
|
{
|
|
foreach (Mapping<T> current in this.mappings)
|
|
{
|
|
target.SetValue(current.Property, current.F(elem));
|
|
}
|
|
}
|
|
}
|
|
public IPointEnumerator GetEnumerator(DependencyObject context)
|
|
{
|
|
return new QueueListSource<T>.QueueListIterator(this);
|
|
}
|
|
private void RaiseDataChanged()
|
|
{
|
|
if (this.DataChanged != null)
|
|
{
|
|
this.DataChanged(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
public delegate void FIFOHandler(T item);
|
|
}
|
|
|
|
|
|
internal sealed class Mapping<TSource>
|
|
{
|
|
internal DependencyProperty Property
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
internal Func<TSource, object> F
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
}
|
|
|
|
public sealed class ReverseObservableQueue<T> : List<T>, INotifyCollectionChanged, INotifyPropertyChanged
|
|
{
|
|
public ReverseObservableQueue(int capacity)
|
|
: base(capacity)
|
|
{
|
|
this.capacity = capacity;
|
|
}
|
|
|
|
int capacity;
|
|
object async = new object();
|
|
|
|
public void ReverseEnqueue(T item)
|
|
{
|
|
lock (async)
|
|
{
|
|
while (Count >= capacity) RemoveAt(Count - 1);
|
|
Insert(0, item);
|
|
this.OnPropertyChanged("Count");
|
|
this.OnPropertyChanged("Item[]");
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, 0));
|
|
}
|
|
}
|
|
|
|
public T ReverseDequeue()
|
|
{
|
|
lock (async)
|
|
{
|
|
if (base.Count > 1)
|
|
{
|
|
T item = this[Count - 1];
|
|
Remove(item);
|
|
this.OnPropertyChanged("Count");
|
|
this.OnPropertyChanged("Item[]");
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, Count - 1));
|
|
return item;
|
|
}
|
|
return default(T);
|
|
}
|
|
}
|
|
|
|
public new void Clear()
|
|
{
|
|
lock (async)
|
|
{
|
|
base.Clear();
|
|
this.OnPropertyChanged("Count");
|
|
this.OnPropertyChanged("Item[]");
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
}
|
|
|
|
private void OnPropertyChanged(string propertyName)
|
|
{
|
|
if (this.PropertyChanged != null)
|
|
{
|
|
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
|
|
{
|
|
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
|
|
}
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (this.CollectionChanged != null)
|
|
{
|
|
try
|
|
{
|
|
var Handlers = CollectionChanged.GetInvocationList();
|
|
foreach (NotifyCollectionChangedEventHandler handler in Handlers)
|
|
{
|
|
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
|
|
// If the subscriber is a DispatcherObject and different thread
|
|
if (dispatcherObject != null && !dispatcherObject.CheckAccess())
|
|
{
|
|
// Invoke handler in the target dispatcher's thread
|
|
dispatcherObject.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, handler, this, e);
|
|
}
|
|
else // Execute handler as is
|
|
handler(this, e);
|
|
}
|
|
}
|
|
catch (Exception err) { }
|
|
}
|
|
}
|
|
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
}
|
|
|
|
public class ObservableCollectionEx<T> : ObservableCollection<T>
|
|
{
|
|
// Override the event so this class can access it
|
|
public override event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
|
{
|
|
// Be nice - use BlockReentrancy like MSDN said
|
|
using (BlockReentrancy())
|
|
{
|
|
NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
|
|
if (eventHandler == null)
|
|
return;
|
|
|
|
Delegate[] delegates = eventHandler.GetInvocationList();
|
|
// Walk thru invocation list
|
|
foreach (NotifyCollectionChangedEventHandler handler in delegates)
|
|
{
|
|
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
|
|
// If the subscriber is a DispatcherObject and different thread
|
|
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
|
|
{
|
|
// Invoke handler in the target dispatcher's thread
|
|
dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
|
|
}
|
|
else // Execute handler as is
|
|
handler(this, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|