<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" media="screen" href="/styles/xslt/rss.xslt"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:c9="http://channel9.msdn.com">
<channel>
	<title>Channel 9</title>
    <atom:link rel="self" type="application/rss+xml" href="http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts/RSS"></atom:link>
    <itunes:summary></itunes:summary>
    <itunes:author>Microsoft</itunes:author>
    <itunes:subtitle></itunes:subtitle>
    <image>
      <url>http://mschnlnine.vo.llnwd.net/d1/Dev/App_Themes/C9/images/feedimage.png</url>
      <title>Channel 9</title>
      <link>http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts</link>
    </image>
    <itunes:image href=""></itunes:image>
    <itunes:category text="Technology"></itunes:category>
    <description>Channel 9 keeps you up to date with the latest news and behind the scenes info from Microsoft that developers love to keep up with. From LINQ to SilverLight – Watch videos and hear about all the cool technologies coming and the people behind them.</description>
    <link>http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts</link>
    <language>en</language>
    <pubDate>Sat, 25 May 2013 04:37:26 GMT</pubDate>
    <lastBuildDate>Sat, 25 May 2013 04:37:26 GMT</lastBuildDate>
    <generator>Rev9</generator>
    <c9:totalResults>5</c9:totalResults>
    <c9:pageCount>1</c9:pageCount>
    <c9:pageSize>25</c9:pageSize>
  <item>
      <title>Investigate your network with NetPing</title>
      <description><![CDATA[<span id="c4fmetadata">
<table cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td width="50">&nbsp;</td>
<td><span class="entry_description">NetPing is an extensible utility that allows for custom display of networked Windows computers, as well as the ability to perform custom actions on these computers.</span></td>
</tr>
<tr>
<td colspan="2">
<div class="entry_author">Jeff Key</div>
<div class="entry_company"><a href="http://www.sliver.com">Website</a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Intermediate</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
Less than 1 hour</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input"></span></div>
<div class="entry_details"><b>Download: </b><a href="http://www.codeplex.com/NetPing/Release/ProjectReleases.aspx?ReleaseId=3645">Download</a>
<ul>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<p></p>
<p>Many households, and most businesses, have more than one computer. Being able to remotely monitor and maintain these computers can save valuable minutes every day. NetPing makes these tasks a little easier by serving two purposes: Simple display of computer
 information and the ability to perform actions on these computers. What makes the application truly useful, though, is the ability to easily add to the application's functionality with add-ins written in .NET. Add-ins come in two flavors: ColoumnProviders,
 which display information for each computer in a single column, and ContextMenuItemProviders that allow the user to execute an action via context menu.
</p>
<p><img height="161" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2241600/clip_image00232.jpg" width="500">
</p>
<h3>How NetPing works</h3>
<p>When NetPing is launched, it loads all add-ins in the AddIns directory. When the Start button is pressed, all IP addresses in the given range are pinged and ColumnProvider add-ins are executed for each that respond. Finally, ContextMenuItemProvider add-ins
 are executed when the user right-clicks a row and selects an item from the context menu.
</p>
<p>In the screen shot below, ‘Name', ‘Uptime' and ‘Operating System' are ColumnProvider add-ins. ‘Remote Deskop' and ‘System Information' are ContextMenuItemProviders.
</p>
<p align="center"><img height="74" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2241600/clip_image0048.jpg" width="500">
</p>
<h3>The NetPing projects</h3>
<p>NetPing consists of the following projects: </p>
<p>· <b>NetPing: </b>This contains the core application. MainForm and HostListViewItem are where the most of the action lives, but most of this action involves threading and is outside the scope of this article. The classes are fully commented, so the adventurous
 are welcome to dig around. <b></b></p>
<p>· <b>NetPing.Common: </b>Common contains what could be considered the NetPing ‘SDK' (software development kit). It contains the classes that add-in authors use to integrate with NetPing.<b></b>
</p>
<p>· <b>NetPing.AddIns: </b>Finally, AddIns contains the ‘Name', ‘Uptime', ‘Operating System', ‘Remote Desktop' and ‘System Information' add-ins. Note that any number of add-in assemblies may be put into the AddIns directory – this project need not be modified
 to extend NetPing.<b></b> </p>
<h3>Writing ColumnProvider add-ins</h3>
<p>The IColumnProvider interface, shown below, must be implemented by all ColumnProvider add-ins:
</p>
<p align="center"><img height="303" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2241600/clip_image0067.jpg" width="469">
</p>
<p>The abstract ColumnProviderBase class is provided to make ColumnProvider creation a little easier. Classes that inherit ColumnProviderBase are only required to implement Execute, which returns a ColumnValue object.
</p>
<p>The ColumnValue class represents three different values: </p>
<p><b>Text: </b>This is the text that will be displayed. </p>
<p><b>SortKey:</b> SortKey is a string that will be used when comparing values during a sort, since the text value of a field doesn't necessarily represent its value. For example, the Uptime column displays uptime in English but its SortKey is a numeric representation
 of the boot date and time, which sorts chronologically. </p>
<p><b>ColumnIndex: </b>Indicates which column the row should update with the value.
</p>
<p>The simplest ColumnProvider, ComputerNameColumn, is shown below in both Visual Basic and C#.
</p>
<p>First, the VB version: </p>
<pre class="csharpcode"><span class="kwrd">Friend</span> <span class="kwrd">Class</span> ComputerNameColumn
    <span class="kwrd">Inherits</span> ColumnProviderBase

    <span class="kwrd">Public</span> <span class="kwrd">Sub</span> <span class="kwrd">New</span>()
        <span class="kwrd">MyBase</span>.<span class="kwrd">New</span>(<span class="str">&quot;Name&quot;</span>, 125)
    <span class="kwrd">End</span> <span class="kwrd">Sub</span>

    <span class="kwrd">Public</span> <span class="kwrd">Overrides</span> <span class="kwrd">Function</span> Execute(<span class="kwrd">ByVal</span> ipAddress <span class="kwrd">As</span> IPAddress) <span class="kwrd">As</span> ColumnValue
        <span class="kwrd">Dim</span> hostEntry <span class="kwrd">As</span> IPHostEntry

        <span class="kwrd">Try</span>
            hostEntry = Dns.GetHostEntry(ipAddress)
        <span class="kwrd">Catch</span>
            hostEntry = <span class="kwrd">Nothing</span>
        <span class="kwrd">End</span> <span class="kwrd">Try</span>

        <span class="kwrd">Dim</span> hostName <span class="kwrd">As</span> <span class="kwrd">String</span>

        <span class="kwrd">If</span> hostEntry <span class="kwrd">Is</span> <span class="kwrd">Nothing</span> <span class="kwrd">Then</span>
            hostName = <span class="str">&quot;&quot;</span>
        <span class="kwrd">Else</span>
            hostName = hostEntry.HostName
        <span class="kwrd">End</span> <span class="kwrd">If</span>

        <span class="kwrd">Return</span> <span class="kwrd">New</span> ColumnValue(ColumnIndex, hostName)
    <span class="kwrd">End</span> <span class="kwrd">Function</span>
<span class="kwrd">End</span> Class</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>And the C# version: </p>
<pre class="csharpcode"><span class="kwrd">class</span> ComputerNameColumn : ColumnProviderBase
{
    <span class="kwrd">public</span> ComputerNameColumn() 
        : <span class="kwrd">base</span>(<span class="str">&quot;Name&quot;</span>, 125)
    {}

    <span class="kwrd">public</span> <span class="kwrd">override</span> ColumnValue Execute(IPAddress ipAddress)
    {
        IPHostEntry hostEntry;

        <span class="kwrd">try</span>
        {
            hostEntry = Dns.GetHostEntry(ipAddress);
        }
        <span class="kwrd">catch</span> 
        {
            hostEntry = <span class="kwrd">null</span>;
        }

        <span class="kwrd">string</span> hostName;

        <span class="kwrd">if</span> (hostEntry == <span class="kwrd">null</span>)
        {
            hostName = <span class="str">&quot;&quot;</span>;
        }
        <span class="kwrd">else</span>
        {
            hostName = hostEntry.HostName;
        }

        <span class="kwrd">return</span> <span class="kwrd">new</span> ColumnValue(ColumnIndex, hostName);
    }
}
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h3>Creating ContextMenuItemProviders add-ins</h3>
<p>Writing ContextMenuItemProviders is even easier. The IContextMenuItemProvider interface is shown below, again in C# and Visual Basic:
</p>
<p align="center"><img height="163" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/2241600/clip_image0087.jpg" width="328">
</p>
<p><b>Text</b> is what is shown on the context menu and <b>Execute</b> is called when the user selects the menu item.
</p>
<p>The SystemInformationMenuItem is shown below in C#: </p>
<pre class="csharpcode"><span class="kwrd">class</span> SystemInformationMenuItem : IContextMenuItemProvider
{
    <span class="kwrd">public</span> <span class="kwrd">void</span> Execute(IPAddress ipAddress)
    {
        <span class="kwrd">try</span>
        {
            Process.Start(<span class="str">&quot;cmd.exe&quot;</span>, <span class="str">&quot;/k systeminfo /s &quot;</span> &#43; ipAddress);
        }
        <span class="kwrd">catch</span> (Exception e)
        {
            MessageBox.Show(<span class="str">&quot;An error occurred while retrieving system information:\n\n&quot;</span> &#43; e.Message, <br>                            <span class="str">&quot;System information&quot;</span>, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    <span class="kwrd">public</span> <span class="kwrd">string</span> Text
    {
        get { <span class="kwrd">return</span> <span class="str">&quot;System Information&quot;</span>; }
    }
}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>And in Visual Basic: </p>
<pre class="csharpcode"><span class="kwrd">Friend</span> <span class="kwrd">Class</span> SystemInformationMenuItem
    <span class="kwrd">Implements</span> IContextMenuItemProvider

    <span class="kwrd">Public</span> <span class="kwrd">Sub</span> Execute(<span class="kwrd">ByVal</span> ipAddress <span class="kwrd">As</span> IPAddress) <span class="kwrd">Implements</span> IContextMenuItemProvider.Execute
        <span class="kwrd">Try</span>
            Process.Start(<span class="str">&quot;cmd.exe&quot;</span>, <span class="str">&quot;/k systeminfo /s &quot;</span> &amp; ipAddress.ToString())
        <span class="kwrd">Catch</span> e <span class="kwrd">As</span> Exception
            MessageBox.Show(<span class="str">&quot;An error occurred while retrieving system information:&quot;</span> &amp; _<br>                            Constants.vbLf &#43; Constants.vbLf &#43; e.Message, <span class="str">&quot;System information&quot;</span>, _<br>                            MessageBoxButtons.OK, MessageBoxIcon.<span class="kwrd">Error</span>)
        <span class="kwrd">End</span> <span class="kwrd">Try</span>
    <span class="kwrd">End</span> <span class="kwrd">Sub</span>

    <span class="kwrd">Public</span> <span class="kwrd">ReadOnly</span> <span class="kwrd">Property</span> Text() <span class="kwrd">As</span> <span class="kwrd">String</span> <span class="kwrd">Implements</span> IContextMenuItemProvider.Text
        <span class="kwrd">Get</span>
            <span class="kwrd">Return</span> <span class="str">&quot;System Information&quot;</span>
        <span class="kwrd">End</span> <span class="kwrd">Get</span>
    <span class="kwrd">End</span> <span class="kwrd">Property</span>
<span class="kwrd">End</span> Class</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h3>What next?</h3>
<p>All kinds of information is available via <a href="http://www.microsoft.com/whdc/system/pnppwr/wmi/default.mspx">
WMI</a> (Windows Management Instrumentation). Add-ins can be written to display a computer's CPU, RAM, disk drive information and so on. The OperatingSystemColumn and UptimeColumn classes use WMI to retrieve their values and are a good place to start.
</p>
<p>Regarding ContextMenuItemProviders, anything that requires an IP address can be executed. For example, a reboot command could be initiated, ports scanned, Computer Management opened, etc. The options are endless!
</p>
<h3>Using with Windows Vista</h3>
<p>Vista's User Account Control may get between NetPing and Vista computers' WMI, and behavior is different for workspace and domain compters. The following articles explain the issue and solutions:
</p>
<p>· <a href="http://msdn2.microsoft.com/en-us/library/aa822854.aspx">Connecting to WMI Remotely Starting with Vista</a>
</p>
<p>· <a href="http://msdn2.microsoft.com/en-us/library/aa826699.aspx">User Account Control and WMI</a>
</p>
<p></p>
<hr>
<p></p>
<p>Jeff is the Technical Product Manager at <a href="http://www.inrule.com">InRule Technology</a>&nbsp;and a Visual C# MVP. More stuff like this is available at
<a href="http://www.sliver.com">his website</a>.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts/RSS&WT.dl=0&WT.entryid=Entry:RSSView:f4b6836a3764436dadae9e7600d45801">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Investigate-your-network-with-NetPing</comments>
      <itunes:summary>



&amp;nbsp;
NetPing is an extensible utility that allows for custom display of networked Windows computers, as well as the ability to perform custom actions on these computers.



Jeff Key
Website

Difficulty: Intermediate
Time Required: 
Less than 1 hour
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware: 
Download: Download








 
Many households, and most businesses, have more than one computer. Being able to remotely monitor and maintain these computers can save valuable minutes every day. NetPing makes these tasks a little easier by serving two purposes: Simple display of computer
 information and the ability to perform actions on these computers. What makes the application truly useful, though, is the ability to easily add to the application&#39;s functionality with add-ins written in .NET. Add-ins come in two flavors: ColoumnProviders,
 which display information for each computer in a single column, and ContextMenuItemProviders that allow the user to execute an action via context menu.
 

 
How NetPing works
When NetPing is launched, it loads all add-ins in the AddIns directory. When the Start button is pressed, all IP addresses in the given range are pinged and ColumnProvider add-ins are executed for each that respond. Finally, ContextMenuItemProvider add-ins
 are executed when the user right-clicks a row and selects an item from the context menu.
 
In the screen shot below, ‘Name&#39;, ‘Uptime&#39; and ‘Operating System&#39; are ColumnProvider add-ins. ‘Remote Deskop&#39; and ‘System Information&#39; are ContextMenuItemProviders.
 

 
The NetPing projects
NetPing consists of the following projects:  
&#183; NetPing: This contains the core application. MainForm and HostListViewItem are where the most of the action lives, but most of this action involves threading and is outside the scope of this article. The classes are fully commented, so the adventurous
 are welcome to dig around.  
&#183; NetPing.Common: Common contains what could be considered the NetPing ‘SDK&#39; (s</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Investigate-your-network-with-NetPing</link>
      <pubDate>Mon, 23 Apr 2007 02:10:37 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Investigate-your-network-with-NetPing</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/2241600_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/2241600_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Jeff Key</dc:creator>
      <itunes:author>Jeff Key</itunes:author>
      <slash:comments>1</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Investigate-your-network-with-NetPing/RSS</wfw:commentRss>
      <category>utility</category>
      <category>Windows</category>
    </item>
  <item>
      <title>Holiday Wish List</title>
      <description><![CDATA[<span id="c4fmetadata">
<table class="" cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td class="" width="50">&nbsp;</td>
<td class=""><span class="entry_description">In this article, Jeff shows&nbsp;off some RAD features in Visual Studio 2005 to create a Holiday Wish List.</span></td>
</tr>
<tr>
<td class="" colspan="2">
<div class="entry_author">Jeff Key</div>
<div class="entry_company"><a href="http://www.inrule.com/">InRule Technology</a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Easy</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
1-3 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input"></span></div>
<div class="entry_details"><b>Download: </b>
<ul>
<li><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/WishList_CS.msi">C# Download</a>
</li><li><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/WishList_VB.msi">VB Download</a>
</li></ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<p>&nbsp;</p>
<p>I don't know about you, but the one aspect of development I like least is the typing bit. Drag-and-drop development has been evolving over the years, but has rarely made it past being a good demo. Luckily for us, the Visual Studio 2005 editions' drag-and-drop
 feature set is more evolved than in the past, and works surprisingly well. It is the holiday season, and my gift to you is an overview of some of the features that really do take the pain out of some of the more tedious parts of development.
</p>
<p>The application accompanying this article maintains a simple holiday wish list. It lives in the notification area, and the user can print out or email the list. My primary goal was to use the RAD features available in Visual Studio 2005 to reduce the amount
 of complexity and typing required. </p>
<h3>Table layout</h3>
<p>Anyone that's done both Web and Windows development can probably appreciate the beauty of the HTML table. Sure, you can reliably put anything anywhere on a Windows dialog, but that isn't necessarily a good thing. The simple structure of the HTML table makes
 it easy to write functional user interfaces that are easy on the eyes. </p>
<p>Windows Forms in .NET 1.x introduced easy-to-use layout logic via anchoring and docking. These made manual auto-resizing logic a thing of the past, and gave the developer easy-to-use tools for creating a better user experience. Still, the developer had to
 make sure the controls' initial layouts were correct. This was more time consuming than it should have been.
</p>
<p>Apparently some folks in Redmond thought the same thing, because the <b>TableLayoutPanel</b> was born. The table is really simple to use, and anyone with HTML table experience will be productive right out of the gate.
</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/wishlist_1.gif">
</p>
<p><b>Figure 1: Simple table layout</b> </p>
<p>TableLayoutPanels are really simple to use. Each cell can contain only one control, and controls can be anchored to any side (or sides) of the containing cell. Column and row sizes can be absolute, a percentage, or autosize. This combination makes dynamically-sized
 forms a snap to create. </p>
<p>In the image above, the table has three columns; the left and right are set at 48%, and the middle is 4%. Each text box is anchored to the left and right, and each label is anchored to the top and left. The rows are all autosized, which makes them as small
 as the largest control within them. Finally, the table is anchored on the form, so the controls grow or shrink and change their placement on the form when the user resizes it. The image below illustrates this:
</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/wishlist_2.gif">
</p>
<p><b>Figure 2: Table in action</b> </p>
<p>More complex table layouts are possible with familiar HTML table concepts like column spanning, row spanning, padding and margins, as well as table nesting. The tables below have examples of some of these features:
</p>
<p>The Selected Gift table has four columns. The URL TextBox and button are in their own table. It's nested in a spanned cell with a zero margin, which gives it a fluid appearance within the outer table. The buttons in the Gifts table are in their own tables,
 anchored to either side of the outer table. </p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/wishlist_3.gif">
</p>
<p><b>Figure 3: Complex table layouts</b> </p>
<p>TableLayoutPanels are easy to use, and much better than previous methods required to accomplish the same result, but there are a few best practices that make life a little easier when using them. The Windows Forms 2.0 Layout link below has a short video
 explaining some of these. </p>
<h3>Settings Bindings</h3>
<p>Almost every application, no matter how small, has user settings. Maintaining these settings requires maintaining a settings file, wrapping those settings up so that they're consumable within the app, and creating the UI to adjust the settings. None of this
 is a major undertaking, but it takes time away from more important things. If you're coding for fun, you probably don't have much time to do it, and tasks like these are just begging to be automated.
</p>
<p>You're in luck, my friend! This part of life has gotten a whole lot easier. I briefly covered the new user settings features in Visual Studio in my
<a href="http://msdn.microsoft.com/coding4fun/coolapplications/urlbuddy/default.aspx">
URL Buddy</a> article. Basically, Visual Studio provides an interface for you to enter settings names and optional default values. It creates strongly-typed classes for accessing this data, so reading/writing these settings at run time is only a matter of accessing
 a strongly-typed property. These settings can be app- or user-level, with user-specific settings saved in the user's ApplicationData folder.
</p>
<p>Visual Studio 2005's Property Bindings are similar to Visual Studio 2003's Dynamic Properties, which allowed for a read-only mapping to an appSettings entry in an application's config file. The big difference is that the new version supports two-way binding,
 so changes may be saved. That makes all the difference in the world! Mapping the value of a property to a setting is as easy as picking it from a list:
</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/wishlist_4.gif">
</p>
<p><b>Figure 4: Mapping a user setting to a TextBox's Text property</b> </p>
<p>Rounding out this little bit of heaven is automatic creation of settings right from the property grid. Let me give a scenario, because this little nugget is such a great time-saver I want to make sure it's remembered: You're in the middle of writing some
 very important code, but realize that you need the user's email address and want to save it between sessions. You go to your options form and create a TextBox, then open the PropertyBinding (&quot;Application Settings&quot; above) dialog. You click the drop-drown button
 next to Text, click &quot;(New…)&quot;, then enter the name (&quot;YourEmailAddress&quot;), scope (&quot;User&quot;) and optional default value (&quot;someone@someplace.com&quot;). That's it! You now have a strongly-typed YourEmailAddress property on the application's Settings class. The value will
 be loaded automatically from the user's config file, saved very easily (a single method call for all settings), and the value is bound to the TextBox. And you didn't write a single line of code.
</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1184139/wishlist_5.gif">
</p>
<p><b>Figure 5: Adding a setting from the property grid</b> </p>
<h3>Summary</h3>
<p>Form layout and application settings are two things that are required in nearly every application, but take away development time from the application's intended purpose. I've touched on just two of the many features in Visual Studio 2005 and the .NET Framework
 2.0 that help the developer focus on what's important. A little exploration within Visual Studio will unearth even more goodies.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts/RSS&WT.dl=0&WT.entryid=Entry:RSSView:b1a76d9053da42b492709e7600d79195">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Holiday-Wish-List</comments>
      <itunes:summary>



&amp;nbsp;
In this article, Jeff shows&amp;nbsp;off some RAD features in Visual Studio 2005 to create a Holiday Wish List.



Jeff Key
InRule Technology

Difficulty: Easy
Time Required: 
1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware: 
Download: 

C# Download
VB Download







&amp;nbsp; 
I don&#39;t know about you, but the one aspect of development I like least is the typing bit. Drag-and-drop development has been evolving over the years, but has rarely made it past being a good demo. Luckily for us, the Visual Studio 2005 editions&#39; drag-and-drop
 feature set is more evolved than in the past, and works surprisingly well. It is the holiday season, and my gift to you is an overview of some of the features that really do take the pain out of some of the more tedious parts of development.
 
The application accompanying this article maintains a simple holiday wish list. It lives in the notification area, and the user can print out or email the list. My primary goal was to use the RAD features available in Visual Studio 2005 to reduce the amount
 of complexity and typing required.  
Table layout
Anyone that&#39;s done both Web and Windows development can probably appreciate the beauty of the HTML table. Sure, you can reliably put anything anywhere on a Windows dialog, but that isn&#39;t necessarily a good thing. The simple structure of the HTML table makes
 it easy to write functional user interfaces that are easy on the eyes.  
Windows Forms in .NET 1.x introduced easy-to-use layout logic via anchoring and docking. These made manual auto-resizing logic a thing of the past, and gave the developer easy-to-use tools for creating a better user experience. Still, the developer had to
 make sure the controls&#39; initial layouts were correct. This was more time consuming than it should have been.
 
Apparently some folks in Redmond thought the same thing, because the TableLayoutPanel was born. The table is really simple to use, and anyone with HTML table experienc</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Holiday-Wish-List</link>
      <pubDate>Fri, 01 Dec 2006 10:32:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Holiday-Wish-List</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1184139_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1184139_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Jeff Key</dc:creator>
      <itunes:author>Jeff Key</itunes:author>
      <slash:comments>0</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Holiday-Wish-List/RSS</wfw:commentRss>
    </item>
  <item>
      <title>Randomly Opening CD Tray</title>
      <description><![CDATA[<span id="c4fmetadata">
<table class="" cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td class="" width="50">&nbsp;</td>
<td class=""><span class="entry_description">A simple prank that opens the CD-ROM drive at random intervals.</span></td>
</tr>
<tr>
<td class="" colspan="2">
<div class="entry_author">Jeff Key</div>
<div class="entry_company"><a href="http://www.inrule.com/">InRule -&gt; The Leading Innovator in Business Rule Engine Technology for .NET</a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Easy</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
Less than 1 hour</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input"></span></div>
<div class="entry_details"><b>Download: </b><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/918646/WebSpeedster.msi">Download</a>
<ul>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<p>&nbsp;</p>
<p>The best April Fools' Day pranks are those that can be enjoyed by both the fooler and the foolee. This short article covers one of the simpler pranks in the &quot;Why is my computer doing that?&quot; category: the malfunctioning CD-ROM drive that opens at random intervals.
 This prank is best used in a cube farm where multiple instances can be easily deployed and monitored.
</p>
<h4>The Prank</h4>
<p>The prank itself is very simple: a little program that, once launched, waits <i>
n</i> minutes, then opens the CD-ROM drive door. It then continually opens the door at random intervals until the user closes the application. (Both
<i>n</i> and the random interval constraint can be modified by the fooler via the application's config file.)
</p>
<p>The most difficult part of the prank is getting the application onto the foolee's computer.
</p>
<h4>The Inspiration</h4>
<p>I've recently begun using the <a href="http://webaccelerator.google.com/">Google Web Accelerator</a>, which speeds up your Web browsing. Or so they say. The only indication that it is doing anything is a report of how much time it has saved me:
</p>
<p><img alt="" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/918646/cdtray1.gif" border="0">
</p>
<p>If you're reading this article, you probably have an idea how the Accelerator works. However, 99 percent of its users don't. They take its word that it's working, and this trust is a primary component of our evil deed.
</p>
<h4>&quot;Web Speedster&quot;</h4>
<p>Our prank appears to be similar to the Web Accelerator. Once launched, it sits in the notification area until the user closes it. Unbeknownst to the user, the Web Speedster is the source of the randomly opening CD-ROM drive door. The initial CD-ROM door
 opening is delayed so the foolee doesn't make the connection with the Speedster.
</p>
<p><img alt="" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/918646/cdtray2.gif" border="0">
</p>
<p>The application itself is tiny — less than a dozen lines of code. The most interesting part is how the door is opened, since the .NET Framework doesn't natively provide this functionality.
</p>
<p>The .NET Framework has almost everything most people need to create great applications, but sometimes developers need to access unmanaged functionality provided by other libraries, including Windows itself.
<b>Platform Invoke</b>, or <b>P/Invoke</b>, is the gateway to these libraries. Methods in other libraries are defined with external method declarations like the following, which lets us call the
<b>mciSendString</b> method in Windows WinMM DLL: </p>
<p><b>Visual C#</b></p>
<pre class="csharpcode">[DllImport(<span class="str">&quot;winmm.dll&quot;</span>)]<br><span class="kwrd">static</span> <span class="kwrd">extern</span> Int32 mciSendString(String command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b></p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">Declare</span> <span class="kwrd">Function</span> mciSendStringA <span class="kwrd">Lib</span> <span class="str">&quot;winmm.dll&quot;</span> (command <span class="kwrd">As</span> <span class="kwrd">String</span>, _<br>   buffer <span class="kwrd">As</span> StringBuilder, bufferSize <span class="kwrd">As</span> Int32, hwndCallback <span class="kwrd">As</span> IntPtr) <span class="kwrd">As</span> Int32</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>(<a href="http://www.pinvoke.net/">PInvoke.NET</a> is a great resource for finding these declarations.)
</p>
<p>Calls to these methods are done just like calls to &quot;normal&quot; methods:</p>
<pre><code></code></pre>
<pre class="csharpcode">mciSendString(<span class="str">&quot;set CDAudio door open&quot;</span>, <span class="kwrd">null</span>, 0, IntPtr.Zero);</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p>And that is all that's required to make a function call into Windows. Easy! </p>
<h4>Summary</h4>
<p>While the .NET Framework is huge, it doesn't do everything. However, if Windows can do what you need, chances are very good that you can get to that functionality with P/Invoke.
</p>
<p>The next step is to compile the program, send it out to unsuspecting foolees (ideally within earshot), and let the fun begin!</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts/RSS&WT.dl=0&WT.entryid=Entry:RSSView:bdcdb274f58d48dfa7229e7600d9edfa">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Randomly-Opening-CD-Tray</comments>
      <itunes:summary>



&amp;nbsp;
A simple prank that opens the CD-ROM drive at random intervals.



Jeff Key
InRule -&amp;gt; The Leading Innovator in Business Rule Engine Technology for .NET

Difficulty: Easy
Time Required: 
Less than 1 hour
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware: 
Download: Download








&amp;nbsp; 
The best April Fools&#39; Day pranks are those that can be enjoyed by both the fooler and the foolee. This short article covers one of the simpler pranks in the &amp;quot;Why is my computer doing that?&amp;quot; category: the malfunctioning CD-ROM drive that opens at random intervals.
 This prank is best used in a cube farm where multiple instances can be easily deployed and monitored.
 
The Prank
The prank itself is very simple: a little program that, once launched, waits 
n minutes, then opens the CD-ROM drive door. It then continually opens the door at random intervals until the user closes the application. (Both
n and the random interval constraint can be modified by the fooler via the application&#39;s config file.)
 
The most difficult part of the prank is getting the application onto the foolee&#39;s computer.
 
The Inspiration
I&#39;ve recently begun using the Google Web Accelerator, which speeds up your Web browsing. Or so they say. The only indication that it is doing anything is a report of how much time it has saved me:
 

 
If you&#39;re reading this article, you probably have an idea how the Accelerator works. However, 99 percent of its users don&#39;t. They take its word that it&#39;s working, and this trust is a primary component of our evil deed.
 
&amp;quot;Web Speedster&amp;quot;
Our prank appears to be similar to the Web Accelerator. Once launched, it sits in the notification area until the user closes it. Unbeknownst to the user, the Web Speedster is the source of the randomly opening CD-ROM drive door. The initial CD-ROM door
 opening is delayed so the foolee doesn&#39;t make the connection with the Speedster.
 

 
The application itself is tiny — less than a dozen lines</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Randomly-Opening-CD-Tray</link>
      <pubDate>Wed, 01 Nov 2006 06:46:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Randomly-Opening-CD-Tray</guid>      
      <dc:creator>Jeff Key</dc:creator>
      <itunes:author>Jeff Key</itunes:author>
      <slash:comments>12</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Randomly-Opening-CD-Tray/RSS</wfw:commentRss>
    </item>
  <item>
      <title>Building a Web Site with ASP .NET 2.0 to Navigate Your Music Library</title>
      <description><![CDATA[<span id="c4fmetadata">
<table class="" cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td class="" width="50">&nbsp;</td>
<td class=""><span class="entry_description">This article focuses on a common solution for managing shared music files. It is accomplished by keeping shared music files on a single computer and have other computers' media libraries point to, and stay current
 with, the files on that computer.</span></td>
</tr>
<tr>
<td class="" colspan="2">
<div class="entry_author">Jeff Key</div>
<div class="entry_company"><a href="http://blogs.msdn.com/controlpanel/blogs/"></a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Intermediate</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
1-3 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/">Visual Studio Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input"></span></div>
<div class="entry_details"><b>Download: </b><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/Web_20Coder_20-_20Music_20Library_20Web_20Site.msi">Download</a>
<ul>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<p>&nbsp;</p>
<p><strong>Introduction<br>
</strong>It's easy to assume that most people with computers have media libraries. As computers become cheaper, easier to network and easier to use, people are more likely to create home networks. Since servers aren't practical in a home setting, information
 is distributed amongst the computers in the network, which makes managing and finding formation difficult. This is probably most evident in music libraries.<br>
Most media players maintain a private database containing all of the songs they're aware of. Keeping these databases up-to-date can be a chore on a single computer, and keeping databases current on several computers often seems impossible.
<br>
A common solution is to keep all music files on a single computer and have other computers' media libraries point to, and stay current with, the files on that computer. Anyone that has tried this in practice knows that it's an adequate solution, but not an
 ideal one. Personally, I just want to listen to music, not manage it.<br>
My home setup consists of a Windows Media Center computer that drives my TV and has all of my music on it, my workstation, and occasionally a laptop. Keeping the workstation current with the music on the Media Center has always been a hassle, and doing so on
 the laptop just isn't practical. I wanted a simple, clean way to listen to music anywhere in my apartment, from any computer, without having to worry about the accuracy of the library. I chose to build a little website whose only purpose would be to get me
 to the music I wanted to listen to as painlessly as possible. I used <a href="http://lab.msdn.microsoft.com/express/vwd/">
Microsoft Visual Web Developer 2005 Express Edition Beta 2</a> to make this happen.<br>
<strong>COM: A friend from the past<br>
</strong>Back in the old days we had a technology called <a href="http://www.microsoft.com/com/default.mspx">
COM</a> that let us use libraries written in any language from our code, regardless of the langauge we were using (as long as it supported COM), much like .NET does today. COM did a great job solving some problems, but introduced a few others that became more
 apparent over time. .NET is, among other things, a solution to many of these problems.<br>
Years ago, Microsoft started exposing operating system and application services via COM. This let common folk write code against Microsoft Word, Windows Scripting Host, Windows Media Player, and countless others. Due to the number of languages, frameworks,
 etc. that support COM, it's still a major player in the Microsoft world and it's common to find COM support in Microsoft applications before .NET support. Luckily, .NET does a great job talking to COM (most of the time).<br>
This history lesson is relevant because the site uses the Windows Media Player COM component to access its media library. Adding a reference to a COM component is just as easy as adding a reference to a .NET assembly. Click the Website menu and select Add Reference,
 then click the COM tab on the Add Reference dialog. </p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/AddWmpComReference.PNG">
</p>
<p><strong>Figure 1: Adding a reference to the Windows Media Player COM component<br>
</strong></p>
<p><strong>Database<br>
</strong>Quick access to a subset of the WMP library's information is required, so an in-memory database consisting of Artist, Album, Database and Track classes is required. The Database class has a static (or shared in Visual Basic) constructor that populates
 the database with a call to the Refresh method: </p>
<p><b>Visual C#</b> </p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Refresh()
    {
        WindowsMediaPlayer wmp = <span class="kwrd">new</span> WindowsMediaPlayer();
        IWMPPlaylist playlist = wmp.mediaCollection.getAll();
        Dictionary&lt;<span class="kwrd">string</span>, Artist&gt; artistDictionary = 
<span class="kwrd">new</span> Dictionary&lt;<span class="kwrd">string</span>, Artist&gt;();

        <span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i &lt; playlist.count; i&#43;&#43;)
        {
            IWMPMedia media = (IWMPMedia)playlist.get_Item(i);
            <span class="kwrd">string</span> albumArtistName = media.getItemInfo(<span class="str">&quot;AlbumArtist&quot;</span>);
            <span class="kwrd">string</span> albumName = media.getItemInfo(<span class="str">&quot;Album&quot;</span>);
            <span class="kwrd">string</span> trackName = media.getItemInfo(<span class="str">&quot;Title&quot;</span>);
            <span class="kwrd">string</span> trackLocation = media.getItemInfo(<span class="str">&quot;SourceUrl&quot;</span>);
            <span class="kwrd">string</span> trackNumberString = media.getItemInfo(<span class="str">&quot;OriginalIndex&quot;</span>);

            Artist artist;
            <span class="kwrd">string</span> artistSortName = Artist.GetSortName(albumArtistName);

            <span class="kwrd">if</span> (!artistDictionary.TryGetValue(artistSortName, <span class="kwrd">out</span> artist))
            {
                artist = <span class="kwrd">new</span> Artist(albumArtistName);
                artistDictionary.Add(artistSortName, artist);
            }

            Album album;

            <span class="kwrd">if</span> (!artist.Albums.TryGetValue(albumName, <span class="kwrd">out</span> album))
            {
                album = <span class="kwrd">new</span> Album(albumName, artist);
                artist.Albums.Add(albumName, album);
            }

            Track track;

            <span class="kwrd">if</span> (!album.Tracks.TryGetValue(trackName, <span class="kwrd">out</span> track))
            {
                <span class="kwrd">int</span> trackNumber;

                <span class="kwrd">if</span> (<span class="kwrd">int</span>.TryParse(trackNumberString, <span class="kwrd">out</span> trackNumber))
                {
                    track = <span class="kwrd">new</span> Track(trackNumber, trackName, trackLocation);
                }
                <span class="kwrd">else</span>
                {
                    track = <span class="kwrd">new</span> Track(trackName, trackLocation);
                }

                track.Album = album;
                album.Tracks.Add(trackName, track);
            }
        }

        ArtistList.AddRange(artistDictionary.Values);
        ArtistList.Sort();
    }</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b><code>&nbsp;&nbsp;</code> </p>
<pre class="csharpcode"><span class="kwrd">Private</span> <span class="kwrd">Shared</span> <span class="kwrd">Sub</span> Refresh()
        <span class="kwrd">Dim</span> wmp <span class="kwrd">As</span> WindowsMediaPlayer = <span class="kwrd">New</span> WindowsMediaPlayer
        <span class="kwrd">Dim</span> playlist <span class="kwrd">As</span> IWMPPlaylist = wmp.mediaCollection.getAll()
        <span class="kwrd">Dim</span> artistDictionary <span class="kwrd">As</span> Dictionary(Of <span class="kwrd">String</span>, Artist) = _
<span class="kwrd">New</span> Dictionary(Of <span class="kwrd">String</span>, Artist)

        <span class="kwrd">For</span> i <span class="kwrd">As</span> <span class="kwrd">Integer</span> = 0 <span class="kwrd">To</span> playlist.count - 1
            <span class="kwrd">Dim</span> media <span class="kwrd">As</span> IWMPMedia = playlist.Item(i)
            <span class="kwrd">Dim</span> albumArtistName <span class="kwrd">As</span> <span class="kwrd">String</span> = media.getItemInfo(<span class="str">&quot;AlbumArtist&quot;</span>)
            <span class="kwrd">Dim</span> albumName <span class="kwrd">As</span> <span class="kwrd">String</span> = media.getItemInfo(<span class="str">&quot;Album&quot;</span>)
            <span class="kwrd">Dim</span> trackName <span class="kwrd">As</span> <span class="kwrd">String</span> = media.getItemInfo(<span class="str">&quot;Title&quot;</span>)
            <span class="kwrd">Dim</span> trackLocation <span class="kwrd">As</span> <span class="kwrd">String</span> = media.getItemInfo(<span class="str">&quot;SourceUrl&quot;</span>)
            <span class="kwrd">Dim</span> trackNumberString <span class="kwrd">As</span> <span class="kwrd">String</span> = _
                media.getItemInfo(<span class="str">&quot;OriginalIndex&quot;</span>)

            <span class="kwrd">Dim</span> theArtist <span class="kwrd">As</span> Artist
            <span class="kwrd">Dim</span> artistSortName <span class="kwrd">As</span> <span class="kwrd">String</span> = _
                Artist.GetSortName(albumArtistName)

<span class="kwrd">If</span> <span class="kwrd">Not</span> artistDictionary.TryGetValue(artistSortName, theArtist) <span class="kwrd">Then</span>
                theArtist = <span class="kwrd">New</span> Artist(albumArtistName)
                artistDictionary.Add(artistSortName, theArtist)
            <span class="kwrd">End</span> <span class="kwrd">If</span>

            <span class="kwrd">Dim</span> theAlbum <span class="kwrd">As</span> Album

            <span class="kwrd">If</span> <span class="kwrd">Not</span> theArtist.Albums.TryGetValue(albumName, theAlbum) <span class="kwrd">Then</span>
                theAlbum = <span class="kwrd">New</span> Album(albumName, theArtist)
                theArtist.Albums.Add(albumName, theAlbum)
            <span class="kwrd">End</span> <span class="kwrd">If</span>

            <span class="kwrd">Dim</span> theTrack <span class="kwrd">As</span> Track

            <span class="kwrd">If</span> <span class="kwrd">Not</span> theAlbum.Tracks.TryGetValue(trackName, theTrack) <span class="kwrd">Then</span>
                <span class="kwrd">Dim</span> trackNumber <span class="kwrd">As</span> <span class="kwrd">Integer</span>

                <span class="kwrd">If</span> <span class="kwrd">Integer</span>.TryParse(trackNumberString, trackNumber) <span class="kwrd">Then</span>
                    theTrack = <span class="kwrd">New</span> Track(trackNumber, trackName, trackLocation)
                <span class="kwrd">Else</span>
                    theTrack = <span class="kwrd">New</span> Track(trackName, trackLocation)
                <span class="kwrd">End</span> <span class="kwrd">If</span>

                theTrack.Album = theAlbum
                theAlbum.Tracks.Add(trackName, theTrack)
            <span class="kwrd">End</span> <span class="kwrd">If</span>
        <span class="kwrd">Next</span>

        ArtistList.AddRange(artistDictionary.Values)
        ArtistList.Sort()
    <span class="kwrd">End</span> Sub</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><br>
The WMP library is a flat structure of media items, so the Artist -&gt; Albums -&gt; Tracks hierarchy must be created manually while populating the database. Accessing the WMP library is simple; calling the getAll method off of a WindowsMediaPlayer object's mediaCollection
 property returns a playlist of all items in the library, represented by the IWMPPlaylist interface. Collecting the information needed from each media item (represented by the IWMPMedia interface) is just as easy, via a call to IWMPMedia's getItemInfo method.
 Once all of the relevant information for the media item is collected, the artist, album and track objects must be retrieved or created.
<br>
Note that I've used a generic Dictionary object to store the artists while we're iterating through the playlist. Some media collections consist of tens of thousands of items, and having to do a brute-force search through the ArtistList for every media item
 would further slow down an already slow operation. (Why the Dictionary object is quicker is beyond the scope of this article. Please see Scott Mitchell's examination of the Hashtable class, Dictionary's non-generic counterpart, in an
<a href="http://msdn.microsoft.com/vcsharp/default.aspx?pull=/library/en-us/dv_vstechart/html/datastructures_guide2.asp">
article</a> from his fantastic series on data structures.)<br>
<strong>Designing the website<br>
</strong>To keep things as simple as possible, we'll make three pages: Artists, Artist's Albums, and Album.
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/ArtistsPage.png">
</p>
<p><strong>Figure 2: The Artists page</strong> </p>
<p>The Artists page (Figure 2) shows ten groups of artists at a time. Each time a group is clicked, that group is shown and so on until the user gets to the artist she's looking for.
</p>
<p><img src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/AlbumsPage.png">
</p>
<p><strong>Figure 3: The Albums page</strong> </p>
<p>The Albums page (Figure 3) shows all of the albums, including artwork, for an artist. Clicking the album name brings the user to the Album page, and clicking the play button will launch a playlist containing the tracks in the album in Windows Media Player.
</p>
<p><img height="530" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/AlbumPage.png" width="500">
</p>
<p><strong>Figure 4: The Album page</strong> </p>
<p>The Album page shows the large album artwork and tracks. The user can play the entire album or individual tracks.<br>
Each page uses object data binding and shares a common master page. ASP.NET has a new feature called Master Pages, which allows you to create a consistent layout for the Web site and share common functionality without having to duplicate it for every page.
 The Master Page in this website is used to share Cascading Style Sheet (CSS) information and host the &quot;breadcrumb bar,&quot; or SiteMapPath, as it's called in ASP.NET. I won't be covering the SiteMapPath control, but the implementation should be interesting to
 anyone that needs to drive one programmatically and not through a static definition. More information about master pages can be found in Fritz Onion's
<a href="http://msdn.microsoft.com/msdnmag/issues/04/06/ASPNET20MasterPages/default.aspx">
Master Your Site Design with Visual Inheritance and Page Templates</a>.<br>
The Albums and Album page also have images and play buttons that launch Windows Media Player playlist files. Album images are cached in a Web-accessible directory, and WMP playlist files are generated on the fly in an HTTP handler. Both of these topics are
 covered below.<br>
<strong>Binding objects to grids<br>
</strong>The new ObjectDataSource is the middleman between the grid and objects. It must be configured to work with a class to return data requested by the grid. We'll step through creating the data source for the Albums page.
</p>
<p><img height="387" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/ObjectDataSourceWizard1.png" width="500">
</p>
<p><strong>Figure 5: The ObjectDataSource wizard, first page</strong> </p>
<p>The &quot;Binder&quot; class is selected as the business object. The objects used to represent the artists, et al, are simple and primarily represent data, and therefore don't have much in the way of persistence methods. The Binder class provides the functionality
 that may otherwise be handled by the objects themselves. </p>
<p><img height="387" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/ObjectDataSourceWizard2.png" width="500">
</p>
<p><strong>Figure 6: The ObjectDataSource wizard, second page</strong> </p>
<p>The next page is where we choose which method we would like to use to get the data, so we pick GetAlbums. Update, Insert and Delete methods can also be specified, but are not required.
</p>
<p><img height="387" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/913360/ObjectDataSourceWizard3.png" width="500">
</p>
<p><strong>Figure 7: The ObjectDataSource wizard, third page</strong> </p>
<p>The final page specifies where the parameter values will come from. The value can come from one of the following: Cookie, Control, Form, Profile, QueryString and Session.
<br>
Once the data source is configured, setting it as the DataSource of the grid is all that's required to make the magic happen.<br>
<strong>Generating custom grid values<br>
</strong>All of the grids use template columns and custom code to generate their hyperlinks. For example, the artists page (figure 2) consists of a single GridView that's bound to a generic List of RangeListItems. RangeListItem is a custom class that represents
 the first and last artist IDs in the range, as well as the text that is displayed in the grid. The hyperlinks are created by using a template field in the grid and calling a method in the page's server-side script section.
</p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">&lt;</span><span class="html">asp:TemplateField</span> <span class="attr">ItemStyle-HorizontalAlign</span>=<span class="attr">Center</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">ItemTemplate</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;</span><span class="html">asp:HyperLink</span> <span class="attr">ID</span><span class="kwrd">=&quot;HyperLink1&quot;</span> <span class="attr">runat</span><span class="kwrd">=&quot;server&quot;</span> <span class="attr">NavigateURL</span><span class="kwrd">='&lt;%# CreateNavigateURL(GetDataItem() %&gt;'</span>
            <span class="attr">Text</span><span class="kwrd">='&lt;%# Eval(&quot;Text&quot;) %&gt;'</span><span class="kwrd">&gt;&lt;/</span><span class="html">asp:HyperLink</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;/</span><span class="html">ItemTemplate</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">asp:TemplateField</span><span class="kwrd">&gt;</span></pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>The NavigateUrl attribute is populated by a call to the CreateNavigateUrl method:
<p><b>Visual C#</b> </p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">string</span> CreateNavigateUrl(<span class="kwrd">object</span> o)
    {
        RangeListItem listItem = (RangeListItem)o;

        <span class="kwrd">if</span> (listItem.StartIndex == listItem.EndIndex)
        {
            <span class="kwrd">return</span> <span class="str">&quot;albums.aspx?artist=&quot;</span> &#43; listItem.StartIndex;
        }
        <span class="kwrd">else</span>
        {
<span class="kwrd">return</span> <span class="kwrd">string</span>.Format(<span class="str">&quot;default.aspx?start={0}&amp;end={1}&quot;</span>, listItem.StartIndex, listItem.EndIndex);
        }
    }
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b> </p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">Private</span> <span class="kwrd">Function</span> CreateNavigateUrl(<span class="kwrd">ByVal</span> o <span class="kwrd">As</span> <span class="kwrd">Object</span>) <span class="kwrd">As</span> <span class="kwrd">String</span>
        <span class="kwrd">Dim</span> listItem <span class="kwrd">As</span> RangeListItem = <span class="kwrd">CType</span>(o, RangeListItem)
        
        <span class="kwrd">If</span> (listItem.StartIndex = listItem.EndIndex) <span class="kwrd">Then</span>
            <span class="kwrd">Return</span> <span class="str">&quot;albums.aspx?artist=&quot;</span> &amp; listItem.StartIndex
        <span class="kwrd">Else</span>
            <span class="kwrd">Return</span> <span class="kwrd">String</span>.Format(<span class="str">&quot;default.aspx?start={0}&amp;end={1}&quot;</span>, listItem.StartIndex, listItem.EndIndex)
        <span class="kwrd">End</span> <span class="kwrd">If</span>
<span class="kwrd">End</span> <span class="kwrd">Function</span>
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>More information about templates in ASP.NET 2.0 can be found in Dino Esposito's
<a href="http://msdn.microsoft.com/msdnmag/issues/04/08/GridView/default.aspx">Move Over DataGrid, There's a New Grid in Town!</a>.<br>
<strong>Displaying Album Art<br>
</strong><br>
Windows Media Player downloads album art, both large and small versions, when possible. These image files are stored in the same folders as the album tracks, but are hidden by default. These images must be copied to a child folder of the Web site so the browser
 can access them. This is done by first finding the correct image, then copying that file to a subfolder of the Web site and giving it a unique name to so it can be more easily retrieved the next time it's required. If album art isn't found, a small transparent
 image is used instead.
<p><b>Visual C#</b> </p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">string</span> GetAlbumArtUrl(AlbumArtSize size)
    {
        <span class="kwrd">string</span> albumArtFileName = GetCustomAlbumArtFileName(size);
<span class="kwrd">string</span> albumArtFullFilename = Path.Combine(_albumArtDirectory, 
    albumArtFileName);

        <span class="kwrd">bool</span> fileExists = File.Exists(albumArtFullFilename);

        <span class="kwrd">if</span> (!fileExists)
        {
            <span class="kwrd">string</span> dir = Path.GetDirectoryName(Tracks[0].Location);
            <span class="kwrd">string</span> filename;

            <span class="kwrd">if</span> (Directory.Exists(dir))
            {
                filename = GetRealAlbumArtFileName(dir, size);

                <span class="kwrd">if</span> (filename != <span class="kwrd">null</span>)
                {
                    File.Copy(filename, albumArtFullFilename);
                    File.SetAttributes(albumArtFullFilename, 
                        FileAttributes.Normal);
                    fileExists = <span class="kwrd">true</span>;
                }
            }
        }

        <span class="kwrd">string</span> url;

        <span class="kwrd">if</span> (fileExists)
        {
            url = <span class="str">&quot;Images/AlbumArt/&quot;</span> &#43; albumArtFileName;
        }
        <span class="kwrd">else</span>
        {
            url = <span class="str">&quot;Images/dot.gif&quot;</span>;
        }

        <span class="kwrd">return</span> url;
    }

</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b><code>&nbsp;&nbsp;&nbsp; </code></p>
<pre class="csharpcode"><span class="kwrd">Public</span> <span class="kwrd">Function</span> GetAlbumArtUrl(<span class="kwrd">ByVal</span> size <span class="kwrd">As</span> AlbumArtSize) <span class="kwrd">As</span> <span class="kwrd">String</span>
        <span class="kwrd">Dim</span> albumArtFileName <span class="kwrd">As</span> <span class="kwrd">String</span> = GetCustomAlbumArtFileName(size)
        <span class="kwrd">Dim</span> albumArtFullFilename <span class="kwrd">As</span> <span class="kwrd">String</span> = _
            Path.Combine(_albumArtDirectory, albumArtFileName)

        <span class="kwrd">Dim</span> fileExists <span class="kwrd">As</span> <span class="kwrd">Boolean</span> = File.Exists(albumArtFullFilename)

        <span class="kwrd">If</span> <span class="kwrd">Not</span> fileExists <span class="kwrd">Then</span>
            <span class="kwrd">Dim</span> dir <span class="kwrd">As</span> <span class="kwrd">String</span> = Path.GetDirectoryName(Tracks(0).Location)
            <span class="kwrd">Dim</span> filename <span class="kwrd">As</span> <span class="kwrd">String</span>

            <span class="kwrd">If</span> Directory.Exists(dir) <span class="kwrd">Then</span>
                filename = GetRealAlbumArtFileName(dir, size)

                <span class="kwrd">If</span> <span class="kwrd">Not</span> filename <span class="kwrd">Is</span> <span class="kwrd">Nothing</span> <span class="kwrd">Then</span>
                    File.Copy(filename, albumArtFullFilename)
                    File.SetAttributes(albumArtFullFilename, _
                        FileAttributes.Normal)
                    fileExists = <span class="kwrd">True</span>
                <span class="kwrd">End</span> <span class="kwrd">If</span>
            <span class="kwrd">End</span> <span class="kwrd">If</span>
        <span class="kwrd">End</span> <span class="kwrd">If</span>

        <span class="kwrd">Dim</span> url <span class="kwrd">As</span> <span class="kwrd">String</span>

        <span class="kwrd">If</span> fileExists <span class="kwrd">Then</span>
            url = <span class="str">&quot;Images/AlbumArt/&quot;</span> &amp; albumArtFileName
        <span class="kwrd">Else</span>
            url = <span class="str">&quot;Images/dot.gif&quot;</span>
        <span class="kwrd">End</span> <span class="kwrd">If</span>

        <span class="kwrd">Return</span> url
    <span class="kwrd">End</span> Function</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><br>
The GetCustomAlbumArtFileName method creates a consistent name that is used to save the copy of the image and more easily retrieve it in subsequent requests:
<p><b>Visual C#</b><code>&nbsp;&nbsp;&nbsp;</code> </p>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">string</span> GetCustomAlbumArtFileName(AlbumArtSize size)
    {
        <span class="kwrd">return</span> <span class="kwrd">string</span>.Format(<span class="str">&quot;{0}-{1}-{2}.jpg&quot;</span>, 
            GetAlphanumericString(_artist.Name), 
            GetAlphanumericString(Name),
            size.ToString());
    }

    <span class="kwrd">private</span> <span class="kwrd">string</span> GetAlphanumericString(<span class="kwrd">string</span> s)
    {
        StringBuilder sb = <span class="kwrd">new</span> StringBuilder();

        <span class="kwrd">foreach</span> (<span class="kwrd">char</span> c <span class="kwrd">in</span> s)
        {
            <span class="kwrd">if</span> (<span class="kwrd">char</span>.IsLetterOrDigit(c))
            {
                sb.Append(c);
            }
        }

        <span class="kwrd">return</span> sb.ToString();
    }
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b><code>&nbsp;&nbsp;</code> </p>
<pre class="csharpcode"> <span class="kwrd">Private</span> <span class="kwrd">Function</span> GetCustomAlbumArtFileName(<span class="kwrd">ByVal</span> size <span class="kwrd">As</span> AlbumArtSize) <span class="kwrd">As</span> <span class="kwrd">String</span>
        <span class="kwrd">Return</span> <span class="kwrd">String</span>.Format(<span class="str">&quot;{0}-{1}-{2}.jpg&quot;</span>, _
            GetAlphanumericString(_artist.Name), _
GetAlphanumericString(Name), size.ToString)
    <span class="kwrd">End</span> <span class="kwrd">Function</span>

    <span class="kwrd">Private</span> <span class="kwrd">Function</span> GetAlphanumericString(<span class="kwrd">ByVal</span> s <span class="kwrd">As</span> <span class="kwrd">String</span>) <span class="kwrd">As</span> <span class="kwrd">String</span>
        <span class="kwrd">Dim</span> sb <span class="kwrd">As</span> StringBuilder = <span class="kwrd">New</span> StringBuilder

        <span class="kwrd">For</span> <span class="kwrd">Each</span> c <span class="kwrd">As</span> <span class="kwrd">Char</span> <span class="kwrd">In</span> s
            <span class="kwrd">If</span> <span class="kwrd">Char</span>.IsLetterOrDigit(c) <span class="kwrd">Then</span>
                sb.Append(c)
            <span class="kwrd">End</span> <span class="kwrd">If</span>
        <span class="kwrd">Next</span>

        <span class="kwrd">Return</span> sb.ToString()
    <span class="kwrd">End</span> <span class="kwrd">Function</span>
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>To make things even more interesting, album art filenames can match any of the following patterns:
<p><strong>Large</strong><br>
AlbumArt_{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}_Large.jpg </p>
<p>AlbumArt__Large.jpg </p>
<p>Folder.jpg </p>
<p><strong>Small</strong><br>
AlbumArt_{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}_Small.jpg </p>
<p>AlbumArt__Small.jpg </p>
<p>AlbumArtSmall.jpg </p>
<p>GetRealAlbumArtFileName gets the filename for the album art, if it exists, by searching for the patterns above:
</p>
<p><b>Visual C#</b><code>&nbsp;&nbsp;&nbsp;</code> </p>
<pre class="csharpcode"><span class="kwrd">private</span> <span class="kwrd">string</span> GetRealAlbumArtFileName(<span class="kwrd">string</span> dir, AlbumArtSize size)
    {
        <span class="kwrd">string</span> filename = <span class="kwrd">null</span>;
        <span class="kwrd">string</span>[] filenames = 
Directory.GetFiles(dir, <span class="str">&quot;AlbumArt*&quot;</span> &#43; size &#43; <span class="str">&quot;.jpg&quot;</span>);

        <span class="kwrd">if</span> (filenames.Length &gt; 0)
        {
            filename = filenames[0];
        }
        <span class="kwrd">else</span> <span class="kwrd">if</span> (size == AlbumArtSize.Large)
        {
            FileInfo file = <span class="kwrd">new</span> FileInfo(Path.Combine(dir, <span class="str">&quot;Folder.jpg&quot;</span>));
            
            <span class="kwrd">if</span> (file.Exists)
            {
                filename = file.FullName;
            }
        }
        <span class="kwrd">return</span> filename;
    }
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b> </p>
<pre><code></code></pre>
<pre class="csharpcode"><span class="kwrd">Private</span> <span class="kwrd">Function</span> GetRealAlbumArtFileName(<span class="kwrd">ByVal</span> dir <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> size <span class="kwrd">As</span> AlbumArtSize) <span class="kwrd">As</span> <span class="kwrd">String</span>
        <span class="kwrd">Dim</span> filename <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="kwrd">Nothing</span>
        <span class="kwrd">Dim</span> filenames() <span class="kwrd">As</span> <span class="kwrd">String</span> = _
Directory.GetFiles(dir, <span class="str">&quot;AlbumArt*&quot;</span> &amp; size.ToString() &amp; <span class="str">&quot;.jpg&quot;</span>)

        <span class="kwrd">If</span> (filenames.Length &gt; 0) <span class="kwrd">Then</span>
            filename = filenames(0)
        <span class="kwrd">ElseIf</span> (size = AlbumArtSize.Large) <span class="kwrd">Then</span>
            <span class="kwrd">Dim</span> file <span class="kwrd">As</span> FileInfo = _
<span class="kwrd">New</span> FileInfo(Path.Combine(dir, <span class="str">&quot;Folder.jpg&quot;</span>))
            <span class="kwrd">If</span> file.Exists <span class="kwrd">Then</span>
                filename = file.FullName
            <span class="kwrd">End</span> <span class="kwrd">If</span>
        <span class="kwrd">End</span> <span class="kwrd">If</span>

        <span class="kwrd">Return</span> filename
    <span class="kwrd">End</span> Function</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><br>
<strong>Creating a Playlist<br>
</strong><br>
Playlists are created by the PlaylistCreator class, which is a lightweight web request handler that implements the
<a href="http://msdn2.microsoft.com/library/7ezc17x8%28en-us,vs.80%29.aspx">IHttpHandler interface</a>. The handler is quite simple: It writes out a header, notifying the browser that the type of content it will be returning is &quot;video/x-ms-asf&quot;, then creates
 and returns an XML playlist. The content type header helps the browser determine what to do with the content, and is a very important step since we want to play the playlist, not show it in the browser.<br>
PlaylistCreator writes directly to the response stream with an XmlTextWriter:
<p><b>Visual C#</b><code>&nbsp;&nbsp;&nbsp; </code></p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">void</span> ProcessRequest(HttpContext context)
    {
        _trackIndex = QueryStringHelper.TrackIndex;
        _artistIndex = QueryStringHelper.ArtistIndex;
        _albumIndex = QueryStringHelper.AlbumIndex;

        context.Response.ContentType = <span class="str">&quot;video/x-ms-asf&quot;</span>;

        StreamWriter streamWriter = 
<span class="kwrd">new</span> StreamWriter(context.Response.OutputStream);
        _writer = <span class="kwrd">new</span> XmlTextWriter(streamWriter);
        _writer.WriteProcessingInstruction(<span class="str">&quot;wpl&quot;</span>, <span class="str">&quot;version=\&quot;1.0\&quot;&quot;</span>);

        _writer.WriteStartElement(<span class="str">&quot;smil&quot;</span>);

        CreateHead();
        
        _writer.WriteStartElement(<span class="str">&quot;body&quot;</span>);
        _writer.WriteStartElement(<span class="str">&quot;seq&quot;</span>);

        <span class="kwrd">if</span> (_trackIndex.HasValue)
        {
            CreateTrackEntry();
        }
        <span class="kwrd">else</span> <span class="kwrd">if</span> (_albumIndex.HasValue)
        {
            CreateAlbumEntries();
        }

        _writer.WriteEndElement();
        _writer.WriteEndElement();
        _writer.WriteEndElement();

        _writer.Close();
    }
</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b><code>&nbsp;&nbsp;&nbsp;</code> </p>
<pre class="csharpcode"><span class="kwrd">Public</span> <span class="kwrd">Sub</span> ProcessRequest(<span class="kwrd">ByVal</span> context <span class="kwrd">As</span> HttpContext) <span class="kwrd">Implements</span> IHttpHandler.ProcessRequest
        _trackIndex = QueryStringHelper.TrackIndex
        _artistIndex = QueryStringHelper.ArtistIndex
        _albumIndex = QueryStringHelper.AlbumIndex

        context.Response.ContentType = <span class="str">&quot;video/x-ms-asf&quot;</span>

        <span class="kwrd">Dim</span> streamWriter <span class="kwrd">As</span> StreamWriter = _
<span class="kwrd">New</span> StreamWriter(context.Response.OutputStream)
        _writer = <span class="kwrd">New</span> XmlTextWriter(streamWriter)
        _writer.WriteProcessingInstruction(<span class="str">&quot;wpl&quot;</span>, <span class="str">&quot;version=\&quot;</span><span class="str">&quot;1.0\&quot;</span><span class="str">&quot;&quot;</span>)

        _writer.WriteStartElement(<span class="str">&quot;smil&quot;</span>)

        CreateHead()

        _writer.WriteStartElement(<span class="str">&quot;body&quot;</span>)
        _writer.WriteStartElement(<span class="str">&quot;seq&quot;</span>)

        <span class="kwrd">If</span> _trackIndex.HasValue <span class="kwrd">Then</span>
            CreateTrackEntry()
        <span class="kwrd">ElseIf</span> _albumIndex.HasValue <span class="kwrd">Then</span>
            CreateAlbumEntries()
        <span class="kwrd">End</span> <span class="kwrd">If</span>

        _writer.WriteEndElement()
        _writer.WriteEndElement()
        _writer.WriteEndElement()

        _writer.Close()
    <span class="kwrd">End</span> Sub</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style><br>
<strong>Registering the Playlist Creator<br>
</strong>ASP.NET knows how to handle requests to ASPX pages: it finds the page with the same name and executes it. HTTP handlers are handled a bit differently. An entry must be made in the web.config file associating a file name or pattern with the type of
 the object to use to handle the request. The XML fragment below is from the configuration/system.web section of the web.config file. It's instructing ASP.NET to direct any request with an extention of &quot;wpl&quot; to an object of type &quot;PlaylistCreator&quot;.&nbsp;
<pre class="csharpcode"><span class="kwrd">&lt;</span><span class="html">httpHandlers</span><span class="kwrd">&gt;</span>



          <span class="kwrd">&lt;</span><span class="html">add</span> <span class="attr">path</span><span class="kwrd">=&quot;*.wpl&quot;</span> <span class="attr">type</span><span class="kwrd">=&quot;PlaylistCreator&quot;</span> <span class="attr">verb</span><span class="kwrd">=&quot;*&quot;</span> <span class="attr">validate</span><span class="kwrd">=&quot;false&quot;</span> <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">httpHandlers</span><span class="kwrd">&gt;</span></pre>
<pre class="csharpcode"></pre>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts/RSS&WT.dl=0&WT.entryid=Entry:RSSView:b72d6c0acfa44f7e9a4f9e7600daa991">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Building-a-Web-Site-with-ASP-NET-20-to-Navigate-Your-Music-Library</comments>
      <itunes:summary>



&amp;nbsp;
This article focuses on a common solution for managing shared music files. It is accomplished by keeping shared music files on a single computer and have other computers&#39; media libraries point to, and stay current
 with, the files on that computer.



Jeff Key


Difficulty: Intermediate
Time Required: 
1-3 hours
Cost: Free
Software: Visual Studio Express Editions
Hardware: 
Download: Download








&amp;nbsp; 
Introduction
It&#39;s easy to assume that most people with computers have media libraries. As computers become cheaper, easier to network and easier to use, people are more likely to create home networks. Since servers aren&#39;t practical in a home setting, information
 is distributed amongst the computers in the network, which makes managing and finding formation difficult. This is probably most evident in music libraries.
Most media players maintain a private database containing all of the songs they&#39;re aware of. Keeping these databases up-to-date can be a chore on a single computer, and keeping databases current on several computers often seems impossible.

A common solution is to keep all music files on a single computer and have other computers&#39; media libraries point to, and stay current with, the files on that computer. Anyone that has tried this in practice knows that it&#39;s an adequate solution, but not an
 ideal one. Personally, I just want to listen to music, not manage it.
My home setup consists of a Windows Media Center computer that drives my TV and has all of my music on it, my workstation, and occasionally a laptop. Keeping the workstation current with the music on the Media Center has always been a hassle, and doing so on
 the laptop just isn&#39;t practical. I wanted a simple, clean way to listen to music anywhere in my apartment, from any computer, without having to worry about the accuracy of the library. I chose to build a little website whose only purpose would be to get me
 to the music I wanted to listen to as painlessly as possible. I used </itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Building-a-Web-Site-with-ASP-NET-20-to-Navigate-Your-Music-Library</link>
      <pubDate>Tue, 31 Oct 2006 15:40:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Building-a-Web-Site-with-ASP-NET-20-to-Navigate-Your-Music-Library</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/913360_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/913360_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Jeff Key</dc:creator>
      <itunes:author>Jeff Key</itunes:author>
      <slash:comments>25</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Building-a-Web-Site-with-ASP-NET-20-to-Navigate-Your-Music-Library/RSS</wfw:commentRss>
    </item>
  <item>
      <title>Creating a System Monitor</title>
      <description><![CDATA[<span id="c4fmetadata">
<table class="" cellspacing="0" cellpadding="1" width="100%" border="0">
<tbody>
<tr class="entry_overview">
<td class="" width="50">&nbsp;</td>
<td class=""><span class="entry_description">This is a utility that can host “monitors” and provide a means to communicate to the user in a configurable, extensible way.</span></td>
</tr>
<tr>
<td class="" colspan="2">
<div class="entry_author">Jeff Key</div>
<div class="entry_company"><a href="http://www.sliver.com/"></a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Intermediate</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
3-6 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/"></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input"></span></div>
<div class="entry_details"><b>Download: </b>
<ul>
<li></a><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/912382/SystemMonitorCS_RTM.msi">C# Download</a>
</li><li><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/912382/SystemMonitorVB_RTM.msi">VB Download</a></li></ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<p>&nbsp;</p>
<h3>Introduction</h3>
<p>I lost one hard drive at home and two at the office in less than six months, and in each case the event log had included warnings of impending doom. I don't check event logs often, so the warnings went unnoticed. Utilities exist to monitor hard drive health,
 but it looks like USB hard drives aren't supported yet. This is a problem since I have three at home and two fairly important ones at the office.
</p>
<h3>The problem</h3>
<p>Software exists to monitor my internal hard drives, but not the external ones. Drives on remote machines need to be monitored and alert me when their drives show signs of failure.
</p>
<p>I would like other things monitored, too. System availability, hard drive space, and so on, need to be watched -- especially on critical systems. Utilities of all kinds exist to monitor these things, but maintenance and configuration of disparate utilities
 can be a hassle. </p>
<h3>The solution</h3>
<p>The solution is simple: A utility that can host “monitors” and provide a means to communicate to the user in a configurable, extensible way. Any number of “notifiers” can be associated with a “monitor” and new “notifiers” can be written to allow any kind
 of communication. </p>
<p><img height="194" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/912382/image001.jpg" width="500">
</p>
<p>Figure 1 – System Monitor main window </p>
<p>The following Monitors are included: </p>
<ul>
<li>Disk space: Watches for a configurable amount of free space on a hard drive. </li><li>Event log: Watches for entries in an event log for entries posted by a specified source.
</li><li>Network availability: Notifies the user when the computer's network connection is lost or restored.
</li><li>Ping: Pings a computer and notifies the user if a ping fails </li></ul>
<p>The following Notifiers are included: </p>
<ul>
<li>Balloon tip: Shows a balloon near the icon in the notification area (see image below).
</li><li>Email: Sends an email to a user-configurable address. </li><li>Message box: Displays a message box to the logged-in user. </li></ul>
<h3>Requirements</h3>
<p>The application will: </p>
<ul>
<li>Host custom Monitors. </li><li>Provide an interface for custom Notifiers. </li><li>Provide a generic configuration system using the application's config file, allowing settings to be passed to Monitors and Notifiers.
</li><li>Allow for the association of Notifiers to Monitors. </li><li>Provide two run modes for Monitors: Persistent and Scheduled.
<ul>
<li>Scheduled is run at a specified interval by the application. </li><li>Persistent is never run by the application. </li></ul>
</li><li>Run Scheduled “monitors” at the interval specified in the configuration file </li><li>Allow on-demand execution of Scheduled Monitors. </li><li>Run in the notification area. </li></ul>
<p>We will build this solution using Visual Studio 2005 Beta 2, specifically Visual C# Express, and at the end of this article I will point out some features that make Visual Studio 2005 a compelling choice over previous versions. To download Visual C# Express
 Edition Beta 2, or any of the other Express editions, visit <a href="http://msdn.microsoft.com/express">
http://msdn.microsoft.com/express</a>. </p>
<h3>Architecture</h3>
<p>The architecture is simple: The MainForm class handles the loading of Monitors and Notifiers, which are types that implement IMonitor and INotifier, respectively. Monitors are added to Tasks, which are responsible for running the Monitors on a schedule and
 maintaining status information. The MainForm displays information maintained by the Tasks and provides a means for executing a Monitor on demand.
</p>
<p>The application configuration file plays an important role. It indicates which Monitors to load, which Notifiers should be associated with them, and what settings apply to both. The application itself is just a host and wouldn't do anything without the Monitor/Notifier
 definitions contained in the configuration file. </p>
<p>A typical Monitor definition is shown below. It represents a DiskSpaceMonitor, is run every four minutes and watches the C drive for free disk space below 10,000 megabytes. The Monitor has two Notifiers associated with it: A MessageBoxNotifier and an EmailNotifier.
 The former doesn't have any settings and the latter has three.</p>
<pre class="csharpcode"><span class="kwrd"></span><span class="kwrd">&lt;</span><span class="html">monitor</span> <span class="attr">runFrequency</span><span class="kwrd">=&quot;00:04&quot;</span> <span class="attr">type</span><span class="kwrd">=&quot;SystemMonitor.Monitors.DiskSpaceMonitor,SystemMonitor&quot;</span><span class="kwrd">&gt;</span><br>  <span class="kwrd">&lt;</span><span class="html">settings</span><span class="kwrd">&gt;</span><br>    <span class="kwrd">&lt;</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">=&quot;driveLetter&quot;</span> <span class="attr">value</span><span class="kwrd">=&quot;C&quot;</span> <span class="kwrd">/&gt;</span><br>    <span class="kwrd">&lt;</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">=&quot;freeMegabytes&quot;</span> <span class="attr">value</span><span class="kwrd">=&quot;10000&quot;</span> <span class="kwrd">/&gt;</span><br>  <span class="kwrd">&lt;/</span><span class="html">settings</span><span class="kwrd">&gt;</span><br>  <span class="kwrd">&lt;</span><span class="html">notifiers</span><span class="kwrd">&gt;</span><br>    <span class="kwrd">&lt;</span><span class="html">notifier</span> <span class="attr">type</span><span class="kwrd">=&quot;SystemMonitor.Notifiers.MessageBoxNotifier,SystemMonitor&quot;</span> <span class="kwrd">/&gt;</span><br>    <span class="kwrd">&lt;</span><span class="html">notifier</span> <span class="attr">type</span><span class="kwrd">=&quot;SystemMonitor.Notifiers.EmailNotifier,SystemMonitor&quot;</span><span class="kwrd">&gt;</span><br>      <span class="kwrd">&lt;</span><span class="html">settings</span><span class="kwrd">&gt;</span><br>        <span class="kwrd">&lt;</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">=&quot;host&quot;</span> <span class="attr">value</span><span class="kwrd">=&quot;mail.someDomain.com&quot;</span> <span class="kwrd">/&gt;</span><br>        <span class="kwrd">&lt;</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">=&quot;to&quot;</span> <span class="attr">value</span><span class="kwrd">=&quot;someEmail@someDomain.com&quot;</span> <span class="kwrd">/&gt;</span><br>        <span class="kwrd">&lt;</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">=&quot;from&quot;</span> <span class="attr">value</span><span class="kwrd">=&quot;someEmail@someDomain.com&quot;</span> <span class="kwrd">/&gt;</span><br>      <span class="kwrd">&lt;/</span><span class="html">settings</span><span class="kwrd">&gt;</span><br>    <span class="kwrd">&lt;/</span><span class="html">notifier</span><span class="kwrd">&gt;</span><br>  <span class="kwrd">&lt;/</span><span class="html">notifiers</span><span class="kwrd">&gt;</span><br><span class="kwrd">&lt;/</span><span class="html">monitor</span><span class="kwrd">&gt;</span> <br></pre>
<h3>Implementation</h3>
<h4>Startup</h4>
<p>The application configuration file is read. Monitors and Notifiers are created and their settings are passed to them in their respective Initialize methods. Notifiers are associated with Monitors and Monitors are assigned Tasks.
</p>
<h4>Normal operation</h4>
<p>Persistent Monitors are responsible for their own operation and are left to manage themselves. Tasks for scheduled Monitors invoke the Monitors' Execute methods on a new thread at the interval specified in the configuration file. The MainForm's display is
 updated as scheduled Monitors run and allows for the explicit execution of a Monitor via context menu.
</p>
<h3>Technology highlights </h3>
<p>All of the following are new in version 2 of the .NET Framework. </p>
<h4>ListView grouping</h4>
<p>ListView grouping is easy to use and can make the display easier to read on Windows XP and Windows Server 2003 operating systems. Groups are created by adding ListViewGroup items to a ListView's Groups collection. ListViewItems are added to groups by assigning
 their Groups property the value of an existing ListViewGroup. </p>
<h4>NotifyIcon balloon tips</h4>
<p>Displaying a balloon tip on a notification icon is as easy as calling a single method: NotifyIcon.ShowBalloonTip. Clicked, Closed and Shown events are also available.
</p>
<p><img height="92" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/912382/image002.jpg" width="199" border="0">
</p>
<p>Figure 2 – NotifyIcon balloon tip </p>
<h4>New configuration section handling</h4>
<p>Previous versions of the .NET Framework required manual parsing of custom configuration sections. Version 2 includes completely revamped configuration handling, using strongly-typed class access.
</p>
<p><b>Visual C#</b> </p>
<pre class="csharpcode"><span class="kwrd">class</span> MonitorElement : ConfigurationElement<br>{<br>       [ConfigurationProperty(<span class="str">&quot;runFrequency&quot;</span>)]<br>       <span class="kwrd">public</span> TimeSpan? RunFrequency<br>       {<br>             get { <span class="kwrd">return</span> (TimeSpan?)<span class="kwrd">base</span>[<span class="str">&quot;runFrequency&quot;</span>]; }<br>             set { <span class="kwrd">base</span>[<span class="str">&quot;runFrequency&quot;</span>] = <span class="kwrd">value</span>; }<br>       }<br> <br>       [ConfigurationProperty(<span class="str">&quot;type&quot;</span>)]<br>       <span class="kwrd">public</span> <span class="kwrd">string</span> TypeName<br>      {<br>              get { <span class="kwrd">return</span> (<span class="kwrd">string</span>)<span class="kwrd">base</span>[<span class="str">&quot;type&quot;</span>]; }<br>             set { <span class="kwrd">base</span>[<span class="str">&quot;type&quot;</span>] = <span class="kwrd">value</span>; }<br>       }<br> <br>       [ConfigurationProperty(<span class="str">&quot;settings&quot;</span>, <br>        IsDefaultCollectionProperty = <span class="kwrd">false</span>)]<br>       <span class="kwrd">public</span> SettingElementCollection Settings<br>       {<br>             get <br>            { <span class="kwrd">return</span> (SettingElementCollection)<span class="kwrd">base</span>[<span class="str">&quot;settings&quot;</span>]; }<br>       }<br> <br>       [ConfigurationProperty(<span class="str">&quot;notifiers&quot;</span>, <br>        IsDefaultCollectionProperty = <span class="kwrd">false</span>)]<br>       <span class="kwrd">public</span> NotifierElementCollection Notifiers<br>       {<br>            get { <span class="kwrd">return</span> (NotifierElementCollection)<span class="kwrd">base</span>[<br>                  <span class="str">&quot;notifiers&quot;</span>]; }<br>       }<br>}</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b> </p>
<pre class="csharpcode"><span class="kwrd">Class</span> MonitorElement<br>    <span class="kwrd">Inherits</span> ConfigurationElement<br><br>    &lt;ConfigurationProperty(<span class="str">&quot;runFrequency&quot;</span>)&gt; _<br>        <span class="kwrd">Property</span> RunFrequency() <span class="kwrd">As</span> Nullable(Of TimeSpan)<br>        <span class="kwrd">Get</span><br>            <span class="kwrd">Return</span> <span class="kwrd">CType</span>(<span class="kwrd">MyBase</span>.Item(<span class="str">&quot;runFrequency&quot;</span>), <br>            Nullable(Of TimeSpan))<br>       <span class="kwrd">End</span> <span class="kwrd">Get</span><br>        <span class="kwrd">Set</span>(<span class="kwrd">ByVal</span> value <span class="kwrd">As</span> Nullable(Of TimeSpan))<br>            <span class="kwrd">MyBase</span>.Item(<span class="str">&quot;runFrequency&quot;</span>) = value<br>        <span class="kwrd">End</span> <span class="kwrd">Set</span><br>    <span class="kwrd">End</span> <span class="kwrd">Property</span><br><br>    &lt;ConfigurationProperty(<span class="str">&quot;type&quot;</span>)&gt; _<br>    <span class="kwrd">Public</span> <span class="kwrd">Property</span> TypeName() <span class="kwrd">As</span> <span class="kwrd">String</span><br>        <span class="kwrd">Get</span><br>            <span class="kwrd">Return</span> <span class="kwrd">CStr</span>(<span class="kwrd">MyBase</span>.Item(<span class="str">&quot;type&quot;</span>))<br>        <span class="kwrd">End</span> <span class="kwrd">Get</span><br>        <span class="kwrd">Set</span>(<span class="kwrd">ByVal</span> value <span class="kwrd">As</span> <span class="kwrd">String</span>)<br>            <span class="kwrd">MyBase</span>.Item(<span class="str">&quot;type&quot;</span>) = value<br>        <span class="kwrd">End</span> <span class="kwrd">Set</span><br>    <span class="kwrd">End</span> <span class="kwrd">Property</span><br><br>    &lt;ConfigurationProperty(<span class="str">&quot;settings&quot;</span>, IsDefaultCollection:=<span class="kwrd">False</span>)&gt; _<br>    <span class="kwrd">Public</span> <span class="kwrd">ReadOnly</span> <span class="kwrd">Property</span> Settings() <span class="kwrd">As</span> SettingElementCollection<br>        <span class="kwrd">Get</span><br>            <span class="kwrd">Return</span> <span class="kwrd">CType</span>(<span class="kwrd">MyBase</span>.Item(<span class="str">&quot;settings&quot;</span>), <br>            SettingElementCollection)<br>        <span class="kwrd">End</span> <span class="kwrd">Get</span><br>    <span class="kwrd">End</span> <span class="kwrd">Property</span><br><br>    &lt;ConfigurationProperty(<span class="str">&quot;notifiers&quot;</span>, IsDefaultCollection:=<span class="kwrd">False</span>)&gt; _<br>    <span class="kwrd">Public</span> <span class="kwrd">ReadOnly</span> <span class="kwrd">Property</span> Notifiers() <span class="kwrd">As</span> NotifierElementCollection<br>        <span class="kwrd">Get</span><br>            <span class="kwrd">Return</span> <span class="kwrd">CType</span>(<span class="kwrd">MyBase</span>.Item(<span class="str">&quot;notifiers&quot;</span>),  <br>             NotifierElementCollection)<br>        <span class="kwrd">End</span> <span class="kwrd">Get</span><br>    <span class="kwrd">End</span> <span class="kwrd">Property</span><br><span class="kwrd">End</span> Class</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h4>Improved SMTP support</h4>
<p>Simple SMTP support in previous versions of the .NET Framework was dismal. IIS was required, as was its SMTP component, which was no picnic to correctly configure. I stumbled upon the new SMTP support recently and it was a wonderful surprise. The snippet
 below just works. No configuration necessary! </p>
<p><b>Visual C#</b> </p>
<pre class="csharpcode">SmtpClient mailClient = <span class="kwrd">new</span> SmtpClient();<br>mailClient.Host = _host;<br>mailClient.Send(_to, _from, title, message);</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<p><b>Visual Basic</b> </p>
<pre class="csharpcode"><span class="kwrd">Dim</span> mailClient <span class="kwrd">As</span> <span class="kwrd">New</span> SmtpClient()<br>mailClient.Host = _host<br>mailClient.Send(_to, _from, title, message)</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h4>Network</h4>
<p>A whole new namespace exists within System.Net: NetworkInformation. The Ping and NetworkChanges classes are used in this sample. Many more are available and worth checking out.
</p>
<h4>Generic collections</h4>
<p>It's no mistake that System.Collections.Generic has replaced System.Collections in class templates. Two of the more significant, related benefits:
</p>
<ul>
<li>No longer need to write strongly-typed collections (most of the time). </li><li>Increased type safety over the standard collections. </li></ul>
<p>The result is less time spent writing code and fewer runtime errors. What more could you want?
</p>
<h4>Nullable types</h4>
<p>Representing the absence of a value type's value has been an issue since .NET 1.0, and people have solved it in very different ways. The generic Nullable structure provides a way for value types to have null values.
</p>
<p>Both Visual Basic and C# can use the generic Nullable structure, but only C# has a new type modifier: A question mark appended to the type name.
</p>
<p>In the example below, all expressions evaluate to true. </p>
<p><b>Visual C#</b> </p>
<pre class="csharpcode"><span class="kwrd">int</span>? int1 = <span class="kwrd">null</span>;<br><span class="kwrd">if</span> (int1 == <span class="kwrd">null</span>)<br>    MessageBox.Show(<span class="str">&quot;null&quot;</span>);<br><span class="kwrd">if</span> (!int1.HasValue)<br>    MessageBox.Show(<span class="str">&quot;has no value&quot;</span>);<br>int1 = 10;<br><span class="kwrd">if</span> (int1 == 10)<br>    MessageBox.Show(<span class="str">&quot;equal&quot;</span>);</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<pre><b>Visual Basic</b></pre>
<pre class="csharpcode"><span class="kwrd">Dim</span> int1 <span class="kwrd">As</span> Nullable(Of <span class="kwrd">Integer</span>) = <span class="kwrd">New</span> Nullable(Of <span class="kwrd">Integer</span>)<br><span class="kwrd">If</span> <span class="kwrd">Not</span> int1.HasValue <span class="kwrd">Then</span><br>    MessageBox.Show(<span class="str">&quot;null&quot;</span>)<br><span class="kwrd">End</span> <span class="kwrd">If</span><br><span class="kwrd">If</span> <span class="kwrd">Not</span> int1.HasValue <span class="kwrd">Then</span><br>    MessageBox.Show(<span class="str">&quot;has no value&quot;</span>)<br><span class="kwrd">End</span> <span class="kwrd">If</span><br>int1 = <span class="kwrd">New</span> Nullable(Of <span class="kwrd">Integer</span>)(10)<br><span class="kwrd">If</span> (int1.GetValueOrDefault <span class="kwrd">Is</span> 10) <span class="kwrd">Then</span><br>    MessageBox.Show(<span class="str">&quot;equal&quot;</span>)<br><span class="kwrd">End</span> If</pre>
<style type="text/css">
<!--
.csharpcode, .csharpcode 
	{font-size:small;
	color:black;
	font-family:consolas,"Courier New",courier,monospace;
	background-color:#ffffff}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	width:100%;
	margin:0em}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<h4>Easier threading with BackgroundWorker </h4>
<p>The BackgroundWorker class joins the Thread, ThreadPool, asynchronous delegate invocation and friends in the world of .NET Framework-based threading. As with all things threading, a cursory overview isn't sufficient. Allocate some time to learn the BackgroundWorker
 class if you think you'll need threading support in the future. The design accounts for common usage scenarios and is a valuable tool for anyone's bag of tricks.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Niners/c4f.Jeff-Key/Posts/RSS&WT.dl=0&WT.entryid=Entry:RSSView:3fc38c09635d443982bb9e7600db3918">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Creating-a-System-Monitor</comments>
      <itunes:summary>



&amp;nbsp;
This is a utility that can host “monitors” and provide a means to communicate to the user in a configurable, extensible way.



Jeff Key


Difficulty: Intermediate
Time Required: 
3-6 hours
Cost: Free
Software: 
Hardware: 
Download: 

C# Download
VB Download






&amp;nbsp; 
Introduction
I lost one hard drive at home and two at the office in less than six months, and in each case the event log had included warnings of impending doom. I don&#39;t check event logs often, so the warnings went unnoticed. Utilities exist to monitor hard drive health,
 but it looks like USB hard drives aren&#39;t supported yet. This is a problem since I have three at home and two fairly important ones at the office.
 
The problem
Software exists to monitor my internal hard drives, but not the external ones. Drives on remote machines need to be monitored and alert me when their drives show signs of failure.
 
I would like other things monitored, too. System availability, hard drive space, and so on, need to be watched -- especially on critical systems. Utilities of all kinds exist to monitor these things, but maintenance and configuration of disparate utilities
 can be a hassle.  
The solution
The solution is simple: A utility that can host “monitors” and provide a means to communicate to the user in a configurable, extensible way. Any number of “notifiers” can be associated with a “monitor” and new “notifiers” can be written to allow any kind
 of communication.  

 
Figure 1 – System Monitor main window  
The following Monitors are included:  

Disk space: Watches for a configurable amount of free space on a hard drive. Event log: Watches for entries in an event log for entries posted by a specified source.
Network availability: Notifies the user when the computer&#39;s network connection is lost or restored.
Ping: Pings a computer and notifies the user if a ping fails 
The following Notifiers are included:  

Balloon tip: Shows a balloon near the icon in the notification area (see image belo</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Creating-a-System-Monitor</link>
      <pubDate>Tue, 31 Oct 2006 14:39:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Creating-a-System-Monitor</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/912382_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/912382_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Jeff Key</dc:creator>
      <itunes:author>Jeff Key</itunes:author>
      <slash:comments>9</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Creating-a-System-Monitor/RSS</wfw:commentRss>
    </item>    
</channel>
</rss>