C# 24-Hour Trainer (2015)
Section I
The Visual Studio IDE and Controls
Lesson 8
Using Standard Dialogs
Many applications need to display dialogs to let the user select certain standard pieces of information. Probably the most common dialogs let the user select a file to open and select a file to save into. Other dialogs let the user select colors, filesystem folders, fonts, and printers for printing.
Closely related to the print dialog are the print preview dialog (which lets the user see a preview of a printout before sending it to the printer, possibly saving paper if the user then cancels the printout) and the page setup dialog (which lets the user select things like margins before printing).
You could build all of these dialogs yourself (or you will be able to once you've finished reading this book), but why should you? If so many programs need the exact same features, why shouldn't someone build standard dialogs that everyone can use?
Happily that's exactly what Microsoft did.
C# comes with the following standard dialogs that handle these common tasks:
· ColorDialog
· FolderBrowserDialog
· FontDialog
· OpenFileDialog
· PageSetupDialog
· PrintDialog
· PrintPreviewDialog
· SaveFileDialog
NOTE
You might remember that in Lesson 1, I said, “Normally you don't need to worry about whether a feature is provided by Visual Studio, the C# language, or the .NET Framework.” That's true here as well, but it's informative to note that these dialogs are actually provided by the .NET Framework, not C#. That doesn't change the way you use them, but it means they're the same dialogs used by all .NET languages such as Visual Basic, Visual C++, or JScript.
By building these standard dialogs into the .NET Framework, Microsoft lets programmers using many languages share the same common features.
These dialogs provide some fairly sophisticated features for you automatically with no additional code. For example, the OpenFileDialog class lets the user browse through the filesystem to select a file to open. The dialog can automatically verify that the file actually exists so the user cannot type in the name of a non-existent file and click Open.
Similarly, the SaveFileDialog class automatically prompts the user if the selected file does exist. For example, if the user selects the existing file Test.txt, the dialog displays the message “Test.txt already exists. Do you want to replace it?” If the user doesn't click Yes, the dialog doesn't close. By the time the dialog closes, the user must have picked a file that doesn't yet exist or signed off on destroying the original file.
In this lesson you learn how to display these standard dialogs. You learn how to initialize them to show the user the program's current settings, how to tell which button the user clicked, and how to use the selections the user made.
NOTE
This lesson actually cheats a bit on the printing dialogs. Although it explains how to display these dialogs, you can't do anything really useful with them until you know how to print, which is a much more complicated topic. Lesson 30 gets into the details of how to print.
Using Dialogs in General
You can use all of the standard dialogs in more or less the same way. The only differences are in how you initialize the dialogs so they show colors, fonts, files, or whatever and in how you handle the results.
You can use a standard dialog in Windows Forms applications by following these four steps:
1. Add the dialog to the form.
2. Initialize the dialog to show current settings.
3. Display the dialog and check the return result.
4. Process the results.
Adding the Dialog to the Form
You can add a dialog to a form just as you add any other component, such as a Timer. Like other components, the dialog appears below the form in the Component Tray.
The control Toolbox has a Dialogs tab that contains most of the standard dialogs so they are easy to find. The printing-related dialogs are contained in the Printing tab so they're also easy to find (if you know to look there). Figure 8.1 shows the Toolbox's Printing and Dialogs tabs.
Figure 8.1
Initializing the Dialog
Most of the standard dialogs start with some initial selection. The FontDialog starts with a font selected, the ColorDialog starts with a color selected, and so forth. Normally you should initialize the dialog so it shows the user your program's current settings. For example, a FontDialog should show the program's current font.
Usually making these initial selections is easy. Simply set the dialog's key property (Font, Color, Filename) to the value you want to display.
For example, the following code sets a ColorDialog's Color property to the form's current BackColor value. (Recall that this means the form or other object that is currently executing the code.)
backgroundColorDialog.Color = this.BackColor;
The only real trick here is in knowing what properties to set. Table 8.1 lists the key properties for the different kinds of dialogs.
Table 8.1
Dialog |
Key Property |
ColorDialog |
Color |
FolderBrowserDialog |
SelectedPath |
FontDialog |
Font |
OpenFileDialog |
FileName |
SaveFileDialog |
FileName |
The PageSetupDialog, PrintDialog, and PrintPreviewDialog are a bit different from the others so I won't say anything more about them here. Printing is covered in more detail in Lesson 30.
I just said that you should initialize the dialogs to show current values, but the file open and save dialogs have a special feature that might make you decide to skip this step. When you use them, they remember the directories they displayed last. That means if the user opens one of these dialogs again, it starts in the same directory it was in last time. In fact, if the user closes and restarts the program, the dialogs still remember where they were last.
NOTE
If you have several different OpenFileDialogs (or SaveFileDialogs) in the same program, they all share the same idea of where they were last.
The only reason you might want to initialize these dialogs is if you want the program to separately track more than one file. For example, you might want different places to save text files, bitmaps, and RTF files.
Also note that the OpenFileDialog and SaveFileDialog remember the same directory, so if you want to be able to load from one directory and save into another, you might want to initialize the dialogs.
Displaying the Dialog and Checking the Return Result
You display all of the standard dialogs by calling their ShowDialog methods. ShowDialog displays the dialog modally and then returns a value to tell the program whether the user clicked OK, Cancel, or some other button.
NOTE
A modal dialog prevents the user from interacting with the program until it is closed. It forces the user to make a choice. In contrast, a modeless dialog would let the user move to the program's other forms without closing the dialog.
NOTE
Note that the OK buttons on some of the dialogs don't actually say “OK.” The OpenFileDialog's OK button says “Open,” the SaveFileDialog's OK button says “Save,” and the PrintDialog's OK button says “Print.” As far as the program is concerned, however, they're all OK buttons, and you test for them all in the same way.
Your code should test the returned result and, if the user clicked OK, it should do something with the user's selection.
Unfortunately to make that test, you need to use an if statement, and if statements aren't covered until Lesson 18. Luckily this particular use of if statements is quite simple, so I feel only a little guilty about showing it to you now.
The following code shows how a program can display a ColorDialog named backgroundColorDialog:
if (backgroundColorDialog.ShowDialog() == DialogResult.OK)
{
...
}
The code calls the dialog's ShowDialog method. It then uses the if statement to compare the value that ShowDialog returns to the value DialogResult.OK. If the values are equal (that's what == means in C#), the program does whatever is inside the braces (which I've omitted here).
If the user clicks the Cancel button, ShowDialog returns the value DialogResult.Cancel, so the if test fails and the program skips the code inside the braces.
NOTE
If the user closes the dialog in any way other than clicking the OK button, the ShowDialog method returns DialogResult.Cancel. For example, if the user presses Alt+F4 or clicks the X button on the dialog's upper-right corner, the dialog considers itself canceled.
Processing the Results
Finally, if the user clicked OK, the program should do something with whatever the user selected in the dialog. Often this means doing the opposite of the step where you initialized the dialog. For example, suppose a program uses the following code to initialize itsColorDialog:
backgroundColorDialog.Color = this.BackColor;
Then it would use the following code to set the form's BackColor property to the color that the user selected:
this.BackColor = backgroundColorDialog.Color;
Putting It All Together
The following code shows the whole sequence for a ColorDialog. The program initializes the dialog, displays it and checks the return value, and processes the result:
backgroundColorDialog.Color = this.BackColor;
if (backgroundColorDialog.ShowDialog() == DialogResult.OK)
{
this.BackColor = backgroundColorDialog.Color;
}
This looks a bit more complicated than code examples in previous lessons, but it's not too bad. The only new part is the if test. The other statements simply set the dialog's Color property equal to the form's BackColor property and vice versa, and you've been setting properties for quite a while now.
Using Dialog Properties
Table 8.1 earlier in this lesson listed the dialogs' key properties, but some of the dialogs have other useful properties, too.
For example, the ColorDialog has an AllowFullOpen property that determines whether the user can click the dialog's Define Custom Colors button to show an area on the right where the user can create new colors. Figure 8.2 shows a ColorDialog displaying this area.
Figure 8.2
You can learn more about these extra properties by reading the online help. For example, Microsoft's help page for the ColorDialog is msdn.microsoft.com/library/system.windows.forms.colordialog.aspx. You can replace colordialog in this URL with the name of another dialog to find its web page.
Table 8.2 summarizes the ColorDialog's most useful properties.
Table 8.2
Property |
Purpose |
AllowFullOpen |
Determines whether the user can create custom colors. |
Color |
The selected color. |
FullOpen |
Determines whether the custom color area is open when the dialog appears. |
Table 8.3 summarizes the FolderBrowserDialog's most useful properties.
Table 8.3
Property |
Purpose |
RootFolder |
The root folder where the dialog starts browsing. The Properties window lets you pick from values such as Desktop, Favorites, History, and MyComputer. |
SelectedPath |
The selected folder. |
Table 8.4 summarizes the FontDialog's most useful properties.
Table 8.4
Property |
Purpose |
FixedPitchOnly |
Determines if the dialog allows the user to select only fixed-width fonts. This is useful, for example, if you are going to use the font to build a report and you need the characters to all have the same width so columns line up properly. |
Font |
The selected font. |
FontMustExist |
Determines whether the dialog raises an error if the selected font doesn't exist (for example, if the user types “ExtraBold” for the font style and that style isn't available for the selected font). |
MaxSize |
The largest allowed size for the font. |
ShowColor |
Determines whether the dialog lets the user select a font color. If you set this to True, use the dialog's Color property to see which color was selected. |
ShowEffects |
Determines whether the dialog lets the user select underline, strikeout, and font color. (To select font color, ShowColor and ShowEffects must both be True.) |
Table 8.5 summarizes the OpenFileDialog's most useful properties.
Table 8.5
Property |
Purpose |
AddExtension |
If this is True and the user selects a filename without an extension, the dialog adds the default extension to the name. |
CheckFileExists |
If this is True, the dialog won't let the user pick a file that doesn't exist. |
CheckPathExists |
If this is True, the dialog won't let the user pick a file path that doesn't exist. |
DefaultExt |
The default file extension. |
FileName |
The selected file's name. |
Filter |
The file selection filter. (See the section “Using File Filters” later in this lesson for details.) |
FilterIndex |
The index of the currently selected filter. (See the section “Using File Filters” later in this lesson for details.) |
InitialDirectory |
The directory where the dialog initially starts. |
ReadOnlyChecked |
Indicates whether the user checked the dialog's Read Only box. |
ShowReadOnly |
Determines whether the dialog displays its Read Only box. |
Title |
The text displayed in the dialog's title bar. |
The SaveFileDialog has many of the same properties as the OpenFileDialog. See Table 8.5 for descriptions of the properties AddExtension, CheckFileExists, CheckPathExists, DefaultExt, FileName, Filter, FilterIndex, InitialDirectory, and Title.
Table 8.6 summarizes SaveFileDialog properties that are not shared with the OpenFileDialog.
Table 8.6
Property |
Purpose |
CreatePrompt |
If this is True, and the user selects a file that doesn't exist, the dialog asks if the user wants to create the file. |
OverwritePrompt |
If this is True and the user selects a file that already exists, the dialog asks if the user wants to overwrite it. |
ValidateNames |
Determines whether the dialog verifies that the filename doesn't contain any invalid characters. |
Table 8.7 summarizes the PrintDialog's most useful property.
Table 8.7
Property |
Purpose |
Document |
You set this property to tell the dialog what document object to print. Lesson 30 has more to say about this. |
Table 8.8 summarizes the PrintPreviewDialog's most useful property.
Table 8.8
Property |
Purpose |
Document |
You set this property to tell the dialog what document object to preview. Lesson 30 has more to say about this. |
Using File Filters
Most of the dialogs' properties are fairly easy to understand. Two properties that are particularly confusing and important, however, are the Filter and FilterIndex properties provided by the OpenFileDialog and SaveFileDialog.
The Filter property is a list of text prompts and file-matching patterns separated by the | character. The items alternate between text prompts and the corresponding filter. The dialog provides a dropdown list where the user can select one of the text prompts. When the user selects a prompt, the dialog uses the corresponding filter to decide which files to display.
For example, consider the following value:
Bitmap Files|*.bmp|Graphic Files|*.bmp;*.gif;*.png;*.jpg|All Files|*.*
This value represents three categories of files:
· The text prompt “Bitmap Files” with filter *.bmp.
· The text prompt “Graphic Files” with filter *.bmp;*.gif;*.png;*.jpg. That filter matches files ending with .bmp, .gif, .png, or .jpg.
· The text prompt “All Files” with filter *.*.
Figure 8.3 shows an OpenFileDialog. The filter dropdown (just above the Open and Cancel buttons) has the text prompt “Graphics Files” selected. (The dialog automatically added the filter in parentheses just to confuse the user.) The dialog is listing the files in this directory that match the filter. In this case, the directory contains seven .png files.
Figure 8.3
Once you understand the Filter property, the FilterIndex property is simple. FilterIndex is simply the index of the selected filter, where 1 means the first filter, 2 means the second, and so forth. (Remember in Lesson 7 when I said, “almost all numbering starts with 0 in C#”? This is one of the rare exceptions.) You can use FilterIndex to initially select the filter that you think will be most useful to the user.
The OpenFileDialog and SaveFileDialog both use the same type of Filter and FilterIndex properties. In fact, usually if a program displays both of these dialogs, they should use the same Filter value. If a program can load .txt and .rtf files, it should probably be able to save .txt and .rtf files.
NOTE
To carry this idea one step further, you could set the SaveFileDialog's FilterIndex property to the value selected by the user in the OpenFileDialog under the assumption that a user who loads a .txt file is later likely to want to save it as a .txt file.
Using Dialogs in WPF
Unfortunately, WPF provides only a PrintDialog and doesn't include the other standard dialogs.
If you've been paying attention, you're probably saying, “Wait. Earlier in this lesson you said that the standard dialogs were provided by the .NET Framework. Doesn't that mean WPF programs can use them, too?” (If you said this and are reading this book as part of a programming course, tell your instructor that you deserve 5 extra points on the next quiz.)
That's true—WPF programs can use the standard dialogs, but not in the same way a Windows Forms application does.
WPF normally doesn't display the common dialogs in the Toolbox, so you can't add them to a window and you can't set their properties in the Properties window at design time. Instead, you need to create, initialize, and display the dialogs with code.
Before you write any code, you need to tell Visual Studio about the part of the .NET Framework that contains the dialogs. To do that, open the Project menu and select Add Reference to open the Reference Manager shown in Figure 8.4.
Figure 8.4
On this dialog, check the boxes next to System.Windows.Forms and System.Drawing, and click OK. (The first reference tells where the dialogs are defined. The second lets the program understand Color and Font objects, so you need it if you're working with those two dialogs.)
Now you can use code similar to the following to make a WPF program display an OpenFileDialog:
// Create the OpenFileDialog.
System.Windows.Forms.OpenFileDialog fileDialog =
new System.Windows.Forms.OpenFileDialog();
// Set the Filter.
fileDialog.Filter = "Text Files|*.txt|RTF Files|*.rtf|All Files|*.*";
// Display the dialog and check the result.
if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
// Process the selected file.
MessageBox.Show(fileDialog.FileName);
}
The first statement (which spans two lines because it's so long) creates a System.Windows.Forms.OpenFileDialog object. That statement really just creates an OpenFileDialog object. The rest of the declaration tells Visual Studio that this kind of object is located in theSystem.Windows.Forms part of the .NET Framework.
Next the code initializes the dialog. This example just sets the dialog's Filter property, but you could set other properties, too, such as FilterIndex, CheckFileExists, and ShowReadOnly.
The code then displays the dialog by calling its ShowDialog method as before and compares the returned result with System.Windows.Forms.DialogResult.OK. If the user clicked the OK button, the program processes the result. This example simply displays the selected file's name in a message box, but a real application would do something like open the file.
Unfortunately, the results returned by some of the dialogs aren't directly usable by a WPF program. For example, the ColorDialog lets the user select a Color but WPF programs use Brushes instead of Colors. Similarly, the FontDialog lets the user pick a Font but WPF programs don't use Font objects directly. Some of this lesson's exercises show how you can work around some of those issues.
Try It
In this Try It, you get to try out all of the standard dialogs except the PageSetupDialog (which is hard to use until you're doing actual printing). You initialize, display, and process the results of the dialogs (if the user clicks the OK button).
Lesson Requirements
In this lesson, you:
· Use Labels, TextBoxes, and Buttons to make a form similar to the one shown in Figure 8.5.
· Add ColorDialog, FontDialog, FolderBrowserDialog, OpenFileDialog, SaveFileDialog, PrintDialog, and PrintPreviewDialog components to the form.
· When the user clicks the BackColor button, display the ColorDialog but don't allow the user to define custom colors. If the user clicks OK, set the form's BackColor property to the dialog's Color value.
· When the user clicks the Font button, display the FontDialog, allowing the user to select the font's color. If the user clicks OK, set the form's Font property to the dialog's Font value and its ForeColor property to the dialog's Color property.
· When the user clicks the Folder button, display the FolderBrowserDialog. Make the dialog start browsing at MyComputer. If the user clicks OK, make the Folder TextBox display the dialog's SelectedPath property.
· When the user clicks the Open File button, display the OpenFileDialog. Use a filter that lets the user select text files, RTF files, or all files. If the user clicks Open, make the Open File TextBox display the dialog's FileName property and set the SaveFileDialog'sFilterIndex equal to the OpenFileDialog's FilterIndex.
· When the user clicks the Save File button, display the SaveFileDialog. Use the same filter used by the OpenFileDialog. If the user clicks Save, make the Save File TextBox display the dialog's FileName property and set the OpenFileDialog's FilterIndex equal to theSaveFileDialog's FilterIndex.
· When the user clicks the Print button, display the PrintDialog and ignore the return result.
· When the user clicks the Print Preview button, display the PrintPreviewDialog and ignore the return result.
Figure 8.5
NOTE
You can download the code and resources for this lesson from the website at www.wrox.com/go/csharp24hourtrainer2e.
Hints
1. Be sure to initialize each of the dialogs before displaying them.
Step-by-Step
· Use Labels, TextBoxes, and Buttons to make a form similar to the one shown in Figure 8.5.
1. Add and arrange the controls in whatever manner you find easiest.
2. Set the Buttons' Anchor properties to Top, Right. Set the TextBoxes' Anchor properties to Top, Left, Right.
· Add ColorDialog, FontDialog, FolderBrowserDialog, OpenFileDialog, SaveFileDialog, PrintDialog, and PrintPreviewDialog components to the form.
1. Add the dialogs. They appear in the Component Tray, not on the form.
2. Give the dialogs good names.
· When the user clicks the BackColor button, display the ColorDialog but don't allow the user to define custom colors. If the user clicks OK, set the form's BackColor property to the dialog's Color value.
1. To prevent the user from defining custom colors, set the ColorDialog's AllowFullOpen property to False.
2. Use code similar to the following:
3. private void backColorButton_Click(object sender, EventArgs e)
4. {
5. backgroundColorDialog.Color = BackColor;
6. if (backgroundColorDialog.ShowDialog() == DialogResult.OK)
7. {
8. BackColor = backgroundColorDialog.Color;
9. }
}
· When the user clicks the Font button, display the FontDialog, allowing the user to select the font's color. If the user clicks OK, set the form's Font property to the dialog's Font value and its ForeColor property to the dialog's Color property.
1. To allow the user to select the font's color, set the dialog's ShowColor property to True.
2. Use code similar to the following:
3. private void fontButton_Click(object sender, EventArgs e)
4. {
5. formFontDialog.Font = Font;
6. formFontDialog.Color = ForeColor;
7. if (formFontDialog.ShowDialog() == DialogResult.OK)
8. {
9. Font = formFontDialog.Font;
10. fontTextBox.Text = formFontDialog.Font.ToString();
11. ForeColor = formFontDialog.Color;
12. }
}
· When the user clicks the Folder button, display the FolderBrowserDialog. Make the dialog start browsing at MyComputer. If the user clicks OK, make the Folder TextBox display the dialog's SelectedPath property.
1. To start browsing at MyComputer, use the Properties window to set the dialog's RootFolder property to MyComputer.
2. Use code similar to the following:
3. private void folderButton_Click(object sender, EventArgs e)
4. {
5. if (testFolderBrowserDialog.ShowDialog() == DialogResult.OK)
6. {
7. folderTextBox.Text = testFolderBrowserDialog.SelectedPath;
8. }
}
· When the user clicks the Open File button, display the OpenFileDialog. Use a filter that lets the user select text files, RTF files, or all files. If the user clicks Open, make the Open File TextBox display the dialog's FileName property and set the SaveFileDialog'sFilterIndex equal to the OpenFileDialog's FilterIndex.
1. Use the filter:
Text Files|*.txt|RTF Files|*.rtf|All Files|*.*
2. Use code similar to the following:
3. private void openFileButton_Click(object sender, EventArgs e)
4. {
5. if (testOpenFileDialog.ShowDialog() == DialogResult.OK)
6. {
7. openFileTextBox.Text = testOpenFileDialog.FileName;
8. testSaveFileDialog.FilterIndex =
9. testOpenFileDialog.FilterIndex;
10. }
}
· When the user clicks the Save File button, display the SaveFileDialog. Use the same filter used by the OpenFileDialog. If the user clicks Save, make the Save File TextBox display the dialog's FileName property and set the OpenFileDialog's FilterIndex equal to theSaveFileDialog's FilterIndex.
1. Use the filter:
Text Files|*.txt|RTF Files|*.rtf|All Files|*.*
2. Use code similar to the following:
3. private void saveFileButton_Click(object sender, EventArgs e)
4. {
5. if (testSaveFileDialog.ShowDialog() == DialogResult.OK)
6. {
7. saveFileTextBox.Text = testSaveFileDialog.FileName;
8. testOpenFileDialog.FilterIndex =
9. testSaveFileDialog.FilterIndex;
10. }
}
· When the user clicks the Print button, display the PrintDialog. Ignore the return result.
1. Use code similar to the following:
2. private void printButton_Click(object sender, EventArgs e)
3. {
4. testPrintDialog.ShowDialog();
}
· When the user clicks the Print Preview button, display the PrintPreviewDialog. Ignore the return result.
1. Use code similar to the following:
2. private void printPreviewButton_Click(object sender, EventArgs e)
3. {
4. testPrintPreviewDialog.ShowDialog();
}
Exercises
1. [WPF] Repeat the Try It with a WPF program. Because a WPF program can't directly use the values selected by the ColorDialog or FontDialog, just display the user's selections in TextBoxes. For the ColorDialog, display the dialog's Color.ToString() value. For theFontDialog, display the dialog's Font.ToString() value. (Hint: Don't worry about setting the dialogs' FilterIndex properties.)
2. [WPF] Copy the program you wrote for Exercise 1 and use the color information. Use code similar to the following to set the window's background color:
3. Color backColor = new Color()
4. {
5. A = 255,
6. R = colorDialog.Color.R,
7. G = colorDialog.Color.G,
8. B = colorDialog.Color.B
9. };
Background = new SolidColorBrush(backColor);
For the font color, use a similar technique to set the foreground color of the font TextBox. (Setting the foreground color for the entire window is harder.)
10.[WPF] Make a program similar to the one shown in Figure 8.6.
Figure 8.6
Hints:
· If any of the event handlers make the program crash when it starts, add the following statement at the beginning of the event handler to prevent the program from trying to use controls before they are created.
if (!IsLoaded) return;
· For the font RadioButtons' Checked events, use code similar to the following:
sampleLabel.FontFamily = new FontFamily("Arial");
· For the Slider's ValueChanged event, use code similar to the following:
· if (!IsLoaded) return;
· sizeGroupBox.Header = "Size: " + sizeSlider.Value.ToString();
sampleLabel.FontSize = sizeSlider.Value;
· Give Checked and Unchecked event handlers to the Bold CheckBox. Make them set sampleLabel.FontWeight to FontWeights.Bold or FontWeights.Normal.
· Give Checked event handlers to the Normal, Italic, and Oblique RadioButtons. Make them set sampleLabel.FontStyle to FontStyles.Normal, FontStyles.Italic, and FontStyles.Oblique, respectively.
11.[SimpleEdit] Copy the SimpleEdit program you built in Lesson 7, Exercise 6 (or download Lesson 7's version from the book's website) and add the file open and save dialogs for the File menu's Open and Save As commands. Use Filter properties that let the user select RTF files, text files, or all files. Continue using the RichTextBox's LoadFile and SaveFile methods even though they don't work properly for non-RTF files.
12.[SimpleEdit] Copy the SimpleEdit program you built for Exercise 4 and add a font selection dialog for the Format menu's Font item, and the font tool strip button. If the user selects a font and clicks OK, make the RichTextBox's selected text use the selected font.
13.[SimpleEdit] Copy the SimpleEdit program you built for Exercise 5 and modify it so it allows the user to select a color on the font dialog.
14.[SimpleEdit] Copy the SimpleEdit program you built for Exercise 6 and add color selection dialogs for the Format and context menus' Text Color and Background Color items. (Allow custom colors.)
NOTE
Please select the videos for Lesson 8 online at www.wrox.com/go/csharp24hourtrainer2evideos.