Windows Forms is the direct evolutionary step of Win32 programming and Microsoft Foundation Classes (MFC). It is relatively easy to learn and use for long-time programmers.
Because Windows Forms applications have a lot of code that is generated and maintained for you by Visual Studio, many of the examples in this chapter require things that are not presented in the book. For the complete, runnable examples, please see the sample projects for this chapter on the book’s Web site.
Solution: Two types of forms are available: modal and modeless. Modal forms must be dealt with and dismissed before the rest of the application can be used, whereas modeless forms can stick around while the user goes back and forth to other forms (see Figure 16.1).
In .NET, there is a difference in how the forms are launched:
and
See the Modal vs. Modeless example in the sample source code.
Solution: This section starts an example that will be used over the next few sections. In the example, you’ll build up a very, very simple file browser application, complete with menus, status bar, toolbar, and a split pane window (see Figure 16.2).
All of these are instances of controls that can be placed inside a ToolStripContainer, which is a convenient layout control that allows easy placement of controls on all sides of a form.
Controls derived from ToolStrip are far more powerful than their predecessors. For example, not only can they host standard text items and buttons, but they can host combo boxes, progress bars, and more—anything that is derived from ToolStripItem.
Here is some sample code to demonstrate the initialization of the menu bar and its placement in the ToolStripContainer:
Here are two of the menu item handlers:
We’ll look at the other menu event handlers in the next section.
In larger applications, you may want to derive your own class from ToolStripMenuItem so that you can associate your own state data, such as for command handling or document workspace context.
Solution: If a menu item is not valid, it should be disabled as a clue to the user.
Solution: Adding a status bar is just as easy as adding a menu. Add this to your code:
Solution: Add this code to your form to add a toolbar underneath the menu:
Here are the event handlers:
Solution: .NET provides the SplitContainer for just this. You can even nest SplitContainers inside of other SplitContainers to achieve more complex layouts.
In our sample program, let’s add a SplitContainer with a TreeView on the left and a TextView on the right:
To review the full source for this example, see the ToolAndStatusAndSplit example in the accompanying source code.
Solution: Form inheritance works similarly to regular class inheritance. You can put common behavior in the base form and then specialize and augment it in inherited forms. Figure 16.3 demonstrates this by adding more columns to the ListView in the InheritedForm window.
To modify UI elements from the base form, you must make them public or protected.
Listings 16.1 and 16.2 come from the DerivedForms example in this chapter’s source code.
Solution: Sometimes the built-in Windows Forms controls don’t provide the functionality you need. You can create your own control that can be anything from a mere combination of existing controls behind your own interface, to custom drawings or themes.
This rather long example provides a custom control that combines a track bar, a numeric control, a label, and a drawing to enable the user to select a single color component value:
You can now reuse this control easily in forms, as shown in Figure 16.4, which demonstrates a color picking application that combines three of our controls.
Solution: Use a timer. .NET provides many types of timers, but perhaps one of the most common and easiest to use is the System.Windows.Forms.Timer
class, which uses the normal event-handling mechanism to notify users when it ticks.
Here’s a simple example that alternates the text in a Label every second. The Button and Label are defined in Form’s code-behind file, which you can find in the TimerDemo sample project for this chapter.
Be wary of the accuracy of timers. This timer is implemented using the normal event model and is not designed to be super-accurate. For a more accurate timer, you can use the one discussed in Chapter 23, “Threads, Asynchronous, and Parallel Programming.” Even then, however, don’t put all your trust in its accuracy. Windows is not a real-time operating system and is therefore not guaranteed to honor strict timing requests.
Solution: Configuration settings are often the bane of a programmer’s existence, and they are implemented as an afterthought. Thankfully, .NET takes some of the sting out with some built-in classes. These classes enjoy direct IDE support in Visual Studio.
Most new GUI projects automatically create a settings file for you, but if you need to create one, follow these steps:
1. Right-click your project in the Solution Explorer.
2. Select Add, New Item.
3. Find the entry for Settings File and highlight it (see Figure 16.5).
4. Give it a meaningful name. (Settings.settings usually works for me.)
5. Once you have a new settings file, double-click it in the Solution Explorer to open the settings editor (see Figure 16.6).
Table 16.1 describes the differences between user settings and application settings.
Behind the scenes, Visual Studio will generate properties of the correct type for your code to use. Here’s an example:
For a demo and the full source code, see the ConfigValuesDemo project in this chapter’s sample code.
Solution: The key is to use ListView’s virtual mode, which uses callbacks to retrieve the data it needs to display.
You should use virtual mode intelligently to avoid continually re-creating objects that the garbage collector will need to clean up. For this reason, the following sample code sets up a simple caching system where ListViewItem objects are reused when the ListView needs to update which items it is currently displaying:
To see the full source code, including the code-behind for the controls, look at the VirtualListView project in the accompanying source.
Solution: This support could be added in future versions of .NET, but until then, here is an example of how to achieve this functionality in a control derived from Panel by taking over some of the underlying Win32 processing:
Look at the full HorizTiltWheelDemo project in the accompanying sample source code for the all the code.
Solution: There is a simple way and a simpler way to do this, depending on your requirements—if you need to put multiple formats to the clipboard simultaneously or if only one will do. I’ll cover each method in the following sections, as well as how to put your own custom objects on the clipboard.
The naive way to cut and paste is to simply use the methods Clipboard.SetText()
, Clipboard.SetImage()
, and so on.
Many programs support more advanced clipboard functionality. For example, when you copy text to the clipboard from Microsoft Word, it copies that text in many formats simultaneously, including plain text, Word format, HTML, and even an image of the text you can paste into a graphics program! This is a built-in feature of the Windows clipboard that you can take advantage of. When pasting, it is up to each program to decide what format to ask for by default, or even to give the user a choice.
Here is how to put both text and an image on the clipboard:
To determine what formats are available for you to paste, the Clipboard
class defines some static methods:
These all return a bool
. There is also a ContainsData
method, which is used to determine if a custom format is present. This is used in the next section.
To determine what formats are present, you can use this code:
Running this code after I copied this sentence from Microsoft Word yielded the following output:
If your program is, for example, a CAD program for widgets, chances are you will want to support cut and paste functionality on widgets. You have two options:
• Transform your widgets into a standard clipboard format (such as text) and put that on the clipboard. Then translate it back when pasting.
• Put arbitrary binary data on the clipboard and then serialize and deserialize your class (with minimal effort).
The good news is that the second option is easy to use.
Suppose you have a ListView containing the name, sex, and age of various people. To put these rows on the clipboard, define an intermediary class that is serializable:
When you want to put it on the clipboard, you can do something like this, assuming the existence of a ListView that contains this information:
To retrieve the custom data type from the clipboard, use this:
See the ClipboardDemo sample project, which demonstrates the use of the clipboard with text, image, and custom data.
However, what about this situation?
In this case, an error prohibits the cursor from being set back to normal.
Solution: Although you could wrap this in a try
...finally
block, here is a simple hack that shortcuts this by using the IDisposable
interface:
Now you can just do the following, and the cursor is automatically reset: