Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp .NET Programming (2002) [eng]

.pdf
Скачиваний:
58
Добавлен:
16.08.2013
Размер:
7.98 Mб
Скачать

Figure 3.11: Expanding the entry in the System log shows the full text of the message box. Note Depending on your operating system, you can also find the log entry by opening the

Event Viewer (found in the Administrative Tools Control Panel applet provided by Windows 2000 or Windows XP), and looking for a recent Application Popup entry in the System log. You can expand the entry in the Event Viewer to find the text of the message box.

RadioButtons and Message Boxes

The next application will provide several variations on the theme of allowing the user to decide which text, caption, and buttons should be displayed in a message box.

RadioButton controls are typically used when you want to let a user make one-and only onechoice from multiple options. When you add more than one RadioButton to a container such as a Panel or GroupBox, these RadioButtons form a kind of group in which only one can be 'checked,' or selected, at a time. We'll use RadioButtons placed on a GroupBox to allow the user to pick the buttons for the message box. (I've hard-coded the value of the MessageBoxIcon parameter to the value MessageBoxIcon.Exclamation, but you could expand this example by allowing the user to pick the message box icon as well as the buttons.)

When the user interface is done, it should look something like the one shown in Figure 3.12.

Figure 3.12: The user interface is shown on the left; on the right, the Tag property is highlighted in the Properties window.

I've left the names of the RadioButton controls with the default, so they are named radioButton1 through radioButton6. In the real world, it might be a good idea to name them

after the choice they represent, e.g., rboOK, etc. The Checked property of radioButton1 is set to true, meaning that will be the one checked when the application first starts.

Note You can create RadioButtons of uniform size by copying and pasting a RadioButton that has already been positioned on a form. This achieves the same results as dragging the RadioButton control from the Toolbox and can save some time.

It's a good idea to rename any control that you will need to refer to in code with a name that reflects the type of the control and its function in the application. But, in many cases, Label controls are never used in code; they are essentially 'decorative' in nature. In these cases, there's no real need to change the default control name, which is Labeln, as in Label1, Label2, and so on. (This, of course, is a matter of your own taste as a programmer-or the standards that have been established in a team project.)

You can use the Align and Make Same Size options on the Format menu to standardize the appearance of labels and buttons on a form. Alternatively, you can use the control's position coordinates, which can be found in the Location and Size properties in the Properties window, to align controls. For example, to make sure that two controls are aligned vertically, simply set the Location property X coordinate of each control to the same number.

Variation the First

As a programmer, one thing I hate almost more than anything else is typing the same string of text multiple times. Looking at the user interface shown in Figure 3.12, I made the observation that I had already essentially entered the values of the MessageBoxButtons constants (see Table 3.3) in the Text property of each radio button. As things stood, the value of each radio button's Text property was as shown in Table 3.6.

 

 

 

 

Table 3.6: The DialogResult Enumeration

 

 

 

 

 

 

 

 

 

Constant

 

Description

 

 

 

 

 

 

 

 

 

 

 

 

Abort

 

The Abort button was clicked.

 

 

 

 

 

 

 

 

 

 

 

 

Cancel

 

The Cancel button was clicked.

 

 

 

 

 

 

 

 

 

 

Ignore

 

The Ignore button was clicked.

 

 

 

 

 

 

 

 

 

 

 

 

No

 

The No button was clicked.

 

 

 

 

 

 

 

 

 

 

 

 

None

 

The user did not click a button.

 

 

 

 

 

 

 

 

 

 

 

 

OK

 

The OK button was clicked.

 

 

 

 

 

 

 

 

 

 

 

 

Retry

 

The Retry button was clicked.

 

 

 

 

 

 

 

 

 

 

 

 

Yes

 

The Yes button was clicked.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Table 3.7: Values for the Buttons Pane

 

 

 

 

 

 

 

 

 

 

Name

 

 

 

Text

 

Checked (Initial Value)

 

Tag Value (For Casting)

 

 

 

radioButton1

 

Abort, Retry, and Ignore

 

true

 

2

 

 

 

radioButton2

 

OK Only

 

false

 

0

 

 

 

radioButton3

 

OK and Cancel

 

false

 

1

 

 

 

radioButton4

 

Retry and Cancel

 

false

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

radioButton5

 

Yes and No

 

false

 

4

 

 

 

 

 

 

radioButton6

 

Yes, No, and Cancel

 

false

 

3

 

 

 

 

 

 

 

Of course, the value of the radio button Text property would be available to me programmatically at run time. Could I use the value of the selected radio button to identify the corresponding MessageBoxButtons enumeration constant? Anything to avoid having to code something along these lines:

MessageBoxButtons mbb;

if (radioButton1.Checked) {

mbb = MessageBoxButtons.AbortRetryIgnore;

}

and so on for all the radio buttons.

Actually, this boils down to two problems: identifying the checked radio button, and placing the corresponding enumeration constant in the MessageBoxButtons variable that will be used to display the message box. I'll show you a fairly elegant answer to the first issue in a second.

Regarding the second problem, it occurred to me that maybe I could cast the string in the radio button Text property to an enumeration constant-after all, they appear to be pretty similar, and, if necessary, I could even make them identical. This, it turns out, does not work: you cannot cast a string to an enumeration constant, no matter how close the two are.

You can, however, cast an integer to an enumeration constant, provided you know what integer equates to what enumeration constant value. Short of trial and error, there seems to be no way to find this information, so I used trial and error to come up with the values shown in Table 3.6. I placed the corresponding value in each radio button's Tag property, which is otherwise unused, so that I could access it at run time.

Warning Please be careful if you try this 'at home.' There is no guarantee that the internal values of enumeration constants will stay the same in updated versions of .NET. Furthermore, using a cryptic integer value is bad programming practice because it defeats the purpose of enumeration constants-which is to clearly identify the value represented.

Setting Up the Message Box

Within the click event that will display the message box, here are the preliminary variable declarations and assignments:

string subject = txtMsgText.Text; string caption = txtMsgCaption.Text;

MessageBoxButtons mbb = MessageBoxButtons.OK; MessageBoxIcon mbi = MessageBoxIcon.Exclamation; DialogResult answer;

Note that I've provided the variable mbb with an initial value, even though this will likely change depending upon the user's radio button selection.

Determining the Checked Radio Button

One way to determine which radio button is checked is to use a foreach loop to cycle through the controls on the group box (in other words, the whole bunch of radio buttons). Here's how this works:

foreach (object c in this.grpButtons.Controls) { if (c is RadioButton) {

RadioButton rb = (RadioButton) c; if (rb.Checked == true) {

//Is it checked?

//If so, do the assignment...

}

}

}

Inside the loop, each control is checked to make sure it is a RadioButton using a variable declared as (lowest common denominator) object, c, to do the iteration. If it is, a new variable, rb, of type RadioButton is declared. The iterating object variable is then cast to rb, and the Checked property of rb examined. If rb.Checked evaluates to true, then do the assignment.

Note I could have used a variable of type Control rather than Object to iterate through the group of controls. But I still would have to cast it to type RadioButton-because not all controls have a Checked property.

Note The all-lowercase keyword object is legal shorthand for System.Object, commonly referred to as type Object.

Assigning the Value

The following line of code uses the ToString() method to convert the contents of the Tag property to a string and the ToInt16 method of the Convert object to convert it to integer. The integer value is then cast to the correct constant enumeration constant, which is stored in the variable mbb:

mbb = (MessageBoxButtons) Convert.ToInt16(rb.Tag.ToString());

If this seems a little baroque-or maybe even a little over-the-top rococo-well, maybe it is! But if you followed the example through, you should have learned something about C# objects, casting, and conversion methods.

Displaying the Box and Doing Something Wild with the Result

Now that we have the user's choice, it's easy to display the message box:

answer = MessageBox.Show(subject, caption, mbb, mbi);

Finally, it would be nice to do something with the result. Here's one way to display it in another message box:

MessageBox.Show("You clicked " + answer.ToString(), "Message Box Demo");

The complete click event code is shown in Listing 3.6. If the project is run now, the user can enter text and caption and select from a set of buttons (Figure 3.13).

Figure 3.13: The user can enter message box text and caption and can choose the buttons it will display.

Listing 3.6: Displaying a Message Box Based on Which RadioButton Was Checked

private void btnDisplay_Click(object sender, System.EventArgs e) { string subject = txtMsgText.Text;

string caption = txtMsgCaption.Text; MessageBoxButtons mbb = MessageBoxButtons.OK; MessageBoxIcon mbi = MessageBoxIcon.Exclamation; DialogResult answer;

foreach (object c in this.grpButtons.Controls) { if (c is RadioButton) {

RadioButton rb = (RadioButton) c; if (rb.Checked == true) {

mbb = (MessageBoxButtons) Convert.ToInt16(rb.Tag.ToString()); break;

}

}

}

answer = MessageBox.Show(subject, caption, mbb, mbi); MessageBox.Show("You clicked " + answer.ToString(), "Message Box Demo");

}

When the Display Box button is clicked, the designated message box is generated. Next, the button selected in that message box is displayed.

There are a couple of ways to programmatically evaluate the response. Clearly, you could evaluate answer.ToString(), which in the case of the message box shown in Figure 3.13 would contain "Yes", "No", or "Cancel". More rigorously, you could evaluate the

DialogResult enumeration constants, shown in Table 3.5, against the value of the variable answer.

Second Variation: The switch Statement

The code in Listing 3.6 certainly didn't take much work to enter: it is short, which is good. But, for reasons I explained earlier, relying on the integer equivalent of the MessageBoxButtons enumeration constants is not a very good idea. So, let's bite the bullet and try a variation.

We know a good deal programmatically about each RadioButton in the group of controls, including its Text and Name properties. Either of these could be tested when the Checked control is retrieved to determine the right enumeration constant value.

A switch statement is used to branch depending on the value of an expression. Using the Text property, we can use a switch statement to evaluate the value of rb.Text and assign the enumeration constant accordingly.

Tip Select...Case is the VB equivalent of the C# switch statement.

Here's how the switch statement looks once we know we have the right RadioButton:

if (rb.Checked == true) { switch (rb.Text) {

case "Abort, Retry, and Ignore":

mbb = MessageBoxButtons.AbortRetryIgnore; break;

case "OK":

mbb = MessageBoxButtons.OK; break;

case "OK and Cancel":

mbb = MessageBoxButtons.OKCancel; break;

...

OK, so I did have to type in the text strings and all those constants (at least they were supplied by the Code Editor's auto-completion facility). Listing 3.7 shows the complete code for this variation.

Listing 3.7: Variation on the Message Box Using a switch

private void btnVar1_Click(object sender, System.EventArgs e) { string subject = txtMsgText.Text;

string caption = txtMsgCaption.Text; MessageBoxButtons mbb = MessageBoxButtons.OK; MessageBoxIcon mbi = MessageBoxIcon.Exclamation; DialogResult answer;

foreach (Control c in this.grpButtons.Controls) { if (c is RadioButton) {

RadioButton rb = (RadioButton) c; if (rb.Checked == true) {

switch (rb.Text) {

case "Abort, Retry, and Ignore":

mbb = MessageBoxButtons.AbortRetryIgnore;

break; case "OK":

mbb = MessageBoxButtons.OK; break;

case "OK and Cancel":

mbb = MessageBoxButtons.OKCancel; break;

case "Retry and Cancel":

mbb = MessageBoxButtons.RetryCancel; break;

case "Yes and No":

mbb = MessageBoxButtons.YesNo; break;

case "Yes, No, and Cancel":

mbb = MessageBoxButtons.YesNoCancel; break;

}

}

break;

}

}

answer = MessageBox.Show(subject, caption, mbb, mbi); MessageBox.Show("You clicked " + answer.ToString(), "Message Box Demo");

}

Third Variation: Conditional on Top

As I was looking at the second variation, it occurred to me that I really haven't gained that much with my fancy loop through the controls contained by the GroupBox. I still had to use a conditional that explicitly assigned each enumeration constant based on a hand-coded value (the control's Text property).

Why not simplify matters by moving the conditionals to the top and forgetting about the loop? Listing 3.8 shows how to do this using if statements-it's the good, oldfashioned way. As you can see in Figure 3.14, it's easy to have lots of fun with the lowly message box.

Figure 3.14: It's easy to have lots of fun with message boxes. Listing 3.8: Another Switching Variation

private void btnVar2_Click(object sender, System.EventArgs e) { string subject = txtMsgText.Text;

string caption = txtMsgCaption.Text; MessageBoxButtons mbb = MessageBoxButtons.OK; MessageBoxIcon mbi = MessageBoxIcon.Exclamation; DialogResult answer;

if (radioButton1.Checked){

mbb = MessageBoxButtons.AbortRetryIgnore;

}

else if (radioButton2.Checked){ mbb = MessageBoxButtons.OK;

}

else if (radioButton3.Checked) { mbb = MessageBoxButtons.OKCancel;

}

else if (radioButton4.Checked) {

mbb = MessageBoxButtons.RetryCancel;

}

else if (radioButton5.Checked) { mbb = MessageBoxButtons.YesNo;

}

else if (radioButton6.Checked) {

mbb = MessageBoxButtons.YesNoCancel;

}

answer = MessageBox.Show(subject, caption, mbb, mbi); MessageBox.Show("You clicked " + answer.ToString(), "Message Box Demo");

}

Changing the Shape of a Window

Why should windows always be square? One of the great things about .NET is that it is easy to make visual objects, such as Windows forms, any shape that you would like. For example, an oval window might make users think your application is really cool! The trick is to assign a GraphicsPath object to the Form's Region property. The GraphicsPath object can be defined as almost any kind of shape.

Let's do this with the form we've been working with in the message box example. To start, at the top of the form module, import the System.Drawing.Drawing2D namespace to make referring to the Drawing2D library members easier:

using System.Drawing.Drawing2D

Making a Square Window Round

Next, within the form class code, create a procedure, ApplyInitialRegion. Within ApplyInitialRegion, define a GraphicsPath object using its AddEllipse method, and assign it to the Region property of the Form object.

private void ApplyInitialRegion() {

GraphicsPath myGraphicsPath = new GraphicsPath(); myGraphicsPath.AddEllipse(new Rectangle(0, 0, 600, 450)); this.Region = new Region(myGraphicsPath);

}

All that remains is to create a way to call the form, which you can do in a button Click event, as shown in Listing 3.9. When the click event is run, the form turns into an oval (Figure 3.15).

Listing 3.9: Making a Square Form Oval

...

using System.Drawing.Drawing2D;

...

private void ApplyInitialRegion() {

GraphicsPath myGraphicsPath = new GraphicsPath(); myGraphicsPath.AddEllipse(new Rectangle(0, 0, 600, 450)); this.Region = new Region(myGraphicsPath);

}

private void btnCircle_Click(object sender, System.EventArgs e) { ApplyInitialRegion();

}

...

Figure 3.15: The Region property of the form can be used to make it any shape you'd like. Warning Depending on the shape you choose for your form, it may not have the normal

mechanisms for closure, as in the example here, because the top part of form has been eliminated. Make sure that you provide a way to close the form (such as a Close button).

You can open a form with whatever shape you'd like by placing the call to the method that changes the form's shape-in this example, ApplyInitialRegion()-in the form load event or in the form's constructor.

Constructors

A constructor is a block of code that is executed when an object that is an instance of a class is created by using the new keyword. In C#, the constructor is a public procedure within the class with the same name as the class; for example, for the Form1 class (see Listing 3.1), you can find the constructor within the class:

public class Form1 : System.Windows.Forms.Form

{

...

public Form1()

{

...

}

...

}

You'll find more about constructors in Chapter 8.

A Polygon Form

By way of variation, let's turn the form into a cute polygon, rather than an oval. To do this, we need to create an array of points:

Point[] myArray = { new Point(230, 200), new Point(400, 100), new Point(570, 200), new Point(500, 400), new Point(300, 400)

};

Next, instead of calling the AddEllipse method for the GraphicsPath, we should call the AddPolygon method, with the array of points as the argument:

myGraphicsPath.AddPolygon(myArray);

Listing 3.10 shows the revised ApplyInitialRegion() procedure; if you run it, the form will be a nice polygon, like the one in Figure 3.16.

Listing 3.10: The Form Is a Nice Polygon

private void ApplyInitialRegion() {

GraphicsPath myGraphicsPath = new GraphicsPath(); Point[] myArray = {

new Point(230, 200), new Point(400, 100), new Point(570, 200), new Point(500, 400), new Point(300, 400)

};

myGraphicsPath.AddPolygon(myArray); this.Region = new Region(myGraphicsPath);

}