Chapter 6: Modern Controls
Many of the controls you've looked at so far (like buttons, menus, and text boxes) have been around since the early days of Windows 3.1 without much more than the occasional facelift. As development entered the 32-bit world, more sophisticated controls began to appear and gain popularity. Controls like the TabControl, ListView, and TreeView began to do wonders organizing complex information. At the same time, the ToolBar and StatusBar revamped the look of the standard Windows application with a more modern feel.
In this chapter, you learn about all these controls. More important, you learn the tricks and techniques you need to master them. Custom control classes, one of my favorite themes, returns in this chapter with a few remarkable examples. You see how to create subclassed controls that are fine-tuned for specific data, or can automatically communicate and synchronize themselves with other controls.
The ImageList
The ImageList is a special type of collection that holds images of a preset size and color depth. Other controls access pictures in the ImageList using the appropriate index numbers. In this way, an ImageList acts as a resource for other controls, providing icons for controls like the ToolBar and TreeView.
Note
In some respects, the ImageList isn't really a control. It doesn't have a graphical representation, and the end user never interacts with it directly. On the other hand, ImageList objects are usually created and configured at design time when you are building the user interface. They are also closely linked to other modern controls like ListView, TreeView, and ToolBar controls.To create an ImageList at design time, drag it onto your form (it will appear in the component tray). The basic properties for the ImageList are described in Table 6-1.
Table 6-1: ImageList Members
MemberDescription
ColorDepthA value from the ColorDepth enumeration that identifies the color resolution of the images in the control. Some common choices are 8-bit (256 color mode), 16-bit (high color), and 24-bit (true color).
ImagesThe collection of Image objects that are provided to other controls.
ImageSizeA Size structure that defines the size of the contained images. ImageList controls can only contain images that share the same size and color-depth. Images are converted to the specified format when they are added.
TransparentColorSome image types, like icons and GIFs, define a transparent color that allows the background to show through. By setting the Transparent Color property, you can define a new transparent color that will be used when this image is displayed. This is useful for graphic formats that don't directly support transparency, like bitmaps.
Draw()This method provides a quick and easy way to take an image and output it to a GDI+ drawing surface.
Tip
Transparent regions are a must when mixing custom images and standard controls. If you simply use an icon with a grey background, your interface becomes garish and ugly on a computer where the default color scheme is not used, as a grey box appears around the image.You also run into problems if the icon can be selected, at which point it is highlighted with a blue background.You can add, remove, and rearrange images using the ListView designer. Just click the ellipsis (…) next to the Images property in the Properties window. Images can be drawn from almost any common bitmap file, including bitmaps, GIFs, JPEGs, and icons. When you add a picture, some related read-only properties about its size and format appear in the window (see Figure 6-1).

Figure 6-1: The ImageList designer
Dealing with the ImageList in Code
If you look at the automatically generated code, you'll see that the image files you add are stored in a resource file in your project (as is any binary data added at design time). When the form is loaded, the images are deserialized into Image objects and placed in the collection. A special class, the ImageListStreamer, makes this process a simple one-line affair, regardless of how many images are in your ImageList. This code is inserted automatically by VS .NET, and doesn't need to be modified manually
this.imagesLarge.ImageStream = ((System.Windows.Forms.ImageListStreamer)
(resources.GetObject("imagesLarge.ImageStream")));
If you want to have an ImageList object around for a longer period (for example, to use in different forms), you should create it directly in code. You might also want to create Image objects out of graphic files rather than use a project resource.
First, you need a variable to reference the ImageList.
private ImageList iconImages = new ImageList();
Then, you can create a method that fills the ImageList.
// Configure the ImageList.
iconImages.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
iconImages.ImageSize = new System.Drawing.Size(16, 16);
// Get all the icon files in the current directory.
string[] iconFiles = Directory.GetFiles(Application.StartupPath, "*.ico");
// Create an Image object for each file and add it to the ImageList.
// You can also use an Image subclass (like Icon).
foreach (string iconFile in iconFiles)
{
Icon newIcon = new Icon(iconFile);
iconImages.Images.Add(newIcon);
}
Once you have images in an ImageList control, you can use them to provide pictures to another control. Many modern controls provide an ImageList property, which stores a reference to an ImageList control. Individual items in the control (like tree nodes or list rows) then use an ImageIndex or similar property, which identifies a single picture in the ImageList by index number (starting at 0). You look at examples that use this technique later in this chapter.
In the meantime, you should also note that the ImageList can be a useful way to store images that you need to use in any scenario. The example that follows loops through an ImageList and draws its images directly onto the surface of a form. The result is shown in Figure 6-2.

Figure 6-2: Directly outputting an ImageList
// Get the graphics device context for the form.
Graphics g = this.CreateGraphics();
// Draw each image using the ImageList.Draw() method.
for (int i = 0; i < iconImages.Images.Count; i++)
{
iconImages.Draw(g, 30 + i * 30, 30, i);
}
// Release the graphics device context.
g.Dispose();
As with all manual drawing, these icons are erased as soon as the form is repainted (for example if you minimize and then maximize it). I tackle this issue in Chapter 12.