|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Using Windows Form ControlsIn recent years, Visual Basic has won great acclaim for granting programmers the tools for creating highly detailed user interfaces via an intuitive form designer, along with an easy to learn programming language that together produced probably the best environment for rapid application development out there. One of the things that Visual Basic does, and other rapid application development tools, such as Delphi, also does, is provide access to a number of prefabricated controls that the developer can use to quickly build the user interface (UI) for an application. At the center of most Visual Basic Windows applications stands the form designer. You create a user interface by dragging and dropping controls from a toolbox to your form, placing them where you want them to be when you run the program, and then double-clicking the control to add handlers for the control. The controls provided out of the box by Microsoft along with custom controls that can be bought at reasonable prices, have supplied programmers with an unprecedented pool of reusable, thoroughly tested code that is no further away than a click with the mouse. What was central to Visual Basic is now, through Visual Studio.NET, available to C# programmers. Most of the controls used before .NET were, and still are, special COM objects, known as ActiveX controls. These are usually able to render themselves at both design and runtime. Each control has a number of properties allowing the programmer to do a certain amount of customization, such as setting the background color, caption, and its position on the form. The controls that we'll see in this chapter have the same look and feel as ActiveX controls, but they are not – they are .NET assemblies. However, it is still possible to use the controls that have been designed for older versions of Visual Studio but there is a small performance overhead because .NET has to wrap the control when you do so. For obvious reasons, when they designed .NET, Microsoft did not want to render the immense pool of existing controls redundant, and so have provided us with the means to use the old controls, even if future controls are built as pure .NET components. These .NET assemblies can be designed in such a way that you will be able to use them in any of the Visual Studio languages, and the hope and belief is that the growing component industry will latch on, and start producing pure .NET components. We'll look at creating control ourselves in the next chapter. An in depth explanation of .NET assemblies is provided in Chapter 21. Please refer to it if you want to know more about what an assembly is. We have already seen the form designer in action, if only briefly, in the examples provided earlier in this book. In this chapter, we'll take a closer look at it, and especially how we use a number of controls, all of which come out of the box with Visual Studio.NET. Presenting all of the controls present in Visual Studio.NET will be an impossible task within the scope of this book, and so we'll be presenting the most commonly used controls, ranging from labelsand text boxes, to list views and status bars. The Windows Form DesignerWe'll start out by taking a brief tour of the Windows Form Designer. This is the main playing ground when you are laying out your user interface. It is perfectly possible to design forms without using Visual Studio.NET, but designing an interface in Notepad can be a quite painful experience. Let's look at the environment we'll be using. Start Visual Studio.NET and create a new C# Windows Application project by selecting File | New | Project. In the dialog that appears, click Visual C# Projects in the tree to the left and then select Windows Application in the list to the right. For now, simply use the default name suggested by Visual Studio and click OK. This should bring up a window much like the one shown below:
If you are famliar with the forms designer found in Visual Basic you will notice the similarities – obviously someone desided that the designer was a winner and desided to allow it to be used it in other Visual Studio languages as well. If you are not familiar with the Visual Basic designer, then quite a few things are going on in the above screenshot, so let's take a moment and go through the panels one by one. In the center of the screen is the form that you are designing. You can drag and drop controls from the toolbox onto the form. The toolbox is collapsed in the picture above, but if you move the mouse pointer to the far left of the screen over the Toolbox tab, it will unfold. You can then click the pin at the top right of the panel to pin it down. This will rearrange the work area so that the toolbox is now always on top, and isn't obscuring the form. We'll take a closer look at the toolbox and what it contains shortly. Also collapsed on the left hand bar is the Server Explorer – represented by the computers icon on top of the toolbox tab. You can think of this as a small version of the Windows Control Panel. From here, you can browse computers on a network, add and remove database connections, and much more. To the right of the window are two panels. The top-right one is the Solution Explorer and the class view. In the Solution Explorer, you can see all open projects and their associated files. By clicking the tab at the bottom of the Solution Explorer, you activate the Class Viewer. In this, you can browse all of the classes in your projects and all of the classes that they are derived from. At the bottom right of the screen, is the Properties panel. This panel will contain all of the properties of the selected item for easy reference and editing. We'll be using this panel quite a bit in this chapter. Also in this panel, the Dynamic Help tab is visible. This panel will show help tips to you for any selected objects and code even while you type. If your computer uses one of the older microprocessors or has a small amount of RAM, then I suggest that you remove this from the panel when it is not needed, as all that searching for help can make performance rather sluggish. The ToolboxLet's have a closer look at the toolbox. If you haven't already, move your mouse pointer over the toolbox on the left of the screen, and pin it to the foreground by clicking the pin at the top right of the panel that unfolds: If you accidentally remove the toolbox by clicking the X instead, you can make it reappear by selecting Toolbox from the View menu, or by pressing Ctrl-Alt-X.
The toolbox contains a selection of all the controls available to you as a .NET developer. In particular, it provides the selection that is of importance to you as a Windows Application developer. If you had chosen to create a Web Forms project, rather than a Windows Application, you would have been given a different toolbox to use. You are not limited to use this selection. You can customize the toolbox to fit your needs, but in this chapter, we'll be focusing on the controls found in the selection that is shown in the picture above – in fact, we'll look at most of the controls that are shown here. Now that we know where we'll be doing the work, let's look at controls in general. ControlsMost controls in .NET derive from the System.Windows.Forms.Control class. This class defines the basic functionality of the controls, which is why many properties and events in the controls we'll see are identical. Many of these classes are themselves base classes for other controls, as is the case with the Label and TextBoxBase classes in the diagram below:
Some controls, named custom or user controls, derive from another class: System.Windows.Forms.UserControl. This class is itself derived from the Control class and provides the functionality we need to create controls ourselves. We'll cover this class in Chapter 14. Incidentally, controls used for designing Web user interfaces derive from yet another class, System.Web.UI.Control. PropertiesAll controls have a number of properties that are used to manipulate the behavior of the control. The base class of most controls, Control, has a number of properties that other controls either inherit directly or override to provide some kind of custom behavior. The table below shows some of the most common properties of the Control class. These properties will be present in most of the controls we'll visit in this chapter, and they will therefore, not be explained in detail again, unless the behavior of the properties is changed for the control in question. Note that this table is not meant to be exhaustive; if you want to see all f the properties in the class, please refer to the MSDN library:
Anchor and Dock PropertiesThese two properties are especially useful when you are designing your form. Ensuring that a window doesn't become a mess to look at if the user decides to resize the window is far from trivial, and numerous lines of code have been written to achieve this. Many programs solve the problem by simply disallowing the window from being resized, which is clearly the easiest way around the problem, but not the best. The Anchor and Dock properties that have been introduced with .NET lets you solve this problem without writing a single line of code. The Anchor property is used to to specify how the control behaves when a user resizes the window. You can specify if the control should resize itself, anchoring itself in proportion to its own edges, or stay the same size, anchoring its position relative to the window's edges. The Dock property is related to the Anchor property. You can use it to specify that a control should dock to an edge of its container. If a user resizes the window, the control will continue to be docked to the edge of the window. If, for instance, you specify that a control should dock with the bottom of its container, the control will resize itself to always occupy the bottom part of the screen, no matter how the window is resized. The control will not be resized in the process; it simply stays docked to the edge of the window. See the text box example later in this chapter for the exact use of the Anchor property. EventsWhen a user clicks a button or presses a button, you as the programmer of the application, want to be told that this has happened. To do so, controls use events. The Control class defines a number of events that are common to the controls we'll use in this chapter. The table below describes a number of these events. Once again, this is just a selection of the most common events; if you need to see the entire list, please refer to the MSDN library:
We will see many of these events in the examples in the rest of the chapter. We are now ready to start looking at the controls themselves, and we'll start with one that we've seen in previous chapters, the Button control. The Button ControlWhen you think of a button, you are probably thinking of a rectangular button that can be clicked to perform some task. However, technically there are three buttons in Visual Studio.NET. This is because radio buttons (as the name implies) and check boxes are also buttons. Because of this, the Button class is not derived directly from Control, but from another class called ButtonBase, which is derived from Control. We'll focus on the Button control in this section and leave radio buttons and check boxes for later in the chapter. The button control exists on just about any Windows dialog you can think of. A button is primarily used to perform three kinds of tasks:
Working with the button control is very straightforward. It usually consists of adding the control to your form and double-clicking it to add the code to the Click event, which will probably be enough for most applications you'll work on. Let's look at some of the commonly used properties and events of the control. This will give you an idea what can be done with it. After that, we'll create a small example that demonstrates some of the basic properties and events of a button. Button PropertiesWe'll list the properties as members of the button base, even if technically they are defined in the ButtonBase base class. Only the most commonly used properties are explained here. Please refer to MSDN for a complete listing:
Button EventsBy far the most used event of a button is the Click event. This happens whenever a user clicks the button, by which we mean pressing the left mouse button and releasing it again while over the button. This means that if you left-click on the button and then draw the mouse away from the button before releasing it the Click event will not be raised. Also, the Click event is raised when the button has focus and the user press Enter. If you have a button on a form, you should always handle this event. Let's move to the example. We'll create a dialog with three buttons. Two of the buttons will change the language used from English to Danish and back (feel free to use whatever language you prefer). The last button closes the dialog. Try it Out – Button Test1. Open Visual Studio.NET and create a new C# Windows Application. Name the 2. Pin the Toolbox down, and double-click the Button control three times. Then move the buttons and resize the form as shown in the picture below:
3. Right-click a button and select Properties. Then change the Name property for each of the buttons as indicated in the picture above by selecting the Name edit field in the Properties panel and typing the text. 4. Change the Text properties of each of the three buttons to the same as the name except for the first three letters (btn). 5. We want to display a flag in front of the text to make it clear what we are talking about. Select the English button and find the Image property. Click (…) to the right of it to bring up a dialog where you can select an image. The flag icons we want to display come with Visual Studio.NET. If you installed to the default location (on an English language installation) they should be located in C:\Program Files\Microsoft Visual Studio.NET\Common7\Graphics\icons\Flags. Select the icon flguk.ico. Repeat this process with the Danish button, selecting the flgden.ico file instead (if you want to use a different flag here, then this directory will have other flags to choose from). 6. You'll notice at this point that the button text and icon is placed on top of each other, so we need to change the alignment of the icon. For both the English and Danish buttons, change the ImageAlign property to MiddleLeft. 7. At this point, you may want to adjust the width of the buttons so that the text doesn't start right where the images end. Do this by selecting each of the buttons and pull out the right notch that appear. 8. Finally, click on the form and change the Text property to "Do you speak English?" That's it for the user interface of our dialog. You should now have something that looks like this:
Now we are ready to add the event handlers to the dialog. Double-click the English button. This will take you to directly to the event handler. The Click event is the default for the button it is that event that is created when you double-click the button. Other controls have other defaults. Adding the Event HandlersWhen you double-click the control two things happens in the code behind the form. First of all, a subscription to the event is created in the InitializeComponent() method: this.btnEnglish.Click += new System.EventHandler(this.btnEnglish_Click); If you want to subscribe an event other than the default one, you will need to write the subscription code yourself, as we will do in the remainder of this chapter. It is important to remember that the code in the InitializeComponent() method is overwritten every time you switch from design mode to the code. Because of this, you should never write your event subscriptions in this method. Instead, use the constructor of the class. The second thing that happens, is that the event handler itself is added: private void btnEnglish_Click(object sender, System.EventArgs e) { this.Text = "Do you speak English?"; } The method name is a concatenation of the name of the control, an underscore and the name of the event that is handled. The first parameter, object sender, will hold the control that was clicked. In this example, this will always be the control indicated by the name of the method, but in other cases many controls may use the same method to handle an event, and it that case you can find out exactly which control is calling by checking this value. The text box example later in this chapter demonstrates how to use a single method for multiple controls. The other parameter, System.EventArgs e, holds information about what happened. In this case, we'll not be needing any of this information. As you will recall from earlier in this book, the this keyword identifies the current instance of the class. Because the class we are working on is represented by that instance, we can access the properties and controls it contains through that keyword. Setting the Text property on this as we do in the code above then means that we are setting the Text property of the current instance of the form. Return to the Form Designer and double-click the Danish button and you will be taken to the event handler for that button. Here is the code: private void btnDanish_Click(object sender, System.EventArgs e) { this.Text = "Taler du dansk?"; } This method is identical to the btnEnglish_Click, except that the text is in Danish. Finally, we add the event handler for the OK button in the same way as we've done twice now. The code is a little different though: private void btnOK_Click(object sender, System.EventArgs e) { Application.Exit(); } With this, we exit the application and, with it, this first example. Compile it, run it, and press a few of the buttons. You will get output similar to this:
The Label and LinkLabel ControlsThe Label control is probably the most used control of them all. Look at any Windows application and you'll see them on just about any dialog you can find. The label is a simple control with one purpose only: to present a caption or short hint to explain something on the form to the user. Out of the box, Visual Studio.NET includes two label controls that are able to present them selves to the user in two distinct ways:
The two controls are found at the top of the control panel on the Window Forms tab. In the picture below, one of each of the two types of Label have been dragged to a to illustrate the difference in appearance between the two:
If you have experience with Visual Basic you may notice that the Text property is used to set the text that is displayed, rather than the Caption property. You will find that all intrinsic .NET controls use the name Text to describe the main text for a control. Before .NET, Caption and Text were used interchangeably. And that's it for most uses of the Label control. Usually you need to add no event handling code for a standard Label. In the case of the LinkLabel, however, some extra code is needed if you want to allow the user to click it and take him or her to the web page shown in the text. The Label control has a surprising number of properties that can be set. Most of these are derived from Control, but some are new. The following table lists the most common ones. If nothing else is stated, the properties exist in both the Label and LinkLabel controls:
The TextBox ControlText boxes should be used when you want the user to enter text that you have no knowledge of at design time (for example the name of the user). The primary function of a text box is for the user to enter text, but any characters can be entered, and it is quite possible to force the user to enter numeric values only. Out of the box .NET comes with two basic controls to take text input from the user: TextBox and RichTextBox (we'll discuss RichTextBox later in this chapter). Both controls are derived from a base class called TextBoxBase which itself is derived from Control. TextBoxBase provides the base functionality for text manipulation in a text box, such as selecting text, cutting to and pasting from the Clipboard, and a wide range of events. We'll not focus so much now on what is derived from where, but instead look at the simpler of the two controls first – TextBox. We'll build one example that demonstrates the TextBox properties and build on that to demonstrate the RichTextBox control later. TextBox PropertiesAs has been stated earlier in this chapter, there are simply too many properties for us to describe them all, and so this listing includes only the most common ones:
TextBox EventsCareful validation of the text in the TextBox controls on a form can make the difference between happy users and very angry ones. You have probably experienced how annoying it is, when a dialog only validates its contents when you click OK. This approach to validating the data usually results in a message box being displayed informing you that the data in "TextBox number three" is incorrect. You can then continue to click OK until all the data is correct. Clearly this is not a good approach to validating data, so what can we do instead? The answer lies in handling the validation events a TextBox control provides. If you want to make sure that invalid characters are not entered in the text box or only values within a certain range are allowed, then you will want to indocate to the user of the control whether the value engtered is valid or not. The TextBox control provides these events (all of which are inherited from Control):
Try it Out – TextBoxTestWe'll create a dialog on which you can enter your name, address, occupation, and age. The purpose of this example is to give you a good grounding in manipulating properties and using events, not to create something that is incredibly useful. We'll build the user interface first: 1. Select Files | New and create a new Windows Application under C# Projects. Name the project TextBoxTest. 2. Create the form shown below by dragging the labels, text boxes, and buttons onto the design surface. Before you can resize the two text boxes txtAddress and txtOutput as shown you must set their Multiline property to true. Do this by right-clicking the controls and select Properties:
3. Name the controls as are indicated in the picture above. 4. Set the text property of for each of the text boxes to an empty string, which means that they will contain nothing when the application is first run. 5. Set the text property of all other controls to the same as the name of the control except for the first three letters. Set the text property of the form as indicated in the caption in the picture. 6. Set the Scrollbars property of the two controls txtOutput and txtAddress 7. Set the ReadOnly property of the txtOutput control to true. 8. Set the CausesValidation property of the button btnHelp to false. Remember from the discussion of the Validating and Validated events that setting this to false will allow the user to click this button without having to be convered about entering invalid data. 9. When you have sized the form to fit snugly around the controls, it is time to anchor the controls so they behave properly when the form is resized. Set the Anchor property as shown in the table below:
Tip: You can copy the Anchor property text from one control and paste into the other controls with the same values. You do not need to use the drop-down button. The reason why txtOutput is anchored rather than docked to the bottom of the form is that we want the output text area to be resized as we pull the form. If we had docked the control to the bottom of the form, it would be moved to stay at the bottom, but it would not be resized. 10. One final thing should be set. On the form, find the Size and MinSize properties. Our form has little meaning if it is sized to something smaller than it is now, therefore you should set the MinSize property to the same as the Size property. The job of setting up the visual part of the form is now complete. If you run it nothing happens when you click the buttons or enter text, but if you maximize or pull in the dialog, the controls behave exactly as you want them to in a proper user interface, staying put and resizing to fill the whole of the dialog. If you've ever tried to accomplish the same task in a language like Visual Basic 6, you will know how much work you have just been spared. Now it is time to look at the code. Right click on the form and select View Code. If you have the toolbox pinned, you should remove the pin to make more space for the code window. Surprisingly little code is visible in the editor. At the top of our class, the controls are defined, but it isn't until you expand the region labeled Windows Form Designer Generated Code that you can see where all your work went. It should be stressed that you should never edit the code in this section! The next time you change something in the designer, it will be overwritten, or even worse, you could change something such that the form designer can no longer show the form. You should take a minute to look over the statements in this section. You will see exactly why it is possible to create a Windows Application without using Visual Studio.NET. Everything in this section could simply be entered in Notepad or a similar text editor and compiled. You will also see why that is not advisable. Keeping track of everything in here is difficult at the best of times; it is easy to introduce errors and, because you cannot see the effects of what you are doing, arranging the controls on the form to look right is a cumbersome task. This does, however, open the door for third party software producers to write their own programming environments to rival Visual Studio.NET, because the compilers used to create the forms are included with the .NET framework, rather than with Visual Studio.NET. Adding the Event HandlersWe can pull out that last bit of effort from the form designer however, before we add our own code. Go back to the Form Designer (by clicking the tab on the top of the text editor), and double-click the button btnOK. Repeat this with the other button. As we saw in the button example earlier in this chapter this causes event handlers for the click event of the buttons to be created. When the OK button is clicked, we want to transfer the text in the input text boxes to the read-only output box. Here is the code for the two click events: private void btnOK_Click(object sender, System.EventArgs e) { // No testing for invalid values are made, as that should // not be necessary string output; // Concatenate the text values of the four TextBoxes output = "Name: " + this.txtName.Text + "\r\n"; output += "Address: " + this.txtAddress.Text + "\r\n"; output += "Occupation: " + this.txtOccupation.Text + "\r\n"; output += "Age: " + this.txtAge.Text; // Insert the new text this.txtOutput.Text = output; } private void btnHelp_Click(object sender, System.EventArgs e) { // Write a short description of each TextBox in the Output TextBox string output; output = "Name = Your name\r\n; output += "Address = Your address\r\n"; output += "Occupation = Only allowed value is 'Programmer'\r\n"; output += "Age = Your age"; // Insert the new text this.txtOutput.Text = output; } In both functions the Text properties of the text boxes are used, either retrieved or set in the btnOK_Click() function or simply set as in the btnHelp_Click() function. We insert the information the user has entered without bothering to check if it is correct. This means that we must do the checking elsewhere. In this example, there are a number of criteria that have to be met in order for the values to be correct:
From this we can see that the check that must be done for two of the text boxes (txtName and txtAddress) is the same. We also see that we should prevent the user from entering anything invalid into the Age box, and finally we must check if the user is a programmer. To prevent the user from clicking OK before anything is entered, we start by setting the OK button's Enabled property to false in the constructor of our form, making sure not to set the property until after the generated code in InitializeComponent() has been called: public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); this.btnOK.Enabled = false; } Now we'll create the handler for the two text boxes that must be checked to see if they are empty. We do this by subscribing to the Validating event of the text boxes. We inform the control that the event should be handled by a method named txtBoxEmpty_Validating(). We also need a way to know the state of our controls. For this purpose, we use the Tag property of the text boxes. If you recall the discussion of this property earlier in the chapter, we said that only strings can be assigned to the Tag property from the Forms Designer. However, as we are setting the Tag value from code, we can do pretty much what we want with it, and it is more appropriate to enter a Boolean value here. To the constructor we add the following statements: this.btnOK.Enabled = false; // Tag values for testing if the data is valid this.txtAddress.Tag = false; this.txtAge.Tag = false; this.txtName.Tag = false; this.txtOccupation.Tag = false; // Subscriptions to events this.txtName.Validating += new System.ComponentModel.CancelEventHandler(this.txtBoxEmpty_Validating); this.txtAddress.Validating += new System.ComponentModel.CancelEventHandler(this.txtBoxEmpty_Validating); Please see Chapter 12 for a complete explanation of events if you are not entirely comfortable with them yet. Unlike the button event handler we've seen previously, the event handler for the Validating event is a specialized version of the standard handler, System.EventHandler. The reason this event need a special handler is that should the validating fail, there must be a way to prevent any further processing. If we were to cancel further processing, that would effectively mean that it would be impossible to leave a text box until the data entered is valid. We will not do anything as drastic as that in this example. The Validating and Validated events combined with the CausesValidation property fixes a nasty problem that occurred when using the GotFocus and LostFocus events to perform validation of controls. The problem occurred when the GotFocus and LostFocus events were continually fired, because validation code was attempting to shift the focus between control, which created an infinite loop. We add the event handler as follows: private void txtBoxEmpty_Validating(object sender, System.ComponentModel.CancelEventArgs e) { // We know the sender is a TextBox, so we cast the sender object to that TextBox tb = (TextBox)sender; // If the text is empty we set the background color of the // Textbox to red to indicate a problem. We use the tag value // of the control to indicate if the control contains valid // information. if (tb.Text.Length == 0) { tb.BackColor = Color.Red; tb.Tag = false; // In this case we do not want to cancel further processing, // but if we had wanted to do this, we would have added this line: // e.Cancel = true; } else { tb.BackColor = System.Drawing.SystemColors.Window; tb.Tag = true; } // Finally, we call ValidateAll which will set the value of // the OK button. ValidateAll(); } Because more than one text box is using this method to handle the event, we cannot be sure which is calling the function. We do know, however, that the effect of calling the method should be the same no matter who is calling, so we can simply cast the sender parameter to a text box and work on that. If the length of the text in the text box is zero, we set the background color to red and the tag to false. If it is not, we set the background color to the standard Windows color for a window. You should always use the colors found in the System.Drawing.SystemColors enumeration when you want to set a standard color in a control. If you simply set the color to white, your application will look strange if the user has changed the default color settings. The ValidateAll()function is described at the end of this example. Keeping with the Validating event, the next handler we'll add is for the Occupation text box. The procedure is exactly the same as for the two previous handlers, but the validation code is different, because occupation must be Programmer or an empty string to be valid. We therefore, add a new line to the constructor. this.txtOccupation.Validating += new System.ComponentModel.CancelEventHandler(this.txtOccupation_Validating); And then the handler itself: private void txtOccupation_Validating(object sender, System.ComponentModel.CancelEventArgs e) { // Cast the sender object to a textbox TextBox tb = (TextBox)sender; // Check if the values are correct if (tb.Text.CompareTo("Programmer") == 0 || tb.Text.Length == 0) { tb.Tag = true; tb.BackColor = System.Drawing.SystemColors.Window; } else { tb.Tag = false; tb.BackColor = Color.Red; } // Set the state of the OK button ValidateAll(); } Our second to last challenge is the age text box. We don't want the user to type anything but positive numbers (including 0 to make the test simpler). To achieve this we'll use the KeyPress event to remove any unwanted characters before they are shown in the text box. First, we subscribe to the KeyPress event. We do this as we've done with the previous event handlers in the constructor: this.txtAge.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtAge_KeyPress); This event handler is specialized as well. The System.Windows.Forms.KeyPressEventHandler is supplied, because the event needs information about the key that was pressed. We then add the event handler itself: private void txtAge_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { if ((e.KeyChar < 48 || e.KeyChar > 57) && e.KeyChar != 8) e.Handled = true; // Remove the character } The ASCII values for the characters between 0 and 9 lies between 48 and 57, so we make sure that the character is within this range. We make one exception though. The ASCII value 8 is the Backspace key, and for editing reasons, we allow this to slip through. Setting the Handled property of KeyPressEventArgs to true tells the control that it shouldn't do anything else with the character, and so it is not shown. As it is now, the control is not marked as invalid or valid. This is because we need another check to see if anything was entered at all. This is a simple thing as we've already written the method to perform this check, and we simply subscribe to the Validating event for the Age control as well by adding this line to the constructor: this.txtAge.Validating += new System.ComponentModel.CancelEventHandler(this.txtBoxEmpty_Validating); One last case must be handled for all the controls. If the user has entered valid text in all the textboxes and then changes something, making the text invalid, the OK button remains enabled. So we have to handle one last event handler for all of the text boxes: the Change event which will turn off the OK button should any text field contain invalid data. The Change event is fired whenever the text in the control changes. We subscribe to the event by adding the following lines to the constructor: this.txtName.TextChanged += new System.EventHandler(this.txtBox_TextChanged); this.txtAddress.TextChanged += new System.EventHandler(this.txtBox_TextChanged); this.txtAge.TextChanged += new System.EventHandler(this.txtBox_TextChanged); this.txtOccupation.TextChanged += new System.EventHandler(this.txtBox_TextChanged); The Change event uses the standard event handler we know from the Click event. Finally, we add the event itself: private void txtBox_TextChanged(object sender, System.EventArgs e) { // Cast the sender object to a Textbox TextBox tb = (TextBox)sender; // Test if the data is valid and set the tag and background // color accordingly. if (tb.Text.Length == 0 && tb != txtOccupation) { tb.Tag = false; tb.BackColor = Color.Red; } else if (tb == txtOccupation && (tb.Text.Length != 0 && tb.Text.CompareTo("Programmer") != 0)) { // Don't set the color here, as it will color change while the user // is typing tb.Tag = false; } else { tb.Tag = true; tb.BackColor = SystemColors.Window; } // Call ValidateAll to set the OK button ValidateAll(); } This time, we must find out exactly which control is calling the event handler, because we don't want the background color of the Occupation textbox to change to red when the user starts typing. We do this by checking the Name property of the textbox that was passed to us in the sender parameter. Only one thing remains: the ValidateAll method that enables or disables the OK button: private void ValidateAll() { // Set the OK button to enabled if all the Tags are true this.btnOK.Enabled = ((bool)(this.txtAddress.Tag) && (bool)(this.txtAge.Tag) && (bool)(this.txtName.Tag) && (bool)(this.txtOccupation.Tag)); } The method simply sets the value of the Enabled property of the OK button to true if all of the Tag properties are true. We need to cast the value of the Tag properties to a Boolean, because it is stored as an object type. If you test the program now, you should see something like this:
Notice that you can click the Help button while you are in a textbox with invalid data without the background color changing to red. The example we've just completed is quite long compared to the others you will see in this chapter. This is because we'll build on this example rather than inventing the wheel. Remember you can download the source code for the examples in this book from www.wrox.com. The RadioButton and CheckBox ControlsAs mentioned earlier, the RadioButton and CheckBox controls share their base class with the button control, though their appearance and use differs substantially from the button. Radio buttons traditionally displays themselves as a label with a dot to the left of it, which can be either selected or not. You should use the radio buttons when you want to give the user a choice between several mutually exclusive options. An example of this could be, if you want to ask for the gender of the user. To group radio boxes together so that they create one logical unit you must use a GroupBox control. By first placing a group box on a form, and then placing the RadioButton controls you need within the borders of the group box, the RadioButton controls will know to change their state to reflect that only one within the group box can be selected. If you do not place them within a group box, only one RadioButton on the form can be selected at any given time. A CheckBox traditionally displays itself as a label with a small box with a checkmark to the left of it. You should use the check box when you want to allow the user to choose one or more options. An example could be a questionnaire asking which operating systems the user has tried (for example, Windows 95, Windows 98, Linux, Max OS X, and so on.) We'll look at the important properties and events of the two controls, starting with the RadioButton, and then move on to a quick example of their use. RadioButton PropertiesAs the control derives from ButtonBase and we've already seen in our example that used the button earlier, there are only a few properties to describe. As always, should you need a complete list, please refer to the MSDN library:
RadioButton EventsYou will usually only use one event when working with RadioButtons, but as always there many others that can be subscribed to. We'll only cover two in this chapter, and the only reason the second event is mentioned is, that there is a subtle difference between the two that should be noted:
CheckBox PropertiesAs you would imagine, the properties and events of this control is very similar to those of the RadioButton, but there are two new ones:
CheckBox EventsYou will normally use only one or two events on this control. Note that, even though the CheckChanged event exists on both the RadioButton and the CheckBox controls, the effects of the events differ:
This concludes the events and properties of the RadioButton and CheckBox controls. But before we look at an example using these, let's take a look at the GroupBox control which we mentioned earlier. The GroupBox ControlBefore we move on to the example, we'll look at the group box control. This control is often used in conjunction with the RadioButton and CheckBox controls to display a frame around, and a caption above, a series of controls that are logically linked in some way. Using the group box is as simple as dragging it onto a form, and then dragging the controls it should contain onto it (but not the other way round – you can't lay a group box over some pre-exisiting controls). The effect of this is that the parent of the controls becomes the group box, rather than the form, and it is therefore possible to have more than one RadioButton selected at any given time. Within the group box, however, only one RadioButton can be selected. The relationship between parent and child probably need to be explained a bit more. When a control is placed on a form, the form is said to become the parent of the control, and hence the control is the child of the form. When you place a GroupBox on a form, it becomes a child of a form. As a group box can itself contain controls, it becomes the parent of these controls. The effect of this is that moving the GroupBox will move all of the controls placed on it. Another effect of placing controls on a group box, is that it allows you to change certain properties of all the controls it contains simply by setting the corresponding property on the group box. For instance, if you want to disable all the controls within a group box, you can simply set the Enabled property of the group box to false. We will demonstrate the use of the GroupBox in the following example. Try it Out – RadioButton and CheckBox ExampleWe'll modify the example we used when we demonstrated the use of text boxes. In that example, the only possible occupation was Programmer. Instead of forcing the user to write this if he or she is a programmer, we'll change this text box to a check box. To demonstrate the use of the RadioButton, we'll ask the user to provide one more piece of information: his or her gender. Change the text box example like this: 1. Remove the label named lblOccupation and the text box named txtOccupation. 2. Resize the form in order to fit a group box with the information about the users sex onto it, and name the new controls as shown in the picture below:
3. The text of the RadioButton and CheckBox controls should be the same as the names of the controls without the first three letters. 4. Set the Checked property of the chkProgrammer check box to true. 5. Set the Checked property of either rdoMale or rdoFemale to true. Note that you cannot set both to true. If you try to, the value of the other RadioButton is automatically changed to false. No more needs to be done on the visual part of the example, but there are a number of changes in the code. First, we need to remove all our reference to the text box that has been removed. Go to the code and complete the following steps. 6. In the constructor of the form, remove the two lines which refer to txtOccupation. This includes subscriptions to the Validating and TextChanged events and the line, which sets the tag of the txtBox to false. 7. Remove the methodtxtOccupation_Validating entirely. This example can be found in the code download for this chapter as the RadioButtonAndCheckBox Visual Studio.NET project. Adding the Event HandlersThe txtBox_TextChanged method included tests to see if the calling control was the txtOccupation TextBox. We now know for sure that it will not be, and so we change the method by removing the elseif block and modify the if test as follows: private void txtBox_TextChanged(object sender, System.EventArgs e) { // Cast the sender object to a Textbox TextBox tb = (TextBox)sender; // Test if the data is valid and set the tag background // color accordingly. if (tb.Text.Length == 0) { tb.Tag = false; tb.BackColor = Color.Red; } else { tb.Tag = true; tb.BackColor = SystemColors.Window; } // Call ValidateAll to set the OK button ValidateAll(); } The last place in which we check the value of the text box we've removed is in the ValidateAll() method. Remove the check entirely so the code becomes: private void ValidateAll() { // Set the OK button to enabled if all the Tags are true this.btnOK.Enabled = ((bool)(this.txtAddress.Tag) && (bool)(this.txtAge.Tag) && (bool)(this.txtName.Tag)); } Since we are using a check box rather than a text box we know that the user cannot enter any invalid information, as he or she will always be either a programmer or not. We also know that the user is either male or female, and because we set the property of one of the RadioButtons to true, the user is prevented from choosing an invalid value. Therefore, the only thing left to do is change the help text and the output. We do this in the button event handlers: private void btnHelp_Click(object sender, System.EventArgs e) { // Write a short descrption of each TextBox in the Output TextBox string output; output = "Name = Your name\r\n"; output += "Address = Your address\r\n"; output += "Programmer = Check 'Programmer' if you are a programmer\r\n"; output += "Sex = Choose your sex\r\n"; output += "Age = Your age"; // Insert the new text this.txtOutput.Text = output; } Only the help text is changed, so nothing surprising in the help method. It gets slightly more interesting in the OK method: private void btnOK_Click(object sender, System.EventArgs e) { // No testing for invalid values are made, as that should // not be neccessary string output; // Concatenate the text values of the four TextBoxes output = "Name: " + this.txtName.Text + "\r\n"; output += "Address: " + this.txtAddress.Text + "\r\n"; output += "Occupation: " + (string)(this.chkProgrammer.Checked ? "Programmer" : "Not a programmer") + "\r\n"; output += "Sex: " + (string)(this.rdoFemale.Checked ? "Female" : "Male") + "\r\n"; output += "Age: " + this.txtAge.Text; // Insert the new text this.txtOutput.Text = output; } The first of the new lines, which are highlighted, is the line in which the occupation of the user is printed. We investigate the Checked property of the check box, and if it is true, we write the string Programmer. If it is false, we write Not a programmer. The second line examines only the radio button rdoFemale. If the Checked property is true on that control, we know that the user is female. If it is false we know that the user is male. Because there are only two options here, we do not need to check the other radio button, because its Checked property will always be the opposite of the first radio button. Had we used more than two radio buttons, we would have had to loop through all of them, until we found one on which the Checked property was true. When you run the example now, you should be able to get a result similar to this:
The RichTextBox ControlLike the normal TextBox, the RichTextBox control is derived from TextBoxBase. Because of this, it shares a number of features with the TextBox, but is much more diverse. Where a TextBox is commonly used with the purpose of obtaining short text strings from the user, the RichTextBox is used to display and enter formatted text (for example bold, underline, and italic). It does so using a standard for formatted text called Rich Text Format or RTF. In the previous example, we used a standard TextBox. We could just as well have used a RichTextBox to do the job. In fact, as we'll see in the example later, you can remove the TextBox name txtOutput and insert a RichTextBox in its place with the same name, and the example behaves exactly as it did before. RichTextBox PropertiesIf this kind of textbox is more advanced than the one we explored in the previous section, you'd expect there are new properties that can be used, and you'd be correct. Here are descriptions of the most commonly used properties of the RichTextBox:
As you can see from the listing above, most of the new properties have to do with a selection. This is because, any formatting you will be applying when a user is working on his or her text will probably be done on a selection made by that user. In case no selection is made, the formatting will start from the point in the text where the cursor is located, called the insertion point. RichTextBox EventsMost of the events used by the RichTextBox are the same as those used by the TextBox. There are a few new of interest though:
Try it Out – RichTextBox ExampleWe'll create a very basic text editor in this example. It demonstrates how to change basic formatting of text and how to load and save the text from the RichTextBox. For the sake of simplicity, the example loads from and saves to a fixed file. As always, we'll start by designing the form: 1. Create a new C# Windows Application project and name it RichTextBoxTest. 2. Create the form as shown in the picture below. The textbox named txtSize should be a TextBox control. The textbox named rtfText should be a RichTextBox control:
3. Name the controls as indicated in the picture above and clear the Text property of both rtfText and txtSize. 4. Excluding the text boxes, set the Text of all controls to the same as the names except for the first three letters. 5. Change the Text property of the txtSize text box to 10. 6. Anchor the controls as in the following table:
7. Set the MinimumSize property of the form to the same as the Size property. Adding the Event HandlersThat concludes the visual part of the example and we'll move straight to the code. Double-click the Bold button to add the Click event handler to the code. Here is the code for the event: private void btnBold_Click(object sender, System.EventArgs e) { Font oldFont; Font newFont; // Get the font that is being used in the selected text oldFont = this.rtfText.SelectionFont; // If the font is using bold style now, we should remove the // Formatting if (oldFont.Bold) newFont = new Font(oldFont, oldFont.Style & ~FontStyle.Bold); else newFont = new Font(oldFont, oldFont.Style | FontStyle.Bold); // Insert the new font and return focus to the RichTextBox this.rtfText.SelectionFont = newFont; this.rtfText.Focus(); } We start by getting the font which is being used in the current selection and assigning it to a local variable. Then we check if this selection is already bold. If it is, we want to remove the bold setting; otherwise we want to set it. We create a new font using the oldFont as the prototype, but add or remove the bold style as needed. Finally, we assign the new font to the selection and return focus to the RichTextBox. See Chapter 16 for a description of the Font object. The event handlers for btnItalic and btnUnderline are the same as the above one, except we are checking the appropriate styles. Double-click the two buttons Italic and Underline and add this code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||