Changed license to Apache License 2.0
This commit is contained in:
parent
857917aad5
commit
0037b7de46
47 changed files with 5942 additions and 5849 deletions
105
Source/AsyncProgressBar/AsyncProgressBar.Designer.cs
generated
105
Source/AsyncProgressBar/AsyncProgressBar.Designer.cs
generated
|
@ -1,53 +1,52 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2019 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
namespace Nuclex.Windows.Forms {
|
||||
|
||||
partial class AsyncProgressBar {
|
||||
|
||||
/// <summary>Required designer variable.</summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>Clean up any resources being used.</summary>
|
||||
/// <param name="disposing">
|
||||
/// true if managed resources should be disposed; otherwise, false.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if(disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
components = new System.ComponentModel.Container();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Windows.Forms
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
namespace Nuclex.Windows.Forms {
|
||||
|
||||
partial class AsyncProgressBar {
|
||||
|
||||
/// <summary>Required designer variable.</summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>Clean up any resources being used.</summary>
|
||||
/// <param name="disposing">
|
||||
/// true if managed resources should be disposed; otherwise, false.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if(disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
components = new System.ComponentModel.Container();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Windows.Forms
|
||||
|
|
|
@ -1,71 +1,70 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2019 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Nuclex.Support;
|
||||
|
||||
namespace Nuclex.Windows.Forms {
|
||||
|
||||
/// <summary>Unit Test for the asynchronously updating progress bar</summary>
|
||||
[TestFixture, Explicit]
|
||||
public class AsyncProgressBarTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that asynchronous progress assignment is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestProgressAssignment() {
|
||||
using(AsyncProgressBar progressBar = new AsyncProgressBar()) {
|
||||
|
||||
// Let the control create its window handle
|
||||
progressBar.CreateControl();
|
||||
progressBar.Minimum = 0;
|
||||
progressBar.Maximum = 100;
|
||||
|
||||
Assert.AreEqual(0, progressBar.Value);
|
||||
|
||||
// Assign the new value. This will be done asynchronously, so we call
|
||||
// Application.DoEvents() to execute the message pump once, guaranteeing
|
||||
// that the call will have been executed after Application.DoEvents() returns.
|
||||
progressBar.AsyncSetValue(0.33f);
|
||||
Application.DoEvents();
|
||||
|
||||
Assert.AreEqual(33, progressBar.Value);
|
||||
|
||||
progressBar.AsyncSetValue(0.66f);
|
||||
Application.DoEvents();
|
||||
|
||||
Assert.AreEqual(66, progressBar.Value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Windows.Forms
|
||||
|
||||
#endif // UNITTEST
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Nuclex.Support;
|
||||
|
||||
namespace Nuclex.Windows.Forms {
|
||||
|
||||
/// <summary>Unit Test for the asynchronously updating progress bar</summary>
|
||||
[TestFixture, Explicit]
|
||||
public class AsyncProgressBarTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that asynchronous progress assignment is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestProgressAssignment() {
|
||||
using(AsyncProgressBar progressBar = new AsyncProgressBar()) {
|
||||
|
||||
// Let the control create its window handle
|
||||
progressBar.CreateControl();
|
||||
progressBar.Minimum = 0;
|
||||
progressBar.Maximum = 100;
|
||||
|
||||
Assert.AreEqual(0, progressBar.Value);
|
||||
|
||||
// Assign the new value. This will be done asynchronously, so we call
|
||||
// Application.DoEvents() to execute the message pump once, guaranteeing
|
||||
// that the call will have been executed after Application.DoEvents() returns.
|
||||
progressBar.AsyncSetValue(0.33f);
|
||||
Application.DoEvents();
|
||||
|
||||
Assert.AreEqual(33, progressBar.Value);
|
||||
|
||||
progressBar.AsyncSetValue(0.66f);
|
||||
Application.DoEvents();
|
||||
|
||||
Assert.AreEqual(66, progressBar.Value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Windows.Forms
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
|
@ -1,140 +1,139 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2019 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nuclex.Windows.Forms {
|
||||
|
||||
/// <summary>Progress bar with optimized multi-threading behavior</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a background thread is generating lots of progress updates, using synchronized
|
||||
/// calls can drastically reduce performance. This progress bar optimizes that case
|
||||
/// by performing the update asynchronously and keeping only the most recent update
|
||||
/// when multiple updates arrive while the asynchronous update call is still running.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This design eliminates useless queueing of progress updates, thereby reducing
|
||||
/// CPU load occuring in the UI thread and at the same time avoids blocking the
|
||||
/// worker thread, increasing its performance.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public partial class AsyncProgressBar : ProgressBar {
|
||||
|
||||
/// <summary>Initializes a new asynchronous progress bar</summary>
|
||||
public AsyncProgressBar() {
|
||||
InitializeComponent();
|
||||
|
||||
this.Disposed += new EventHandler(progressBarDisposed);
|
||||
this.updateProgressDelegate = new MethodInvoker(updateProgress);
|
||||
|
||||
// Could probably use VolatileWrite() as well, but for consistency reasons
|
||||
// this is an Interlocked call, too. Mixing different synchronization measures
|
||||
// for a variable raises a red flag whenever I see it :)
|
||||
Interlocked.Exchange(ref this.newProgress, -1.0f);
|
||||
}
|
||||
|
||||
/// <summary>Called when the progress bar is being disposed</summary>
|
||||
/// <param name="sender">Progress bar that is being disposed</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
private void progressBarDisposed(object sender, EventArgs arguments) {
|
||||
|
||||
// CHECK: This method is only called on an explicit Dispose() of the control.
|
||||
// It is legal to call Control.BeginInvoke() without calling Control.EndInvoke(),
|
||||
// so the code is quite correct even if no Dispose() occurs, but is it also clean?
|
||||
// http://www.interact-sw.co.uk/iangblog/2005/05/16/endinvokerequired
|
||||
|
||||
// Since this has to occur in the UI thread, there's no way that updateProgress()
|
||||
// could be executing just now. But the final call to updateProgress() will not
|
||||
// have EndInvoke() called on it yet, so we do this here before the control
|
||||
// is finally disposed.
|
||||
if(this.progressUpdateAsyncResult != null) {
|
||||
EndInvoke(this.progressUpdateAsyncResult);
|
||||
this.progressUpdateAsyncResult = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Asynchronously updates the value to be shown in the progress bar</summary>
|
||||
/// <param name="value">New value to set the progress bar to</param>
|
||||
/// <remarks>
|
||||
/// This will schedule an asynchronous update of the progress bar in the UI thread.
|
||||
/// If you change the progress value again before the progress bar has completed its
|
||||
/// update cycle, the original progress value will be skipped and the progress bar
|
||||
/// jumps directly to the latest progress value. Updates are not queued, there is
|
||||
/// at most one update waiting on the UI thread. It is also strictly guaranteed that
|
||||
/// the last most progress value set will be shown and never skipped.
|
||||
/// </remarks>
|
||||
public void AsyncSetValue(float value) {
|
||||
|
||||
// Update the value to be shown on the progress bar. If this happens multiple
|
||||
// times, that's not a problem, the progress bar updates as fast as it can
|
||||
// and always tries to show the most recent value assigned.
|
||||
float oldValue = Interlocked.Exchange(ref this.newProgress, value);
|
||||
|
||||
// If the previous value was -1, the UI thread has already taken out the most recent
|
||||
// value and assigned it (or is about to assign it) to the progress bar control.
|
||||
// In this case, we'll wait until the current update has completed and immediately
|
||||
// begin the next update - since we know that the value the UI thread has extracted
|
||||
// is no longer the most recent one.
|
||||
if(oldValue == -1.0f) {
|
||||
if(this.progressUpdateAsyncResult != null) {
|
||||
EndInvoke(this.progressUpdateAsyncResult);
|
||||
}
|
||||
|
||||
this.progressUpdateAsyncResult = BeginInvoke(this.updateProgressDelegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Synchronously updates the value visualized in the progress bar</summary>
|
||||
private void updateProgress() {
|
||||
|
||||
// Cache these to shorten the code that follows :)
|
||||
int minimum = base.Minimum;
|
||||
int maximum = base.Maximum;
|
||||
|
||||
// Take out the most recent value that has been given to the asynchronous progress
|
||||
// bar up until now and replace it by -1. This enables the updater to see when
|
||||
// the update has actually been performed and whether it needs to start a new
|
||||
// invocation to ensure the most recent value will remain at the end.
|
||||
float progress = Interlocked.Exchange(ref this.newProgress, -1.0f);
|
||||
|
||||
// Restrain the value to the progress bar's configured range and assign it.
|
||||
// This is done to prevent exceptions in the UI thread (theoretically the user
|
||||
// could change the progress bar's min and max just before the UI thread executes
|
||||
// this method, so we cannot validate the value in AsyncSetValue())
|
||||
int value = (int)(progress * (maximum - minimum)) + minimum;
|
||||
base.Value = Math.Min(Math.Max(value, minimum), maximum);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>New progress being assigned to the progress bar</summary>
|
||||
private float newProgress;
|
||||
/// <summary>Delegate for the progress update method</summary>
|
||||
private MethodInvoker updateProgressDelegate;
|
||||
/// <summary>Async result for the invoked control state update method</summary>
|
||||
private volatile IAsyncResult progressUpdateAsyncResult;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Windows.Forms
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nuclex.Windows.Forms {
|
||||
|
||||
/// <summary>Progress bar with optimized multi-threading behavior</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a background thread is generating lots of progress updates, using synchronized
|
||||
/// calls can drastically reduce performance. This progress bar optimizes that case
|
||||
/// by performing the update asynchronously and keeping only the most recent update
|
||||
/// when multiple updates arrive while the asynchronous update call is still running.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This design eliminates useless queueing of progress updates, thereby reducing
|
||||
/// CPU load occuring in the UI thread and at the same time avoids blocking the
|
||||
/// worker thread, increasing its performance.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public partial class AsyncProgressBar : ProgressBar {
|
||||
|
||||
/// <summary>Initializes a new asynchronous progress bar</summary>
|
||||
public AsyncProgressBar() {
|
||||
InitializeComponent();
|
||||
|
||||
this.Disposed += new EventHandler(progressBarDisposed);
|
||||
this.updateProgressDelegate = new MethodInvoker(updateProgress);
|
||||
|
||||
// Could probably use VolatileWrite() as well, but for consistency reasons
|
||||
// this is an Interlocked call, too. Mixing different synchronization measures
|
||||
// for a variable raises a red flag whenever I see it :)
|
||||
Interlocked.Exchange(ref this.newProgress, -1.0f);
|
||||
}
|
||||
|
||||
/// <summary>Called when the progress bar is being disposed</summary>
|
||||
/// <param name="sender">Progress bar that is being disposed</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
private void progressBarDisposed(object sender, EventArgs arguments) {
|
||||
|
||||
// CHECK: This method is only called on an explicit Dispose() of the control.
|
||||
// It is legal to call Control.BeginInvoke() without calling Control.EndInvoke(),
|
||||
// so the code is quite correct even if no Dispose() occurs, but is it also clean?
|
||||
// http://www.interact-sw.co.uk/iangblog/2005/05/16/endinvokerequired
|
||||
|
||||
// Since this has to occur in the UI thread, there's no way that updateProgress()
|
||||
// could be executing just now. But the final call to updateProgress() will not
|
||||
// have EndInvoke() called on it yet, so we do this here before the control
|
||||
// is finally disposed.
|
||||
if(this.progressUpdateAsyncResult != null) {
|
||||
EndInvoke(this.progressUpdateAsyncResult);
|
||||
this.progressUpdateAsyncResult = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Asynchronously updates the value to be shown in the progress bar</summary>
|
||||
/// <param name="value">New value to set the progress bar to</param>
|
||||
/// <remarks>
|
||||
/// This will schedule an asynchronous update of the progress bar in the UI thread.
|
||||
/// If you change the progress value again before the progress bar has completed its
|
||||
/// update cycle, the original progress value will be skipped and the progress bar
|
||||
/// jumps directly to the latest progress value. Updates are not queued, there is
|
||||
/// at most one update waiting on the UI thread. It is also strictly guaranteed that
|
||||
/// the last most progress value set will be shown and never skipped.
|
||||
/// </remarks>
|
||||
public void AsyncSetValue(float value) {
|
||||
|
||||
// Update the value to be shown on the progress bar. If this happens multiple
|
||||
// times, that's not a problem, the progress bar updates as fast as it can
|
||||
// and always tries to show the most recent value assigned.
|
||||
float oldValue = Interlocked.Exchange(ref this.newProgress, value);
|
||||
|
||||
// If the previous value was -1, the UI thread has already taken out the most recent
|
||||
// value and assigned it (or is about to assign it) to the progress bar control.
|
||||
// In this case, we'll wait until the current update has completed and immediately
|
||||
// begin the next update - since we know that the value the UI thread has extracted
|
||||
// is no longer the most recent one.
|
||||
if(oldValue == -1.0f) {
|
||||
if(this.progressUpdateAsyncResult != null) {
|
||||
EndInvoke(this.progressUpdateAsyncResult);
|
||||
}
|
||||
|
||||
this.progressUpdateAsyncResult = BeginInvoke(this.updateProgressDelegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Synchronously updates the value visualized in the progress bar</summary>
|
||||
private void updateProgress() {
|
||||
|
||||
// Cache these to shorten the code that follows :)
|
||||
int minimum = base.Minimum;
|
||||
int maximum = base.Maximum;
|
||||
|
||||
// Take out the most recent value that has been given to the asynchronous progress
|
||||
// bar up until now and replace it by -1. This enables the updater to see when
|
||||
// the update has actually been performed and whether it needs to start a new
|
||||
// invocation to ensure the most recent value will remain at the end.
|
||||
float progress = Interlocked.Exchange(ref this.newProgress, -1.0f);
|
||||
|
||||
// Restrain the value to the progress bar's configured range and assign it.
|
||||
// This is done to prevent exceptions in the UI thread (theoretically the user
|
||||
// could change the progress bar's min and max just before the UI thread executes
|
||||
// this method, so we cannot validate the value in AsyncSetValue())
|
||||
int value = (int)(progress * (maximum - minimum)) + minimum;
|
||||
base.Value = Math.Min(Math.Max(value, minimum), maximum);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>New progress being assigned to the progress bar</summary>
|
||||
private float newProgress;
|
||||
/// <summary>Delegate for the progress update method</summary>
|
||||
private MethodInvoker updateProgressDelegate;
|
||||
/// <summary>Async result for the invoked control state update method</summary>
|
||||
private volatile IAsyncResult progressUpdateAsyncResult;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Windows.Forms
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue