From 4261d9b4490f3d6baa728bd1a71ffc51bef07e01 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Sun, 22 Jul 2007 21:55:35 +0000 Subject: [PATCH] Ported the ProgressReporter and the TrackingBar to the AsyncProgressBar control to eliminate the very similar code both controls were using to perform thread synchronization git-svn-id: file:///srv/devel/repo-conversion/nuwi@10 d2e56fa2-650e-0410-a79f-9358c0239efd --- Nuclex.Windows.Forms.csproj | 2 + Source/AsyncProgressBar/AsyncProgressBar.cs | 1 + .../ProgressReporterForm.Designer.cs | 4 +- .../ProgressReporter/ProgressReporterForm.cs | 63 +---------------- Source/TrackingBar/TrackingBar.cs | 70 ++++--------------- 5 files changed, 21 insertions(+), 119 deletions(-) diff --git a/Nuclex.Windows.Forms.csproj b/Nuclex.Windows.Forms.csproj index 7607c03..4e36029 100644 --- a/Nuclex.Windows.Forms.csproj +++ b/Nuclex.Windows.Forms.csproj @@ -101,6 +101,8 @@ ProgressReporterForm.cs + false + ProgressReporterForm.Designer false diff --git a/Source/AsyncProgressBar/AsyncProgressBar.cs b/Source/AsyncProgressBar/AsyncProgressBar.cs index 607ceb8..aa299f2 100644 --- a/Source/AsyncProgressBar/AsyncProgressBar.cs +++ b/Source/AsyncProgressBar/AsyncProgressBar.cs @@ -73,6 +73,7 @@ namespace Nuclex.Windows.Forms { /// Synchronously updates the value visualized in the progress bar private void updateProgress() { + // Cache these to shorten the code that follows :) int minimum = base.Minimum; int maximum = base.Maximum; diff --git a/Source/ProgressReporter/ProgressReporterForm.Designer.cs b/Source/ProgressReporter/ProgressReporterForm.Designer.cs index 7768ada..746888b 100644 --- a/Source/ProgressReporter/ProgressReporterForm.Designer.cs +++ b/Source/ProgressReporter/ProgressReporterForm.Designer.cs @@ -25,7 +25,7 @@ namespace Nuclex.Windows.Forms { private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.cancelButton = new System.Windows.Forms.Button(); - this.progressBar = new System.Windows.Forms.ProgressBar(); + this.progressBar = new Nuclex.Windows.Forms.AsyncProgressBar(); this.statusLabel = new System.Windows.Forms.Label(); this.controlCreationTimer = new System.Windows.Forms.Timer(this.components); this.SuspendLayout(); @@ -92,7 +92,7 @@ namespace Nuclex.Windows.Forms { #endregion private System.Windows.Forms.Button cancelButton; - private System.Windows.Forms.ProgressBar progressBar; + private Nuclex.Windows.Forms.AsyncProgressBar progressBar; private System.Windows.Forms.Label statusLabel; private System.Windows.Forms.Timer controlCreationTimer; } diff --git a/Source/ProgressReporter/ProgressReporterForm.cs b/Source/ProgressReporter/ProgressReporterForm.cs index 8016635..9077bd5 100644 --- a/Source/ProgressReporter/ProgressReporterForm.cs +++ b/Source/ProgressReporter/ProgressReporterForm.cs @@ -22,7 +22,6 @@ namespace Nuclex.Windows.Forms { internal ProgressReporterForm() { InitializeComponent(); - this.updateProgressDelegate = new MethodInvoker(updateProgress); this.asyncEndedDelegate = new EventHandler(asyncEnded); this.asyncProgressUpdatedDelegate = new EventHandler( asyncProgressUpdated @@ -112,8 +111,7 @@ namespace Nuclex.Windows.Forms { // If the new state is 2, the form was ready to close (since the state // is incremented once when the form becomes ready to be closed) - int newState = Interlocked.Increment(ref this.state); - if(newState == 2) { + if(Interlocked.Increment(ref this.state) == 2) { // Close the dialog. Ensure the Close() method is invoked from the // same thread the dialog was created in. @@ -132,53 +130,7 @@ namespace Nuclex.Windows.Forms { /// Contains the new progress achieved by the progression /// private void asyncProgressUpdated(object sender, ProgressUpdateEventArgs arguments) { - - // Set the new progress without any synchronization - this.currentProgress = arguments.Progress; - - // Another use of the double-checked locking idiom, here we're trying to optimize - // away the lock in case some "trigger-happy" progressions send way more - // progress updates than the poor control can process :) - if(!this.progressUpdatePending) { - lock(this) { - if(!this.progressUpdatePending) { - this.progressUpdatePending = true; - this.progressUpdateAsyncResult = BeginInvoke(this.updateProgressDelegate); - } - } // lock - } - - } - - /// Synchronously updates the value visualized in the progress bar - private void updateProgress() { - lock(this) { - - // Reset the update flag so incoming updates will cause the control to - // update itself another time. - this.progressUpdatePending = false; - EndInvoke(this.progressUpdateAsyncResult); - - // Until the first progress event is received, the progress reporter shows - // a marquee bar to entertain the user even when no progress reports are - // being made at all. - if(this.progressBar.Style == ProgressBarStyle.Marquee) - this.progressBar.Style = ProgressBarStyle.Blocks; - - // Transform the progress into an integer in the range of the progress bar's - // min and max values (these should normally be set to 0 and 100). - int min = this.progressBar.Minimum; - int max = this.progressBar.Maximum; - int progress = (int)(this.currentProgress * (max - min)) + min; - - // Update the control - this.progressBar.Value = Math.Min(Math.Max(progress, min), max); - - // Assigning the value sends PBM_SETPOS to the control which, - // according to MSDN, already causes a redraw! - //base.Invalidate(); - - } // lock + this.progressBar.AsyncSetValue(arguments.Progress); } /// @@ -196,8 +148,7 @@ namespace Nuclex.Windows.Forms { // If the new state is 2, then the form was requested to close before it had // been fully constructed, so we should close it now! - int newState = System.Threading.Interlocked.Increment(ref this.state); - if(newState == 2) + if(Interlocked.Increment(ref this.state) == 2) Close(); } @@ -228,14 +179,6 @@ namespace Nuclex.Windows.Forms { private EventHandler asyncEndedDelegate; /// Delegate for the asyncProgressUpdated() method private EventHandler asyncProgressUpdatedDelegate; - /// Delegate for the progress update method - private MethodInvoker updateProgressDelegate; - /// Whether an update of the control state is pending - private volatile bool progressUpdatePending; - /// Async result for the invoked control state update method - private volatile IAsyncResult progressUpdateAsyncResult; - /// Most recently reported progress of the tracker - private volatile float currentProgress; /// Whether the form can be closed and should be closed /// /// 0: Nothing happened yet diff --git a/Source/TrackingBar/TrackingBar.cs b/Source/TrackingBar/TrackingBar.cs index 21e4b33..c951435 100644 --- a/Source/TrackingBar/TrackingBar.cs +++ b/Source/TrackingBar/TrackingBar.cs @@ -32,7 +32,7 @@ using Nuclex.Support.Tracking; namespace Nuclex.Windows.Forms { /// Progress bar for tracking the progress of background operations - public partial class TrackingBar : ProgressBar { + public partial class TrackingBar : AsyncProgressBar { /// Initializes a new tracking bar public TrackingBar() { @@ -42,21 +42,20 @@ namespace Nuclex.Windows.Forms { this.isIdle = true; base.Visible = false; + // Create the tracker and attach ourselfes to its events + this.tracker = new ProgressionTracker(); + this.tracker.AsyncIdleStateChanged += this.asyncIdleStateChangedDelegate; + this.tracker.AsyncProgressUpdated += this.asyncProgressUpdateDelegate; + // Initialize the delegates we use to update the control's state and those // we use to register ourselfes to the tracker's events this.updateIdleStateDelegate = new MethodInvoker(updateIdleState); - this.updateProgressDelegate = new MethodInvoker(updateProgress); this.asyncIdleStateChangedDelegate = new EventHandler( asyncIdleStateChanged ); this.asyncProgressUpdateDelegate = new EventHandler( asyncProgressUpdated ); - - // Create the tracker and attach ourselfes to its events - this.tracker = new ProgressionTracker(); - this.tracker.AsyncIdleStateChanged += this.asyncIdleStateChangedDelegate; - this.tracker.AsyncProgressUpdated += this.asyncProgressUpdateDelegate; } /// Tracks the specified progression in the tracking bar @@ -86,22 +85,7 @@ namespace Nuclex.Windows.Forms { private void asyncProgressUpdated( object sender, ProgressUpdateEventArgs arguments ) { - - // Set the new progress without any synchronization - this.currentProgress = arguments.Progress; - - // Another use of the double-checked locking idiom, here we're trying to optimize - // away the lock in case some "trigger-happy" progressions send way more - // progress updates than the poor control can process :) - if(!this.progressUpdatePending) { - lock(this) { - if(!this.progressUpdatePending) { - this.progressUpdatePending = true; - this.progressUpdateAsyncResult = BeginInvoke(this.updateProgressDelegate); - } - } // lock - } - + AsyncSetValue(arguments.Progress); } /// Called when the tracker becomes enters of leaves the idle state @@ -113,33 +97,13 @@ namespace Nuclex.Windows.Forms { // lost because otherwise, the progress bar might stay on-screen when in fact, // the background operation has already finished and nothing is happening anymore. this.isIdle = arguments.Idle; - Invoke(this.updateIdleStateDelegate); - } + // Update the bar's idle state + if(InvokeRequired) + Invoke(this.updateIdleStateDelegate); + else + updateIdleState(); - /// Synchronously updates the value visualized in the progress bar - private void updateProgress() { - lock(this) { - - // Reset the update flag so incoming updates will cause the control to - // update itself another time. - this.progressUpdatePending = false; - EndInvoke(this.progressUpdateAsyncResult); - - // Transform the progress into an integer in the range of the progress bar's - // min and max values (these should normally be set to 0 and 100). - int min = base.Minimum; - int max = base.Maximum; - int progress = (int)(this.currentProgress * (max - min)) + min; - - // Update the control - base.Value = progress; - - // Assigning the value sends PBM_SETPOS to the control which, - // according to MSDN, already causes a redraw! - //base.Invalidate(); - - } // lock } /// @@ -148,23 +112,15 @@ namespace Nuclex.Windows.Forms { /// private void updateIdleState() { + // Only show the progress bar when something is happening base.Visible = !this.isIdle; } - /// Whether an update of the control state is pending - private volatile bool progressUpdatePending; - /// Async result for the invoked control state update method - private volatile IAsyncResult progressUpdateAsyncResult; /// Whether the progress bar is in the idle state private volatile bool isIdle; - /// Most recently reported progress of the tracker - private volatile float currentProgress; - /// Tracker used to sum and update the total progress private ProgressionTracker tracker; - /// Delegate for the progress update method - private MethodInvoker updateProgressDelegate; /// Delegate for the idle state update method private MethodInvoker updateIdleStateDelegate; /// Delegate for the OnAsyncProgressionEnded method