<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Visual Basic 2005: A Developer's Notebook/Windows Applications - Revision history</title>
		<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications&amp;action=history</link>
		<description>Revision history for this page on the wiki</description>
		<language>en</language>
		<generator>MediaWiki 1.11.0</generator>
		<lastBuildDate>Fri, 24 May 2013 16:39:42 GMT</lastBuildDate>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications&amp;diff=9294&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:57, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:57:33 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications&amp;diff=9180&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:31, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:31:16 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications&amp;diff=9115&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:24, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:24:43 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications&amp;diff=8996&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Visual Basic 2005: A Developer's Notebook/TOC}}&lt;br /&gt;
.NET 1.0 introduced a whole new toolkit for writing Windows applications. This toolkit—called ''Windows Forms''—quickly won the hearts of developers with its rich features for creating self-sizing windows, customized controls, and dynamic graphics. But for all its strengths, the Windows Forms toolkit left out a few features that many VB 6 developers had come to expect, including a masked edit control and a way to display HTML web pages. The Windows Forms toolkit also lacked some of the frills found in modern Windows applications, like Office XP-style toolbars and menus with thumbnail images. As you'll see in this chapter, .NET 2.0 includes all of these elements and more.&lt;br /&gt;
&lt;br /&gt;
== Use Office-Style Toolbars ==&lt;br /&gt;
&lt;br /&gt;
With .NET 1.0 and 1.1, VB developers have had to content themselves with either the woefully out-of-date &amp;lt;tt&amp;gt;ToolBar&amp;lt;/tt&amp;gt; control, or draw their own custom toolbars by hand. In .NET 2.0, the situation improves with a rich new &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; control that sports a modern, flat look, correctly handles Windows XP themes, and supports a wide range of graphical widgets, such as buttons, labels, drop-down lists, drop-down menus, text boxes, and more.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
To use the &amp;lt;tt&amp;gt;System.Windows.Forms.ToolStrip&amp;lt;/tt&amp;gt; control, just drag the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; from the Menus &amp;amp; Toolbars section of the Visual Studio toolbox onto a form. To control which side of the form the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; lines up with, set the &amp;lt;tt&amp;gt;Docking&amp;lt;/tt&amp;gt; property. For example, [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-1|Figure 3-1]] shows a form, &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt;, with two ToolStrip controls, one docked to the top of the form and the other to the right side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-1. Three ToolStrip objects in one RaftingContainer'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt69.png|Three ToolStrip objects in one RaftingContainer]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Finally, a ToolStrip control whose looks are worthy of a modern Windows application''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To add buttons to the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;, you can use the Visual Studio designer. Just click the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; smart tag and select Edit Items. You can choose new items from a drop-down list and configure their properties in a window like the one shown in [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-2|Figure 3-2]]. Or, select Insert Standard Items to create standard &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; buttons for document management (new, open, save, close) and editing (cut, copy, paste).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-2. The ToolStrip designer'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt70.png|The ToolStrip designer]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to mastering the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; control is learning about all the different widgets you can put inside it. These include:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;ToolStripButton&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Represents an item on the toolbar that the user can click. It can include text or an image (or both). This is the most common &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; item.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ToolStripLabel&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Represents a non-selectable item on the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;. It can include text or an image (or both).&lt;br /&gt;
;&amp;lt;tt&amp;gt;ToolStripSeparator&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Divides adjacent items in a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; with a thin engraved line.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ToolStripDropDownButton&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ToolStripSplitButton&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Represent a drop-down menu with items. The only difference is how the drop-down list is drawn. The &amp;lt;tt&amp;gt;ToolStripDropDownButton&amp;lt;/tt&amp;gt; shows its items as a menu, with a thumbnail margin and the ability to check items. In both cases, the menu items are &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; objects that are added to the collection exposed by the &amp;lt;tt&amp;gt;DropDownItems&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ToolStripComboBox&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ToolStripTextBox&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;ToolStripProgressBar&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Allow you to add familiar .NET controls to a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;, such as &amp;lt;tt&amp;gt;ComboBox&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;TextBox&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;ProgressBar&amp;lt;/tt&amp;gt;. All of these items derive from &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt;, which you can use to create your own &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; controls (as described in the next section, &amp;quot;Add Any Control to a ToolStrip&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
All the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; items derive from the &amp;lt;tt&amp;gt;ToolStripItem&amp;lt;/tt&amp;gt; class. That means they all support a few basic properties (the most important include &amp;lt;tt&amp;gt;Text&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;Image&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;ImageAlign&amp;lt;/tt&amp;gt;, all of which set the display content). &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; items all provide a &amp;lt;tt&amp;gt;Click&amp;lt;/tt&amp;gt; event you can use to detect when the user clicks a toolbar button.&lt;br /&gt;
&lt;br /&gt;
For example, if you want to react to a click of a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; item that you've named &amp;lt;tt&amp;gt;TestToolStripButton&amp;lt;/tt&amp;gt;, you can use the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''When the user clicks a button on the ToolStrip, that button's Click event fires. This is different than the legacy ToolBar control, which fired a generic Click event no matter which button was clicked''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Private Sub TestToolStripButton_Click(ByVal sender As Object, _&lt;br /&gt;
   ByVal e As System.EventArgs) Handles TestToolStripButton.Click&lt;br /&gt;
     &lt;br /&gt;
     MessageBox.Show(&amp;quot;You clicked &amp;quot; &amp;amp; CType(sender, ToolStripItem).Name)&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
Once you've created a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; and added at least one item, you can take advantage of a significant amount of out-of-the-box formatting. The following are just a few of the impressive features provided by &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* It matches the Office XP toolbar look, with a blue gradient background, etched sizing grips, and hot tracking (highlighting an item as the mouse moves over it).&lt;br /&gt;
* It correctly supports Windows XP themes. That means if you change the color scheme to Olive Green or Silver, all &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; controls update themselves automatically, allowing your application to blend in with the scenery.&lt;br /&gt;
* It allows user customization. If you enable the &amp;lt;tt&amp;gt;ToolStrip.AllowReorder&amp;lt;/tt&amp;gt; property, the user can rearrange the orders of buttons in a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; by holding down the Alt key and dragging items from one place to another, or even drag a button from one &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; to another.&lt;br /&gt;
* It supports ''overflow menus''. If you enable this feature (by setting &amp;lt;tt&amp;gt;ToolStrip.CanOverflow&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;) and shrink the window so the entire &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; no longer fits, a special drop-down menu appears at the right with all the extra buttons, as shown in [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-3|Figure 3-3]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-3. An overflow menu'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt72.png|An overflow menu]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the previous example, the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; is fixed in place. If you want, you can give the user the ability to drag a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;, either to dock it in a different place or to rearrange several that appear together. To make this possible, you need to add a &amp;lt;tt&amp;gt;ToolStripContainer&amp;lt;/tt&amp;gt; to your form, which shows up as a box with a blue gradient background (like the background of the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;). Although you can use more than one &amp;lt;tt&amp;gt;ToolStripContainer&amp;lt;/tt&amp;gt;, usually you'll just use one and dock it to fill all or a portion of your window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''To add a ToolStripContainer and place a ToolStrip in it in one step, click the ToolStrip smart tag and then click the &amp;quot;Embed in ToolStripContainer&amp;quot; link''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;ToolStripContainer&amp;lt;/tt&amp;gt; actually wraps four &amp;lt;tt&amp;gt;ToolStripPanel&amp;lt;/tt&amp;gt; objects, one for each side. These objects are exposed through properties such as &amp;lt;tt&amp;gt;ToolStripContainer.LeftToolStripPanel&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ToolStripContainer.TopToolStripPanel&amp;lt;/tt&amp;gt;, and so on. Each panel can hold an unlimited number of &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; objects, which are then docked to the corresponding side. The interesting part is that once you place a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; in a &amp;lt;tt&amp;gt;ToolStripContainer&amp;lt;/tt&amp;gt;, the user gains the ability to drag a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; freely about its panel at runtime. Users can even drag a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; from one &amp;lt;tt&amp;gt;ToolStripPanel&amp;lt;/tt&amp;gt; to another to change the side it's docked on (or even to an entirely separate &amp;lt;tt&amp;gt;ToolStripContainer&amp;lt;/tt&amp;gt; in the same window).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
If you want to prevent the user from docking the ToolStrip to the left side of the container, set the &amp;lt;tt&amp;gt;ToolStripContainer.LeftToolStripPanelVisible&amp;lt;/tt&amp;gt; property to false. You can also use similar properties to prevent docking to the right, top, or bottom sides.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...updating the rest of your interface to look as good as the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;? .NET 2.0 actually provides four controls that sport the flat, modern look of Windows XP, and support Windows XP theming. These are &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;StatusStrip&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;ContextMenuStrip&amp;lt;/tt&amp;gt;, which replace &amp;lt;tt&amp;gt;ToolBar&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;StatusBar&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;MainMenu&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;ContextMenu&amp;lt;/tt&amp;gt;. You can quickly refresh your application's interface just by updating these old standbys to the new controls.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
In Visual Studio 2005, you won't see the legacy controls like &amp;lt;tt&amp;gt;ToolBar&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;StatusBar&amp;lt;/tt&amp;gt;, because they're left out of the toolbox by default. If you want to use them, right-click the toolbox, choose Choose Items, and select these controls from the list.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information, read about the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; classes in the MSDN help library reference. You can also refer to a few more recipes in this chapter:&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;Add Any Control to a ToolStrip&amp;quot; explains how to add other controls to a &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* &amp;quot;Add Icons to Your Menu&amp;quot; explains how to use the new &amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt; control.&lt;br /&gt;
&lt;br /&gt;
== Add Any Control to a ToolStrip ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; supports a wide range of &amp;lt;tt&amp;gt;ToolStripItem&amp;lt;/tt&amp;gt; classes, allowing you to add everything from buttons and drop-down menus to text-boxes and labels. However, in some situations you might want to go beyond the standard options and use other .NET controls, or even place your own custom controls in the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;. In order to make this work, you need to use the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Want to outfit a ToolStrip with a custom control? Thanks to the ToolStripControlHost, you can add just about anything''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
There's no way to add standard .NET controls directly to the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;, because the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; only supports classes that derive from &amp;lt;tt&amp;gt;ToolStripItem&amp;lt;/tt&amp;gt;. You could create a class that derives from &amp;lt;tt&amp;gt;ToolStripItem&amp;lt;/tt&amp;gt; to implement a custom &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; element, but this approach is fairly complex and tedious. A much simpler approach is to use the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt;, which can wrap just about any .NET control.&lt;br /&gt;
&lt;br /&gt;
To use the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt; with a non-&amp;lt;tt&amp;gt;ToolStripItem&amp;lt;/tt&amp;gt; control, just pass the control object as a constructor argument when you create the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt;. Then, add the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt; object to the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt;. You can use the code in [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-1|Example 3-1]] to add a &amp;lt;tt&amp;gt;CheckBox&amp;lt;/tt&amp;gt; control to the &amp;lt;tt&amp;gt;ToolStrip.Items&amp;lt;/tt&amp;gt; collection. [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-4|Figure 3-4]] shows the result.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 3-1. Adding a Checkbox control to a ToolStrip.Items collection'''&lt;br /&gt;
&lt;br /&gt;
 ' Create a CheckBox.&lt;br /&gt;
 Dim CheckStrip As New CheckBox( )&lt;br /&gt;
     &lt;br /&gt;
 ' Set the CheckBox so it takes the size of its text.&lt;br /&gt;
 CheckStrip.AutoSize = True&lt;br /&gt;
 CheckStrip.Text = &amp;quot;Sample CheckBox in ToolStrip&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Make sure the CheckbBox is transparent (so the &lt;br /&gt;
 ' ToolStrip gradient background shows through).&lt;br /&gt;
 CheckStrip.BackColor = Color.FromArgb(0, 255, 0, 0)&lt;br /&gt;
     &lt;br /&gt;
 ' Create the ToolStripControlHost that wraps the CheckBox.&lt;br /&gt;
 Dim CheckStripHost As New ToolStripControlHost(CheckStrip)&lt;br /&gt;
     &lt;br /&gt;
 ' Set the ToolStripControlHost to take the full width&lt;br /&gt;
 ' of the control it wraps.&lt;br /&gt;
 CheckStripHost.AutoSize = True&lt;br /&gt;
     &lt;br /&gt;
 ' Add the wrapped CheckBox.&lt;br /&gt;
 ToolStrip1.Items.Add(CheckStripHost)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-4. A ToolStrip with a CheckBox'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt73.png|A ToolStrip with a CheckBox]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...customizing the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt;? If you're using a &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt; to host another control, you might want to add properties to the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt; to expose data from the hosted control. For example, you could add a Checked property to the &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt; used in this example so that you could easily set or retrieve the checked state of the wrapped &amp;lt;tt&amp;gt;CheckBox&amp;lt;/tt&amp;gt; control. In order to use this technique, you need to create a custom class that derives from &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
The MSDN help reference includes an example with a &amp;lt;tt&amp;gt;ToolStripControlHost&amp;lt;/tt&amp;gt; that hosts a date control. For more information, look up the index entry &amp;quot;ToolStrip → wrapping controls in. &amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Add Icons to Your Menu ==&lt;br /&gt;
&lt;br /&gt;
Windows applications have been undergoing a gradual facelift since Windows XP and Office XP first appeared on the scene. Today, many modern Windows applications use a fine-tuned menu that sports a blue shaded margin on its left side, and an optional icon for each menu command. (To see what this looks like, you can jump ahead to [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-5|Figure 3-5]].)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Jazz up your dullest menus with thumbnail images''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you wanted to create a polished-looking menu with this appearance in .NET 1.0 or 1.1, you needed to draw it yourself using GDI+ code. Although there are several surprisingly good examples of this technique available on the Internet, it's more than a little messy. In .NET 2.0, the situation improves dramatically. Even though the original &amp;lt;tt&amp;gt;MainMenu&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ContextMenu&amp;lt;/tt&amp;gt; controls are unchanged, two new controls—&amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ContextMenuStrip&amp;lt;/tt&amp;gt;—provide the same functionality but render the menu with the new Office XP look.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ContextMenuStrip&amp;lt;/tt&amp;gt; classes leverage all the hard work that went into building the &amp;lt;tt&amp;gt;ToolStrip&amp;lt;/tt&amp;gt; class. Essentially, a &amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt; is a special container for &amp;lt;tt&amp;gt;ToolStripItem&amp;lt;/tt&amp;gt; objects. The &amp;lt;tt&amp;gt;MenuStrip.Items&amp;lt;/tt&amp;gt; property holds a collection of top-level menu headings (like File, Edit, View, and Help), each of which is represented by a &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; object. Each &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; has a &amp;lt;tt&amp;gt;DropDownItemsProperty&amp;lt;/tt&amp;gt;, which exposes another collection of &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; objects, one for each contained menu item.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-2|Example 3-2]] shows code that creates the familiar Windows File menu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 3-2. Creating a Windows File menu'''&lt;br /&gt;
&lt;br /&gt;
 ' Add the top-level items to the menu.&lt;br /&gt;
 MenuStrip1.Items.AddRange(New ToolStripItem( ) _&lt;br /&gt;
   {fileToolStripMenuItem})&lt;br /&gt;
     &lt;br /&gt;
 ' Set the text for the File menu, and set &amp;quot;F&amp;quot; as the &lt;br /&gt;
 ' quick access key (so that Alt+F will open the menu.)&lt;br /&gt;
 fileToolStripMenuItem.Text = &amp;quot;&amp;amp;File&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Add the child items to the File menu.&lt;br /&gt;
 fileToolStripMenuItem.DropDownItems.AddRange(New ToolStripItem( ) _&lt;br /&gt;
   {newToolStripMenuItem, openToolStripMenuItem, _&lt;br /&gt;
   toolStripSeparator, saveToolStripMenuItem, _&lt;br /&gt;
   saveAsToolStripMenuItem, toolStripSeparator1, _&lt;br /&gt;
   printToolStripMenuItem, printPreviewToolStripMenuItem, _&lt;br /&gt;
   toolStripSeparator2, exitToolStripMenuItem})&lt;br /&gt;
     &lt;br /&gt;
 ' Configure the File child items.&lt;br /&gt;
 ' Set the text and shortcut key for the New menu option.&lt;br /&gt;
 newToolStripMenuItem.ShortcutKeys = CType((Keys.Control Or Keys.N), Keys)&lt;br /&gt;
 newToolStripMenuItem.Text = &amp;quot;&amp;amp;New&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Set the text and shortcut key for the Open menu option.&lt;br /&gt;
 openToolStripMenuItem.ShortcutKeys = CType((Keys.Control Or Keys.O), Keys)&lt;br /&gt;
 openToolStripMenuItem.Text = &amp;quot;&amp;amp;Open&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' (Code for configuring other omitted menu items.)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually, you won't enter this information by hand—instead, it's part of the designer code that Visual Studio generates automatically as you set the properties in the Properties window. However, it does show you how the menu works and what you'll need to do if you want to dynamically add new items at runtime.&lt;br /&gt;
&lt;br /&gt;
As [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-2|Example 3-2]] reveals, the structure of a &amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt; control is the same as the structure of its predecessor, the &amp;lt;tt&amp;gt;MainMenu&amp;lt;/tt&amp;gt; control, with menu objects containing other menu objects. The only difference is in the type of object used to represent menu items (it's now &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;MenuItem&amp;lt;/tt&amp;gt;) and the name of the property used to hold the collection of contained menu items (&amp;lt;tt&amp;gt;ToolStripMenuItem.DropDownItems&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;MenuItem.ChildItems&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
To reap the real benefits of the new &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt;, you need to use one property that wasn't available with ordinary &amp;lt;tt&amp;gt;MenuItem&amp;lt;/tt&amp;gt; objects: the &amp;lt;tt&amp;gt;Image&amp;lt;/tt&amp;gt; property, which sets the thumbnail icon that appears in the menu margin.&lt;br /&gt;
&lt;br /&gt;
 newToolStripMenuItem.Image = CType( _&lt;br /&gt;
   resources.GetObject(&amp;quot;newToolStripMenuItem.Image&amp;quot;), _&lt;br /&gt;
   System.Drawing.Image)&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-5|Figure 3-5]] shows the standard File menu.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-5. The new MenuStrip'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt75.png|The new MenuStrip]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually, you'll load all your images using the Visual Studio Properties Window at design time. In that case, they'll be embedded as a resource inside your assembly. Another option is to load them into an &amp;lt;tt&amp;gt;ImageList&amp;lt;/tt&amp;gt; and then set the &amp;lt;tt&amp;gt;ImageKey&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;IndexProperty&amp;lt;/tt&amp;gt; of &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; to point to an image in the &amp;lt;tt&amp;gt;ImageList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''To quickly generate a basic menu framework (including the standard menu commands for the File, Edit, Tools, and Help menu), click the MenuStrip smart tag and select Insert Standard Items''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...painting a menu from scratch? Hopefully, you won't need to. The &amp;lt;tt&amp;gt;ToolStripMenuItem&amp;lt;/tt&amp;gt; gives you a little bit more flexibility than the original &amp;lt;tt&amp;gt;MenuItem&amp;lt;/tt&amp;gt; class—not only can you insert images, but you can also choose a nonstandard font by setting the &amp;lt;tt&amp;gt;ToolStripMenuItem.Font&amp;lt;/tt&amp;gt; property. Here's an example:&lt;br /&gt;
&lt;br /&gt;
 fileToolStripMenuItem.Font = New Font(&amp;quot;Verdana&amp;quot;, 10, FontStyle.Regular)&lt;br /&gt;
&lt;br /&gt;
This technique is useful when you want to show a list of fonts in some sort of document editing application, and you want to render the font names in their corresponding typefaces in the menu.&lt;br /&gt;
&lt;br /&gt;
If you need to perform more radical alterations to how a menu is drawn, you'll need to use another renderer. The &amp;lt;tt&amp;gt;MenuStrip&amp;lt;/tt&amp;gt;, like all the &amp;quot;strip&amp;quot; controls, provides a &amp;lt;tt&amp;gt;RenderMode&amp;lt;/tt&amp;gt; and a &amp;lt;tt&amp;gt;Renderer&amp;lt;/tt&amp;gt; property. The &amp;lt;tt&amp;gt;RenderMode&amp;lt;/tt&amp;gt; property allows you to use one of the built-in renderers by choosing a value from the &amp;lt;tt&amp;gt;ToolStripRenderMode&amp;lt;/tt&amp;gt; enumeration (such as &amp;lt;tt&amp;gt;Professional&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;System&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;Custom&amp;lt;/tt&amp;gt;). If you want to use a renderer of your own, select &amp;lt;tt&amp;gt;Custom&amp;lt;/tt&amp;gt; and then supply a new renderer object in the &amp;lt;tt&amp;gt;Renderer&amp;lt;/tt&amp;gt; property. This renderer could be an instance of a third-party class or an instance of a class you've created (just derive from &amp;lt;tt&amp;gt;ToolStripRenderer&amp;lt;/tt&amp;gt; and override the methods to supply your specialized painting logic).&lt;br /&gt;
&lt;br /&gt;
== Put the Web in a Window ==&lt;br /&gt;
&lt;br /&gt;
There's no shortage of reasons why you might want to integrate a web page window into your application. Maybe you want to show your company web site, create a customized browser, or display HTML product documentation. In .NET 1.0 and .NET 1.1, you could use a web browser window through COM interop, but there were a number of quirky or missing features. The new &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; control in .NET 2.0 addresses these issues with easy web integration, support for printing and saving documents, and the ability to stop a user from navigating to the wrong web site.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''. NET's new managed WebBrowser control lets you show an HTML page or allow a user to browse a web site from inside your Windows application—with no interop headaches''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;System.Windows.Forms.WebBrowser&amp;lt;/tt&amp;gt; control wraps an Internet Explorer window. You can drop the &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; control onto any Windows form straight from the Visual Studio .NET toolbox.&lt;br /&gt;
&lt;br /&gt;
To direct the &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; to show a page, you simply set the &amp;lt;tt&amp;gt;Url&amp;lt;/tt&amp;gt; property to the target web page. All navigation in the &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; is asynchronous, which means your code continues running while the page is downloading. To check if the page is complete, verify that the &amp;lt;tt&amp;gt;ReadyState&amp;lt;/tt&amp;gt; property is &amp;lt;tt&amp;gt;Completed&amp;lt;/tt&amp;gt; or, better yet, react to a &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The WebBrowser control supports everything IE does, including JavaScript, ActiveX controls, and plug-ins''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; events unfold in this order:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''WebBrowser provides methods that duplicate the browser functions every web surfer is familiar with, such as Stop( ), Refresh( ), GoBack( ), GoForward( ), GoHome( ), GoSearch( ), Print( ), ShowPrintDialog( ), and ShowSave-AsDialog( )''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;tt&amp;gt;Navigating&amp;lt;/tt&amp;gt; fires when you set a new &amp;lt;tt&amp;gt;Url&amp;lt;/tt&amp;gt; or the user clicks a link. This is your chance to cancel the navigation before anything happens.&lt;br /&gt;
# &amp;lt;tt&amp;gt;Navigated&amp;lt;/tt&amp;gt; fires after &amp;lt;tt&amp;gt;Navigating&amp;lt;/tt&amp;gt;, just before the web browser begins downloading the page.&lt;br /&gt;
# The &amp;lt;tt&amp;gt;ProgressChanged&amp;lt;/tt&amp;gt; event fires periodically during a download and gives you information about how many bytes have been downloaded and how many are expected in total.&lt;br /&gt;
# &amp;lt;tt&amp;gt;DocumentCompleted&amp;lt;/tt&amp;gt; fires when the page is completely loaded. This is your chance to process the page.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-3|Example 3-3]] shows the event-handling code for a form, &amp;lt;tt&amp;gt;WebForm&amp;lt;/tt&amp;gt;, which hosts a &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; along with a simple status bar and progress bar. The &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; displays a local HTML file (note how the URL starts with file:///, not http://) and ensures that any external web links are opened in standalone Internet Explorer windows.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-EX-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 3-3. Building a basic browser window'''&lt;br /&gt;
&lt;br /&gt;
 Public Class WebForm&lt;br /&gt;
     &lt;br /&gt;
     Private Sub WebForm_Load(ByVal sender As Object, ByVal e As EventArgs) _&lt;br /&gt;
       Handles MyBase.Load&lt;br /&gt;
         ' Prevent the user from dragging and dropping links onto this browser.&lt;br /&gt;
         Browser.AllowWebBrowserDrop = False&lt;br /&gt;
     &lt;br /&gt;
         ' Go to the local documentation page.&lt;br /&gt;
         Browser.Url = new Uri(&amp;quot;file:///&amp;quot; &amp;amp; _&lt;br /&gt;
           My.Application.StartupPath &amp;amp; &amp;quot;\Doc.html&amp;quot;)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub Browser_Navigating(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As WebBrowserNavigatingEventArgs) Handles Browser.Navigating&lt;br /&gt;
         If Not e.Url.IsFile Then&lt;br /&gt;
             ' Don't resolve this external link.&lt;br /&gt;
             ' Instead, use the Navigate( ) method to open a&lt;br /&gt;
             ' standalone IE window.&lt;br /&gt;
             e.Cancel = True&lt;br /&gt;
             Browser.Navigate(e.Url, True)&lt;br /&gt;
         End If&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub Browser_Navigated(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As WebBrowserNavigatedEventArgs) Handles Browser.Navigated&lt;br /&gt;
         ' Show the progress bar.&lt;br /&gt;
         Progress.Visible = True&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub Browser_ProgressChanged(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As WebBrowserProgressChangedEventArgs) _&lt;br /&gt;
       Handles Browser.ProgressChanged&lt;br /&gt;
         ' Update the progress bar.&lt;br /&gt;
         Progress.Maximum = e.MaximumProgress&lt;br /&gt;
         Progress.Value = e.CurrentProgress&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub Browser_DocumentCompleted(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As WebBrowserDocumentCompletedEventArgs) _&lt;br /&gt;
       Handles Browser.DocumentCompleted&lt;br /&gt;
         ' Hide the progress bar.&lt;br /&gt;
         Progress.Visible = False&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub Browser_StatusTextChanged(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As EventArgs) Handles Browser.StatusTextChanged&lt;br /&gt;
         ' Display the text that IE would ordinarily show&lt;br /&gt;
         ' in the status bar.&lt;br /&gt;
         Status.Text = Browser.StatusText&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Class&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-6|Figure 3-6]] shows the form with its customized &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; window. The window also includes a &amp;lt;tt&amp;gt;StatusStrip&amp;lt;/tt&amp;gt; to display status text and a progress indicator when pages are being loaded.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-6. An embedded web window'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt77.png|An embedded web window]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The WebBrowser window is stripped to the bare minimum and doesn't include a toolbar, address bar, or status bar (although you can add other controls to your form)''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...other web surfing tricks? &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; gives you almost all of the power of IE to use in your own applications. Here are a few more tricks you might want to try:&lt;br /&gt;
&lt;br /&gt;
* Instead of setting the &amp;lt;tt&amp;gt;Url&amp;lt;/tt&amp;gt; property, call the &amp;lt;tt&amp;gt;Navigate( )&amp;lt;/tt&amp;gt; method, which has two useful overloads. The first (shown in the previous example), allows you to launch a standalone browser window. The second allows you to load a document into a specific frame in the current page.&lt;br /&gt;
* Instead of using URLs, you can load an HTML document directly from another resource, using the &amp;lt;tt&amp;gt;DocumentStream&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;DocumentText&amp;lt;/tt&amp;gt; property. The &amp;lt;tt&amp;gt;DocumentStream&amp;lt;/tt&amp;gt; accepts a reference to any &amp;lt;tt&amp;gt;Stream&amp;lt;/tt&amp;gt; object, while the &amp;lt;tt&amp;gt;DocumentText&amp;lt;/tt&amp;gt; property accepts a string that contains the HTML data.&lt;br /&gt;
* Once you've loaded a document, you can explore it using the HTML document model that's built into .NET. The jumping-off point is the &amp;lt;tt&amp;gt;Document&amp;lt;/tt&amp;gt; property, which returns an &amp;lt;tt&amp;gt;HtmlDocument&amp;lt;/tt&amp;gt; object that models the current document, including its tags and content.&lt;br /&gt;
* You can direct the &amp;lt;tt&amp;gt;WebBrowser&amp;lt;/tt&amp;gt; to a directory to give the user quick-and-dirty file browsing abilities. Keep in mind, however, that you won't be able to prevent them from copying, moving, or deleting files!&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For the full set of properties, look up the &amp;lt;tt&amp;gt;System.Windows.Forms.WebBrowser&amp;lt;/tt&amp;gt; class in the MSDN class library reference.&lt;br /&gt;
&lt;br /&gt;
== Validate Input While the User Types ==&lt;br /&gt;
&lt;br /&gt;
Visual Basic 6 and Access both provide developers with ''masked editing controls'': text input controls that automatically format your input as you type it in based on a specific ''mask''. For example, if you type 1234567890 into a masked input control that uses a telephone-number mask, the number is displayed as the string (123) 456-7890.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''VB 6 programmers accustomed to the ActiveX MaskedEdit control were disappointed to find . NET did not include a replacement. In . NET 2.0, the new MaskedTextBox fills the gap''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Masked input controls not only improve the presentation of certain values—they also prevent errors. Choosing the right mask ensures that certain characters will be rejected outright (for example, a telephone- number mask will not accept letters). Masked input controls also neatly avoid ''canonicalization errors'', which occur when there is more than one way of representing the same information. For example, with the telephone number mask, the user will immediately realize that an area code is required, even if you don't specifically explain this requirement.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
.NET 2.0 includes a new control named &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; that extends the &amp;lt;tt&amp;gt;TextBox&amp;lt;/tt&amp;gt; control. Once you've added a &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; to a form, you can set the mask in two ways:&lt;br /&gt;
&lt;br /&gt;
* You can choose one of the prebuilt masks.&lt;br /&gt;
* You can define your own custom mask.&lt;br /&gt;
&lt;br /&gt;
To set a mask, click the &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; smart tag and select Set Mask. The Input Mask dialog box appears, with a list of commonly used masks, including masks for phone numbers, zip codes, dates, and so on. When you select a mask from the list, the mask is displayed in the Mask text box. You can now customize the mask. You can also try the mask out using the Try It text box, as shown in [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-7|Figure 3-7]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-7&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-7. Selecting a mask for the MaskedTextBox'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt78.png|Selecting a mask for the MaskedTextBox]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Thanks to the wonders of COM Interop, it's still possible to use the VB 6 MaskedEdit control in . NET. However, the . NET MaskedTextBox control improves on several limitations and quirks in the MaskedEdit control, so it's still superior''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mask you choose will be stored in the &amp;lt;tt&amp;gt;MaskTextBox.Mask&amp;lt;/tt&amp;gt; property. Once you've chosen a mask, it will be applied whenever the user types in the &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt;. If you want to respond to user mistakes (like invalid characters) to provide more information, you can respond to the &amp;lt;tt&amp;gt;MaskInputRejected&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
If you want to build a custom mask, you need to understand a little more about how masking works. Essentially, a mask is built out of two types of characters: ''placeholders'', which designate where the user must supply a character; and ''literals'', which are used to format the value. For example, in the phone number mask (999)-000-000, the hyphens and brackets are literals. These characters are always present and can't be deleted, modified, or moved by the user. The number 0 is a placeholder that represents any number character, while the number 9 is a placeholder that represents an optional numeric character.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-TABLE-1|Table 3-1]] lists and explains all the characters you can use to create a mask. You can use this as a reference to build your own masks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-TABLE-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 3-1. Mask characters'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Character !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Required digit (0-9).&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Optional digit or space. If left blank, a space is inserted automatically.&lt;br /&gt;
|-&lt;br /&gt;
| # || Optional digit, space, or plus/minus symbol. If left blank, a space is inserted automatically.&lt;br /&gt;
|-&lt;br /&gt;
| L || Required ASCII letter (a-z or A-Z).&lt;br /&gt;
|-&lt;br /&gt;
| ? || Optional ASCII letter.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp; || Required Unicode character. Allows anything that isn't a control key, including punctuation and symbols.&lt;br /&gt;
|-&lt;br /&gt;
| C || Optional Unicode character.&lt;br /&gt;
|-&lt;br /&gt;
| A || Required alphanumeric character (allows letter or number but not punctuation or symbols).&lt;br /&gt;
|-&lt;br /&gt;
| a || Optional alphanumeric character.&lt;br /&gt;
|-&lt;br /&gt;
| . || Decimal placeholder.&lt;br /&gt;
|-&lt;br /&gt;
| , || Thousands placeholder.&lt;br /&gt;
|-&lt;br /&gt;
| : || Time separator.&lt;br /&gt;
|-&lt;br /&gt;
| / || Date separator.&lt;br /&gt;
|-&lt;br /&gt;
| $ || Currency symbol.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt; || All the characters that follow will be converted automatically to lowercase as the user types them in. (There is no way to switch a subsequent portion of the text back to mixed-case entry mode once you use this character.)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;gt; || All the characters that follow will be converted automatically to uppercase as the user types them in.&lt;br /&gt;
|-&lt;br /&gt;
| \ || Escapes a masked character, turning it into a literal. Thus, if you use &amp;lt;tt&amp;gt;\&amp;amp;&amp;lt;/tt&amp;gt; it is interpreted as a literal character ''&amp;amp;'', which will be inserted in the text box.&lt;br /&gt;
|-&lt;br /&gt;
| All other characters || All other characters are treated as literals, and are shown in the text box.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, there are a few more properties that the &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; provides (and you might want to take advantage of). These include:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;BeepOnError&amp;lt;/tt&amp;gt;&lt;br /&gt;
: If the user inputs an invalid character and &amp;lt;tt&amp;gt;BeepOnError&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;, the &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; will play the standard error chime.&lt;br /&gt;
;&amp;lt;tt&amp;gt;PromptChar&amp;lt;/tt&amp;gt;&lt;br /&gt;
: When the text box is empty, every required value is replaced with a prompt character. By default, the prompt character is the underscore (&amp;lt;tt&amp;gt;_&amp;lt;/tt&amp;gt;), so a mask for a telephone number will display &amp;lt;tt&amp;gt;(_ _ _)-_ _ _-_ _ _ _&amp;lt;/tt&amp;gt; while empty.&lt;br /&gt;
;&amp;lt;tt&amp;gt;MaskCompleted&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; if there are no empty characters in the text box (meaning the user has entered the required value).&lt;br /&gt;
;&amp;lt;tt&amp;gt;InputText&amp;lt;/tt&amp;gt;&lt;br /&gt;
: &amp;lt;tt&amp;gt;InputText&amp;lt;/tt&amp;gt; returns the data in the &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; without any literal characters. For example, in a &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; that allows the user to enter a telephone number, the &amp;lt;tt&amp;gt;Text&amp;lt;/tt&amp;gt; property will return the fully formatted number, like &amp;lt;tt&amp;gt;(123)-456-7890&amp;lt;/tt&amp;gt;, while &amp;lt;tt&amp;gt;InputText&amp;lt;/tt&amp;gt; returns just the numeric content, or &amp;lt;tt&amp;gt;1234567890&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...using masked editing in other input controls? It is possible, but not easy. The &amp;lt;tt&amp;gt;MaskedTextBox&amp;lt;/tt&amp;gt; relies on a special &amp;lt;tt&amp;gt;MaskedEditProvider&amp;lt;/tt&amp;gt; class in the &amp;lt;tt&amp;gt;System.ComponentModel&amp;lt;/tt&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
To create a different type of masked control, you need to create a custom control that uses the &amp;lt;tt&amp;gt;MaskedEditProvider&amp;lt;/tt&amp;gt; internally. When your control receives a key press, you need to determine the attempted action and pass it on to the &amp;lt;tt&amp;gt;MaskedEditProvider&amp;lt;/tt&amp;gt; using methods like &amp;lt;tt&amp;gt;Add( )&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;Insert( )&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;Remove( )&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;Replace( )&amp;lt;/tt&amp;gt;. Then, you can retrieve the new display value by calling &amp;lt;tt&amp;gt;MaskedEditProvider.ToDisplayString( )&amp;lt;/tt&amp;gt;, and refresh your custom control appropriately. The hard part is handling all of this low-level editing without causing flicker or losing the user's place in the input string. For more information, you can refer to the full example that's included with the downloadable code in the MaskedEditing project.&lt;br /&gt;
&lt;br /&gt;
== Create Text Boxes thatAuto-Complete ==&lt;br /&gt;
&lt;br /&gt;
In many of the nooks and crannies of the Windows operating system, you'll find AutoComplete text boxes. These text boxes suggest one or more values as you type.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''With . NET's new auto-complete features, you can create intelligent text boxes able to suggest possible values based on recent entries or a default list''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually, AutoComplete values are drawn from your recent history. For example, when you type a URL into Internet Explorer's address bar, you'll see a list that includes URLs you've surfed to in the past. Now with .NET 2.0, you can harness the same AutoComplete features with your own custom lists or one of the lists maintained by the operating system.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;TextBox&amp;lt;/tt&amp;gt; and the &amp;lt;tt&amp;gt;ComboBox&amp;lt;/tt&amp;gt; controls both support the AutoComplete feature in .NET 2.0. To use AutoComplete, first set the control's &amp;lt;tt&amp;gt;AutoCompleteMode&amp;lt;/tt&amp;gt; property to one of the following values:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;Append&amp;lt;/tt&amp;gt;&lt;br /&gt;
: In this mode, the AutoComplete value is automatically inserted into the control as you type. However, the added portion is selected so that the new portion will be replaced if you continue typing. (Alternatively, you can just click delete to remove it.)&lt;br /&gt;
;&amp;lt;tt&amp;gt;Suggest&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This is the friendliest mode. As you type, a drop-down list of matching AutoComplete values appears underneath the control. If one of these entries matches what you want, you can select it.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SuggestAppend&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This mode combines &amp;lt;tt&amp;gt;Append&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;Suggest&amp;lt;/tt&amp;gt;. As with &amp;lt;tt&amp;gt;Suggest&amp;lt;/tt&amp;gt;, a list of candidate matches is shown in a drop-down list. However, the first match is also inserted into the control and selected.&lt;br /&gt;
&lt;br /&gt;
After choosing the type of AutoComplete, you need to specify what list will be used for suggestions. Do this by setting the &amp;lt;tt&amp;gt;AutoCompleteSource&amp;lt;/tt&amp;gt; property to one of the following values:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;FileSystem&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes recently entered file paths. Use &amp;lt;tt&amp;gt;FileSystemDirectories&amp;lt;/tt&amp;gt; instead to include only directory paths.&lt;br /&gt;
;&amp;lt;tt&amp;gt;HistoryList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes URLs from Internet Explorer's history list.&lt;br /&gt;
;&amp;lt;tt&amp;gt;RecentlyUsedList&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes all the documents in the user's &amp;quot;most recently used list,&amp;quot; which appears in the Start menu (depending on system settings).&lt;br /&gt;
;&amp;lt;tt&amp;gt;AllUrl&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes the URLs of all sites that the current user has visited recently, whether they were typed in manually by the user or linked to from a web page.&lt;br /&gt;
;&amp;lt;tt&amp;gt;AllSystemSources&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes the full list of URLs and file paths.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ListItems&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes the items in the &amp;lt;tt&amp;gt;ComboBox.Items&amp;lt;/tt&amp;gt; collection. This choice isn't valid with the &amp;lt;tt&amp;gt;TextBox&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;CustomSource&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Includes the items in the &amp;lt;tt&amp;gt;AutoCompleteCustomSource&amp;lt;/tt&amp;gt; collection. You need to add these items yourself.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-8|Figure 3-8]] shows an AutoComplete text box using &amp;lt;tt&amp;gt;AutoSuggestAppend&amp;lt;/tt&amp;gt; as the &amp;lt;tt&amp;gt;AutoCompleteMode&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;AllUrl&amp;lt;/tt&amp;gt; as the &amp;lt;tt&amp;gt;AutoCompleteSource&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-8&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-8. An AutoComplete text box'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt79.png|An AutoComplete text box]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;TextBox&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ComboBox&amp;lt;/tt&amp;gt; controls both provide the same functionality. If you use &amp;lt;tt&amp;gt;AutoSuggest&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;AutoSuggestAppend&amp;lt;/tt&amp;gt; with a &amp;lt;tt&amp;gt;ComboBox&amp;lt;/tt&amp;gt;, the list of matches is displayed in a list under the control. However, this list shouldn't be confused with the list of entries that you've added to the &amp;lt;tt&amp;gt;ComboBox.Items&amp;lt;/tt&amp;gt; property. When you click the drop-down arrow for the &amp;lt;tt&amp;gt;ComboBox&amp;lt;/tt&amp;gt;, you'll see your list of items, ''not'' the list of AutoComplete suggestions. Both lists are completely separate, and there is no programmatic way for you to interact with the AutoComplete list. The only exception is if you create a &amp;lt;tt&amp;gt;ComboBox&amp;lt;/tt&amp;gt; with an &amp;lt;tt&amp;gt;AutoCompleteSource&amp;lt;/tt&amp;gt; of &amp;lt;tt&amp;gt;CustomSource&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;ListItems&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...using AutoComplete in other controls? Unfortunately, there's no managed way to do it in .NET. However, you can retrieve the information you need directly from the registry. For example, if you look in the ''Software\Microsoft\Internet Explorer\TypedURLs'' section of the &amp;lt;tt&amp;gt;HKEY_CURRENT_USER&amp;lt;/tt&amp;gt; registry key, you'll find the list of recently typed in URLs. To retrieve these items programmatically, refer to classes like the &amp;lt;tt&amp;gt;RegistryKey&amp;lt;/tt&amp;gt; in the &amp;lt;tt&amp;gt;Microsoft.Win32&amp;lt;/tt&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
== Play a Windows System Sound ==&lt;br /&gt;
&lt;br /&gt;
The Windows operating system alerts users to system events by mapping them to sounds recorded in specific audio files. The problem is that these files are stored in different locations on different computers. In .NET 1.0 and 1.1, there's no easy way to find the default system sounds and play them in your own application. A new &amp;lt;tt&amp;gt;SystemSounds&amp;lt;/tt&amp;gt; class in .NET 2.0 addresses this problem, allowing you to play the most common sounds with a single line of code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to sound the infamous Windows chime? With the new SystemSounds class, these audio files are right at your fingertips''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;SystemSounds&amp;lt;/tt&amp;gt; class in the &amp;lt;tt&amp;gt;System.Windows.Forms&amp;lt;/tt&amp;gt; namespace provides five shared properties. Each of these properties is a separate &amp;lt;tt&amp;gt;SystemSound&amp;lt;/tt&amp;gt; object that represents a specific operating-system event. Here's the full list:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;Asterisk&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Beep&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Exclamation&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Hand&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Question&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you decide which sound you want to use, you simply need to call its &amp;lt;tt&amp;gt;Play( )&amp;lt;/tt&amp;gt; method to play the sound. Here's an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''To configure which WAV files are used for each sound, select the Sounds and Audio Devices icon in the Control Panel''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 SystemSounds.Beep.Play( )&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...playing arbitrary WAV files? The &amp;lt;tt&amp;gt;SystemSounds&amp;lt;/tt&amp;gt; class works best if you just need an easy way to add a sound for simple user feedback. If you need to play an audio file of your own choosing, you need to use the &amp;lt;tt&amp;gt;SoundPlayer&amp;lt;/tt&amp;gt;, as discussed in the next lab, &amp;quot;Play Simple WAV Audio.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Play Simple WAV Audio ==&lt;br /&gt;
&lt;br /&gt;
Neither .NET 1.0 or .NET 1.1 provided a managed way to play audio. This shortcoming is finally addressed in .NET 2.0 with the new &amp;lt;tt&amp;gt;SoundPlayer&amp;lt;/tt&amp;gt; class, which allows you to play audio synchronously or asynchronously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Using the SoundPlayer class, you can play WAV files without diving into the Windows API''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
You can instantiate a &amp;lt;tt&amp;gt;SoundPlayer&amp;lt;/tt&amp;gt; object programmatically, or you can add one to the component tray by dragging it from the toolbox at design time. Once you've created the &amp;lt;tt&amp;gt;SoundPlayer&amp;lt;/tt&amp;gt;, you need to point it to the sound content you want to play. You do this by setting one of two properties:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;SoundLocation&amp;lt;/tt&amp;gt;&lt;br /&gt;
: If you have a file path or URL that points to a WAV file, specify this information in the &amp;lt;tt&amp;gt;SoundLocation&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Stream&amp;lt;/tt&amp;gt;&lt;br /&gt;
: If you have a &amp;lt;tt&amp;gt;Stream&amp;lt;/tt&amp;gt;-based object that contains WAV audio content, use the &amp;lt;tt&amp;gt;Stream&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
Once you've set the &amp;lt;tt&amp;gt;Stream&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SoundLocation&amp;lt;/tt&amp;gt; property, you need to tell &amp;lt;tt&amp;gt;SoundPlayer&amp;lt;/tt&amp;gt; to actually load the audio data by calling the &amp;lt;tt&amp;gt;Load( )&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;LoadAsync( )&amp;lt;/tt&amp;gt; method. The &amp;lt;tt&amp;gt;Load( )&amp;lt;/tt&amp;gt; method pauses your code until all the audio is loaded into memory. On the other hand, &amp;lt;tt&amp;gt;LoadAsync( )&amp;lt;/tt&amp;gt; carries out its work on another thread and fires the &amp;lt;tt&amp;gt;LoadCompleted&amp;lt;/tt&amp;gt; event once it's finished and the audio's available. Usually, you'll use &amp;lt;tt&amp;gt;Load()&amp;lt;/tt&amp;gt; unless you have an extremely large audio file or it takes a long time to read the whole audio file (for example, when retrieving the audio over a slow network or Internet connection).&lt;br /&gt;
&lt;br /&gt;
Finally, once the audio is available, you can call one of the following methods:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;PlaySync( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Pauses your code until the audio playback is finished.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Play( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Plays the audio on another thread, allowing your code to continue with other tasks and making sure that your application's interface remains responsive.&lt;br /&gt;
;&amp;lt;tt&amp;gt;PlayLooping( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Similar to &amp;lt;tt&amp;gt;Play( )&amp;lt;/tt&amp;gt;, except that it loops the audio, repeating it continuously.&lt;br /&gt;
&lt;br /&gt;
To halt asynchronous playback at any time, just call &amp;lt;tt&amp;gt;Stop( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The following code snippet shows an example that plays a sample sound synchronously:&lt;br /&gt;
&lt;br /&gt;
 Dim Player As New SoundPlayer( )&lt;br /&gt;
 Player.SoundLocation = Application.StartupPath &amp;amp; &amp;quot;\mysound.wav&amp;quot;&lt;br /&gt;
 Try&lt;br /&gt;
     Player.Load( )&lt;br /&gt;
     Player.PlaySync( )&lt;br /&gt;
 Catch Err As Exception&lt;br /&gt;
     ' An error will occur here if the file can't be read&lt;br /&gt;
     ' or if it has the wrong format.&lt;br /&gt;
 End Try&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...other types of audio? Unfortunately, the &amp;lt;tt&amp;gt;SoundPlayer&amp;lt;/tt&amp;gt; can only play the WAV audio format. If you want to play other types of multimedia, like MP3 or WMA files, you need to use a different solution, and there are no managed classes to help you out.&lt;br /&gt;
&lt;br /&gt;
Two options include:&lt;br /&gt;
&lt;br /&gt;
* Use COM Interop to access the Quartz library, which is a part of DirectX. The Quartz library allows you to play any file type supported by Windows Media Player, including MP3, WMA, and video formats like MPEG and AVI. For an example in C# code, refer to Microsoft's sample project at ''http://msdn.microsoft.com/library/en-us/csref/html/vcwlkcominteroppart1cclienttutorial.asp''&lt;br /&gt;
* Use the managed DirectX 9.0 libraries. You'll need to install the DirectX client and SDK on the computer for this to work, but it gives you a great deal of power, including the ability to render three-dimensional graphics. See ''http://msdn.microsoft.com/library/en-us/directx9_m/directx/dx9intro.asp'' for an introduction.&lt;br /&gt;
&lt;br /&gt;
== Create a Windows Explorer-like Split Window ==&lt;br /&gt;
&lt;br /&gt;
.NET 1.0 gave developers the tools they needed to create split windows of the kind seen in Windows Explorer with the &amp;lt;tt&amp;gt;Splitter&amp;lt;/tt&amp;gt; control. Unfortunately, creating these windows wasn't always easy, because it commonly required a combination of a &amp;lt;tt&amp;gt;Splitter&amp;lt;/tt&amp;gt; and three &amp;lt;tt&amp;gt;Panel&amp;lt;/tt&amp;gt; controls, all of which needed to be docked in the correct order. If you needed to split a window in more than one way, the task became even more awkward. Thankfully, .NET 2.0 streamlines the process with a &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; control.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Split windows are easier than ever now that the SplitContainer control replaces the bare-bones Splitter''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Essentially, the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; control represents two panels separated by a splitter bar. The user can drag the bar to one side or another to change the relative amount of space given to each section. To help signal the availability of this functionality, the mouse pointer switches from a single- to a double-headed arrow icon when the user mouses over the splitter bar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''A SplitContainer control is often used when the content in the two panels is related. When the user makes a selection in the first panel, the content in the second is refreshed''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a simple interface with the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;, you should first decide how much screen real estate the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; will occupy. For example, if you need to reserve some space below the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;, start by docking a &amp;lt;tt&amp;gt;Panel&amp;lt;/tt&amp;gt; to the bottom of the form. When you add the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;, its &amp;lt;tt&amp;gt;Dock&amp;lt;/tt&amp;gt; property will automatically be set to &amp;lt;tt&amp;gt;DockStyle.Fill&amp;lt;/tt&amp;gt; so that it fills whatever space is left over.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; always consists of two panels. If you set the &amp;lt;tt&amp;gt;Orientation&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;Orientation.Vertical&amp;lt;/tt&amp;gt; (the default), the splitter runs from top to bottom, creating left and right panels. The other option is &amp;lt;tt&amp;gt;Orientation.Horizontal&amp;lt;/tt&amp;gt;, which creates top and bottom panels with a splitter bar running from left to right between them.&lt;br /&gt;
&lt;br /&gt;
Once you've set the appropriate orientation, the next step is to add controls to each side of the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;. If you want a single control on each side, you simply need to drag the control to the appropriate panel in the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; and set the &amp;lt;tt&amp;gt;Dock&amp;lt;/tt&amp;gt; property of the control to &amp;lt;tt&amp;gt;DockStyle.Fill&amp;lt;/tt&amp;gt;, so that it fills all the available space between the splitter bar and the edges of the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If you need to add more than one control in the same region of the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;, start by adding a &amp;lt;tt&amp;gt;Panel&amp;lt;/tt&amp;gt; and setting the &amp;lt;tt&amp;gt;Dock&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;DockStyle.Fill&amp;lt;/tt&amp;gt;. Then, you can anchor other controls inside the &amp;lt;tt&amp;gt;Panel&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Once you've set up the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt;, you don't need to write any code to manage the control resizing or user interaction. [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-9|Figure 3-9]] shows an example. (The complete SplitWindow project is available with the downloadable samples.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-9&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-9. A vertically split window'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt82.png|A vertically split window]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''You can also nest a SplitContainer inside another SplitContainer. This is most useful if you are using different orientations (for example, dividing a window into left and right regions and then dividing the region on the right into top and bottom compartments)''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...restricting how a &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; can be resized? The &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; provides several properties tailored for this purpose. For example, you can set the &amp;lt;tt&amp;gt;Panel1MinSize&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;Panel2MinSize&amp;lt;/tt&amp;gt; properties with the minimum pixel width of the appropriate panels. Once you set these properties, the user won't be able to move the splitter bar to a position that shrinks the panel to less than its minimum allowed size. You can also stop resizing altogether by setting the &amp;lt;tt&amp;gt;IsSplitterFixed&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt; (in which case you can still adjust the position of the splitter bar by programmatically modifying the &amp;lt;tt&amp;gt;SplitterDistance&amp;lt;/tt&amp;gt; property).&lt;br /&gt;
&lt;br /&gt;
Additionally, you can configure how the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; behaves when the whole form is resized. By default, the panels are sized proportionately. However, you can designate one of the panels as a ''fixed panel'' by setting the &amp;lt;tt&amp;gt;FixedPanel&amp;lt;/tt&amp;gt; property. In this case, that panel won't be modified when the form is resized. (For example, in Windows Explorer the directory tree is in a fixed panel, and it doesn't change size when you expand or shrink the window.) Finally, you can even hide a panel temporarily by setting the &amp;lt;tt&amp;gt;Panel1Collapsed&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;Panel2Collapsed&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more details on the &amp;lt;tt&amp;gt;SplitContainer&amp;lt;/tt&amp;gt; and some how-to tips, look up &amp;quot;SplitContainer → Overview&amp;quot; in the index of the MSDN help reference.&lt;br /&gt;
&lt;br /&gt;
== Take Control of Window Layout ==&lt;br /&gt;
&lt;br /&gt;
.NET 2.0 includes two new container controls that can lay out all the controls they contain in a set pattern. Both of these controls extend the &amp;lt;tt&amp;gt;Panel&amp;lt;/tt&amp;gt; class with additional layout logic. The &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt; arranges controls evenly over several rows (from left to right), or in multiple columns (from top to bottom). The &amp;lt;tt&amp;gt;TableLayoutPanel&amp;lt;/tt&amp;gt; places its controls into a grid of invisible cells, allowing to you to keep consistent column widths and row heights.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The new . NET layout controls give you a way to lay out controls in set patterns automatically, which can save a good deal of effort with highly dynamic or configurable interfaces''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The layout controls are used most often in the following two scenarios:&lt;br /&gt;
&lt;br /&gt;
* You have a dynamic interface that generates some of its elements programmatically. Using the layout controls, you can arrange a group of controls neatly without calculating a position for each control (and then setting the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; property accordingly).&lt;br /&gt;
* You have a localized interface that must adapt to different languages that require vastly different amounts of on-screen real estate. As a result, when the display text changes, the controls must also adjust their size. In this case, layout controls can help you make sure the controls remain properly arranged even when their size varies.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-4|Example 3-4]] demonstrates an implementation of the first scenario. It starts with a form that includes an empty &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt; has its &amp;lt;tt&amp;gt;BorderStyle&amp;lt;/tt&amp;gt; set to &amp;lt;tt&amp;gt;BorderStyle.Fixed3D&amp;lt;/tt&amp;gt; so the border is visible.&lt;br /&gt;
&lt;br /&gt;
No controls are added to the &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt; at design time. Instead, several new buttons are added programmatically when a &amp;lt;tt&amp;gt;cmdGenerate&amp;lt;/tt&amp;gt; button is clicked.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-EX-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 3-4. Laying out buttons dynamically'''&lt;br /&gt;
&lt;br /&gt;
 Private Sub Button1_Click(ByVal sender As System.Object, _&lt;br /&gt;
   ByVal e As System.EventArgs) Handles Button1.Click&lt;br /&gt;
     For i As Integer = 0 To 10&lt;br /&gt;
         ' Create a new button.&lt;br /&gt;
         Dim Button As New Button&lt;br /&gt;
         Button.Text = &amp;quot;Dynamic Button #&amp;quot; &amp;amp; String.Format(&amp;quot;{0:00}&amp;quot;, i)&lt;br /&gt;
     &lt;br /&gt;
         ' Size the button the width of the text.&lt;br /&gt;
         Button.AutoSize = True&lt;br /&gt;
     &lt;br /&gt;
         ' Add the button to the layout panel.&lt;br /&gt;
         FlowLayoutPanel1.Controls.Add(Button)&lt;br /&gt;
     Next&lt;br /&gt;
 End Sub&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the code doesn't set the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; property for each button. That's because the &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt; won't use this information. Instead, it will arrange the buttons in the order they are added, spacing each button out from left to right and then top to bottom. (To reverse this order, change the &amp;lt;tt&amp;gt;FlowLayoutPanel.FlowDirection&amp;lt;/tt&amp;gt; property.)&lt;br /&gt;
&lt;br /&gt;
There is one piece of information that the &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt; ''does'' use. That's the &amp;lt;tt&amp;gt;Margin&amp;lt;/tt&amp;gt; property of each container control. This sets the minimum border required between this control and the next. The code above doesn't change the &amp;lt;tt&amp;gt;Button.Margin&amp;lt;/tt&amp;gt; property, because the default setting of 3 pixels is perfectly adequate.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-10|Figure 3-10]] shows what the buttons look like once they've been added.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-10&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-10. Laying out buttons dynamically'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt83.png|Laying out buttons dynamically]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''There are actually four different components of the Margin property: Margin.Left, Margin.Right, Margin.Top, and Margin.Bottom. You can set these individually to specify different margins for the control on each side''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
.NET also includes a &amp;lt;tt&amp;gt;TableLayoutPanel&amp;lt;/tt&amp;gt;. This panel works like the &amp;lt;tt&amp;gt;FlowLayoutPanel&amp;lt;/tt&amp;gt;, laying out controls automatically, but it aligns them according to invisible column and row grid lines. For example, if you have a number of controls that were sized differently, you can use the &amp;lt;tt&amp;gt;TableLayoutPanel&amp;lt;/tt&amp;gt; to ensure that each control is spaced out evenly in an imaginary cell.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...more advanced layout examples? There's a lot more you can do with a creative use of layout controls. Of course, just because you ''can'' doesn't mean you ''should''. Microsoft architects recommend you use layout controls only in specialized scenarios where the anchoring and docking features of Windows Forms aren't enough. If you don't have a highly dynamic interface, layout managers may introduce more complexity than you need.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
To get started with more advanced uses of layout controls, refer to some of the information in the MSDN help library reference. Look up &amp;quot;TableLayoutPanel control → about&amp;quot; in the index of the MSDN help reference. This displays general information about the &amp;lt;tt&amp;gt;TableLayoutPanel&amp;lt;/tt&amp;gt; control and provides a link to two walkthroughs that show how the &amp;lt;tt&amp;gt;TableLayoutPanel&amp;lt;/tt&amp;gt; can work in a complex localizable application.&lt;br /&gt;
&lt;br /&gt;
== Control When Your Application Shuts Down ==&lt;br /&gt;
&lt;br /&gt;
In Visual Studio 2005, a new &amp;quot;Shutdown mode&amp;quot; option lets you control when your application should end. You can wrap up as soon as the main window is closed (the window that's designated as the ''startup object''), or you can wait until ''all'' the application windows are closed. And if neither of these choices offers what you want, you can take complete control with the &amp;lt;tt&amp;gt;Application&amp;lt;/tt&amp;gt; class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''In . NET 2.0, it's easier than ever to specify when your Windows application should call it quits''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In Visual Studio, double-click the My Project item in the Solution Explorer. A tabbed window with application settings will appear, as shown in [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-11|Figure 3-11]]. Click the Application tab, and look at the Windows Application Properties section at the bottom of the tab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-11&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-11. Application settings in Visual Studio 2005'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt84.png|Application settings in Visual Studio 2005]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You have two out-of-the-box choices for the &amp;quot;Shutdown mode&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
;When startup form closes&lt;br /&gt;
: This option matches the standard behavior of an application in .NET 1.0 and 1.1. As soon as the startup form is closed, the entire program shuts down, taking any other open windows with it. (The startup form is the form identified in the &amp;quot;Startup object&amp;quot; box.)&lt;br /&gt;
;When last form closes&lt;br /&gt;
: This option matches the behavior of Visual Basic 6. Your application keeps on rolling as long as a window is open. When the last window is closed, the application follows suit, and shuts itself down.&lt;br /&gt;
&lt;br /&gt;
If neither of these options is suitable, you can take matters into your own hands. First, select the &amp;quot;Startup with custom Sub Main&amp;quot; checkbox. Now, you need to add a subroutine named &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; to your application. You can place this subroutine in an existing form or class, as long as you make sure to add the &amp;lt;tt&amp;gt;Shared&amp;lt;/tt&amp;gt; accessibility keyword. Here's an example:&lt;br /&gt;
&lt;br /&gt;
 Public Class MyForm&lt;br /&gt;
     &lt;br /&gt;
     '''Public Shared Sub Main'''&lt;br /&gt;
         ' '''(Startup code goes here.)'''&lt;br /&gt;
                '''End Sub'''&lt;br /&gt;
    &lt;br /&gt;
 End Class&lt;br /&gt;
&lt;br /&gt;
Shared methods are always available, even if there isn't a live instance of the containing class. For example, if you add a &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method to a form, the .NET runtime can call your &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method even though there isn't a form object.&lt;br /&gt;
&lt;br /&gt;
Another choice is to add the &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method to a ''module''. In a module, every method, function, property, and variable acts as though it's shared, so you won't need to add the &amp;lt;tt&amp;gt;Shared&amp;lt;/tt&amp;gt; keyword. Here's an example:&lt;br /&gt;
&lt;br /&gt;
 Public Module MyModule&lt;br /&gt;
     &lt;br /&gt;
     '''Public Sub Main'''&lt;br /&gt;
         ' '''(Startup code goes here.)'''&lt;br /&gt;
                '''End Sub'''&lt;br /&gt;
    &lt;br /&gt;
 End Module&lt;br /&gt;
&lt;br /&gt;
Whatever you choose, make sure the class or module that contains the &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method is selected in the &amp;quot;Startup object&amp;quot; box.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Using a module is a great choice if you have extensive initialization to perform, because it separates your startup code from your form code''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you use a &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method to start your application, the application only runs as long as the &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method is active. As soon as &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; ends, your application finishes. Here's an example of a prematurely terminated application:&lt;br /&gt;
&lt;br /&gt;
 Public Sub Main&lt;br /&gt;
     ' Show one form modelessly (without blocking the code).&lt;br /&gt;
     Form1.Show( )&lt;br /&gt;
     &lt;br /&gt;
     ' Show another form modelessly (at the same time as the first).&lt;br /&gt;
     Form2.Show( )&lt;br /&gt;
     &lt;br /&gt;
     ' After this line, the Main method ends, the application shuts&lt;br /&gt;
     ' itself down, and both windows close (after only being open a&lt;br /&gt;
     ' for a few milliseconds of screen time).&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
And here's the correct code that shows two windows in sequence:&lt;br /&gt;
&lt;br /&gt;
 Public Sub Main&lt;br /&gt;
     ' Show one form modally (code stops until the window is closed).&lt;br /&gt;
     Form1.ShowDialog( )&lt;br /&gt;
     &lt;br /&gt;
     ' After the first window is closed, show the second modally.&lt;br /&gt;
     Form2.ShowDialog( )&lt;br /&gt;
     &lt;br /&gt;
     ' Now the application ends.&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
In some cases, you might want to start your application with a &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method to perform some basic initialization and show a few forms. Then, you might want to wait until all the forms are closed before the application ends. This pattern is easy to implement, provided you use the &amp;lt;tt&amp;gt;Application&amp;lt;/tt&amp;gt; class. The basic idea is to call &amp;lt;tt&amp;gt;Application.Run( )&amp;lt;/tt&amp;gt; to keep your application alive indefinitely, and call &amp;lt;tt&amp;gt;Application.Exit( )&amp;lt;/tt&amp;gt; at some later point to end it. Here's how you could start the application with two visible windows:&lt;br /&gt;
&lt;br /&gt;
 Public Sub Main&lt;br /&gt;
     ' Show two forms modelessly (and at the same time).&lt;br /&gt;
     Form1.Show( )&lt;br /&gt;
     Form2.Show( )&lt;br /&gt;
     &lt;br /&gt;
     ' Keep the application going until you say otherwise.&lt;br /&gt;
     Application.Run( )&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
To specify that the application should end when either window closes, use this code in the &amp;lt;tt&amp;gt;Form.Unload&amp;lt;/tt&amp;gt; event handler of both forms:&lt;br /&gt;
&lt;br /&gt;
 Private Sub Form1_FormClosed(ByVal sender As Object, _&lt;br /&gt;
   ByVal e As FormClosedEventArgs) Handles Me.FormClosed&lt;br /&gt;
     Application.Exit( )&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...cleaning up when the application calls it quits? When your application ends you might want to release unmanaged resources, delete temporary files, or save some final settings. The &amp;lt;tt&amp;gt;Application&amp;lt;/tt&amp;gt; class provides a solution with its &amp;lt;tt&amp;gt;ApplicationExit&amp;lt;/tt&amp;gt; event. All you need to do is attach the event to a suitable event handler in the &amp;lt;tt&amp;gt;Main( )&amp;lt;/tt&amp;gt; method. Here's an example that uses a method named &amp;lt;tt&amp;gt;Shutdown( )&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The ApplicationExit Event always fires (and the code in an event handler for it always runs), even if the application has been derailed by an unhandled exception''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 AddHandler Application.ApplicationExit, AddressOf Shutdown&lt;br /&gt;
&lt;br /&gt;
And here's the &amp;lt;tt&amp;gt;Shutdown( )&amp;lt;/tt&amp;gt; method that runs automatically just before the application ends:&lt;br /&gt;
&lt;br /&gt;
 Public Sub Shutdown(ByVal sender As Object, ByVal e As EventArgs)&lt;br /&gt;
     MessageBox.Show(&amp;quot;Cleaning up.&amp;quot;)&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information, refer to the &amp;lt;tt&amp;gt;Application&amp;lt;/tt&amp;gt; class in the MSDN class library reference (it's in the &amp;lt;tt&amp;gt;System.Windows.Forms&amp;lt;/tt&amp;gt; namespace).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
This lab uses the &amp;lt;tt&amp;gt;Application&amp;lt;/tt&amp;gt; class from the &amp;lt;tt&amp;gt;System.Windows.Forms&amp;lt;/tt&amp;gt; namespace. This item is similar to, but different from, the &amp;lt;tt&amp;gt;My.Application&amp;lt;/tt&amp;gt; object. Technically, the &amp;lt;tt&amp;gt;My.Application&amp;lt;/tt&amp;gt; object is a dynamically created class (generated by Visual Studio and hidden from view), which inherits from &amp;lt;tt&amp;gt;WindowsFormsApplicationBase&amp;lt;/tt&amp;gt;. Overall, the &amp;lt;tt&amp;gt;My.Application&amp;lt;/tt&amp;gt; object usually acts as a slightly simplified version of the &amp;lt;tt&amp;gt;System.Windows.Forms.Application&amp;lt;/tt&amp;gt; class. This allows .NET to offer one class to programmers who want simplicity, and another to those who want the full set of features. In other words, .NET lets VBers have their cake and eat it too (but only by creating two different cakes).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prevent Your Application from Starting Twice ==&lt;br /&gt;
&lt;br /&gt;
Want to make sure that the user can run no more than one copy of your application on the same computer? In VB .NET 1.0, you'd need to go through the awkward task of searching all the loaded processes to make sure your program wasn't already in memory. In VB 2005, the work is done for you.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''There's no longer a need to write code to check whether your application is already running. VB 2005 will perform the check for you''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In Visual Studio, double-click the My Project item in the Solution Explorer. A tabbed window with application settings will appear. Click the Application tab, and look at the Windows Application Properties section at the bottom of the tab. Now click the &amp;quot;Make single instance application&amp;quot; checkbox and build the project.&lt;br /&gt;
&lt;br /&gt;
If you try to start the application while it's already running, it will ignore you completely, and nothing will happen.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...showing a custom error message? If you need to show an error message, check for other instances without stopping the application, or otherwise tweak the code, then you'll need to perform the check youself by using the &amp;lt;tt&amp;gt;System.Diagnostics.Process&amp;lt;/tt&amp;gt; class. Here's the code to get you started:&lt;br /&gt;
&lt;br /&gt;
 ' Get the full name of the process for the current application.&lt;br /&gt;
 Dim ModuleName, ProcessName As String&lt;br /&gt;
 ModuleName = Process.GetCurrentProcess.MainModule.ModuleName&lt;br /&gt;
 ProcessName = System.IO.Path.GetFileNameWithoutExtension(ModuleName)&lt;br /&gt;
     &lt;br /&gt;
 ' Check for other processes with this name.&lt;br /&gt;
 Dim Proc( ) As System.Diagnostics.Process&lt;br /&gt;
 Proc = Process.GetProcessesByName(ProcessName)&lt;br /&gt;
 If Proc.Length &amp;gt; 1 Then&lt;br /&gt;
     ' (There is another instance running.)&lt;br /&gt;
 Else&lt;br /&gt;
     ' (There are no other instances running.)&lt;br /&gt;
 End If&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information, look up the &amp;quot;ProcessInfo class&amp;quot; index entry in the MSDN help, or look up &amp;quot;Process class sample&amp;quot; index entry for a full-fledged example.&lt;br /&gt;
&lt;br /&gt;
== Communicate Between Forms ==&lt;br /&gt;
&lt;br /&gt;
In previous versions of .NET, you were responsible for tracking every open form. If you didn't, you might unwittingly strand a window, leaving it open but cut off from the rest of your application. VB 2005 restores the beloved approach of VB 6 developers, where there's always a ''default instance'' of your form ready, waiting, and accessible from anywhere else in your application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''VB 2005 makes it easy for forms to interact, thanks to the new default instances. This feature is a real timesaver—and a potential stumbling block''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
To access the default instance of a form, just use its class name. In other words, if you've created a form that's named (unimaginatively) &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt;, you can show its default instance like this:&lt;br /&gt;
&lt;br /&gt;
 Form1.Show( )&lt;br /&gt;
&lt;br /&gt;
This automatically creates an instance of &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt; and then displays it. This instance of &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt; is designated as the default instance.&lt;br /&gt;
&lt;br /&gt;
To communicate between forms, you simply add dedicated public methods. For example, if &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt; needs to be able to refresh &amp;lt;tt&amp;gt;Form2&amp;lt;/tt&amp;gt;, you could add a &amp;lt;tt&amp;gt;RefreshData( )&amp;lt;/tt&amp;gt; method to &amp;lt;tt&amp;gt;Form2&amp;lt;/tt&amp;gt;, like this:&lt;br /&gt;
&lt;br /&gt;
 Public Class Form2&lt;br /&gt;
     Private Sub RefreshData( )&lt;br /&gt;
         MessageBox.Show(&amp;quot;I've been refreshed!&amp;quot;)&lt;br /&gt;
     End Sub&lt;br /&gt;
 End Class&lt;br /&gt;
&lt;br /&gt;
You could then call it like this:&lt;br /&gt;
&lt;br /&gt;
 Form2.RefreshData( )&lt;br /&gt;
&lt;br /&gt;
This calls the &amp;lt;tt&amp;gt;RefreshData( )&amp;lt;/tt&amp;gt; method of the default instance of &amp;lt;tt&amp;gt;Form2&amp;lt;/tt&amp;gt;. The fact that &amp;lt;tt&amp;gt;RefreshData( )&amp;lt;/tt&amp;gt; is a method you added (not an inherited method, like the &amp;lt;tt&amp;gt;Show( )&amp;lt;/tt&amp;gt; method) makes no difference in how you use it.&lt;br /&gt;
&lt;br /&gt;
You can also get at the forms using the &amp;lt;tt&amp;gt;My&amp;lt;/tt&amp;gt; collection. For example, the code above is equivalent to this slightly longer statement:&lt;br /&gt;
&lt;br /&gt;
 My.Forms.Form2.RefreshData( )&lt;br /&gt;
&lt;br /&gt;
You can always access the default instance of a form, even if it isn't currently visible. In fact, .NET creates the default instance of the form as soon as you access one of its properties or methods. If you only want to find out what forms are currently open, you're better off using the &amp;lt;tt&amp;gt;My.Application.OpenForms&amp;lt;/tt&amp;gt; collection. Here's an example that iterates through the collection and displays the caption of each form:&lt;br /&gt;
&lt;br /&gt;
 For Each frm As Form In My.Application.OpenForms&lt;br /&gt;
     MessageBox.Show(frm.Text)&lt;br /&gt;
 Next&lt;br /&gt;
&lt;br /&gt;
This handy trick just wasn't possible in earlier versions of .NET without writing your own code to manually track forms.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...potential problems? Conveniences such as default instances come at a price. In this case, you don't need to worry about wasted memory or any performance slowdown, since .NET is clever enough to create the forms as you need them. The real problem that you might face results from the fact that default instances confuse the concepts of classes and objects, making it all too easy to accidentally refer to different instances of the same form in different parts of your application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''You can also get a reference to the application's startup form using the My.Application.StartupForm property''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, imagine you use this code to show a form:&lt;br /&gt;
&lt;br /&gt;
 Dim FormObject As New Form1&lt;br /&gt;
 FormObject.Show( )&lt;br /&gt;
&lt;br /&gt;
In this example, the form you've shown is an instance of &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt;, but it isn't the default instance. That means that if another part of your code uses code like this:&lt;br /&gt;
&lt;br /&gt;
 Form1.Refresh( )&lt;br /&gt;
&lt;br /&gt;
it won't have the effect you expect. The visible instance of &amp;lt;tt&amp;gt;Form1&amp;lt;/tt&amp;gt; won't be refreshed. Instead, the default instance (which probably isn't even visible) will be refreshed. Watch out for this problem—it can lead to exasperating headaches! (In all fairness to .NET, this isn't a new problem. Visual Basic 6 developers encountered the same headaches when creating forms dynamically. The difference is that Visual Basic 6 developers almost always rely on default instances, while .NET developers—until now—haven't.)&lt;br /&gt;
&lt;br /&gt;
== Improve Redraw Speeds for GDI+ ==&lt;br /&gt;
&lt;br /&gt;
Every control and form in .NET inherits from the base &amp;lt;tt&amp;gt;Control&amp;lt;/tt&amp;gt; class. In .NET 2.0, the &amp;lt;tt&amp;gt;Control&amp;lt;/tt&amp;gt; class sports a new property named &amp;lt;tt&amp;gt;DoubleBuffered&amp;lt;/tt&amp;gt;. If you set this property to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;, the form or control will automatically use double-buffering, which dramatically reduces flicker when you add custom drawing code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to turbocharge your GDI+ animations? In . NET 2.0, the Form class can do the double-buffering for you''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In some applications you need to repaint a window or control frequently. For example, you might refresh a window every 10 milliseconds to create the illusion of a continuous animation. Every time the window is refreshed, you need to erase the current contents and draw the new frame from scratch.&lt;br /&gt;
&lt;br /&gt;
In a simple application, your drawing logic might draw a single shape. In a more complex animation, you could easily end up rendering dozens of different graphical elements at a time. Rendering these elements takes a small but significant amount of time. The problem is that if you paint each graphical element directly on the form, the animation will flicker as the image is repeatedly erased and reconstructed. To avoid this annoying problem, developers commonly use a technique known as ''double-buffering''. With double-buffering, each new frame is fully assembled in memory, and only painted on the form when it's complete.&lt;br /&gt;
&lt;br /&gt;
.NET 2.0 completely saves you the hassle of double-buffering. All you need to do is set the &amp;lt;tt&amp;gt;DoubleBuffered&amp;lt;/tt&amp;gt; property of the form or control to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;. For example, imagine you create a form and handle the &amp;lt;tt&amp;gt;Paint&amp;lt;/tt&amp;gt; event to supply your own custom painting logic. If the form is set to use double-buffering, it won't be refreshed until the &amp;lt;tt&amp;gt;Paint&amp;lt;/tt&amp;gt; event handler has finished, at which point it will copy the completed image directly onto the form. If &amp;lt;tt&amp;gt;DoubleBuffered&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt;, every time you draw an individual element onto the form in the &amp;lt;tt&amp;gt;Paint&amp;lt;/tt&amp;gt; event handler, the form will be refreshed. As a result, the form will be refreshed dozens of times for anything but the simplest operations.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-5|Example 3-5]] features a form that makes use of custom drawing logic. When the user clicks the &amp;lt;tt&amp;gt;cmdStart&amp;lt;/tt&amp;gt; button, a timer is switched on. This timer fires every few milliseconds and invalidates the form by calling its &amp;lt;tt&amp;gt;Invalidate( )&amp;lt;/tt&amp;gt; method. In response, Windows asks the application to repaint the window, triggering the &amp;lt;tt&amp;gt;OnPaint( )&amp;lt;/tt&amp;gt; method with the custom drawing code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-EX-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 3-5. An animated form'''&lt;br /&gt;
&lt;br /&gt;
 Public Class AnimationForm&lt;br /&gt;
     &lt;br /&gt;
     ' Indicates whether the animation is currently being shown.&lt;br /&gt;
     Private IsAnimating As Boolean = False&lt;br /&gt;
     &lt;br /&gt;
     ' Track how long the animation has been going on.&lt;br /&gt;
     Private StartTime As DateTime&lt;br /&gt;
     &lt;br /&gt;
     Private Sub Form_Paint(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint&lt;br /&gt;
     &lt;br /&gt;
         ' Check if the animation is in progress.&lt;br /&gt;
         If IsAnimating Then&lt;br /&gt;
     &lt;br /&gt;
             ' Get reading to draw the current frame.&lt;br /&gt;
             Dim g As Graphics = e.Graphics&lt;br /&gt;
             g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality&lt;br /&gt;
     &lt;br /&gt;
             ' Paint the background.&lt;br /&gt;
             Dim BackBrush As New LinearGradientBrush( _&lt;br /&gt;
               New Point(0, 0), New Point(100, 100), _&lt;br /&gt;
               Color.Blue, Color.LightBlue)&lt;br /&gt;
             g.FillRectangle(BackBrush, New Rectangle(New Point(0, 0), _&lt;br /&gt;
               Me.ClientSize))&lt;br /&gt;
             g.FillRectangle(Brushes.LightPink, New Rectangle(New Point(10, 10), _&lt;br /&gt;
               New Point(Me.Width - 30, Me.Height - 50)))&lt;br /&gt;
     &lt;br /&gt;
             ' Calculate elapsed time.&lt;br /&gt;
             Dim Elapsed As Double = DateTime.Now.Subtract(StartTime).TotalSeconds&lt;br /&gt;
     &lt;br /&gt;
             Dim Pos As Double = (-100 + 24 * Elapsed ^ 2) / 10&lt;br /&gt;
     &lt;br /&gt;
             ' Draw some moving objects.&lt;br /&gt;
             Dim Pen As New Pen(Color.Blue, 10)&lt;br /&gt;
             Dim Brush As Brush = Brushes.Chartreuse&lt;br /&gt;
             g.DrawEllipse(Pen, CInt(Elapsed * 100), CInt(Pos), 10, 10)&lt;br /&gt;
             g.FillEllipse(Brush, CInt(Elapsed * 100), CInt(Pos), 10, 10)&lt;br /&gt;
     &lt;br /&gt;
             g.DrawEllipse(Pen, CInt(Elapsed * 50), CInt(Pos), 10, 10)&lt;br /&gt;
             g.FillEllipse(Brush, CInt(Elapsed * 50), CInt(Pos), 10, 10)&lt;br /&gt;
     &lt;br /&gt;
             g.DrawEllipse(Pen, CInt(Elapsed * 76), CInt(Pos) * 2, 10, 10)&lt;br /&gt;
             g.FillEllipse(Brush, CInt(Elapsed * 55), CInt(Pos) * 3, 10, 10)&lt;br /&gt;
     &lt;br /&gt;
             g.DrawEllipse(Pen, CInt(Elapsed * 66), CInt(Pos) * 4, 10, 10)&lt;br /&gt;
             g.FillEllipse(Brush, CInt(Elapsed * 72), CInt(Pos) * 3, 10, 10)&lt;br /&gt;
     &lt;br /&gt;
             If Elapsed &amp;gt; 10 Then&lt;br /&gt;
                 ' Stop the animation.&lt;br /&gt;
                 tmrInvalidate.Stop( )&lt;br /&gt;
                 IsAnimating = False&lt;br /&gt;
             End If&lt;br /&gt;
     &lt;br /&gt;
         Else&lt;br /&gt;
             ' There is no animation underway. Paint the background.&lt;br /&gt;
             MyBase.OnPaintBackground(e)&lt;br /&gt;
         End If&lt;br /&gt;
     &lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
     Private Sub tmrInvalidate_Tick(ByVal sender As System.Object, _&lt;br /&gt;
       ByVal e As System.EventArgs) Handles tmrInvalidate.Tick&lt;br /&gt;
         ' Invalidate the form, which will trigger a refresh.&lt;br /&gt;
         Me.Invalidate( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub cmdStart_Click(ByVal sender As System.Object, _&lt;br /&gt;
       ByVal e As System.EventArgs) Handles cmdStart.Click&lt;br /&gt;
         ' Start the timer, which will trigger the repainting process&lt;br /&gt;
         ' at regular intervals.&lt;br /&gt;
         Me.DoubleBuffered = True&lt;br /&gt;
         IsAnimating = True&lt;br /&gt;
         StartTime = DateTime.Now&lt;br /&gt;
         tmrInvalidate.Start( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     ' Ensure that the form background is not repainted automatically&lt;br /&gt;
     ' when the form is invalidated. This isn't necessary, because the&lt;br /&gt;
     ' Paint event will handle the painting for the form.&lt;br /&gt;
     ' If you don't override this method, every time the form is painted&lt;br /&gt;
     ' the window will be cleared and the background color will be painted&lt;br /&gt;
     ' on the surface, which causes extra flicker.&lt;br /&gt;
     Protected Overrides Sub OnPaintBackground( _&lt;br /&gt;
       ByVal pevent As System.Windows.Forms.PaintEventArgs)&lt;br /&gt;
         ' Do nothing.&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Class&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Try running this example with and without double-buffering. You'll see a dramatic difference in the amount of flicker.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...owner-drawn controls? Double-buffering works exactly the same way with owner-drawn controls as with forms, because both the &amp;lt;tt&amp;gt;Form&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;Control&amp;lt;/tt&amp;gt; classes provide the &amp;lt;tt&amp;gt;DoubleBuffered&amp;lt;/tt&amp;gt; property and the &amp;lt;tt&amp;gt;Paint&amp;lt;/tt&amp;gt; event. Of course, there's no point in double-buffering both a form and its controls, since that will only cause your application to consume unnecessary extra memory.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
Overall, the GDI+ drawing functions remain essentially the same in .NET 2.0. To learn more about drawing with GDI+, look up &amp;quot;GDI+ → Examples&amp;quot; in the index of the MSDN help library. You may also be interested in the &amp;quot;GDI+ Images&amp;quot; and &amp;quot;GDI+ Text&amp;quot; entries.&lt;br /&gt;
&lt;br /&gt;
== Handle Asynchronous Tasks Safely ==&lt;br /&gt;
&lt;br /&gt;
One of .NET's most impressive features is its extensive support for multithreaded programming. However, as most programmers discover at some point in their lives, multithreaded programming isn't necessarily easy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to conduct a time-consuming task in the background without dealing with threading issues? The new BackgroundWorker class makes it easy''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
One of the main challenges with Windows applications is that it's not safe to modify a form or control from a background thread, which means that after your background task is finished, there's no straightforward way to update your application's interface. You can use the &amp;lt;tt&amp;gt;Control.Invoke( )&amp;lt;/tt&amp;gt; method to marshal a method to the correct thread, but other problems then appear, such as transferring the information you need to make the update. Fortunately, all of these headaches can be avoided thanks to the new &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; component gives you a foolproof way to run a time-consuming task on a separate, dedicated thread. This ensures that your application interface remains responsive, and it allows your code to carry out other tasks in the foreground. Best of all, the underlying complexities of multithreaded programming are hidden. Once the background process is complete, you simply handle an event, which fires on the main thread. In addition, the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; supports progress reporting and canceling.&lt;br /&gt;
&lt;br /&gt;
You can either create a &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; object programmatically, or you can drag it onto a form from the Components tab of the toolbox. To start your background operation, you call the &amp;lt;tt&amp;gt;RunWorkerAsync( )&amp;lt;/tt&amp;gt; method. If you need to pass an input value to this process, you can supply it as an argument to this method (any type of object is allowed):&lt;br /&gt;
&lt;br /&gt;
 Worker.RunWorkerAsync(inputValue)&lt;br /&gt;
&lt;br /&gt;
Next, you need to handle the &amp;lt;tt&amp;gt;DoWork&amp;lt;/tt&amp;gt; event to perform the background task. The &amp;lt;tt&amp;gt;DoWork&amp;lt;/tt&amp;gt; event fires on the background thread, which means at this point you can't interact with any other part of your application (unless you're willing to use locks or other techniques to safeguard access). Typically, the &amp;lt;tt&amp;gt;DoWork&amp;lt;/tt&amp;gt; event handler retrieves the input value from the &amp;lt;tt&amp;gt;DoWorkEventArgs.Argument&amp;lt;/tt&amp;gt; property and then carries out the time-consuming operation. Once the operation is complete, you simply set the &amp;lt;tt&amp;gt;DoWorkEventArgs.Result&amp;lt;/tt&amp;gt; property with the result. You can use any data type or even a custom object. Here's the basic pattern you'll use:&lt;br /&gt;
&lt;br /&gt;
 Private Sub backgroundWorker1_DoWork(ByVal sender As Object, _&lt;br /&gt;
   ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork&lt;br /&gt;
     &lt;br /&gt;
     ' Get the information that was supplied.&lt;br /&gt;
     Dim Input As Integer = CType(e.Argument, Integer)&lt;br /&gt;
     &lt;br /&gt;
     ' (Perform some time consuming task.)&lt;br /&gt;
     &lt;br /&gt;
     ' Return the result.&lt;br /&gt;
     e.Result = Answer&lt;br /&gt;
     &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
Finally, the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; fires a &amp;lt;tt&amp;gt;RunWorkerCompleted&amp;lt;/tt&amp;gt; event to notify your application that the process is complete. At this point, you can retrieve the result from &amp;lt;tt&amp;gt;RunWorkerCompletedEventArgs&amp;lt;/tt&amp;gt; and update the form accordingly:&lt;br /&gt;
&lt;br /&gt;
 Private Sub backgroundWorker1_RunWorkerCompleted( _&lt;br /&gt;
   ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _&lt;br /&gt;
   Handles backgroundWorker1.RunWorkerCompleted&lt;br /&gt;
     &lt;br /&gt;
     result.Text = &amp;quot;Result is: &amp;quot; &amp;amp; e.Result.ToString( )&lt;br /&gt;
     &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-EX-6|Example 3-6]] shows a form that puts all of these parts together. It performs a time-limited loop for a number of seconds that you specify. This example also demonstrates two more advanced techniques: cancellation and progress. To cancel the operation, you simply need to call the &amp;lt;tt&amp;gt;BackgroundWorker.CancelAsync( )&amp;lt;/tt&amp;gt; method. Your &amp;lt;tt&amp;gt;DoWork&amp;lt;/tt&amp;gt; event-handling code can then check to see if the main form is attempting to cancel the operation and exit gracefully. To maintain progress information, your &amp;lt;tt&amp;gt;DoWork&amp;lt;/tt&amp;gt; event-handling code needs to call the &amp;lt;tt&amp;gt;BackgroundWorker.ReportProgress( )&amp;lt;/tt&amp;gt; method and provide an estimated percent complete (where 0% means &amp;quot;just started&amp;quot; and 100% means &amp;quot;completely finished&amp;quot;). The form code can respond to the &amp;lt;tt&amp;gt;ProgressChanged&amp;lt;/tt&amp;gt; event to read the new progress percentage and update another control, such as a &amp;lt;tt&amp;gt;ProgressBar&amp;lt;/tt&amp;gt;. [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-12|Figure 3-12]] shows this application in action.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-EX-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 3-6. An asynchronous form with the BackgroundWorker'''&lt;br /&gt;
&lt;br /&gt;
 Public Class AsyncForm&lt;br /&gt;
     &lt;br /&gt;
     Private Sub startAsyncButton_Click(ByVal sender As System.Object, _&lt;br /&gt;
       ByVal e As System.EventArgs) Handles startAsyncButton.Click&lt;br /&gt;
     &lt;br /&gt;
         ' Disable the Start button until &lt;br /&gt;
         ' the asynchronous operation is done.&lt;br /&gt;
         startAsyncButton.Enabled = False&lt;br /&gt;
     &lt;br /&gt;
         ' Enable the Cancel button while &lt;br /&gt;
         ' the asynchronous operation runs.&lt;br /&gt;
         cancelAsyncButton.Enabled = True&lt;br /&gt;
     &lt;br /&gt;
         ' Start the asynchronous operation.&lt;br /&gt;
         backgroundWorker1.RunWorkerAsync(Int32.Parse(txtWaitTime.Text))&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     ' This event handler is where the actual work is done.&lt;br /&gt;
     Private Sub backgroundWorker1_DoWork(ByVal sender As Object, _&lt;br /&gt;
       ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork&lt;br /&gt;
     &lt;br /&gt;
         ' Get the information that was supplied.&lt;br /&gt;
         Dim Worker As BackgroundWorker = CType(sender, BackgroundWorker)&lt;br /&gt;
     &lt;br /&gt;
         Dim StartTime As DateTime = DateTime.Now&lt;br /&gt;
         Dim SecondsToWait As Integer = CType(e.Argument, Integer)&lt;br /&gt;
         Dim Answer As Single = 100&lt;br /&gt;
         Do&lt;br /&gt;
             ' Check for any cancellation requests.&lt;br /&gt;
             If Worker.CancellationPending Then&lt;br /&gt;
                 e.Cancel = True&lt;br /&gt;
                 Return&lt;br /&gt;
             End If&lt;br /&gt;
     &lt;br /&gt;
             ' Continue calculating the answer.&lt;br /&gt;
             Answer *= 1.01&lt;br /&gt;
     &lt;br /&gt;
             ' Report the current progress (percentage complete).&lt;br /&gt;
             Worker.ReportProgress(( _&lt;br /&gt;
               DateTime.Now.Subtract(StartTime).TotalSeconds / SecondsToWait) * 100)&lt;br /&gt;
     &lt;br /&gt;
             Thread.Sleep(50)&lt;br /&gt;
         Loop Until DateTime.Now &amp;gt; (StartTime.AddSeconds(SecondsToWait))&lt;br /&gt;
     &lt;br /&gt;
         e.Result = Answer&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     ' This event handler fires when the background work&lt;br /&gt;
     ' is complete.&lt;br /&gt;
     Private Sub backgroundWorker1_RunWorkerCompleted( _&lt;br /&gt;
       ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _&lt;br /&gt;
       Handles backgroundWorker1.RunWorkerCompleted&lt;br /&gt;
     &lt;br /&gt;
         ' Check what the result was, and update the form.&lt;br /&gt;
         If Not (e.Error Is Nothing) Then&lt;br /&gt;
             ' An exception was thrown.&lt;br /&gt;
             MessageBox.Show(e.Error.Message)&lt;br /&gt;
         ElseIf e.Cancelled Then&lt;br /&gt;
             ' Check if the user cancelled the operation.&lt;br /&gt;
             result.Text = &amp;quot;Cancelled&amp;quot;&lt;br /&gt;
         Else&lt;br /&gt;
             ' The operation succeeded.&lt;br /&gt;
             result.Text = &amp;quot;Result is: &amp;quot; &amp;amp; e.Result.ToString( )&lt;br /&gt;
         End If&lt;br /&gt;
     &lt;br /&gt;
         startAsyncButton.Enabled = True&lt;br /&gt;
         cancelAsyncButton.Enabled = False&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     ' This event handler updates the progress bar.&lt;br /&gt;
     Private Sub backgroundWorker1_ProgressChanged( _&lt;br /&gt;
       ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _&lt;br /&gt;
       Handles backgroundWorker1.ProgressChanged&lt;br /&gt;
     &lt;br /&gt;
         Me.progressBar1.Value = e.ProgressPercentage&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub cancelAsyncButton_Click( _&lt;br /&gt;
       ByVal sender As System.Object, ByVal e As System.EventArgs) _&lt;br /&gt;
       Handles cancelAsyncButton.Click&lt;br /&gt;
     &lt;br /&gt;
         ' Cancel the asynchronous operation.&lt;br /&gt;
         Me.backgroundWorker1.CancelAsync( )&lt;br /&gt;
     &lt;br /&gt;
         cancelAsyncButton.Enabled = False&lt;br /&gt;
     End Sub &lt;br /&gt;
     &lt;br /&gt;
 End Class&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-12&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-12. Monitoring a background task'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt104.png|Monitoring a background task]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...other scenarios where you can use the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt;? This example used the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; with a long-running background calculation. Other situations in which the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; proves to be just as indispensable include:&lt;br /&gt;
&lt;br /&gt;
* Contacting a web service&lt;br /&gt;
* Downloading a file over the Internet&lt;br /&gt;
* Retrieving data from a database&lt;br /&gt;
* Reading or writing large amounts of data&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
The MSDN reference includes a detailed walkthrough for using the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt;, and other topics that tackle multithreaded programming in detail. Look up the &amp;quot;background operations&amp;quot; index entry to see a slightly different approach that uses the &amp;lt;tt&amp;gt;BackgroundWorker&amp;lt;/tt&amp;gt; to calculate Fibonacci numbers.&lt;br /&gt;
&lt;br /&gt;
== Use a Better Data-Bound Grid ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt; that shipped with .NET 1.0 and 1.1 had a slew of limitations. It was difficult to customize, nearly impossible to extend, and had no support for important features like modifying or filling the &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt; programmatically, accessing individual cells, or applying per-cell formatting. In many cases, VB developers avoided the &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt; altogether and used third-party grids or even older COM-based controls like the &amp;lt;tt&amp;gt;MSFlexGrid&amp;lt;/tt&amp;gt;. (In fact, third-party component developers regularly thanked Microsoft for making enhanced grid components an easy sell.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''. NET's DataGrid was a significant disappointment in an otherwise state-of-the-art framework. Now the Windows Forms team fills in the gaps with a first-rate grid''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In designing .NET 2.0, the Windows Forms team decided it would be nearly impossible to remedy the shortcomings without breaking backward compatibility. So, they chose to introduce an entirely new &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; control with support for all the missing features and more.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
You can bind the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; to a &amp;lt;tt&amp;gt;DataTable&amp;lt;/tt&amp;gt; object in the same way that you would bind a &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt;. Here's the bare minimum code you might use to bind a table named Customers:&lt;br /&gt;
&lt;br /&gt;
 DataGridView1.DataSource = ds&lt;br /&gt;
 DataGridView.DataMember = &amp;quot;Customers&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Of course, to put this code to work, you need to create the &amp;lt;tt&amp;gt;DataSet&amp;lt;/tt&amp;gt; object &amp;lt;tt&amp;gt;ds&amp;lt;/tt&amp;gt; and fill it with information. For a complete example that adds the necessary ADO.NET code for this step, refer to the downloadable content for this chapter.&lt;br /&gt;
&lt;br /&gt;
When you use this code, the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; creates one column for each field in the data source, and titles it using the field name. The grid also has a significant amount of out-of-the-box functionality. Some of the characteristics you'll notice include:&lt;br /&gt;
&lt;br /&gt;
* The column headers are frozen. That means they won't disappear as you scroll down the list.&lt;br /&gt;
* You can edit values. Just double-click a cell or press F2 to put it in edit mode. (You can disable this feature by setting the &amp;lt;tt&amp;gt;DataColumn.ReadOnly&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; in the underlying &amp;lt;tt&amp;gt;DataTable&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* You can sort columns. Just click the column header once or twice.&lt;br /&gt;
* You can automatically size columns. Just double-click on the column divider between headers to expand a column (the one on the left) to fit the current content.&lt;br /&gt;
* You can select a range of cells. You can highlight one or more cells, or multiple rows, by clicking and dragging. To select the entire table, click the square at the top-left corner.&lt;br /&gt;
* You can add rows by scrolling to the end of the grid and entering new values. To disable this feature, set the &amp;lt;tt&amp;gt;AllowUserToAddRows&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* You can delete rows by selecting the full row (click the row button at the left) and pressing the Delete key. To disable this feature, set the &amp;lt;tt&amp;gt;AllowUserToDeleteRows&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Before going any further with the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt;, there are two methods you'll want to consider using right away: &amp;lt;tt&amp;gt;AutoResizeColumns( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;AutoResizeRows( )&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;AutoResizeColumns( )&amp;lt;/tt&amp;gt; extends all columns to fit header text and cell data. &amp;lt;tt&amp;gt;AutoResizeRows( )&amp;lt;/tt&amp;gt; enlarges the row with multiple lines to fit header text and cell data (the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; supports automatic wrapping). Both of these methods accept a value from an enumeration that allows you to specify additional options (such as extending the column just to fit all the columns, or just the header text):&lt;br /&gt;
&lt;br /&gt;
 ' Create wider columns to fit data.&lt;br /&gt;
 DataGridView1.AutoResizeColumns( _&lt;br /&gt;
   DataGridViewAutoSizeColumnsMode.AllCells)&lt;br /&gt;
     &lt;br /&gt;
 ' Create multi-line columns to fit data.&lt;br /&gt;
 DataGridView1.AutoResizeRows( _&lt;br /&gt;
  DataGridViewAutoSizeRowsMode.HeaderAndColumnsAllCells)&lt;br /&gt;
&lt;br /&gt;
You can also use the &amp;lt;tt&amp;gt;AutoResizeColumn( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;AutoResizeRow( )&amp;lt;/tt&amp;gt; methods to change just a single column or row (specified as an index number).&lt;br /&gt;
&lt;br /&gt;
Once you have created a &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; and populated it with data, you can interact with it through two useful collections: &amp;lt;tt&amp;gt;Columns&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;Rows&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;Columns&amp;lt;/tt&amp;gt; collection exposes a collection of &amp;lt;tt&amp;gt;DataGridViewCell&amp;lt;/tt&amp;gt; objects, one for each column in the grid. You can set the order in which columns are displayed (by setting an index number in the &amp;lt;tt&amp;gt;DisplayIndex&amp;lt;/tt&amp;gt; property), hide a column altogether (set &amp;lt;tt&amp;gt;Visible&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;), or freeze a column so that it always remains visible even as the user scrolls to the side (set &amp;lt;tt&amp;gt;Frozen&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;). You can also modify the column header text (&amp;lt;tt&amp;gt;HeaderText&amp;lt;/tt&amp;gt;), the size (&amp;lt;tt&amp;gt;Width&amp;lt;/tt&amp;gt;), and make it non-editable (&amp;lt;tt&amp;gt;ReadOnly&amp;lt;/tt&amp;gt;). To look up a column, use the index number or the corresponding field name.&lt;br /&gt;
&lt;br /&gt;
For example, here's the code you need to change some column properties in the OrderID column of a bound &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 ' Keep this column visible on the left at all times.&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).Frozen = True&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).DisplayIndex = 0&lt;br /&gt;
     &lt;br /&gt;
 ' Configure the column appearance.&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).HeaderText = &amp;quot;ID&amp;quot;&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).Resizable = DataGridViewTriState.True&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).MinimumWidth = 50&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).Width = 50&lt;br /&gt;
     &lt;br /&gt;
 ' Don't allow the values in this column to be edited.&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).ReadOnly = True&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Rows&amp;lt;/tt&amp;gt; collection allows you to access individual &amp;lt;tt&amp;gt;DataGridViewRow&amp;lt;/tt&amp;gt; objects by index number. Once you have a &amp;lt;tt&amp;gt;DataGridViewRow&amp;lt;/tt&amp;gt;, you can examine its &amp;lt;tt&amp;gt;Cells&amp;lt;/tt&amp;gt; collection to look up individual values in that row.&lt;br /&gt;
&lt;br /&gt;
However, it's more likely that you'll want to access just those rows that correspond to the current user selection. The &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; actually provides three related properties that can help you:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;SelectedRows&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Provides a collection with one &amp;lt;tt&amp;gt;DataGridViewRow&amp;lt;/tt&amp;gt; for each fully selected row. This makes sense if the &amp;lt;tt&amp;gt;SelectionMode&amp;lt;/tt&amp;gt; only allows full row selection.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SelectedColumns&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Provides a collection with one &amp;lt;tt&amp;gt;DataGridViewColumn&amp;lt;/tt&amp;gt; for each fully selected column. This makes sense if the &amp;lt;tt&amp;gt;SelectionMode&amp;lt;/tt&amp;gt; only allows full column selection.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SelectedCells&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Always provides a collection with one &amp;lt;tt&amp;gt;DataGridViewCell&amp;lt;/tt&amp;gt; for each selected cell, regardless of the selection mode. You can use this property if your selection mode allows individual cell selection or if you just want to process each cell separately.&lt;br /&gt;
&lt;br /&gt;
For example, if you're using &amp;lt;tt&amp;gt;DataGridViewSelectionMode.FullRowSelect&amp;lt;/tt&amp;gt;, you can use the following code to retrieve the current selection and display a specific field from each selected row when the user clicks a button:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''You can control the type of selection that's allowed by setting the DataGridView.SelectionMode property. Different values allow selection for individual cells, rows, or columns. DataGridView.MultiSelect determines whether more than one item can be selected at a time''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Private Sub cmdSelection_Click(ByVal sender As System.Object, _&lt;br /&gt;
   ByVal e As System.EventArgs) Handles cmdSelection.Click&lt;br /&gt;
     &lt;br /&gt;
     For Each SelectedRow As DataGridViewRow In DataGridView1.SelectedRows&lt;br /&gt;
         MessageBox.Show(SelectedRow.Cells(&amp;quot;CustomerID&amp;quot;).Value)&lt;br /&gt;
     Next&lt;br /&gt;
     &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
For a full example that puts all of these ingredients together, refer to the BetterDataGrid example in the downloadable samples.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...doing more with the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt;? The features described so far provide a snapshot of &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; basics, but they only scratch the surface of its customizability features. For more information, refer to the following two labs in this chapter ([[Visual Basic 2005: A Developer's Notebook/Windows Applications#Format the DataGridView|Section 3.17]] and [[Visual Basic 2005: A Developer's Notebook/Windows Applications#Add Images and Controls to the DataGridView|Section 3.18]]).&lt;br /&gt;
&lt;br /&gt;
== Format the DataGridView ==&lt;br /&gt;
&lt;br /&gt;
Formatting the .NET 1.x &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt; ranges from awkward to nearly impossible. However, thanks to its multi-layered model, formatting the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; is far easier. This model builds on a single class, the &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt;, which encapsulates key formatting properties. You can assign different &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; objects to separate rows, columns, or even distinct cells.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''By using a few simple style properties, you can configure the appearance of the entire grid, individual columns, or rows with important data''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; already looks better than the &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt; in its default state. For example, you'll notice that the column headers have a modern, flat look and become highlighted when the user moves the mouse over them. However, there's much more you can do with the help of the &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; class.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; collects all the formatting properties of the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt;. It defines appearance-related settings (e.g., color, font), and data formatting (e.g., currency, date formats). All in all, the &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; provides the following key properties:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;Alignment&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Sets how text is justified inside the cell.&lt;br /&gt;
;&amp;lt;tt&amp;gt;BackColor&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ForeColor&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Set the color of the cell background and the color of the cell text.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Font&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Sets the font used for the cell text.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Format&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A format string that configures how numeric or date data values will be formatted as strings. You can use the standard .NET format specifiers and your own custom format strings. For example, &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; designates a currency value. (For more information, look up the index entry &amp;quot;numeric format strings&amp;quot; in the MSDN help.)&lt;br /&gt;
;&amp;lt;tt&amp;gt;NullText&amp;lt;/tt&amp;gt;&lt;br /&gt;
: A string of text that will be substituted for any null (missing) values.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SelectionBackColor&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;SelectionForeColor&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Set the cell background colors and text colors for selected cells.&lt;br /&gt;
;&amp;lt;tt&amp;gt;WrapMode&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Determines if text will flow over multiple lines (if the row is high enough to accommodate it) or if it will be truncated. By default, cells will wrap.&lt;br /&gt;
&lt;br /&gt;
The interesting part is that you can create and set &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; objects at different levels. When the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; displays a cell, it looks for style information in several places. Here's the order from highest to lowest importance:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;tt&amp;gt;DataGridViewCell.Style&amp;lt;/tt&amp;gt;&lt;br /&gt;
# &amp;lt;tt&amp;gt;DataGridViewRow.DefaultCellStyle&amp;lt;/tt&amp;gt;&lt;br /&gt;
# &amp;lt;tt&amp;gt;DataGridView.AlternatingRowsDefaultCellStyle&amp;lt;/tt&amp;gt;&lt;br /&gt;
# &amp;lt;tt&amp;gt;DataGridView.RowsDefaultCellStyle&amp;lt;/tt&amp;gt;&lt;br /&gt;
# &amp;lt;tt&amp;gt;DataGridViewColumn.DefaultCellStyle&amp;lt;/tt&amp;gt;&lt;br /&gt;
# &amp;lt;tt&amp;gt;DataGridView.DefaultCellStyle&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, if &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; finds a &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; object assigned to the current cell (option 1), it always uses it. If not, it checks the &amp;lt;tt&amp;gt;DataGridViewCellStyle&amp;lt;/tt&amp;gt; for the row, and so on.&lt;br /&gt;
&lt;br /&gt;
The following code snippet performs column-specific formatting. It ensures that all the values in the CustomerID column are given a different font, alignment, and set of colors. [[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-13|Figure 3-13]] shows the result.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''If you use the design-time data-binding features of Visual Studio, you can avoid writing this code altogether. Just click the Edit Columns link in the Properties Window and use the designer to choose the formatting''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Dim Style As DataGridViewCellStyle = _&lt;br /&gt;
   DataGridView1.Columns(&amp;quot;CustomerID&amp;quot;).DefaultCellStyle&lt;br /&gt;
 Style.Font = New Font(DataGridView1.Font, FontStyle.Bold)&lt;br /&gt;
 Style.Alignment = DataGridViewContentAlignment.MiddleRight&lt;br /&gt;
 Style.BackColor = Color.LightYellow&lt;br /&gt;
 Style.ForeColor = Color.DarkRed&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-13&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-13. A DataGridView with a formatted column'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt110.png|A DataGridView with a formatted column]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the easiest way to apply custom cell formatting? Sometimes, you want to call attention to cells with certain values. You could handle this task by iterating over the entire grid, looking for those cells that interest you. However, you can save time by responding to the &amp;lt;tt&amp;gt;DataGridView.CellFormatting&amp;lt;/tt&amp;gt; event. This event occurs as the grid is being filled. It gives you the chance to inspect the cell and change its style before it appears.&lt;br /&gt;
&lt;br /&gt;
Here's an example that formats a cell to highlight high prices:&lt;br /&gt;
&lt;br /&gt;
 Private Sub DataGridView1_CellFormatting(ByVal sender As System.Object, _&lt;br /&gt;
   ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs) _&lt;br /&gt;
   Handles DataGridView1.CellFormatting&lt;br /&gt;
     &lt;br /&gt;
     ' Check if this is the right column.&lt;br /&gt;
     If DataGridView1.Columns(e.ColumnIndex).Name = &amp;quot;Price&amp;quot; Then&lt;br /&gt;
         ' Check if this is the right value.&lt;br /&gt;
         If e.Value &amp;gt; 100 Then&lt;br /&gt;
             e.CellStyle.ForeColor = Color.Red&lt;br /&gt;
             e.CellStyle.BackColor = Color.Yellow&lt;br /&gt;
         End If&lt;br /&gt;
     End If&lt;br /&gt;
     &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
Keep in mind that you should reuse style objects if at all possible. If you assign a new style object to each cell, you'll consume a vast amount of memory. A better approach is to create one style object, and assign it to multiple cells that use the same formatting.&lt;br /&gt;
&lt;br /&gt;
== Add Images and Controls to the DataGridView ==&lt;br /&gt;
&lt;br /&gt;
To create a custom column with the &amp;lt;tt&amp;gt;DataGrid&amp;lt;/tt&amp;gt;, you needed to implement the functionality yourself by deriving a custom &amp;lt;tt&amp;gt;DataGridColumnStyle&amp;lt;/tt&amp;gt; class that would need dozens of lines of code. The &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; provides a much simpler model. In fact, you can add new columns right alongside your data-bound columns!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''There's a lot more that you can do with theDataGridView, including adding static buttons and images''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In many scenarios, it's useful to display a button next to each row in a grid. Clicking this button can then remove a record, add an item to a shopping cart, or call up another window with more information. The &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; makes this easy with the &amp;lt;tt&amp;gt;DataGridViewButtonColumn&amp;lt;/tt&amp;gt; class. You simply need to create a new instance, specify the button text, and add it to the end of the grid:&lt;br /&gt;
&lt;br /&gt;
 ' Create a button column.&lt;br /&gt;
 Dim Details As New DataGridViewButtonColumn( )&lt;br /&gt;
 Details.Name = &amp;quot;Details&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Turn off data-binding and show static text.&lt;br /&gt;
 ' (You could use a property from the table by setting&lt;br /&gt;
 ' the DataPropertyName property instead.)&lt;br /&gt;
 Details.UseColumnTextForButtonValue = False&lt;br /&gt;
 Details.Text = &amp;quot;Details...&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Clear the header.&lt;br /&gt;
 Details.HeaderText = &amp;quot;&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Add the column.&lt;br /&gt;
 DataGridView1.Columns.Insert(DataGridView1.Columns.Count, Details)&lt;br /&gt;
&lt;br /&gt;
Once you've performed this easy task, you can intercept the &amp;lt;tt&amp;gt;CellClick&amp;lt;/tt&amp;gt; event to perform another action ([[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-14|Figure 3-14]] shows the result of this simple test):&lt;br /&gt;
&lt;br /&gt;
 Private Sub DataGridView1_CellClick(ByVal sender As System.Object, _&lt;br /&gt;
   ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _&lt;br /&gt;
   Handles DataGridView1.CellClick&lt;br /&gt;
     &lt;br /&gt;
     If DataGridView1.Columns(e.ColumnIndex).Name = &amp;quot;Details&amp;quot; Then&lt;br /&gt;
         MessageBox.Show(&amp;quot;You picked &amp;quot; &amp;amp; _&lt;br /&gt;
         DataGridView1.Rows(e.RowIndex).Cells(&amp;quot;CustomerID&amp;quot;).Value)&lt;br /&gt;
     End If&lt;br /&gt;
     &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-14&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-14. Using a button column'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt114.png|Using a button column]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creating an image column is just as easy. In this case, you simply create and add a new &amp;lt;tt&amp;gt;DataGridViewImageColumn&amp;lt;/tt&amp;gt; object. If you want to show the same static image in each cell, simply set the &amp;lt;tt&amp;gt;Image&amp;lt;/tt&amp;gt; property with the &amp;lt;tt&amp;gt;Image&amp;lt;/tt&amp;gt; object you want to use.&lt;br /&gt;
&lt;br /&gt;
A more sophisticated technique is to show a separate image for each record. You can draw this record from a binary field in the database, or read it from a file specified in a string field. In either case, the technique is basically the same. First of all, you hide the column that contains the real data (the raw binary information for the picture, or the path to the file) by setting its &amp;lt;tt&amp;gt;Visible&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt;. Then, you create a new &amp;lt;tt&amp;gt;DataGridViewImageColumn&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 DataGridView1.DataSource = ds&lt;br /&gt;
 DataGridView1.DataMember = &amp;quot;pub_info&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 ' Hide the binary data.&lt;br /&gt;
 DataGridView1.Columns(&amp;quot;logo&amp;quot;).Visible = False&lt;br /&gt;
     &lt;br /&gt;
 ' Add an image column.&lt;br /&gt;
 Dim ImageCol As New DataGridViewImageColumn( )&lt;br /&gt;
 ImageCol.Name = &amp;quot;Image&amp;quot;&lt;br /&gt;
 ImageCol.Width=200&lt;br /&gt;
 DataGridView1.Columns.Add(ImageCol)&lt;br /&gt;
&lt;br /&gt;
Finally, you can set the binary picture data you need:&lt;br /&gt;
&lt;br /&gt;
 For Each Row As DataGridViewRow In DataGridView1.Rows&lt;br /&gt;
     ' First, you must convert the binary data to a memory stream.&lt;br /&gt;
     ' Then, you can use the memory stream to create an Image object.&lt;br /&gt;
     Try&lt;br /&gt;
         Dim ImageBytes( ) As Byte = Row.Cells(&amp;quot;logo&amp;quot;).Value&lt;br /&gt;
     &lt;br /&gt;
         Dim ms As New MemoryStream(ImageBytes)&lt;br /&gt;
         Dim img As Image = Image.FromStream(ms)&lt;br /&gt;
     &lt;br /&gt;
         ' Finally, bind the image column.&lt;br /&gt;
         Dim ImageCell As DataGridViewImageCell = CType(Row.Cells(&amp;quot;Image&amp;quot;), _&lt;br /&gt;
           DataGridViewImageCell)&lt;br /&gt;
         ImageCell.Value = img&lt;br /&gt;
     &lt;br /&gt;
         ' Now you can release the original information to save space.&lt;br /&gt;
         Row.Cells(&amp;quot;logo&amp;quot;).Value = New Byte( ) {  }&lt;br /&gt;
     &lt;br /&gt;
         Row.Height = 100&lt;br /&gt;
     Catch&lt;br /&gt;
         ' Ignore errors from invalid images.&lt;br /&gt;
     End Try&lt;br /&gt;
     &lt;br /&gt;
 Next&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Windows Applications#vbadn-CHP-3-FIG-15|Figure 3-15]] shows the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; with image data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-3-FIG-15&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 3-15. Using an image column'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_3_tt117.png|Using an image column]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''In many cases, DataGridView is intelligent enough to recognize image data types and use them seamlessly in image columns, with no conversion required. However, if any extra work is required (e.g., converting or removing extra header information), you need to use the technique shown here''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
So, you want to do even ''more'' with the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt; control? Because it is one of the key showpieces of the new .NET Windows Forms toolkit, there's a lot of online documentation for the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt;. Look up the index entry &amp;quot;DataGridView control (Windows Forms)&amp;quot; in the MSDN help, and you'll find nearly 100 entries detailing distinct features you can add to solutions that use the &amp;lt;tt&amp;gt;DataGridView&amp;lt;/tt&amp;gt;!&lt;/div&gt;</description>
			<pubDate>Tue, 11 Mar 2008 21:52:04 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/Windows_Applications</comments>		</item>
	</channel>
</rss>