diff --git a/Nuclex.Support (PC).csproj b/Nuclex.Support (PC).csproj
index fce7a8e..a5dc8c4 100644
--- a/Nuclex.Support (PC).csproj
+++ b/Nuclex.Support (PC).csproj
@@ -65,6 +65,10 @@
+
+ false
+ ObservableCollection
+
false
UnintrusivePriorityQueue
@@ -139,6 +143,10 @@
false
Progression
+
+ false
+ ProgressionCollection
+
false
ProgressUpdateEventArgs
diff --git a/Source/Collections/ObservableCollection.cs b/Source/Collections/ObservableCollection.cs
new file mode 100644
index 0000000..4f117a0
--- /dev/null
+++ b/Source/Collections/ObservableCollection.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Nuclex.Support.Collections {
+
+ /// Generic collection of progressions
+ public class ObservableCollection : Collection {
+
+ #region class ItemEventArgs
+
+ /// Arguments class for events that need to pass a progression
+ public class ItemEventArgs : EventArgs {
+
+ /// Initializes a new event arguments supplier
+ /// Item to be supplied to the event handler
+ public ItemEventArgs(ItemType item) {
+ this.item = item;
+ }
+
+ /// Obtains the collection item the event arguments are carrying
+ public ItemType Item {
+ get { return this.item; }
+ }
+
+ /// Item that's passed to the event handler
+ private ItemType item;
+
+ }
+
+ #endregion // class ItemEventArgs
+
+ /// Raised when an item has been added to the collection
+ public event EventHandler ItemAdded;
+ /// Raised when an item is removed from the collection
+ public event EventHandler ItemRemoved;
+ /// Raised the collection is about to be cleared
+ public event EventHandler Clearing;
+
+ /// Removes all elements from the Collection
+ protected override void ClearItems() {
+ OnClearing();
+
+ base.ClearItems();
+ }
+
+ ///
+ /// Inserts an element into the ProgressionCollection at the specified index
+ ///
+ ///
+ /// The object to insert. The value can be null for reference types.
+ ///
+ /// The zero-based index at which item should be inserted
+ protected override void InsertItem(int index, ItemType item) {
+ base.InsertItem(index, item);
+
+ OnAdded(item);
+ }
+
+ ///
+ /// Removes the element at the specified index of the ProgressionCollection
+ ///
+ /// The zero-based index of the element to remove
+ protected override void RemoveItem(int index) {
+ ItemType item = base[index];
+
+ base.RemoveItem(index);
+
+ OnRemoved(item);
+ }
+
+ /// Replaces the element at the specified index
+ ///
+ /// The new value for the element at the specified index. The value can be null
+ /// for reference types
+ ///
+ /// The zero-based index of the element to replace
+ protected override void SetItem(int index, ItemType item) {
+ ItemType oldItem = base[index];
+
+ base.SetItem(index, item);
+
+ OnRemoved(oldItem);
+ OnAdded(item);
+ }
+
+ /// Fires the 'ItemAdded' event
+ /// Item that has been added to the collection
+ protected virtual void OnAdded(ItemType item) {
+ if(ItemAdded != null)
+ ItemAdded(this, new ItemEventArgs(item));
+ }
+
+ /// Fires the 'ItemRemoved' event
+ /// Item that has been removed from the collection
+ protected virtual void OnRemoved(ItemType item) {
+ if(ItemRemoved != null)
+ ItemRemoved(this, new ItemEventArgs(item));
+ }
+
+ /// Fires the 'Clearing' event
+ protected virtual void OnClearing() {
+ if(Clearing != null)
+ Clearing(this, EventArgs.Empty);
+ }
+
+ }
+
+} // namespace Nuclex.Support.Collections
diff --git a/Source/Collections/Parentable.cs b/Source/Collections/Parentable.cs
index a900f4f..51f2c68 100644
--- a/Source/Collections/Parentable.cs
+++ b/Source/Collections/Parentable.cs
@@ -5,7 +5,7 @@ namespace Nuclex.Support.Collections {
/// Base class for objects that can be parented to an owner
/// Type of the parent object
- public class Parentable where ParentType : class {
+ public class Parentable {
/// Assigns a new parent to this instance
internal void SetParent(ParentType parent) {
diff --git a/Source/Collections/ParentingCollection.cs b/Source/Collections/ParentingCollection.cs
index a3a08c8..4d5e866 100644
--- a/Source/Collections/ParentingCollection.cs
+++ b/Source/Collections/ParentingCollection.cs
@@ -15,8 +15,7 @@ namespace Nuclex.Support.Collections {
/// Type of the parent object to assign to items
/// Type of the items being managed in the collection
public class ParentingCollection : Collection
- where ItemType : Parentable
- where ParentType : class {
+ where ItemType : Parentable {
/// Reparents all elements in the collection
/// New parent to take ownership of the items
@@ -30,7 +29,7 @@ namespace Nuclex.Support.Collections {
/// Clears all elements from the collection
protected override void ClearItems() {
for(int index = 0; index < Count; ++index)
- base[index].SetParent(null);
+ base[index].SetParent(default(ParentType));
base.ClearItems();
}
@@ -46,7 +45,7 @@ namespace Nuclex.Support.Collections {
/// Removes an element from the collection
/// Index of the element to remove
protected override void RemoveItem(int index) {
- base[index].SetParent(null);
+ base[index].SetParent(default(ParentType));
base.RemoveItem(index);
}
@@ -70,7 +69,7 @@ namespace Nuclex.Support.Collections {
if(disposeItems) {
// Have the items do their cleanup work
- Reparent(null);
+ Reparent(default(ParentType));
// Dispose of all the items in the collection that implement IDisposable
foreach(ItemType item in this) {
diff --git a/Source/Tracking/IAbortable.cs b/Source/Tracking/IAbortable.cs
new file mode 100644
index 0000000..06489c1
--- /dev/null
+++ b/Source/Tracking/IAbortable.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Nuclex.Support.Tracking {
+
+ /// Interface for abortable processes
+ public interface IAbortable {
+
+ /// Aborts the running process. Can be called from any thread.
+ void AsyncAbort();
+
+ }
+
+} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/MultiProgression.cs b/Source/Tracking/MultiProgression.cs
new file mode 100644
index 0000000..29d013e
--- /dev/null
+++ b/Source/Tracking/MultiProgression.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Nuclex.Support.Tracking {
+
+ public class MultiProgression : Progression
+ where ProgressionType : Progression {
+
+ }
+
+} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/Operation.cs b/Source/Tracking/Operation.cs
new file mode 100644
index 0000000..bba44f9
--- /dev/null
+++ b/Source/Tracking/Operation.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Nuclex.Support.Tracking {
+
+ /// Base class for observable operations running in the background
+ public abstract class Operation : Progression {
+
+ /// Possible outcomes of an operation
+ public enum Outcomes {
+ /// The operation has not ended yet
+ Pending,
+ /// The operation has succeeded
+ Success,
+ /// The operation has failed
+ Failure,
+ }
+/*
+ /// Begins executing the operation
+ public abstract void BeginExecute();
+
+ public virtual void Execute() {
+ try {
+ BeginExecute();
+ this.outcome = EndExecute();
+ }
+ catch(Exception exception) {
+ this.outcome = Outcomes.Failure;
+ }
+ }
+
+ /// Ends the
+ public virtual Outcomes EndExecute() {
+ WaitHandle.WaitOne();
+
+ return this.outcome;
+ }
+
+ /// The Outcome of the operation when it has finished
+ public Outcomes Outcome {
+ get { return this.outcome; }
+ }
+
+ /// Outcome of the operation
+ protected Outcomes outcome;
+*/
+ }
+
+} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/ProgressUpdateEventArgs.cs b/Source/Tracking/ProgressUpdateEventArgs.cs
new file mode 100644
index 0000000..7e50889
--- /dev/null
+++ b/Source/Tracking/ProgressUpdateEventArgs.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Nuclex.Support.Tracking {
+
+ /// Event arguments for a progress update notification
+ public class ProgressUpdateEventArgs : EventArgs {
+
+ /// Initializes the progress update informations
+ /// Achieved progress ranging from 0.0 to 1.0
+ public ProgressUpdateEventArgs(float progress) {
+ this.progress = progress;
+ }
+
+ /// Currently achieved progress
+ public float Progress {
+ get { return this.progress; }
+ }
+
+ /// Achieved progress
+ protected float progress;
+
+ }
+
+} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/Progression.cs b/Source/Tracking/Progression.cs
new file mode 100644
index 0000000..32e68f2
--- /dev/null
+++ b/Source/Tracking/Progression.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Threading;
+
+namespace Nuclex.Support.Tracking {
+
+ /// Base class for actions that give an indication of their progress
+ ///
+ ///
+ /// By encapsulating long-running operations which will ideally be running in
+ /// a background thread in a class that's derived from Progression you can wait
+ /// for the completion of the operation and receive feedback on the achieved
+ /// progress. This is useful for displaying a progress bar, loading screen or
+ /// some means of entertaining the user while he waits for the operation to
+ /// complete. It is also possible to register callbacks which will be fired once
+ /// the progression has ended.
+ ///
+ ///
+ /// This class deliberately does not provide an Execute() or similar method to
+ /// clearly seperate the initiation of an operation from just monitoring it.
+ /// By omitting an Execute() method, it also becomes possible to construct a
+ /// progression just-in-time when it is explicitely asked for.
+ ///
+ ///
+ public abstract class Progression {
+
+ #region class EndedDummyProgression
+
+ /// Dummy progression which always is in the 'ended' state
+ internal class EndedDummyProgression : Progression {
+
+ /// Initializes a new ended dummy progression
+ public EndedDummyProgression() {
+ OnAsyncEnded();
+ }
+
+ }
+
+ #endregion // class EndedDummyProgression
+
+ /// A dummy progression that's always in the 'ended' state
+ ///
+ /// Useful if an operation is already complete when it's being asked for or
+ /// when a progression that's lazily created is accessed after the original
+ /// operation has ended already.
+ ///
+ public static readonly Progression EndedDummy = new EndedDummyProgression();
+
+ /// will be triggered to report when progress has been achieved
+ public event EventHandler AsyncProgressUpdated;
+
+ /// Will be triggered when the progression has ended
+ public event EventHandler AsyncEnded;
+
+ /// Whether the progression has ended already
+ public virtual bool Ended {
+ get { return ended; }
+ }
+
+ /// WaitHandle that can be used to wait for the progression to end
+ public WaitHandle WaitHandle {
+ get {
+ lock(this) {
+
+ // The WaitHandle will only be created when someone asks for it!
+ if(this.doneEvent == null)
+ this.doneEvent = new ManualResetEvent(this.ended);
+
+ }
+
+ return this.doneEvent;
+ }
+ }
+
+ /// Fires the progress update event
+ /// Progress to report (ranging from 0.0 to 1.0)
+ ///
+ /// Informs the observers of this progression about the achieved progress.
+ ///
+ protected virtual void OnAsyncProgressUpdated(float progress) {
+ OnAsyncProgressUpdated(new ProgressUpdateEventArgs(progress));
+ }
+
+ /// Fires the progress update event
+ /// Progress to report (ranging from 0.0 to 1.0)
+ ///
+ /// Informs the observers of this progression about the achieved progress.
+ /// Allows for classes derived from the Progression class to easily provide
+ /// a custom event arguments class that has been derived from the
+ /// Progression's ProgressUpdateEventArgs class.
+ ///
+ protected virtual void OnAsyncProgressUpdated(ProgressUpdateEventArgs eventArguments) {
+ EventHandler copy = AsyncProgressUpdated;
+ if(copy != null)
+ copy(this, eventArguments);
+ }
+
+ /// Fires the AsyncEnded event
+ ///
+ /// This event should be fired by the implementing class when its work is completed.
+ /// It's of no interest to this class whether the outcome of the process was successfull
+ /// or not, the outcome and results of the process taking place need to be communicated
+ /// seperately.
+ ///
+ protected virtual void OnAsyncEnded() {
+ lock(this) {
+ ended = true;
+
+ if(this.doneEvent != null)
+ this.doneEvent.Set();
+ }
+
+ EventHandler copy = AsyncEnded;
+ if(copy != null)
+ copy(this, EventArgs.Empty);
+ }
+
+ /// Event that will be set when the progression is completed
+ ///
+ /// This event is will only be created when it is specifically asked for using
+ /// the WaitHandle property.
+ ///
+ private volatile ManualResetEvent doneEvent;
+ /// Whether the operation has completed yet
+ private volatile bool ended;
+
+ }
+
+} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/ProgressionCollection.cs b/Source/Tracking/ProgressionCollection.cs
new file mode 100644
index 0000000..951882c
--- /dev/null
+++ b/Source/Tracking/ProgressionCollection.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+using Nuclex.Support.Collections;
+
+namespace Nuclex.Support.Tracking {
+
+ /// Non-generic version of the progression collection
+ public class ProgressionCollection : ProgressionCollection { }
+
+ /// Generic collection of progressions
+ public class ProgressionCollection
+ : ObservableCollection where ProgressionType : Progression { }
+
+} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/ThreadedMethodOperation.cs b/Source/Tracking/ThreadedMethodOperation.cs
new file mode 100644
index 0000000..22dcb74
--- /dev/null
+++ b/Source/Tracking/ThreadedMethodOperation.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Nuclex.Support.Tracking {
+
+ public class ThreadedMethodOperation : Operation {
+ }
+
+} // namespace Nuclex.Support.Tracking