From 2d145ca867f8eccd810a64ad0eff1ce908e3eaff Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Mon, 16 Apr 2007 17:18:16 +0000 Subject: [PATCH] Provided a common class for observable collections similar to what the .NET Framework 3.0 offers; ParentingCollection no longer requires the parent type to be a reference type; Laid the foundation for a new progress tracking framework that can be used to drive progress bars, loading screens and such git-svn-id: file:///srv/devel/repo-conversion/nusu@9 d2e56fa2-650e-0410-a79f-9358c0239efd --- Nuclex.Support (PC).csproj | 8 ++ Source/Collections/ObservableCollection.cs | 109 ++++++++++++++++++ Source/Collections/Parentable.cs | 2 +- Source/Collections/ParentingCollection.cs | 9 +- Source/Tracking/IAbortable.cs | 15 +++ Source/Tracking/MultiProgression.cs | 12 ++ Source/Tracking/Operation.cs | 50 ++++++++ Source/Tracking/ProgressUpdateEventArgs.cs | 26 +++++ Source/Tracking/Progression.cs | 128 +++++++++++++++++++++ Source/Tracking/ProgressionCollection.cs | 16 +++ Source/Tracking/ThreadedMethodOperation.cs | 10 ++ 11 files changed, 379 insertions(+), 6 deletions(-) create mode 100644 Source/Collections/ObservableCollection.cs create mode 100644 Source/Tracking/IAbortable.cs create mode 100644 Source/Tracking/MultiProgression.cs create mode 100644 Source/Tracking/Operation.cs create mode 100644 Source/Tracking/ProgressUpdateEventArgs.cs create mode 100644 Source/Tracking/Progression.cs create mode 100644 Source/Tracking/ProgressionCollection.cs create mode 100644 Source/Tracking/ThreadedMethodOperation.cs 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