Introduction
Storing frequently accessed database data, like lookup lists, in ASP.NET’s cache is a great way to optimize a web application’s performance. But anytime you store database data in ASP.NET’s cache, you have to provide a way to update that cache. This ensures changes made in the database make it into the web application’s cache. This is handled using either notifications or polling.
Notifications are possible when using SQL Server 2005 with notification services and .NET’s SqlDependency object from the System.Data.SqlClient namespace. But notification services aren’t available in SQL Server 2008 or other databases. Even if you are using SQL Server 2005, employing this technology requires more server configuration and therefore complicates deployment.
Polling is often handled by setting an expiration of the cached item at some time interval such as hourly. That way the cache holds the data for an hour and then automatically dumps it. Then when the data is requested and the cache is empty, the data is read from the database and placed back into the cache. This approach works fairly well, but with two drawbacks. First, it requires the cache be refreshed even when it is not needed. Second, it runs on the same thread as the request so some unlucky user has to wait a little longer for their request to return while the data is read from the database and stored in the cache.
The BackgroundWorker object in .NET enables a better solution. When the web application starts, it spins off a background thread, and passes to it the application’s HttpContext object, enabling the thread, access to the application’s cache. When the web application ends, it stops the background thread. The background thread runs a continuous loop of sleeping for some time interval, and then polls the database to see if data has changed during that interval. If data has changed, the cache is refreshed. This solution is highly efficient because it runs on a background thread and refreshes the cache only when needed.
Common Language Specification (CLS) compliance generally refers to the claim that CLS rules and restrictions are being followed. However, the concept has a more specific meaning depending on whether you are describing CLS-compliant code or CLS-compliant development tools, such as a compiler. CLS-compliant tools can help you write CLS-compliant code.
CLS-Compliant Code
If you want your code to be CLS-compliant, you must expose functionality in a way that is CLS-compliant in the following places:
Definitions of your public classes.
Definitions of the public members of public classes, and of members accessible to derived classes (family access).
Parameters and return types of public methods of public classes, and of methods accessible to derived classes.
The features you use in the definitions of your private classes, in the definitions of private methods on public classes, and in local variables do not have to follow the CLS rules. You can also use any language features you want in the code that implements your class and still have a CLS-compliant component.
Jagged arrays — that is, arrays of arrays — are CLS-compliant. In the .NET Framework version 1.0, the C# compiler mistakenly reports that they are not. |
You can mark assemblies, modules, types, and members as either CLS-compliant or not CLS compliant using the CLSCompliantAttribute. All assemblies that are intended to be CLS-compliant should be marked as such. An assembly that is not marked as CLS-compliant is considered to be not CLS compliant. If no CLS attribute is applied to a type, that type is assumed to have the same CLS compliance as the assembly in which the type is defined. Similarly, if no CLS attribute is applied to a member, the member is considered to have the same CLS compliance as the type that defines it. You cannot mark a program element as CLS-compliant if its enclosing element is not marked as CLS-compliant. The example at the end of this topic illustrates the use of the CLSCompliantAttribute.
Assemblies, modules, and types can be CLS-compliant even if some parts of the assembly, module, or type are not CLS-compliant, as long as two conditions are met:
If the element is marked as CLS-compliant, the parts that are not CLS-compliant must be marked using the CLSCompliantAttribute with its argument set to false.
A comparable CLS-compliant alternative member must be supplied for each member that is not CLS-compliant.
If you design a CLS–compliant class library, your library will have a guarantee of interoperability with a wide range of programming languages; therefore, your library is likely to have a wider customer base than a version that is not CLS-compliant.
The .NET Framework provides a CLS-compliant class library. For more information about this class library, see .NET Framework Class Library.
CLS-Compliant Tools
Languages that target the runtime have agreed to support CLS features and follow the CLS rules directed to compilers. These language compilers simplify CLS compliance by making the CLS data types and features available for creating components. The levels of CLS compliance among compilers and other tools are described as follows:
CLS-compliant consumer tools.
Consumer tools are languages that enable developers to access all the features supplied by CLS-compliant libraries. Developers using these languages might not be able to extend CLS-compliant libraries by creating new types, but they can use any type defined by a compliant library. This level of compliance can be useful when you want to access a .NET Framework class library, but do not need to author new objects for consumption by others, such as when you are using Web Forms on an ASP.NET page or creating a Windows Forms user interface.
CLS-compliant extender tools.
Extender tools are languages that allow developers to both use and extend types defined in CLS-compliant libraries. Developers can use existing types as well as define new types. Extender tools must follow all the rules that consumer tools must follow, as well as some additional rules, which are described in the specification for the Common Language Infrastructure, Partition I - Architecture, available from the Microsoft Developer Network (MSDN) Web site.
When you design your own CLS-compliant components, it is helpful to use a CLS-compliant tool. Writing CLS-compliant components without this support is more difficult because otherwise you might not have access to all the CLS features you want to use.
Some CLS-compliant language compilers, such as the C# or Visual Basic compilers, enable you to specify that you intend your code to be CLS-compliant. These compilers can check for CLS compliance and let you know when your code uses functionality that is not supported by the CLS. The C# and Visual Basic compilers allow you to mark a program element as CLS-compliant, which will cause the compiler to generate a compile-time error if the code is not CLS-compliant. For example, the following code generates a compiler warning.
<Assembly: CLSCompliant(True)> <CLSCompliant(True)> Public Class MyCompliantClass Public Sub ChangeValue(value As UInt32) End Sub Public Shared Sub Main() Dim i As Integer = 2 Console.WriteLine(i) End Sub End Class
using System; // Assembly marked as compliant. [assembly: CLSCompliant(true)] // Class marked as compliant. [CLSCompliant(true)] public class MyCompliantClass { // ChangeValue exposes UInt32, which is not in CLS. // A compile-time warning results. public void ChangeValue(UInt32 value){ } public static void Main( ) { int i = 2; Console.WriteLine(i); } }
This code generates the following C# warning:
or the following Visual Basic warning:
To remove the warning, you can indicate that ChangeValue is not compliant, as shown in the following example.
' Assembly marked as compliant. <Assembly: CLSCompliant(True)> ' Class marked as compliant. <CLSCompliant(True)> Public Class MyCompliantClass ' Method marked as not compliant. <CLSCompliant(False)> Public Sub ChangeValue(value As UInt32) End Sub Public Shared Sub Main() Dim i As Integer = 2 Console.WriteLine(i) End Sub End Class
using System; // Assembly marked as compliant. [assembly: CLSCompliantAttribute(true)] // Class marked as compliant. [CLSCompliantAttribute(true)] public class MyCompliantClass { // Method marked as not compliant. [CLSCompliantAttribute(false)] public void ChangeValue(UInt32 value){ } public static void Main( ) { int i = 2; Console.WriteLine(i); } }
This code produces no compiler warnings. The output is 2.
For more information about how to specify the CLS-compliance of your code, see the documentation for the language compiler you are using.
See Also
Terms of Use | Trademarks | Privacy Statement |
<HostProtectionAttribute(SecurityAction.LinkDemand, SharedState := True)> _ Public Class BackgroundWorker _ Inherits Component
Dim instance As BackgroundWorker
[HostProtectionAttribute(SecurityAction.LinkDemand, SharedState = true)] public class BackgroundWorker : Component
[HostProtectionAttribute(SecurityAction::LinkDemand, SharedState = true)] public ref class BackgroundWorker : public Component
public class BackgroundWorker extends Component
| The HostProtectionAttribute attribute applied to this type or member has the following Resources property value: SharedState. The HostProtectionAttribute does not affect desktop applications (which are typically started by double-clicking an icon, typing a command, or entering a URL in a browser). For more information, see the HostProtectionAttribute class or SQL Server Programming and Host Protection Attributes. |
To execute a time-consuming operation in the background, create a BackgroundWorker and listen for events that report the progress of your operation and signal when your operation is finished. You can create the BackgroundWorker programmatically or you can drag it onto your form from the Components tab of the Toolbox. If you create the BackgroundWorker in the Windows Forms Designer, it will appear in the Component Tray, and its properties will be displayed in the Properties window.
To set up for a background operation, add an event handler for the DoWork event. Call your time-consuming operation in this event handler. To start the operation, call RunWorkerAsync. To receive notifications of progress updates, handle the ProgressChanged event. To receive a notification when the operation is completed, handle the RunWorkerCompleted event.
| You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events. BackgroundWorker events are not marshaled across AppDomain boundaries. Do not use a BackgroundWorker component to perform multithreaded operations in more than one AppDomain. |
For more information about BackgroundWorker, see How to: Run an Operation in the Background.
The following code example demonstrates the use of the BackgroundWorker class for executing a time-consuming operation asynchronously. The operation computes the selected Fibonacci number, reports progress updates as the calculation proceeds, and permits a pending calculation to be canceled.
Imports System Imports System.Collections Imports System.ComponentModel Imports System.Drawing Imports System.Threading Imports System.Windows.Forms Public Class FibonacciForm Inherits System.Windows.Forms.Form Private numberToCompute As Integer = 0 Private highestPercentageReached As Integer = 0 Private numericUpDown1 As System.Windows.Forms.NumericUpDown Private WithEvents startAsyncButton As System.Windows.Forms.Button Private WithEvents cancelAsyncButton As System.Windows.Forms.Button Private progressBar1 As System.Windows.Forms.ProgressBar Private resultLabel As System.Windows.Forms.Label Private WithEvents backgroundWorker1 As System.ComponentModel.BackgroundWorker Public Sub New() InitializeComponent() End Sub 'New Private Sub startAsyncButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles startAsyncButton.Click ' Reset the text in the result label. resultLabel.Text = [String].Empty ' Disable the UpDown control until ' the asynchronous operation is done. Me.numericUpDown1.Enabled = False ' Disable the Start button until ' the asynchronous operation is done. Me.startAsyncButton.Enabled = False ' Enable the Cancel button while ' the asynchronous operation runs. Me.cancelAsyncButton.Enabled = True ' Get the value from the UpDown control. numberToCompute = CInt(numericUpDown1.Value) ' Reset the variable for percentage tracking. highestPercentageReached = 0 ' Start the asynchronous operation. backgroundWorker1.RunWorkerAsync(numberToCompute) End Sub Private Sub cancelAsyncButton_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cancelAsyncButton.Click ' Cancel the asynchronous operation. Me.backgroundWorker1.CancelAsync() ' Disable the Cancel button. cancelAsyncButton.Enabled = False End Sub 'cancelAsyncButton_Click ' This event handler is where the actual work is done. Private Sub backgroundWorker1_DoWork( _ ByVal sender As Object, _ ByVal e As DoWorkEventArgs) _ Handles backgroundWorker1.DoWork ' Get the BackgroundWorker object that raised this event. Dim worker As BackgroundWorker = _ CType(sender, BackgroundWorker) ' Assign the result of the computation ' to the Result property of the DoWorkEventArgs ' object. This is will be available to the ' RunWorkerCompleted eventhandler. e.Result = ComputeFibonacci(e.Argument, worker, e) End Sub 'backgroundWorker1_DoWork ' This event handler deals with the results of the ' background operation. Private Sub backgroundWorker1_RunWorkerCompleted( _ ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _ Handles backgroundWorker1.RunWorkerCompleted ' First, handle the case where an exception was thrown. If (e.Error IsNot Nothing) Then MessageBox.Show(e.Error.Message) ElseIf e.Cancelled Then ' Next, handle the case where the user canceled the ' operation. ' Note that due to a race condition in ' the DoWork event handler, the Cancelled ' flag may not have been set, even though ' CancelAsync was called. resultLabel.Text = "Canceled" Else ' Finally, handle the case where the operation succeeded. resultLabel.Text = e.Result.ToString() End If ' Enable the UpDown control. Me.numericUpDown1.Enabled = True ' Enable the Start button. startAsyncButton.Enabled = True ' Disable the Cancel button. cancelAsyncButton.Enabled = False End Sub 'backgroundWorker1_RunWorkerCompleted ' This event handler updates the progress bar. Private Sub backgroundWorker1_ProgressChanged( _ ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _ Handles backgroundWorker1.ProgressChanged Me.progressBar1.Value = e.ProgressPercentage End Sub ' This is the method that does the actual work. For this ' example, it computes a Fibonacci number and ' reports progress as it does its work. Function ComputeFibonacci( _ ByVal n As Integer, _ ByVal worker As BackgroundWorker, _ ByVal e As DoWorkEventArgs) As Long ' The parameter n must be >= 0 and <= 91. ' Fib(n), with n > 91, overflows a long. If n < 0 OrElse n > 91 Then Throw New ArgumentException( _ "value must be >= 0 and <= 91", "n") End If Dim result As Long = 0 ' Abort the operation if the user has canceled. ' Note that a call to CancelAsync may have set ' CancellationPending to true just after the ' last invocation of this method exits, so this ' code will not have the opportunity to set the ' DoWorkEventArgs.Cancel flag to true. This means ' that RunWorkerCompletedEventArgs.Cancelled will ' not be set to true in your RunWorkerCompleted ' event handler. This is a race condition. If worker.CancellationPending Then e.Cancel = True Else If n < 2 Then result = 1 Else result = ComputeFibonacci(n - 1, worker, e) + _ ComputeFibonacci(n - 2, worker, e) End If ' Report progress as a percentage of the total task. Dim percentComplete As Integer = _ CSng(n) / CSng(numberToCompute) * 100 If percentComplete > highestPercentageReached Then highestPercentageReached = percentComplete worker.ReportProgress(percentComplete) End If End If Return result End Function Private Sub InitializeComponent() Me.numericUpDown1 = New System.Windows.Forms.NumericUpDown Me.startAsyncButton = New System.Windows.Forms.Button Me.cancelAsyncButton = New System.Windows.Forms.Button Me.resultLabel = New System.Windows.Forms.Label Me.progressBar1 = New System.Windows.Forms.ProgressBar Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker CType(Me.numericUpDown1, System.ComponentModel.ISupportInitialize).BeginInit() Me.SuspendLayout() ' 'numericUpDown1 ' Me.numericUpDown1.Location = New System.Drawing.Point(16, 16) Me.numericUpDown1.Maximum = New Decimal(New Integer() {91, 0, 0, 0}) Me.numericUpDown1.Minimum = New Decimal(New Integer() {1, 0, 0, 0}) Me.numericUpDown1.Name = "numericUpDown1" Me.numericUpDown1.Size = New System.Drawing.Size(80, 20) Me.numericUpDown1.TabIndex = 0 Me.numericUpDown1.Value = New Decimal(New Integer() {1, 0, 0, 0}) ' 'startAsyncButton ' Me.startAsyncButton.Location = New System.Drawing.Point(16, 72) Me.startAsyncButton.Name = "startAsyncButton" Me.startAsyncButton.Size = New System.Drawing.Size(120, 23) Me.startAsyncButton.TabIndex = 1 Me.startAsyncButton.Text = "Start Async" ' 'cancelAsyncButton ' Me.cancelAsyncButton.Enabled = False Me.cancelAsyncButton.Location = New System.Drawing.Point(153, 72) Me.cancelAsyncButton.Name = "cancelAsyncButton" Me.cancelAsyncButton.Size = New System.Drawing.Size(119, 23) Me.cancelAsyncButton.TabIndex = 2 Me.cancelAsyncButton.Text = "Cancel Async" ' 'resultLabel ' Me.resultLabel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D Me.resultLabel.Location = New System.Drawing.Point(112, 16) Me.resultLabel.Name = "resultLabel" Me.resultLabel.Size = New System.Drawing.Size(160, 23) Me.resultLabel.TabIndex = 3 Me.resultLabel.Text = "(no result)" Me.resultLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter ' 'progressBar1 ' Me.progressBar1.Location = New System.Drawing.Point(18, 48) Me.progressBar1.Name = "progressBar1" Me.progressBar1.Size = New System.Drawing.Size(256, 8) Me.progressBar1.TabIndex = 4 ' 'backgroundWorker1 ' Me.backgroundWorker1.WorkerReportsProgress = True Me.backgroundWorker1.WorkerSupportsCancellation = True ' 'FibonacciForm ' Me.ClientSize = New System.Drawing.Size(292, 118) Me.Controls.Add(Me.progressBar1) Me.Controls.Add(Me.resultLabel) Me.Controls.Add(Me.cancelAsyncButton) Me.Controls.Add(Me.startAsyncButton) Me.Controls.Add(Me.numericUpDown1) Me.Name = "FibonacciForm" Me.Text = "Fibonacci Calculator" CType(Me.numericUpDown1, System.ComponentModel.ISupportInitialize).EndInit() Me.ResumeLayout(False) End Sub 'InitializeComponent <STAThread()> _ Shared Sub Main() Application.Run(New FibonacciForm) End Sub 'Main End Class 'FibonacciForm
using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Threading; using System.Windows.Forms; namespace BackgroundWorkerExample { public class FibonacciForm : System.Windows.Forms.Form { private int numberToCompute = 0; private int highestPercentageReached = 0; private System.Windows.Forms.NumericUpDown numericUpDown1; private System.Windows.Forms.Button startAsyncButton; private System.Windows.Forms.Button cancelAsyncButton; private System.Windows.Forms.ProgressBar progressBar1; private System.Windows.Forms.Label resultLabel; private System.ComponentModel.BackgroundWorker backgroundWorker1; public FibonacciForm() { InitializeComponent(); InitializeBackgroundWorker(); } // Set up the BackgroundWorker object by // attaching event handlers. private void InitializeBackgroundWorker() { backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler( backgroundWorker1_RunWorkerCompleted); backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler( backgroundWorker1_ProgressChanged); } private void startAsyncButton_Click(System.Object sender, System.EventArgs e) { // Reset the text in the result label. resultLabel.Text = String.Empty; // Disable the UpDown control until // the asynchronous operation is done. this.numericUpDown1.Enabled = false; // Disable the Start button until // the asynchronous operation is done. this.startAsyncButton.Enabled = false; // Enable the Cancel button while // the asynchronous operation runs. this.cancelAsyncButton.Enabled = true; // Get the value from the UpDown control. numberToCompute = (int)numericUpDown1.Value; // Reset the variable for percentage tracking. highestPercentageReached = 0; // Start the asynchronous operation. backgroundWorker1.RunWorkerAsync(numberToCompute); } private void cancelAsyncButton_Click(System.Object sender, System.EventArgs e) { // Cancel the asynchronous operation. this.backgroundWorker1.CancelAsync(); // Disable the Cancel button. cancelAsyncButton.Enabled = false; } // This event handler is where the actual, // potentially time-consuming work is done. private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // Get the BackgroundWorker that raised this event. BackgroundWorker worker = sender as BackgroundWorker; // Assign the result of the computation // to the Result property of the DoWorkEventArgs // object. This is will be available to the // RunWorkerCompleted eventhandler. e.Result = ComputeFibonacci((int)e.Argument, worker, e); } // This event handler deals with the results of the // background operation. private void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { // First, handle the case where an exception was thrown. if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { // Next, handle the case where the user canceled // the operation. // Note that due to a race condition in // the DoWork event handler, the Cancelled // flag may not have been set, even though // CancelAsync was called. resultLabel.Text = "Canceled"; } else { // Finally, handle the case where the operation // succeeded. resultLabel.Text = e.Result.ToString(); } // Enable the UpDown control. this.numericUpDown1.Enabled = true; // Enable the Start button. startAsyncButton.Enabled = true; // Disable the Cancel button. cancelAsyncButton.Enabled = false; } // This event handler updates the progress bar. private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; } // This is the method that does the actual work. For this // example, it computes a Fibonacci number and // reports progress as it does its work. long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e) { // The parameter n must be >= 0 and <= 91. // Fib(n), with n > 91, overflows a long. if ((n < 0) || (n > 91)) { throw new ArgumentException( "value must be >= 0 and <= 91", "n"); } long result = 0; // Abort the operation if the user has canceled. // Note that a call to CancelAsync may have set // CancellationPending to true just after the // last invocation of this method exits, so this // code will not have the opportunity to set the // DoWorkEventArgs.Cancel flag to true. This means // that RunWorkerCompletedEventArgs.Cancelled will // not be set to true in your RunWorkerCompleted // event handler. This is a race condition. if (worker.CancellationPending) { e.Cancel = true; } else { if (n < 2) { result = 1; } else { result = ComputeFibonacci(n - 1, worker, e) + ComputeFibonacci(n - 2, worker, e); } // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if (percentComplete > highestPercentageReached) { highestPercentageReached = percentComplete; worker.ReportProgress(percentComplete); } } return result; } #region Windows Form Designer generated code private void InitializeComponent() { this.numericUpDown1 = new System.Windows.Forms.NumericUpDown(); this.startAsyncButton = new System.Windows.Forms.Button(); this.cancelAsyncButton = new System.Windows.Forms.Button(); this.resultLabel = new System.Windows.Forms.Label(); this.progressBar1 = new System.Windows.Forms.ProgressBar(); this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit(); this.SuspendLayout(); // // numericUpDown1 // this.numericUpDown1.Location = new System.Drawing.Point(16, 16); this.numericUpDown1.Maximum = new System.Decimal(new int[] { 91, 0, 0, 0}); this.numericUpDown1.Minimum = new System.Decimal(new int[] { 1, 0, 0, 0}); this.numericUpDown1.Name = "numericUpDown1"; this.numericUpDown1.Size = new System.Drawing.Size(80, 20); this.numericUpDown1.TabIndex = 0; this.numericUpDown1.Value = new System.Decimal(new int[] { 1, 0, 0, 0}); // // startAsyncButton // this.startAsyncButton.Location = new System.Drawing.Point(16, 72); this.startAsyncButton.Name = "startAsyncButton"; this.startAsyncButton.Size = new System.Drawing.Size(120, 23); this.startAsyncButton.TabIndex = 1; this.startAsyncButton.Text = "Start Async"; this.startAsyncButton.Click += new System.EventHandler(this.startAsyncButton_Click); // // cancelAsyncButton // this.cancelAsyncButton.Enabled = false; this.cancelAsyncButton.Location = new System.Drawing.Point(153, 72); this.cancelAsyncButton.Name = "cancelAsyncButton"; this.cancelAsyncButton.Size = new System.Drawing.Size(119, 23); this.cancelAsyncButton.TabIndex = 2; this.cancelAsyncButton.Text = "Cancel Async"; this.cancelAsyncButton.Click += new System.EventHandler(this.cancelAsyncButton_Click); // // resultLabel // this.resultLabel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.resultLabel.Location = new System.Drawing.Point(112, 16); this.resultLabel.Name = "resultLabel"; this.resultLabel.Size = new System.Drawing.Size(160, 23); this.resultLabel.TabIndex = 3; this.resultLabel.Text = "(no result)"; this.resultLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // progressBar1 // this.progressBar1.Location = new System.Drawing.Point(18, 48); this.progressBar1.Name = "progressBar1"; this.progressBar1.Size = new System.Drawing.Size(256, 8); this.progressBar1.Step = 2; this.progressBar1.TabIndex = 4; // // backgroundWorker1 // this.backgroundWorker1.WorkerReportsProgress = true; this.backgroundWorker1.WorkerSupportsCancellation = true; // // FibonacciForm // this.ClientSize = new System.Drawing.Size(292, 118); this.Controls.Add(this.progressBar1); this.Controls.Add(this.resultLabel); this.Controls.Add(this.cancelAsyncButton); this.Controls.Add(this.startAsyncButton); this.Controls.Add(this.numericUpDown1); this.Name = "FibonacciForm"; this.Text = "Fibonacci Calculator"; ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit(); this.ResumeLayout(false); } #endregion [STAThread] static void Main() { Application.Run(new FibonacciForm()); } } }
#using <System.Drawing.dll> #using <System.dll> #using <System.Windows.Forms.dll> using namespace System; using namespace System::Collections; using namespace System::ComponentModel; using namespace System::Drawing; using namespace System::Threading; using namespace System::Windows::Forms; public ref class FibonacciForm: public System::Windows::Forms::Form { private: int numberToCompute; int highestPercentageReached; System::Windows::Forms::NumericUpDown^ numericUpDown1; System::Windows::Forms::Button^ startAsyncButton; System::Windows::Forms::Button^ cancelAsyncButton; System::Windows::Forms::ProgressBar^ progressBar1; System::Windows::Forms::Label ^ resultLabel; System::ComponentModel::BackgroundWorker^ backgroundWorker1; public: FibonacciForm() { InitializeComponent(); numberToCompute = highestPercentageReached = 0; InitializeBackgoundWorker(); } private: // Set up the BackgroundWorker object by // attaching event handlers. void InitializeBackgoundWorker() { backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &FibonacciForm::backgroundWorker1_DoWork ); backgroundWorker1->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler( this, &FibonacciForm::backgroundWorker1_RunWorkerCompleted ); backgroundWorker1->ProgressChanged += gcnew ProgressChangedEventHandler( this, &FibonacciForm::backgroundWorker1_ProgressChanged ); } void startAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ ) { // Reset the text in the result label. resultLabel->Text = String::Empty; // Disable the UpDown control until // the asynchronous operation is done. this->numericUpDown1->Enabled = false; // Disable the Start button until // the asynchronous operation is done. this->startAsyncButton->Enabled = false; // Enable the Cancel button while // the asynchronous operation runs. this->cancelAsyncButton->Enabled = true; // Get the value from the UpDown control. numberToCompute = (int)numericUpDown1->Value; // Reset the variable for percentage tracking. highestPercentageReached = 0; // Start the asynchronous operation. backgroundWorker1->RunWorkerAsync( numberToCompute ); } void cancelAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ ) { // Cancel the asynchronous operation. this->backgroundWorker1->CancelAsync(); // Disable the Cancel button. cancelAsyncButton->Enabled = false; } // This event handler is where the actual, // potentially time-consuming work is done. void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e ) { // Get the BackgroundWorker that raised this event. BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender); // Assign the result of the computation // to the Result property of the DoWorkEventArgs // object. This is will be available to the // RunWorkerCompleted eventhandler. e->Result = ComputeFibonacci( safe_cast<Int32>(e->Argument), worker, e ); } // This event handler deals with the results of the // background operation. void backgroundWorker1_RunWorkerCompleted( Object^ /*sender*/, RunWorkerCompletedEventArgs^ e ) { // First, handle the case where an exception was thrown. if ( e->Error != nullptr ) { MessageBox::Show( e->Error->Message ); } else if ( e->Cancelled ) { // Next, handle the case where the user cancelled // the operation. // Note that due to a race condition in // the DoWork event handler, the Cancelled // flag may not have been set, even though // CancelAsync was called. resultLabel->Text = "Cancelled"; } else { // Finally, handle the case where the operation // succeeded. resultLabel->Text = e->Result->ToString(); } // Enable the UpDown control. this->numericUpDown1->Enabled = true; // Enable the Start button. startAsyncButton->Enabled = true; // Disable the Cancel button. cancelAsyncButton->Enabled = false; } // This event handler updates the progress bar. void backgroundWorker1_ProgressChanged( Object^ /*sender*/, ProgressChangedEventArgs^ e ) { this->progressBar1->Value = e->ProgressPercentage; } // This is the method that does the actual work. For this // example, it computes a Fibonacci number and // reports progress as it does its work. long ComputeFibonacci( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e ) { // The parameter n must be >= 0 and <= 91. // Fib(n), with n > 91, overflows a long. if ( (n < 0) || (n > 91) ) { throw gcnew ArgumentException( "value must be >= 0 and <= 91","n" ); } long result = 0; // Abort the operation if the user has cancelled. // Note that a call to CancelAsync may have set // CancellationPending to true just after the // last invocation of this method exits, so this // code will not have the opportunity to set the // DoWorkEventArgs.Cancel flag to true. This means // that RunWorkerCompletedEventArgs.Cancelled will // not be set to true in your RunWorkerCompleted // event handler. This is a race condition. if ( worker->CancellationPending ) { e->Cancel = true; } else { if ( n < 2 ) { result = 1; } else { result = ComputeFibonacci( n - 1, worker, e ) + ComputeFibonacci( n - 2, worker, e ); } // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if ( percentComplete > highestPercentageReached ) { highestPercentageReached = percentComplete; worker->ReportProgress( percentComplete ); } } return result; } void InitializeComponent() { this->numericUpDown1 = gcnew System::Windows::Forms::NumericUpDown; this->startAsyncButton = gcnew System::Windows::Forms::Button; this->cancelAsyncButton = gcnew System::Windows::Forms::Button; this->resultLabel = gcnew System::Windows::Forms::Label; this->progressBar1 = gcnew System::Windows::Forms::ProgressBar; this->backgroundWorker1 = gcnew System::ComponentModel::BackgroundWorker; (dynamic_cast<System::ComponentModel::ISupportInitialize^>(this->numericUpDown1))->BeginInit(); this->SuspendLayout(); // // numericUpDown1 // this->numericUpDown1->Location = System::Drawing::Point( 16, 16 ); array<Int32>^temp0 = {91,0,0,0}; this->numericUpDown1->Maximum = System::Decimal( temp0 ); array<Int32>^temp1 = {1,0,0,0}; this->numericUpDown1->Minimum = System::Decimal( temp1 ); this->numericUpDown1->Name = "numericUpDown1"; this->numericUpDown1->Size = System::Drawing::Size( 80, 20 ); this->numericUpDown1->TabIndex = 0; array<Int32>^temp2 = {1,0,0,0}; this->numericUpDown1->Value = System::Decimal( temp2 ); // // startAsyncButton // this->startAsyncButton->Location = System::Drawing::Point( 16, 72 ); this->startAsyncButton->Name = "startAsyncButton"; this->startAsyncButton->Size = System::Drawing::Size( 120, 23 ); this->startAsyncButton->TabIndex = 1; this->startAsyncButton->Text = "Start Async"; this->startAsyncButton->Click += gcnew System::EventHandler( this, &FibonacciForm::startAsyncButton_Click ); // // cancelAsyncButton // this->cancelAsyncButton->Enabled = false; this->cancelAsyncButton->Location = System::Drawing::Point( 153, 72 ); this->cancelAsyncButton->Name = "cancelAsyncButton"; this->cancelAsyncButton->Size = System::Drawing::Size( 119, 23 ); this->cancelAsyncButton->TabIndex = 2; this->cancelAsyncButton->Text = "Cancel Async"; this->cancelAsyncButton->Click += gcnew System::EventHandler( this, &FibonacciForm::cancelAsyncButton_Click ); // // resultLabel // this->resultLabel->BorderStyle = System::Windows::Forms::BorderStyle::Fixed3D; this->resultLabel->Location = System::Drawing::Point( 112, 16 ); this->resultLabel->Name = "resultLabel"; this->resultLabel->Size = System::Drawing::Size( 160, 23 ); this->resultLabel->TabIndex = 3; this->resultLabel->Text = "(no result)"; this->resultLabel->TextAlign = System::Drawing::ContentAlignment::MiddleCenter; // // progressBar1 // this->progressBar1->Location = System::Drawing::Point( 18, 48 ); this->progressBar1->Name = "progressBar1"; this->progressBar1->Size = System::Drawing::Size( 256, 8 ); this->progressBar1->Step = 2; this->progressBar1->TabIndex = 4; // // backgroundWorker1 // this->backgroundWorker1->WorkerReportsProgress = true; this->backgroundWorker1->WorkerSupportsCancellation = true; // // FibonacciForm // this->ClientSize = System::Drawing::Size( 292, 118 ); this->Controls->Add( this->progressBar1 ); this->Controls->Add( this->resultLabel ); this->Controls->Add( this->cancelAsyncButton ); this->Controls->Add( this->startAsyncButton ); this->Controls->Add( this->numericUpDown1 ); this->Name = "FibonacciForm"; this->Text = "Fibonacci Calculator"; (dynamic_cast<System::ComponentModel::ISupportInitialize^>(this->numericUpDown1))->EndInit(); this->ResumeLayout( false ); } }; [STAThread] int main() { Application::Run( gcnew FibonacciForm ); }
.png)


