All dialog buttons discussed so far set the dialog box's DialogResult property to a value, which not only has the effect of returning that value to the parent form, but also of closing the dialog box. Sometimes you'll want the parent form to process the changes made in the dialog box while leaving the dialog box open. Many programs implement an Apply button for this purpose.
An Apply button typically is disabled, i.e., grayed out, when the dialog first opens. As soon as any change is made in one of the dialog controls, the button is enabled. Clicking the Apply button makes available to the parent form all the changes made in the dialog box, but leaves the dialog box open. The Apply button is then disabled until the next change in the dialog box is made.
You might be tempted to implement the Apply button by creating a public method in the parent Form class that performs whatever work needs to be done when the Apply button is clicked. The Apply button would simply execute that public method when it was clicked, passing any required information as arguments.
This is not a clean way to implement an Apply button, however, because the dialog box class needs to know too much about the parent class. Also, if other classes invoke your dialog box, the dialog box must know their methods as well. This design tightly couples the dialog class to the forms that invoke it, which is generally an indication of poor design.
A preferable design, which decouples the dialog class from the calling class, is to have the Dialog Box raise an event when the Apply button is clicked. The Apply event would be handled by the parent form and any other interested classes. With this event-driven design, the Dialog box and the parent class can be modified independently of one another; they are associated only through the indirection of publishing and subscribing to the Apply event.
The following example demonstrates how to raise and handle an Apply event when the user clicks the Apply button.
Open Visual Studio .NET and create a new Windows Application project called DialogapplyEvent in the language of your choice. Drag a Button control onto the form. Name the button btnCreate and change the Text property to Create Dialog Box. Resize it as necessary to make it look good. Drag a Label control onto the form and name it lblReturn. Blank out its Text property.
|
Right-click anywhere on the form in the Design window and select View Code. This will open the source code for the Form class. Add the code to create the dialog box class, DialogDemo, shown in Example 6-5 (in C#) or in Example 6-6 (in VB.NET), somewhere inside the Form1 class.
Example 6-5. DialogDemo class using an event in C#
// Dialog box class private class DialogDemo : Form { private Button btnApply; private TextBox txt; public event EventHandler ClickApply; public DialogDemo( ) { Text = "Apply Dialog Demo"; FormBorderStyle = FormBorderStyle.FixedDialog; BackColor = System.Drawing.Color.Aquamarine; ControlBox = false; MaximizeBox = false; MinimizeBox = false; ShowInTaskbar = false; Size = new Size(400,200); StartPosition = FormStartPosition.CenterScreen; // Create the OK button Button btnOK = new Button( ); btnOK.Text = "OK"; btnOK.DialogResult = DialogResult.OK; btnOK.Location = new Point(50,50); btnOK.TabIndex = 0; btnOK.Click += new EventHandler(applyButtonOnClick); Controls.Add(btnOK); // Create the Apply button btnApply = new Button( ); btnApply.Text = "Apply"; btnApply.Location = new Point(150,50); btnApply.TabIndex = 1; btnApply.Enabled = false; btnApply.Click += new EventHandler(applyButtonOnClick); Controls.Add(btnApply); // Create the Cancel button Button btnCancel = new Button( ); btnCancel.Text = "Cancel"; btnCancel.DialogResult = DialogResult.Cancel; btnCancel.Location = new Point(250,50); btnCancel.TabIndex = 2; Controls.Add(btnCancel); // create input text box txt = new TextBox( ); txt.Size = new Size(100,15); txt.Location = new Point(150,15); txt.TextChanged += new EventHandler(TextBoxChanged); Controls.Add(txt); } // close DialogDemo constructor private void TextBoxChanged(object sender, EventArgs e) { TextBox txt = (TextBox)sender; DialogDemo dlg = (DialogDemo)txt.Parent; dlg.EnableapplyButton = true; } public bool EnableapplyButton { get {return btnApply.Enabled; } set {btnApply.Enabled = value; } } public string TextOut { get {return txt.Text; } } private void applyButtonOnClick (object sender, EventArgs e) { if (ClickApply != null) ClickApply(this, new EventArgs( )); } } // close for DialogDemo class
Example 6-6. DialogDemo class using an event in VB.NET
' Dialog box class private class DialogDemo inherits Form dim btnApply as Button dim txt as TextBox public event ClickApply as EventHandler public sub new ( ) myBase.New Text = "Apply Dialog Demo" FormBorderStyle = FormBorderStyle.FixedDialog BackColor = System.Drawing.Color.Aquamarine ControlBox = false MaximizeBox = false MinimizeBox = false ShowInTaskbar = false Size = new Size(400,200) StartPosition = FormStartPosition.CenterScreen ' Create the OK button dim btnOK as New Button btnOK.Text = "OK" btnOK.DialogResult = DialogResult.OK btnOK.Location = new Point(50,50) btnOK.TabIndex = 0 AddHandler btnOK.Click, AddressOf applyButtonOnClick Controls.Add(btnOK) ' Create the Apply button btnApply = new Button( ) btnApply.Text = "Apply" btnApply.Location = new Point(150,50) btnApply.TabIndex = 1 btnApply.Enabled = false AddHandler btnApply.Click, AddressOf applyButtonOnClick Controls.Add(btnApply) ' Create the Cancel button dim btnCancel as new Button( ) btnCancel.Text = "Cancel" btnCancel.DialogResult = DialogResult.Cancel btnCancel.Location = new Point(250,50) btnCancel.TabIndex = 2 Controls.Add(btnCancel) ' create input text box txt = new TextBox( ) txt.Size = new Size(100,15) txt.Location = new Point(150,15) AddHandler txt.TextChanged, AddressOf TextBoxChanged Controls.Add(txt) end sub ' close DialogDemo constructor private sub applyButtonOnClick(sender as Object, e as EventArgs) RaiseEvent ClickApply(me, new EventArgs( )) end sub private sub TextBoxChanged(sender as Object, e as EventArgs) dim txt as TextBox = CType(sender,TextBox) dim dlg as DialogDemo = CType(txt.Parent,DialogDemo) dlg.EnableapplyButton = true end sub public property EnableapplyButton as Boolean get return btnApply.Enabled end get set btnApply.Enabled = value end set end property public ReadOnly property TextOut as string get return txt.Text end get end property end class ' close DialogDemo class
The first thing to note about the DialogDemo class is that it is nested inside the class of the parent form. This design implicitly makes the dialog box available to the Form1 class, but not to any other form. If you want to create a dialog box that will be accessible to different parent forms, then it needs public exposure, probably in a class by itself, or perhaps in a class that also contains other dialog box classes used by your application.
The next thing to note about the DialogDemo class is that it inherits from the Form class. The dialog box being created is just a form, like any other form. What makes it modal is the way it is called from the parent form. When the btnCreate button is clicked, the btnCreate_Click event handler method, shown in C# in Example 6-7 and in VB.NET in Example 6-8, is fired. The ShowDialog( ) method displays the form modally. If the Show( ) method of the Form class were used to display the dialog box rather than ShowDialog( ), it would be a modeless dialog.
Create the event handler for the Click event for the btnCreate button by going to the Design view and double-clicking on the button. Add the code highlighted in Example 6-7 (in C#) or in Example 6-8 (in VB.NET) to the code skeleton for the btnCreate Click event-handler method.
Example 6-7. btnCreate event handler using the ClickApply event in C#
private void btnCreate_Click(object sender, System.EventArgs e) { DialogDemo dlg = new DialogDemo( ); dlg.EnableapplyButton = false; // Add the event handler dlg.ClickApply += new EventHandler(DialogDemoOnApply); // Show the dialog modally dlg.ShowDialog( ); if (dlg.DialogResult = = DialogResult.OK) {lblReturn.Text = dlg.TextOut;} else {lblReturn.Text = dlg.DialogResult.ToString( );} }
Example 6-8. btnCreate event handler using the ClickApply event in VB.NET
Private Sub btnCreate_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnCreate.Click dim dlg as new DialogDemo( ) dlg.EnableapplyButton = false ' Add the event handler AddHandler dlg.ClickApply, AddressOf DialogDemoOnApply ' Show the dialog modally dlg.ShowDialog( ) if dlg.DialogResult = DialogResult.OK then lblReturn.Text = dlg.TextOut else lblReturn.Text = dlg.DialogResult.ToString( ) end if End Sub
Before the ShowDialog( ) method can be called, the dialog box must be created. The first line of code in the btnCreate_Click method instantiates a new object of type DialogDemo. The instantiation of the dialog box looks like the following:
DialogDemo dlg = new DialogDemo( );
dim dlg as new DialogDemo( )
Once the dialog box is instantiated, the EnableapplyButton property is set to false, which disables the Apply button. It will be kept disabled until one of the controls in the dialog box is modified, making the Apply button relevant.
The EnableapplyButton property is of type Boolean. It gets and sets the value of the Enabled property of the Apply button. If the Enabled property of a control is false, then the control is visible, but grayed out, and cannot receive focus. Thus, it cannot be clicked on or otherwise manipulated.
After the dialog box returns from the modal display, the DialogResult property is tested. If the DialogResult is OK, indicating that the dialog box was dismissed with the OK button, then the display label, lblReturn, is populated with the value of the read-only TextOut property of DialogDemo. If the DialogResult value is anything other than OK, which in this example can only be Cancel, then the DialogResult itself is displayed in lblReturn. Depending on the requirements of the application, any of a number of techniques can be used to test the DialogResult return value and process the dialog box control values accordingly.
The DialogDemo class shown in C# in Example 6-5 and in VB.NET in Example 6-6 declares two member variables:
private Button btnApply; private TextBox txt;
dim btnApply as Button dim txt as TextBox
These are member variables, so they are visible to all the methods and properties in the DialogDemo class.
The DialogDemo constructor sets various properties of the dialog box, including FormBorderStyle, BackColor, ControlBox, and MaximizeBox.
The constructor then instantiates and specifies several controls on the dialog box: OK, Apply, Cancel, and a TextBox for accepting typed user input. Each control is added to the Controls collection of the dialog box.
Both OK and Apply have the same event handler, ApplyButtonOnClick, added to the delegate for their Click event. This is because both buttons cause the text entered into the TextBox control to be displayed on the parent form.
The TextBox control raises the TextChanged event whenever the content of the TextBox is changed, either by adding or deleting characters. The event is handled by the TextBoxChanged event handler method. In C#, the event handler is added to the delegate with this line of code:
txt.TextChanged += new EventHandler(TextBoxChanged);
and the TextBoxChanged method looks like:
private void TextBoxChanged(object sender, EventArgs e) { this.EnableapplyButton = true; }
In VB.NET, the event handler is added to the delegate with this line of code:
AddHandler txt.TextChanged, AddressOf TextBoxChanged
and the TextBoxChanged method looks like:
private sub TextBoxChanged(sender as Object, e as EventArgs) me.EnableapplyButton = true end sub
An event called ClickApply was declared in the DialogDemo class (Example 6-5 in C#; Example 6-6 in VB.NET). This event will be handled by a method called DialogDemoOnApply, shown in Example 6-9 (in C#) or in Example 6-10 (in VB.NET). Enter the code for DialogDemoOnApply somewhere within the Form1 class, but not within the DialogDemo class.
Example 6-9. DialogDemoOnApply method in C#
private void DialogDemoOnApply(object sender, System.EventArgs e) { DialogDemo dlg = (DialogDemo)sender; lblReturn.Text = dlg.TextOut; dlg.EnableapplyButton = false; }
Example 6-10. DialogDemoOnApply method in VB.NET
private sub DialogDemoOnApply(ByVal sender As System.Object, _ ByVal e As System.EventArgs) dim dlg as DialogDemo = CType(sender, DialogDemo) lblReturn.Text = dlg.TextOut dlg.EnableapplyButton = false end sub
The Apply button ultimately raises the ClickApply event when it is clicked. It does so through a multistep process. The Apply button Click event has an event handler method called ApplyButtonOnClick added to its delegate. This is done with the following line of code:
btnApply.Click += new EventHandler(applyButtonOnClick);
AddHandler btnApply.Click, AddressOf applyButtonOnClick
The ApplyButtonOnClick method raises the ClickApply method. This method is reproduced here:
private void applyButtonOnClick (object sender, EventArgs e) { if (ClickApply != null) ClickApply(this, new EventArgs( )); }
private sub applyButtonOnClick(sender as Object, e as EventArgs) RaiseEvent ClickApply(me, new EventArgs( )) end sub
Since both the OK and Apply buttons perform the same task, with the only difference being that the dialog box closes when the OK button is clicked, they both use the same applyButtonOnClick method as their Click event handler.
|
The parent form handles this ClickApply event by adding an event-handler method, DialogDemoOnApply, to the ClickApply delegate. This was done in the Click event handler for btnCreate, btnCreate_Click, which was listed in Example 6-7 (in C#) and Example 6-8 (in VB.NET). The method was added to the delegate with the following line of code:
dlg.ClickApply += new EventHandler(DialogDemoOnApply);
AddHandler dlg.ClickApply, AddressOf DialogDemoOnApply
The DialogDemoOnApply method, which handles this event and is shown in Example 6-9 in C# and in Example 6-10 in VB.NET, gets an instance of the dialog box class that raised the event by casting the sender object as type DialogDemo. (Remember, even though sender is already of type DialogDemo, the compiler does not know this.) Once the reference to the DialogDemo dialog box is in hand, the value of the read-only TextOut property can be assigned to the label on the parent form and the EnableapplyButton property can be set to false, which disables the Apply button on the dialog box until the TextBox is modified again.
Examine the DialogDemo class more closely. Notice there is no member variable of type Form1 (the parent Form class), and no direct coupling between the dialog box class and the parent class. There is, however, a declared public event delegate called ClickApply:
public event EventHandler ClickApply;
public event ClickApply as EventHandler
This delegate is of type EventHandler, which specifies that the event-handler methods added to the delegate will take two arguments: an object (typically called sender) and an argument of type EventArgs (typically called e).
Looking back at the event handler method added to the ClickApply delegate, you can see that it does in fact correspond to the required signature:
private void DialogDemoOnApply(object sender, System.EventArgs e)
private sub DialogDemoOnApply(ByVal sender As System.Object, _ ByVal e As System.EventArgs)
Windows Forms and the .NET Framework
Getting Started
Visual Studio .NET
Events
Windows Forms
Dialog Boxes
Controls: The Base Class
Mouse Interaction
Text and Fonts
Drawing and GDI+
Labels and Buttons
Text Controls
Other Basic Controls
TreeView and ListView
List Controls
Date and Time Controls
Custom Controls
Menus and Bars
ADO.NET
Updating ADO.NET
Exceptions and Debugging
Configuration and Deployment