Creating Controls at Runtime
Creating a control at runtime involves a few simple steps:
Create the control object as you would any other class.
Set the properties for the control (including basics like size and position).
Add the control object to the Controls collection of a container control, like a Form, GroupBox, Panel, or TabPage.
If you want to handle any of the control's events, use the AddHandler statement to hook up your code.
To demonstrate this process, consider the sample button generator program shown in Figure 11-1. This program creates a button at the specified position every time the user clicks the Create button. An event handler is attached to every new button's Click event, ensuring that .NET can capture user clicks (and display a brief user message at the bottom of the window).
public class ButtonMaker : System.Windows.Forms.Form
{
// (Windows designer code omitted.)
private int buttonCount = 0;
private void cmdCreate_Click(object sender, System.EventArgs e)
{
buttonCount++;
// Create the button. Button newButton = new Button();
newButton.Text = "Button " + buttonCount.ToString();
newButton.Left = int.Parse(txtLeft.Text);
newButton.Top = int.Parse(txtTop.Text);
// Attach the event handler.
newButton.Click += new EventHandler(ButtonHandler);
this.Controls.Add(newButton);
}
private void ButtonHandler(object sender, System.EventArgs e)
{
status.Text = " You clicked ... ";
status.Text += ((Button)sender).Text;
}
}

Figure 11-1: A ButtonMaker program
A System Tray Application
Sometimes the only reason you create a control at runtime is for cleaner, more logical code. One example is found with "invisible" controls that don't really appear on a form. These include the standard dialog controls (for changing colors, choosing fonts, and viewing a print preview) that we saw in Chapter 5. You could add these controls to a form at design time, but why bother? Code that creates it dynamically is more readable. On the other hand, if the control is a part of the form (for example, the PrintPreviewControl instead of a PrintPreviewDialog) it makes sense to create and configure it when you are designing the form.One common example of creating runtime controls for convenience occurs with system tray applications. Often, a system tray application is designed to run quietly in the background, waiting for user interaction or a specific operating system event. This application might even be configured to start every time the computer is logged on. Applications such as this should start minimized in the system tray. You don't want to force the user to interact with any unnecessary windows when the application first loads.If you build this program by adding a NotifyIcon control on a design-time form, your program will need to load the corresponding form before the icon system tray will appear. If you create the icon in a startup routine at runtime, however, no such limitation applies.The next example demonstrates exactly such an application. When it first loads, it creates a system tray icon (see Figure 11-2), attaches two menu items to it, and begins monitoring the file system for changes (using the System.IO.FileSystemWatcher class). No windows are displayed.

Figure 11-2: A dynamic system tray icon
Here's the essential code for the dynamic system tray icon:
public class App
{
// Define the system tray icon control.
private NotifyIcon appIcon = new NotifyIcon();
// Define the menu.
private ContextMenu sysTrayMenu = new ContextMenu();
private MenuItem displayFiles = new MenuItem("Display New Files");
private MenuItem exitApp = new MenuItem("Exit");
// Define the file system watcher and a list to store filenames.
private FileSystemWatcher watch = new FileSystemWatcher();
private ArrayList newFiles = new ArrayList();
public void Start()
{
// Configure the system tray icon.
Icon ico = new Icon("icon.ico");
appIcon.Icon = ico;
appIcon.Text = "My .NET Application";
// Place the menu items in the menu.
sysTrayMenu.MenuItems.Add(displayFiles);
sysTrayMenu.MenuItems.Add(exitApp);
appIcon.ContextMenu = sysTrayMenu;
// Show the system tray icon.
appIcon.Visible = true;
// Hook up the file watcher.
watch.Path = "c:\\";
watch.IncludeSubdirectories = true;
watch.EnableRaisingEvents = true;
// Attach event handlers.
watch.Created += new FileSystemEventHandler(FileCreated);
displayFiles.Click += new EventHandler(DisplayFiles);
exitApp.Click += new EventHandler(ExitApp);
}
// The static startup method.
public static void Main()
{
App app = new App();
app.Start();
// Because no forms are being displayed, you need this
// statement to stop the application from automatically ending.
Application.Run();
}
}
This presents the basic application class framework. In order to log newly created files, you need to handle the FileSystemWatch.Created event, and simply add the name of the new file to the ArrayList.
private void FileCreated(object sender, System.IO.FileSystemEventArgs e)
{
newFiles.Add(e.Name);
}
To enable the system tray icon menu, you also need to add two more event handlers. The menu only provides two options: exit the application or display another window that lists the name of changed files (shown in Figure 11-3).
private void ExitApp(object sender, System.EventArgs e)
{
Application.Exit();
}
private void DisplayFiles(object sender, System.EventArgs e)
{
FileList frmFileList = new FileList();
frmFileList.FillList(newFiles);
frmFileList.Show();
}

Figure 11-3: A list of changed files
Finally, the following code is used in the FileList form to display the ArrayList information. Figure 11-3 shows how the list of changed files might look.
public class FileList : System.Windows.Forms.Form
{
// (Designer code omitted.)
private void cmdClose_Click(object sender, System.EventArgs e)
{
this.Close();
}
public void FillList(ArrayList list)
{
lstFiles.DataSource = list;
}
}
Tip
One example of this type of program is a batch file processor. It might scan a directory for files that correspond to work orders or invoices, and immediately add database records, send emails, or perform some other task.Of course, though this is a useful example, it's not the only approach to freeing the NotifyIcon from the confines of a form. You could also create the App class as a component by inheriting from System.ComponentModel.Component. You'll also need to add some additional code to your class, as shown in this template:
public class Component1 : System.ComponentModel.Component
{
private System.ComponentModel.Container components = null;
public Component1(System.ComponentModel.IContainer container)
{
container.Add(this);
InitializeComponent();
}
public Component1()
{
InitializeComponent();
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
}
This code allows the component to support the disposable pattern and host design-time controls. It looks essentially the same as the automatically generated form code. (In other words, the special form code isn't just form code-it's code that any component requires to support design-time control hosting). If you don't want to type this code yourself, you can coax Visual Studio .NET into creating it by right-clicking the Solution Explorer and selecting Add → New Component.Every component has the ability to host design-time controls-just drag and drop the control onto the design time view of the class, and Visual Studio .NET will create the code in the special hidden designer region. Using this approach, you could configure the NotifyIcon and the menu items at design time, without needing to tie them to a form. This is the approach used in Chapter 8 to create a design-time picture box that can store an icon file for the HelpIconProvider control.
Using Controls in a Drawing Program
Drawing programs exist in two basic flavors. Painting programs, like Microsoft Paint, allow users to create a bitmap with static content. Once the user draws a shape or types some text onto the drawing area, it can't be modified or rearranged. In more sophisticated vector-based drawing programs (everything from Adobe Illustrator to Microsoft Visio), the drawing is actually a collection of objects. The user can click and change any object at any time or remove it entirely.It is relatively easy to create a bitmap drawing program once you learn the appropriate functions for drawing on a form with GDI+. A vector-based drawing or diagramming program, however, is not so easy, because you need to keep track of every object and its location individually. When the user clicks on the drawing surface, you may need some fairly intricate logic to find out which object the user is trying to select, and handle the overlapping painting.One shortcut to making a drawing program is allowing the user to create drawings out of dynamically generated controls. The next example does exactly that, and demonstrates some fundamentals about handling events and context menus with custom controls.The basic application (shown in Figure 11-4) allows the user to create squares of any color, and then resize them or move them around the form to any location. The squares are based on label controls with borders, although you could easily add support for additional controls with only a few more lines of code. This drawing program, with a few minor changes, could become an entity-diagramming tool for creating class models. It could also become more like a traditional paint program-which is the direction I develop the paint program in through Chapter 13.

Figure 11-4: A vector-based drawing application
The application begins with an empty canvas. To create a square, the user right-clicks the form drawing area, and chooses Create New Square from the context menu. The square then appears (with a default size) at the current mouse location. The code that creates the square is shown in the following:
private void mnuNewSquare_Click(object sender, System.EventArgs e)
{
// Create and configure the "square".
Label newLabel = new Label();
newLabel.Size = new Size(40, 40);
newLabel.BorderStyle = BorderStyle.FixedSingle;
// To determine where to place the label, you need to convert the
// current screen-based mouse coordinates into relative form coordinates.
newLabel.Location = this.PointToClient(Control.MousePosition);
// Attach a context menu to the label.
newLabel.ContextMenu = mnuLabel;
// Connect the label to all its event handlers.
newLabel.MouseDown += new MouseEventHandler(lbl_MouseDown);
newLabel.MouseMove += new MouseEventHandler(lbl_MouseMove);
newLabel.MouseUp += new MouseEventHandler(lbl_MouseUp);
// Add the label to the form.
this.Controls.Add(newLabel);
}
There are three things the user can do with a square once it is created:
Right-click to show its context menu, which provides a single option for changing the color.
Click and drag it to a new location.
Click its bottom-right corner and resize it.
All these actions happen in response to the MouseDown event. At this point, the code retrieves a reference that points to the control that fired the event, and then examines whether the right-mouse button was clicked (in which case the menu is shown). If the left mouse button has been clicked, the form switches into resize or drag mode (using one of two Boolean form-level variables), depending on the location of the cursor. Resizing can only be performed from the bottom-right corner.
// Keep track of when fake drag or resize mode is enabled.
private bool isDragging = false;
private bool isResizing = false;
// Store the location where the user clicked the control.
private int clickOffsetX, clickOffsetY;
private void lbl_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
// Retrieve a reference to the active label.
Control currentCtrl;
currentCtrl = (Control)sender;
if (e.Button == MouseButtons.Right)
{
// Show the context menu.
currentCtrl.ContextMenu.Show(currentCtrl, new Point(e.X, e.Y));
}
else if (e.Button == MouseButtons.Left)
{
clickOffsetX = e.X;
clickOffsetY = e.Y;
if ((e.X + 5) > currentCtrl.Width && (e.Y + 5) > currentCtrl.Height)
{
// The mouse pointer is in the bottom right corner,
// so resizing mode is appropriate.
isResizing = true;
}
else
{
// The mouse is somewhere else, so dragging mode is
// appropriate.
isDragging = true;
}
}
}
The MouseMove event changes the position or size of the square if it is in drag or resize mode. It also changes the cursor to the resize icon to alert the user when the mouse pointer is in the bottom right corner.
private void lbl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Retrieve a reference to the active label.
Control currentCtrl;
currentCtrl = (Control)sender;
if (isDragging)
{
// Move the control.
currentCtrl.Left += e.X - clickOffsetX;
currentCtrl.Top += e.Y - clickOffsetY;
}
else if (isResizing)
{
// Resize the control.
currentCtrl.Width = e.X;
currentCtrl.Height = e.Y;
}
else
{
// Change the pointer if the mouse is in the bottom corner.
if ((e.X + 5) > currentCtrl.Width && (e.Y + 5) > currentCtrl.Height)
{
currentCtrl.Cursor = Cursors.SizeNWSE;
}
else
{
currentCtrl.Cursor = Cursors.Arrow;
}
}
}
Figure 11-5 shows the process of resizing a square.

Figure 11-5: Resizing a square
The MouseUp event ends the dragging or resizing operation.
private void lbl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
isDragging = false;
isResizing = false;
}
Finally, the context menu provides a single option which, when clicked, allows the user to change the square's fill color using a common color dialog box. Note that the code retrieves the active control through the SourceControl property of the ContextMenu control.
private void mnuColorChange_Click(object sender, System.EventArgs e)
{
// Show color dialog.
ColorDialog dlgColor = new ColorDialog();
dlgColor.ShowDialog();
// Change label background.
mnuLabel.SourceControl.BackColor = dlgColor.Color;
}
Figure 11-6 shows how a square's background color can be modified using this color dialog.

Figure 11-6: Changing a square's background color
As written, this simple example could easily grow into a more sophisticated drawing framework. For example, you could add context-menu items that allow the user to set text in the label (and change its font), or configure other properties. You could also change the form's context menu, and add additional options for other controls. You could even use methods like Control.BringToFront and Control.SendToBack to allow squares to be layered in various ways, according to the user's selections. Currently, all the event handlers assume they are dealing with generic control events, and thus work with buttons, text boxes, picture boxes, and just about any other control.