|
#region Imports
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Threading;
#endregion
namespace GTryf.Util
{
#region Extensions Class
public static class Extensions
{
#region Simple Array Monitoring
public static ObservableCollection<T> AsMonitored<T>(this T[] array)
{
return new MonitoredProjectedArray<T, T>(array, 100, i => i);
}
public static ObservableCollection<T> AsMonitored<T>(this T[] array, int period)
{
return new MonitoredProjectedArray<T, T>(array, period, i => i);
}
#endregion
#region Projected Array Monitoring
public static ObservableCollection<P> AsMonitoredProjected<T, P>(this T[] array, Func<T, P> project)
{
return new MonitoredProjectedArray<T, P>(array, project);
}
public static ObservableCollection<P> AsMonitoredProjected<T, P>(this T[] array, int period, Func<T, P> project)
{
return new MonitoredProjectedArray<T, P>(array, period, project);
}
#endregion
}
#endregion
#region MonitoredProjectedArray class
class MonitoredProjectedArray<T, P>
: ObservableCollection<P>, IDisposable
{
#region Fields
protected T[] _monitoredArray;
protected Func<T, P> _project;
#endregion
#region Constructors
public MonitoredProjectedArray(T[] a, int period, Func<T, P> project)
: base(a.AsParallel().AsOrdered().Select(project))
{
_monitoredArray = a;
_project = project;
_changedItems = new ConcurrentQueue<ChangedItem>();
AutoResetEvent autoEvent = new AutoResetEvent(false);
TimerCallback tcb = state =>
{
var e = (AutoResetEvent)state;
QueueChangedItems();
e.Set();
};
timer = new Timer(tcb, autoEvent, period, period);
}
public MonitoredProjectedArray(T[] a, Func<T, P> project) : this(a, 500, project) { }
#endregion
#region Destructor
~MonitoredProjectedArray()
{
Dispose();
#if DEBUG
System.Diagnostics.Debug.WriteLine("MonitoredArray Finalized.");
#endif
}
#endregion
#region Overrides
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
// Override OnCollectionChanged so that we make use of the Dispatcher
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
using (BlockReentrancy())
{
// Get the CollectionChanged event handler
System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
if (eventHandler != null)
{
foreach (var handler in eventHandler.GetInvocationList())
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
else
(handler as System.Collections.Specialized.NotifyCollectionChangedEventHandler)(this, e);
}
}
}
}
#endregion
#region Underlying Collection Watcher
#region Changed Items Structure
struct ChangedItem
{
public int Index;
public T NewValue;
}
#endregion
#region Fields used by the monitoring thread
ConcurrentQueue<ChangedItem> _changedItems;
Timer timer;
#endregion
#region Main Worker Method
protected void QueueChangedItems()
{
Parallel.For(0, _monitoredArray.Count(), i =>
{
if (!_project(_monitoredArray[i]).Equals(this[i]))
{
var ci = new ChangedItem() { Index = i, NewValue = _monitoredArray[i] };
if (!_changedItems.Contains(ci))
_changedItems.Enqueue(ci);
}
});
Action updateAction = () =>
{
ChangedItem item;
while (_changedItems.TryDequeue(out item))
this[item.Index] = _project(item.NewValue);
};
Parallel.Invoke(updateAction, updateAction, updateAction, updateAction);
}
#endregion
#endregion
#region IDisposable Members
public void Dispose()
{
timer.Dispose();
}
#endregion
}
#endregion
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I am a software developer (mainly in C# and T-SQL) for a project management company in Athens, Greece. I have been working with computers since early 1987. I am adept at Pascal, C, C++, Java (my MSc was sponsored by Sun Microsystems), Lisp, Scheme, F#, C# VB.Net, Perl and some others that are too obscure to mention. When I want a quick and dirty solution to a programming problem I use a functional language, such as Haskell, Scheme or, more recently, F#.
I also play the keyboards and compose music.
---------------------------------------------------------
MSc Distributed Systems and Networks - University of Kent at Canterbury
BEng Computer Systems Engineering - University of Kent at Canterbury