June 01, 2007

DocProject for Sandcastle 1.6.0 RC Preview

In this blog post you'll see and read about some of the new features of DocProject that will be released with the 1.6.0 Release Candidate. But first, I'd just like to say thanks to everyone who downloaded DocProject and provided feedback. I appreciate it. Please keep the comments and feedback coming!

If you're only interested in screen shots – scroll down :)

1.6.0 Changes

This is one of the biggest releases yet, IMO. Much of the code-base has been refactored to support new features and to improve existing ones. Changes were made not only to the source code, but also to the solution and project files, and to almost every aspect of DocProject. There were also several new additions to the code-base in the form of source code and new projects. But don't worry; the end-user experience has not changed other than to include new features and improve existing ones. And the source code, for the most part, is still basically the same. Your existing knowledge of how to use DocProject will still apply, but it will no longer be the whole story.

Please note that existing DocProjects and DocSites may no longer work as expected. You are encouraged (if not required) to create new documentation projects again by removing existing ones and using the New Project Wizard to recreate them. I have not tested older projects with DocProject 1.6.0, but I suspect that they will not be able to complete a help-build.

Solution and Projects

The first change to the solution was to start using solution folders to categorize the projects. The Sandcastle code-base was then pulled out of the Add-In project (which has been renamed) and included in a new project named, DaveSexton.DocProject.Sandcastle, which appears under the Plug-Ins solution folder along with the Sandcastle/Deployment project. There is also a new project named, DaveSexton.Controls.HtmlEditor. I'll leave that one alone for now and let you figure out its purpose for yourself ;) There have also been several changes to the InstallPrep and Installer projects, although existing functionality has not really been removed or altered, for the most part. The preparation process is still the same as it was before, but with some additional files and requirements.

Building the Installer

If you're planning on building the Installer project then the only concern you should have is that the HtmlEditor project requires the Microsoft.mshtml PIA to be installed on the target machine, so the installer requires a bootstrapper to be built (Setup.exe) that includes the redistributable vs_piaredist.exe file, which installs the required dependencies. In order to include MSHtml in the bootstrapper I created a prerequisite bootstrapper package that is included in the InstallPrep project under the Bootstrapper folder, the contents of which should be copied to Visual Studio's bootstrapper directory manually. The Readme.txt file that appears in the same folder explains its use and also provides import legal information that you must agree to before using DocProject's source code. The agreement is also presented in a dialog when DocProject is installed, but only if the required dependencies must be installed on your system. You should read this file before attempting to build and redistribute the installer and the source code.

Although I haven't tested yet whether building the installer will fail without having the MSHtml bootstrapper package present on your system, I assume that it will. I'll test this to make sure and update the appropriate wikis on CodePlex to indicate end-user requirements to build the installer using the source code, but for now just assume that you must follow the instructions in the Readme.txt file.

1.6.0 Features

Here's a preview of some of the new features that you can expect in DocProject 1.6.0 RC. I'm still testing some of them on Vista, XP, and Windows Server 2003, but things are going well. I expect to deploy by Monday June 4, 2007.

Installation

The installer will now detect the following versions of Visual Studio on your system and automatically install the templates and Add-In, where appropriate, for both Just Me and Everyone installations. Having more than one version of Visual Studio on your system at the same time is acceptable since they can all share the same templates or have their own copies if you install for everyone:

  • Visual Studio 2005 Standard+
    • The Add-In and all templates are installed.
  • Visual C# 2005 Express
    • Only the C# Project Documentation template is installed.
  • Visual Basic 2005 Express
    • Only the VB.NET Project Documentation template is installed.

Note: Currently, the DocSite templates will not work in Visual Web Developer 2005 Express.

The DocProject installation directory will always have the same contents regardless of what versions of Visual Studio are on your system when you install DocProject.  The only variance based on the different versions of Visual Studio are updates to the registry and the location where the Add-In and/or templates are copied.

There is only limited support for express editions, which I'll detail in the release notes for the 1.6.0 RC. Basically, express editions get the New Project Wizard and use the MSBuild task, but projects in express editions do not have Add-In-specific features such as the Include Project Output Dialog and the Tools > Options > DocProject pages. Users can still configure DocProject by editing the project file manually. However, Sandcastle is still configurable in the same way as the full version of DocProject; i.e., by editing files in the Presentation folder inside Visual Studio. Projects are also built in the same way, with support for a build process component as well.

Visual C++ Source Projects

DocProject now provides support for Visual C++ projects as sources for DocProjects and DocSites. Reference Visual C++ projects as you would any other project reference and their APIs will be included in the compiled help.

Html Files Names: GUID or Friendly

By default, the Sandcastle Build Assembler utility, which generates the HTML topic files, is now configured to use GUID file names instead of friendly names that corresponded to the names of the API topics, as used previously. This allows projects that have long, nested directory structures to build without throwing a System.IO.PathTooLongException (special thanks to the people who pointed this out to me :). You can change a setting to use the friendly names if you want: Tools > Options > DocProject > Active Projects > Build > Use friendly HTML file names (disabled, by default).

MSBuild Task and Command-Line Support

The template files for DocProjects and DocSites have been refactored to invoke a custom target named, BuildHelp, which references the DaveSexton.DocProject.targets file.  The targets file, located in DocProject's bin folder, starts the assembly-build process and then, afterwards, starts the help-buld process, which is executed by the Add-In in much the same way that it has in previous versions of DocProject.

To support this feature the installer creates an environment variable named, DocProjectBuildPath, which points to DocProject's bin directory. The environment variable is created with user-scope if you select Just Me in the installer and system-scope if you select Everyone.

The MSBuild task simply delegates the help-build process to the DocProject Add-In, which has been refactored so that it can execute outside of the Visual Studio IDE as well as inside. In other words, MSBuild is now used to start the build process and DocProject's Add-In takes over, using the appropriate method depending upon whether it has been initialized as a Visual Studio Add-In or simply invoked out-of-process by the MSBuild engine. This means that the Add-In will build your project files when they are used as Visual Studio projects, as they always have been, but also on build servers that use MSBuild - without running Visual Studio. No changes to the DocProject or DocSite project files are required for them to be buildable in any environment.

You can build DocProjects and DocSites on the command-line by simply running MSBuild and specifying the project file as an argument. For example, open the Visual Studio Command Prompt and enter the following to build a C# DocProject named, "your project":

msbuild.exe "c:\your project.csproj"

Note: Visual C++ projects must be built by the solution file. MSBuild will warn you about this if you attempt to build DocProjects or DocSites that reference a Visual C++ project. In that case, just pass the solution file to msbuild.exe.

Your DocProjects and DocSites can be specified as targets in other MSBuild project files on a build server as long as DocProject has been installed on the server (or at least with the DocProject assemblies and appropriate configuration present, even if DocProject is not actually installed). Make sure that the relative references to other projects in the DocProject or DocSite project file are valid.

New Project Wizard Header

The first change that you'll notice when creating a new DocProject or DocSite is in the New Project Wizard's header, modified slightly just to keep things interesting ;) (See Figure 1 below)

New Project Wizard – Choose a Sandcastle Presentation

The first useful change that you'll notice in the New Project Wizard is that the Choose a Sandcastle Presentation step provides readable names and a short description of the presentations that are available. (See Figure 1 below)

The data is located in DocProject's configuration file (commonly found at, C:\Program Files\Dave Sexton\DocProject\bin\DaveSexton.DocProject.dll.config) in the new presentation configuration section. This section describes the presentations that are installed with Sandcastle, indicating a human-readable name and description, and providing the locations to all of the tools and directories that DocProject expects. There is also a place to add custom Regular Expression and XPath expressions that will be used by DocProject to automatically transform the imported Build Assembler configuration file (sandcastle.config) when creating a new DocProject or DocSite. You can modify, remove or add new transformations to customize the default configuration files as they are imported into new projects.

Note: You can use the new presentation configuration section to add your own custom presentations too!

Figure 1: New Project Wizard - Chooose a Sandcastle Presentation
Figure 1: New Project Wizard - Choose a Sandcastle Presentation

As you can see from the screen-shot, the wizard looks a little bit different now. Other than the slight change to appearance and the new pre-load behavior, it functions identically to earlier versions of DocProject.

New Project Wizard – Create Shared Content

The next feature is the ability to create a header and footer using an Html Editor. The header and footer content are shared by all API topics and can be found in your project's Presentation\Content\shared_content.xml file, which you may edit manually as well. I'm also considering whether to add more sections that would correspond with other items in the shared_content.xml file, for a future version of DocProject.

Figure 2: New Project Wizard - Create Shared Content Figure 2.1: Html Editor Context Menu
Figure 2: New Project Wizard - Create Shared Content
Figure 2.1: Html Editor Context Menu

Notice in Figure 2 that the Header and Footer sections may be toggled between visible and hidden states by clicking their respective title bars. The box next to the title will contain a plus sign (+) when a section is collapsed and a minus sign (-) when a section is expanded.

The header and footer may be edited at a later time using the API Topic Designer, which is located at Tools > Options > DocProject > Active Projects > Content > API Topic Designer. Locate this setting for your project and click the ellipses button to open the editor, which is identical to the editor in the Create Shared Content wizard step, but without the wizard's chrome.

The Html Editor that is used here may be the beginning of a topic editor control as well; although I'd much rather use Visual Studio's HTML editor instead of reinventing the wheel, which would not be easy. One of the reasons that I wanted to use Sandcastle inside Visual Studio in the first place is so I could use its HTML and XML editors, but more on this discussion another time :)

API Topic Management Dialog

The API Topic Management dialog provides some new options for filtering API topics based on categories and regular expressions, such as the ability to find the next match only and to specify whether the dialog should automatically show matched topics in the TOC (i.e., if a matched node is hidden then its parent nodes will be expanded until it becomes visible, and then it will be scrolled into view).

Figure 3: API Topic Management Dialog - Topic Filters
Figure 3: API Topic Management Dialog – Topic Filters

The TOC is loaded on-demand now, by initially loading only the root topics and then loading the children of each individual topic as they are manually expanded for the first time. This saves some initialization time, but may increase the time that it takes for the filters to work. For this reason, the filters now execute asynchronously and the find next match only option is selected by default.

The API Topic Management dialog also provides support for editing project and namespace summaries in text mode (the contents of which must be valid xml just like with code comments) or using an Html Editor, as in Figure 4 below. You can freely switch back and forth between text mode and html mode and your changes will be persisted.

The Summary Text and Summary HTML tabs are only activated when the project node is selected, as in Figure 4, or when one of the namespace nodes is selected. For example, the Contoso namespace that appears in Figure 4 can be selected in order to edit its summary. If the AppConfigElement node is selected, however, the summary editor will be disabled.

Figure 4: API Topic Management Dialog - Summary HTML Editor
Figure 4: API Topic Management Dialog – Summary HTML Editor

You can open this dialog by locating Tools > Options > DocProject > Active Projects > Configuration > API Topic Management and clicking the ellipses button. It's in the same place that it was in 1.5.0 :)

And More…

There are other features that I haven't discussed here, but I think the ones that I've mentioned are probably the most important.

I hope you're excited to use these new features, since I worked hard to create them. I'd love to know what you think about them and your ideas for other features, so please use the contact form here or on CodePlex, or leave a comment on this blog post and let me know what's on your mind.

Thanks!

May 12, 2007

DocProject for Sandcastle: Custom Topics

Update 2/10/08: DocProject 1.10.0 RC provides first-class support for Sandcastle's conceptual build process, which compiles additional content written in the XML-based Microsoft Assistance Markup Language (MAML) to produce MSDN-like help topics, optionally in combination with auto-generated reference topics.  19 conceptual templates are provided by DocProject and the MAML schemas are provided for IntelliSense in Visual Studio's XML editor.  You can find more information in my blog, here and here.  If you want to include raw HTML topics into help builds, read this post for more information.

Recently, a few people have mentioned that they'd like support in DocProject for generating custom topics that maintain the look and feel of the selected presentation. While designing DocProject I had thought about this and tried to provide an easy way for developers to create custom topics, which is why the partial build functionality was developed. But until now I haven't mentioned much about this feature or how it works.

I just added a tutorial to DocProject on CodePlex, which explains how to create custom topics using the partial build feature:

Tutorial: Creating Custom Topics
http://www.codeplex.com/DocProject/Wiki/View.aspx?title=Creating+Custom+Topics

The tutorial explains how to create an HTML topic template from one of the files that Sandcastle generates (namely, R_Project.htm). It also describes how you can use Visual Studio's HTML designer to generate your own topic files based on the template and how to include them in the Html Help Workshop table of contents (TOC). Code is also provided that extends the default build process component to automate some of the steps in the tutorial.

Enjoy, and let me know what you think :)

April 26, 2007

DocProject for Sandcastle 1.5.0 Release Candidate

In this blog entry I'm going to show a preview of one of the new features that I've included in DocProject for the next release. I'll also write a little about my open-source development habits, how I test DocProject and my plans for DocProject in the future.

But before I begin I'd like to thank everyone that has downloaded DocProject and provided feedback. It's been helpful. Please keep adding work items and starting discussions so that I can continue to be informed of the community's needs.

Those of us that use Visual Studio 2005 do so for different purposes and in different manners, and there's no way that I could test DocProject in every possible situation on my own, so I'm relying on the community to let me know when I've missed a bug in testing and when there's room for improvement. And I'm not just looking for constructive criticism either, so if you have something nice to say about DocProject then please don't be shy :) It's also useful for me to know that people are actually using DocProject and that it's working to meet their needs.

My Multi-Project Open Source Development Life-cycle

I work weeknights and weekends on the planning and development of my open source projects and my proprietary software. I normally don't work from beginning to end on any particular release, but instead jump back and forth between projects. Usually, a context switch occurs when an idea pops into my head or if I get bored with what I'm doing. That's the beauty of working alone and on personal software. I'm currently working full-time on a desktop application for a client so I don't do much coding on personal projects during daylight hours; however, I do try to answer questions and provide help to the community in a timely manner and, from time-to-time, I'll do some wiki-work mid-day. Working from home has its perks ;)

Recently, I published AIP 1.0.0 RTW to CodePlex and now I'm switching gears to finish work on DocProject for the April 30th deployment. I've fixed all of the bugs and added all of the features that have associated work items, including a few that don't, and now I'm on to stabilization.

Stabilizing DocProject

Stabilizing DocProject isn't so easy. Because it's an Add-In I have to make sure that every feature works in Visual Studio 2005, in many different contexts. Of course, I must also ensure that regression issues don't spring up.

I didn't write unit tests when DocProject was first conceived, but I'm actually glad now. Unit tests aren't going to work for a very complex Add-In that is hosted by Visual Studio 2005 because I won't be able to properly emulate VS in any mock object, IMO. And even if I could it wouldn't give me peace of mind. Having a script to automate Visual Studio might be useful, but I don't think it's worth the effort to create one.

I test DocProject by installing it on my Vista box, which is also my development box, and then I create new DocProjects and DocSites from scratch, testing each of the features as I go. I also test DocProject on a Windows Server 2003 VM. To be perfectly honest though, I don't test all of the features each time. There are just too many features to test; although, I usually don't make code changes that will affect every bit. I guess that's another benefit of working alone: I know what I did and usually have a good idea of what components it may have affected. The components that aren't touched at all, for which I'm sure, I'll skip testing.

Thankfully, I haven't run into many regression issues. I think that's due, in part, to the amount of refactoring that I did in previous versions before I added several of the major features that are in DocProject now. There's still certainly a lot of room for refactoring and once I publish the first RTW I think I may revisit those classes for which I made a mental note. Actually, I'll probably review the entire code-base from scratch and see if I can re-architect DocProject for some of my future plans (more on them below).

But for now, I'm going to continue stabilizing DocProject until I'm ready to release 1.5.0, by April 30th. (BTW, I try to schedule deployments on a Monday so that I have an entire weekend prior to really get down and dirty with testing, last-minute tweaks and writing wikis.)

API Topic Management Dialog - Preview

Here's a preview of a new feature that I've added to DocProject. It's a dialog that allows a user to configure the apiFilter configuration element in Sandcastle's MRefBuilder configuration file, which is now included as a project item in the Presentation\Configuration folder. You can also edit the configuration file manually without tripping up the dialog, as long as your changes conform to the xml schema that Sandcastle requires, of course.

An API topic is included by checking its check box node in the tree view control and excluded by unchecking it. All topics are included (checked) by default if the apiFilter configuration section hasn't been used.

The dialog is accessed in the Tools Options Page for Active Projects by clicking the ellipses button of the Configuration > API Topic Management property. Notice in the image below that another new property has been added as well, MRefBuilder Configuration File Name, which specifies the name of the config file in the Presentation\Configuration directory to be used with the Sandcastle MRefBuilder program.

Finding the API Topic Management Dialog
Figure 1: Finding the API Topic Management Dialog

The API Topic Management Dialog
Figure 2: The API Topic Management Dialog

Regular Expression Filter

Use the regular expression filter to include, exclude or locate topics by name using a managed-style regular expression. A link to an MSDN help reference on regular expressions is provided next to a link that pops-up a regular expression quick-help guide, which includes a few examples. You can also choose the regular expression's options.

The Find button will locate all matching topics by highlighting them.

The Apply button will locate all matching topics, highlight each of them, and ensure that the state of each check box matches the state of the Include matching topics check box.

An icon will appear next to the Find button after either button is clicked. The icon's tooltip displays the number of matching topics. The tooltip is automatically displayed for one and a half seconds after the search has completed. Clicking the icon repeatedly will cycle through all of the matching topics, selecting the current node and expanding nodes when necessary. The highlighted color and the icon's appearance differ depending upon whether you have clicked Find or Apply. Find was clicked in Figure 2 and the blue highlights in the tree view are the matching topics.

Clicking the icon to cycle through the matching topics can be useful. Use the spacebar to toggle (check or uncheck) a matching topic and click the icon to cycle to the next match. Using the icon and the Find button instead of the Apply button will allow you to manually choose from all of the matched topics the ones that should be toggled.

Category Filter

Use the category filter to include or exclude topics by API category. For instance, to include all interfaces you would first check the Interfaces checkbox and ensure that no other category is checked. Then make sure that the Include matching topics checkbox is checked to include matching topics (unchecking it will exclude matching topics). Press the Apply button to apply the changes.

Like the regular expression filter, an icon will appear next to the Apply button after it's clicked. The icon's tooltip displays the number of matching topics. The tooltip is automatically displayed for one and a half seconds after the search has completed. Clicking the icon repeatedly will cycle through all of the matching topics, selecting the current node and expanding nodes when necessary.

How It All Works

When you open the dialog it takes some time to fill the tree view, but the dialog remains responsive to user interaction and displays a marquee progress bar in the mean time. In the background, DocProject is actually using the Sandcastle build engine to run a build of the TOC only (the engine was refactored a bit to provide this new functionality).

Basically, all of the steps up to the Build Assembler are executed in the background.  The toc.xml and reflection.xml files that are created by the the Sandcastle build steps are then parsed into the tree view nodes.  The toc.xml file is used for the layout of the tree view and the reflection.xml file provides most of the information used to construct each topic node.  And for each topic node a look-up into the existing apiFilter configuration section of the Presentation\Configuration\MRefBuilder.config file is performed to discover whether the topic should be included or excluded (checked or unchecked, respectively), by default.

A temporary copy of each node is made at the time they are created.  When you modify the tree view, the copy of each node is preserved for the OK and Cancel functionality of the dialog.

Clicking OK on the dialog doesn't apply the changes, however. You must click OK on Visual Studio's Options dialog to commit the changes. This allows you to edit the API Topic Management property multiple times before you finally save your changes. If you click OK on the property's dialog and then reopen it, modify something, and click Cancel, then only the changes that you made since the last time the dialog was opened will be canceled. The original changes for which you clicked OK last will be retained.

When you finally save the changes by clicking OK on the Options dialog, they are written directly to the MRefBuilder.config file, overwriting any existing configuration of the apiFilter element. Other existing elements and content in the configuration file is left unmodified.

One stipulation to use this feature is that you must build all of the source projects once before the dialog will display their topics. If you only build some of the source projects then the dialog will only display those API topics. If you clean the solution or otherwise delete any of the output assemblies from the source projects then the corresponding API topics will not be displayed. In other words, the source assemblies have to be present in order for the dialog to display their topics. Showing the dialog does not automatically build all of the source projects so you must build the projects before using the dialog. If you close Visual Studio after building a source project and then reopen it, the assembly will still be there so you won't need to build the project again to use the API Topic Management dialog for a referencing DocProject or DocSite.

Performance

The MRefBuilder utility is very quick, even for the 13-assembly library that I use to test DocProject's performance. But the dialog, for the same library, does take a considerable amount of time to fill the tree view, and that's what the dialog is doing for the majority of the time that you wait for it to be ready. In testing it took about 5 minutes for the dialog to be ready for 2715 API topics. However, that number only represents API  topics, not other topics such as All Members, so the number presented as the API Topic Management property value is somewhat lower than the total number of topics that end up in the compiled help.

I already see room for improvement in the code base to increase the performance of the dialog, and I was even thinking about possibly adding nodes-on-demand functionality, but I'm going to save those changes for a subsequent release. The idea behind the on-demand behavior, BTW, is that expanding a node for the first time will cause its children to be loaded at that time instead of all nodes being loaded when the dialog opens. However, I have some doubts if that would be useful since the filters will require all nodes to be loaded before they will function properly. Obviously, I still have some things to work out for that feature (which is why I'm not adding it now).

To improve the performance I plan on using an XmlReader to parse the topics instead of an XmlDocument and I plan on using a lot more custom iterators in C# too (i.e., the yield statement).

The performance of the tree view and the filters after all of the topics have been loaded is actually quite good; as is the process that writes the changes to disc when you click OK on the Options dialog.

DocProject's Past, Present and Future

DocProject hasn't been around that long but it has accumulated over 800 downloads between all of its versions, up to the 1.4.0 RC. Currently, DocProject is downloaded about 10 times per day, on average.

I have some plans for the future of DocProject, feature-wise, but nothing is set in stone. Feedback is a must for me. Please let me know what features you want to see in DocProject and I will consider them.

Why doesn't DocProject provide any content-based features?

Currently, DocProject is built for automating Sandcastle from within Visual Studio 2005, with extensibility built-in to provide an open-ended environment for developers to control the help-build process. All of the Sandcastle presentation files (xml transformations and configuration files) are included in DocProjects and DocSites so that users can configure Sandcastle the way they want without being restricted to GUIs. In other words, DocProject is not focused on content-based features. DocProject relies on Sandcastle and you, the user, to develop and configure your own documentation. This has always been the most important guideline that I followed since I started developing the first beta.

Recently I received a request from a user to include some GUI support for things such as adding namespace summaries and customizing the default copyright, header and footer of the HTML topic files generated by Sandcastle (see work item #9717 and vote if you're interested). Although I have considered adding these features in past releases, I always ended up siding with the idea that DocProject is better off without content-related features, for now. What I mean is that DocProject has always been geared toward making it easier to build documentation using Sandcastle and for users to be able to develop their documentation using Sandcastle, directly. I've purposefully avoided creating interfaces for users to manage the HTML topic files that Sandcastle generates in favor of simply including the xml transformations and configuration files as project items so that users could configure Sandcastle themselves. The reasons are simple:

  • Sandcastle is not RTW yet and there are changes between CTP releases, naturally.
  • I consider adding content-based features a major version change from not having any content-based features and I'd rather wait for the first DocProject RTW to correspond with the first Sandcastle RTW before any major changes are implemented in DocProject.
  • I didn't want to be bogged down with presentation-related issues while I was developing and stabilizing DocProject's feature-base for building documentation, which I consider to be more important now since all Sandcastle files are visible to users and are not modified by DocProject in any way. So users are completely free to configure Sandcastle however they want and I don't have to worry about supporting end-user/DocProject configuration mistakes or issues with the appearance of HTML files that Sandcastle generates.
  • It seems a bit ridiculous to me for DocProject to offer a textbox so users could edit an HTML header, for example, inside of the Visual Studio 2005 IDE when they can use the XML editor to edit the entire shared content XML file.
  • I can't help but think that it should be all or nothing. A topic designer must be included or else DocProject should simply rely on Sandcastle and DocProject users to produce the documentation, as it currently does. Without a topic designer, any UI support for presentation would just be a hack, IMO.
Planned Features

I plan to research some content-based features, including the ability to add namespace summaries and maybe even a basic topic designer for the next major release of DocProject.

In preparation for the next release of Sandcastle, which I read will include a new presentation design (A. Raman, MSDN Forums, Apr 20, 2007; Last Post), I've already begun to find places in the code base that I want to refactor. I'm also going to start testing DocProject in the Orcas CTPs so that it will be ready for Orcas documentation, as long as Sandcastle supports it.

I'd also like to add a command-line build feature (work item #9788) and an MSBuild task (work item #9787). I've already thought about how that will work and started refactoring the code base in 1.5.0 in preparation for the changes required to support these features. Some of the changes were included in 1.5.0 simply because they were easy enough to do.

I've begun to create a command-line executable named, docop (DocProject Operator) that will eventually automate the build process of a DocProject or DocSite outside of the Visual Studio 2005 IDE. The plan is to run the MSBuild task using the MSBuild API, which I started playing around with last weekend.

If you are interested in any of the features that I mentioned then please follow the corresponding link and vote on the work item in CodePlex. Thanks!

April 05, 2007

2 in 1: DocProject for Sandcastle 1.4.0 and AIP Beta 1

I've just published 2 open source deployments in one month, one of which is a brand new project: Auto-Input Protection (AIP). Both deployments are available on CodePlex.

DocProject 1.4.0 Release Candidate Is Now Available

DocProject drives the Sandcastle help generation tools using the power of Visual Studio 2005. Choose from various project templates that build compiled help version 1.* or 2.* for all project references. DocProject facilitates the administration and development of project documentation with Sandcastle, allowing you to use the integrated tools of Visual Studio 2005 to customize Sandcastle's output.

Download DocProject from CodePlex, which comes with the complete source code (C#). Try it out and let me know what you think.

Here are some of the new features in 1.4.0:

  • DocProject's Sandcastle build engine now uses the Sandcastle March CTP transformations.
  • DocProjects and DocSites can now build Html Help 1.*, Help 2.*, neither or both via a single build.
  • The New Project wizard has been extended for the Sandcastle build engine, allowing you to choose the type of help that your project will build (see previous feature).
  • DocSites use AJAX to improve client-side performance.
  • DocSite's index can be filtered, client-side.
  • The tools options page, "Dave Sexton's Tools", has been renamed to "DocProject" and was split into separate sub categories.
  • A status notification and progress bar now appear during help builds.
  • Improved error handling, including logging to the Application event log.
  • Several bug fixes.

Use a DocSite template to build an AJAX-enabled ASP.NET Web Application for your compiled help, which includes an interactive TOC, filterable index, breadcrumbs, an auto-generated header and footer, and a link to download the compiled help file. The auto-generated website has been tested for compatibility with the IE7, Firefox and Opera web browsers only.

Example DocSite
Figure 1: Example DocSite shown in Internet Explorer 7 on Windows Vista; Sandcastle vs2005 presentation.

Auto-Input Protection Beta 1 for ASP.NET Is Now Available

AIP is an extensible ASP.NET web control that provides CAPTCHA protection for your blogs, forums, wikis and websites, greatly reducing the likelihood of unwanted form submission from automated spam and hacks.

Want to see it in action? Look no further than in the comments section of my own blog posts. :)

Download AIP from CodePlex, which comes with the complete source code (C#). Try it out and let me know what you think.

Add the AIP web control to a web page with full designer support and only a few modifications to your web.config file:

Default AIP Web Control

Modify the AIP web control's presentation with a custom template and modify the CAPTCHA image that it generates by changing the bitmap and filter providers' settings in your web.config file:

Customized AIP Web Control

Easily implement custom CAPTCHA algorithms that produce randomized patterns with a custom bitmap provider and filter providers:

Customized AIP Web Control - Cross Hatch 1 Customized AIP Web Control - Cross Hatch 2 Customized AIP Web Control - Cross Hatch 3 Customized AIP Web Control - Cross Hatch 4

January 21, 2007

DocProject RC1 Update

I've made some more modifications to the DocProject software. Several classes have been refactored again and some new classes have been added. A few work items have been addressed and now there is support for Visual Basic.NET and ASP.NET Web Application projects (DocSites). You can also cancel the build in the middle of a step.

Download the latest release, version 1.2.0, at DocProject on CodePlex.

Collaboration

Steve Bokser, a programmer and a friend of mine, helped me to test the new release by installing it on his machine before I made it available to the public. We identified a few issues that didn't surface during my own testing since we have different versions of Visual Studio installed. I took care of the issues that I could fix before the CodePlex deployment and I adjusted the release notes and wikis to include information regarding the setup and configuration issues. Thanks Steve!

Custom Templates

An organization may want to create a custom template that their developers can use to create DocProjects or DocSites for their Visual Studio projects. A custom DocProject or DocSite template can provide a standardized means of building compiled help documentation for projects across an entire organization.

A new tutorial has been added to provide guidance for creating new DocProject and DocSite templates:

Creating New Templates
http://www.codeplex.com/DocProject/Wiki/View.aspx?title=Creating%20New%20Templates

Wiki Updates

Many of the How To... wikis have been modified to reflect changes in the new release.

A new diagram that illustrates the different components of DocProject and their relationships appears on the home page wiki now:

DocProject Components
Figure 1: DocProject Components

 

As always, I'd love to hear from anyone that has something to say about this post.  Drop me a comment.

January 09, 2007

DocProject RC1 Is Now Available

Version 1.1.0 of DocProject, a release candidate, is now available at CodePlex. Download the DocProject installer, which also installs the complete source code, Visual Studio solution file and project files, here.

Updates

The latest build includes dramatic changes to the class foundation, refactored to address some of the previous work items. One reason why I made such a drastic overhaul was to provide more robust support for extensibility and the ability to add plug-ins for custom build engines other than just Sandcastle, which is still included as the only build engine that's built-in to DocProject.

Another reason why I did a lot of refactoring was because I saw room for improvement. The code is more flexible now, which was required to support the new plug-in framework, but also makes it easier to navigate the code and to extend it at a later time, IMO.

A description of the changes that were made can be found here.

A few new help wikis

  1. How To Use The Source Code
  2. Tutorial: Creating a DocProject for a new solution

There have also been a number of modifications to some of the previous wikis. The complete list of How To... wikis for DocProject can be found here.

I'm planning to add some more help topics soon, so check CodePlex for updates. One thing I'd like to add is a simple tutorial that explains how to create your own DocProject templates (it's really easy). Another thing I'd like to write about is how to create a custom build engine and plug it into DocProject. It's not quite as easy as creating a new template since it requires writing some code, but it's not too difficult either.

Feedback!

I'd really like your comments and feedback on this software, so don't be shy :). I've already received some positive feedback. Nothing negative yet - not even constructive criticism. I'm sure it's out there, and I can take it, so bring it on!

Please feel free to comment here on my blog or in the DocProject forums at CodePlex.

 

As always, I'd love to hear from anyone that has something to say about this post.  Drop me a comment.

January 02, 2007

DocProject for Sandcastle

Addendum [1/4/07]: DocProject on CodePlex
DocProject is now available on CodePlex. From the home page you can access the How To... wiki, which provides updated guidance and information that was written after this blog entry. Also, I recommend that you download the installer from the codeplex website, here, instead of from my Downloads page since CodePlex will always have the most up-to-date version.

What is DocProject?

DocProject integrates the Sandcastle compiled help builder into the Visual Studio 2005 IDE. A new project template is provided that builds a compiled help file (.chm) for all of its project references. This facilitates the administration and development of project documentation by allowing developers to use a single solution for their projects and related code documentation. Optionally, the compiled help output can be included into the documentation project for further development. The DocProject installer only installs support for building compiled help from a specialized VS 2005 project template. No other features, add-ins or tools are installed. DocProject completely automates the process by examining the project for references to other projects and building the documentation from their assembly output. The user does not have to perform any manual steps for the build to work. Dependency information is taken from the project’s references automatically. The documentation is built by simply building the project.

In order to use DocProject you must have installed the following components:

  • .NET Framework 2.0
  • Visual Studio 2005 (Express Edition is not supported)
  • Html Help Workshop 1.4 SDK [1]
  • Sandcastle December 2006 CTP [2]
  • DocProject for Sandcastle (Installer) [3]

You can download DocProject for Sandcastle here or in the references section at the end of this blog entry [3].

Known Issues

  • Although the installer allows the installation path to be changed, doing so breaks the Add-In, which requires the installed path to be: C:\Program Files\Dave Sexton\DocProject for Sandcastle.
  • Uninstallation does not remove the C# Project Documentation template or the Add-In
  • .
  • Only one comment file can be used per solution since Sandcastle expects a file named comments.xml by default. This means that if multiple projects with comments.xml files are referenced only one comments.xml will be used, indeterminately, when the help is compiled.

License

CC-GNU GPL
This software is licensed under the CC-GNU GPL.

Copyright © Dave Sexton 2006-2007

Installation and Source Code

A Windows Installer package (.msi) has been created to install both the Add-In and "C# Project Documentation" template along with the complete source code and C# project file (.csproj) [3]. The installer uses a Community Components installer file (.vsi) to install the Add-In and Template. Currently, the uninstallation of the Windows Installer package does not remove the Add-In or DocProject template; however, they can be removed easily by opening the configured Add-In and Template directories and deleting the DocProject files manually.

To compile the source code, locate the .csproj file in the C:\Program Files\Dave Sexton\DocProject for Sandcastle\Source folder. The existing build event (on successful build) requires another component for zipping the output in preparation for installation. The component is called DSZip and can be downloaded with the complete source code at [4]. I'll blog about this simple console application in the future if there are enough requests for that.

DocProject Components

Templates

Currently, the only DocProject template is the "C# Project Documentation" template, based on a C# class library. In the future there may be support for a web project (ASP.NET documentation website). Also, there may be support for the execution of custom code from within the project itself.

Add-In

The build process for projects created from the DocProject template are controlled by an Add-In named, "DocProject for Sandcastle". Without the Add-In projects created from the DocProject template are just plain C# class libraries.

Usage Instructions

The following steps can be taken to use DocProject after it has been successfully installed:

  1. Create or open an existing solution.
  2. Add a project that you want DocProject to generate documentation for its assembly output.
  3. Add a new project to the solution and select the C# Project Documentation template.
    1. Select Visual C#.
    2. In the right pane look under My Templates.
  4. Name the new project anything that you'd like and create it.
  5. Add project references using the dialog that appears or cancel the operation and add the references as you normally would by using the Solution Explorer.
  6. Build the solution or project to see DocProject in action.
  7. Optionally, but recommended, open up the solution configuration and remove your documentation project from the build process.

Note that the build output (compiled help (.chm) and supporting files) are replaced each time a DocProject is built.

Configuration

DocProject installs a custom tools options page named, DocProject. It appears under Dave Sexton's Tools in the Options dialog accessible via the Tools menu. The dialog configures settings on a per-project basis.

Code Plex

I've submitted a request to Code Plex for the addition of this project. I'll create a new blog entry when I hear back from them :)

--
References

[1] HTML Help 1.4 SDK
Microsoft HTML Help Downloads
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp

[2] Sandcastle - December 2006 Community Technology Preview (CTP)
http://www.microsoft.com/downloads/details.aspx?FamilyID=E82EA71D-DA89-42EE-A715-696E3A4873B2&displaylang=en

[3] DocProject for Sandcastle Installer
http://www.codeplex.com/DocProject/Release/ProjectReleases.aspx

[4] DSZip Installer
http://www.codeplex.com/DocProject/Release/ProjectReleases.aspx

As always, I'd love to hear from anyone that has something to say about this post.  Drop me a comment.

December 15, 2006

Custom C# Code Snippets

Introduction

Code Snippets in Visual Studio 2005 [1] allow developers to create templates for common C# programming constructs, XML documentation, or any other text that one might want to quickly insert into C# code with only a couple of keystrokes. Snippets even provide the refactoring capabilities found in VS's Refactor menu. Snippets support the addition of fields and can be designed for either explicit, user-defined values or for automatic replacement, allowing a snippet to be easily customized for the particular context in which it's being used.

Visual Studio 2005 provides some out-of-the-box snippets for C# [2]. Pressing Ctrl+Space brings up the IntelliSense list of local variables, members, types and namespaces that are currently in scope relative to the position of the cursor. That same list displays all of the built-in and custom C# code snippets too.

The Benefits of Snipping

I use some of the built-in snippets from time to time, such as try, tryf, #region, #if and maybe a few others, but I believe that the real value in Code Snippets lies in the ability to create your own.

Snippet for Better Code

The quickest way to produce software is to reuse canonical code samples that work. Because there are so many different coding patterns and practices, it can be difficult to remember them all. Code Snippets facilitate the reuse of the expression of programming concepts so that they don't have to be re-analyzed each time they are needed. Of course, not all design patterns may be expressed in simple code snippets. For example, patterns that should span multiple source files cannot be developed into a single snippet; however, multiple snippets might be useful to accommodate patterns of diffusion. Although it might not be easy or effective to handle some of the more robust or complex design patterns using snippets, they are certainly helpful for the simpler, more frequent patterns.

Snippet for Consistency

It's common for personal styles and techniques to be developed or acquired over time. Snippets can be used to easily ensure that all code conforms to personal preference for style and technique. Organizational standards may be expressed and encapsulated using code snippets as well. By effectively utilizing snippets, organizations can maintain consistency across assemblies, projects and teams. Developers can ensure that trusted and proven techniques will be reused without having to remember all of the ins and outs as to why a particular snippet was designed a certain way as long as they know what it's for.

Snippet for Distribution

Snippets provide a means for encapsulating code in a manner that's easy to distribute since a snippet consists only of a single XML file. Snippets can be installed easily into Visual Studio by simply copying a .snippet file to a user's "Code Snippets\Visual C#\" directory (usually located under "My Documents\Visual Studio 2005\").

Snippet for Education

I believe that the best way to learn code is to write code. Code Snippets can encapsulate canonical representations of design patterns so they can be used as a basis for learning in the same way that code samples are used in books, websites and newsgroups to teach programmers the correct way to do something. Snippets are, in that respect, like little, bundled examples. Because of their distributable nature, snippets can be supplied easily to developers as examples of canonical, working code.

There is also the integration factor to consider. Since Visual Studio understands snippets, they can be accessed easily using IntelliSense from within the development environment itself. Visual Studio aids in the customization of snippets as well, providing a means for snippet authors to add contextual fields using replacement tokens, the replacement of which may be performed by Visual Studio or simply edited by the programmer using the snippet. This automated guidance allows developers to actually place working examples into their real code much easier than having to copy, paste and modify, or to type samples from scratch, both of which may be subject to typographical and logical errors.

Unfortunately, I haven't seen many code snippets being passed around on the web. I haven't really tried to figure out why that is, but I'll assume that it's because it just hasn't caught on yet or, more likely, it's not as useful for educational purposes as I'd like to believe.

Snippet for Sanity

Code Snippets can save you precious development time and save you from the frustrating, tedious reproduction of well-known code constructs that can make development really boring and annoying at times.

Snippets and Me

For the remainder of this blog entry I'm going to provide the source for my own personal code snippets and explain why I wrote them in the first place. Afterwards, I'll briefly explain how they can be used.

You can download all of my snippets at once or each may be downloaded separately by clicking on any of the shortcuts below.

My C# Code Snippets
ShortcutTitleSummary
event Event Member Creates the canonical event member declaration and corresponding method for invocation. EventHandler is used as the delegate. The code produced by this snippet is thread-safe.
eventc Component Event Member Creates the canonical event member declaration and corresponding method for invocation for a class that derives from System.ComponentModel.Component. EventHandler is used as the delegate. The code produced by this snippet is thread-safe.
eventg Generic Event Member Creates the canonical event member declaration and corresponding method for invocation. EventHandler<TEventArgs> is used as the delegate and TEventArgs has been tokenized. The code produced by this snippet is thread-safe.
eventcg Generic Component Event Member Creates the canonical event member declaration and corresponding method for invocation for a class that derives from System.ComponentModel.Component. EventHandler<TEventArgs> is used as the delegate and TEventArgs has been tokenized. The code produced by this snippet is thread-safe.
eventargs Custom Event Args Creates the definition for a class that inherits from EventArgs.
layout Layout Regions Creates several #region blocks for the placement of specific code elements from within the body of a class definition.
layoutd Designer Layout Regions Creates several #region blocks for the placement of specific code elements from within the body of a class definition created by a designer.



Event Members
(event, eventg, eventc, eventcg)

The standardized event design pattern [3] for .NET includes a type member using the event keyword with a corresponding protected method to raise the event. Adding an event to a type is therefore a two-step process, at least:

  • Add event member to type
  • Add method to raise event (prefixed with On)

When designing an object to be used by multiple threads it's desirable and recommended [4] to synchronize the event accessors with the method that raises the event to ensure thread-safety. This counts for even more code that, in essence, doesn't change between events:

  • Declare a synchronization object
  • Add locking mechanism to the add and remove event accessors
  • Add locking mechanism to the protected method that will raise the event

When designing types that derive from System.ComponentModel.Component, including classes that inherit from Windows Forms Controls or Web Controls, the protected Events property provides an EventHandlerList that can be used to reduce the memory footprint of the class. To use the Events property there must be some more code added:

  • Declare an object to key the event in the EventHandlerList
  • Use the Events property in the add and remove event accessors
  • Use the Events property in the method that will raise the event

My code snippets for event members handle all of the above scenarios, by default. These snippets can be invaluable to developers that frequently write custom controls or business objects that use events by reducing the amount of time it takes to adorn classes while ensuring that the code adheres to standards and has been tested.

The Different Event Member Snippets

The differences between the four event member snippets can be understood by analyzing the different suffixes on each shortcut:

The event snippet, without any suffix, is the most basic of them all. It provides the standardized, thread-safe event member paradigm in C# and adds some automatic XML documentation as well.

The c suffix indicates that a component-based event will be serialized.  It will use the Events property inherited from System.ComponentModel.Component (last three bullets above).

The g suffix indicates that the serialized event's delegate will be the generic EventHandler<TEventArgs> type. In the case of g, TEventArgs is tokenized so that its value only needs to be specified once when using the snippet. You should use this snippet if you want event arguments other than the standard EventArgs class. However, I don't provide any snippet that tokenizes the delegate itself. If your code is targeting the 1.* .NET Framework then you may want to consider writing your own snippet to replace this one.

The cg suffix is simply a combination of c and g.  (component + generics)

Layout Regions
(layout, layoutd)

I invented the layout region snippets to keep my code really neat. That's basically it. If either layout snippet is used while the cursor is within a class or struct definition, it infers the name of the type and adds a constructor with some documentation. If it's used within an interface definition then the constructor region will be serialized without a name and must be deleted manually. Here's what the layout snippet would serialize if it were to be used within a class named, Tidy:

Note: The text in red is not serialized by the layout snippets. It has been added to describe the purpose of each #region.

#region Public Properties
This region is intended for public properties, but I use it for static fields and static properties that are public as well. When I'm developing custom controls I sometimes also add nested regions to separate the different categories of properties based on the value of the CategoryAttribute. As a rule of thumb, I always group the static members before the instance members within this region.
#endregion

#region Private / Protected
All private, protected and internal instance and static fields are declared here. I also use this region for protected or internal (or both) properties. I always group properties before fields within this region.
#endregion

#region Constructors
/// <summary>
/// Constructs a new instance of the <see cref="Tidy" /> class.
/// </summary>
public Tidy(|)  (Cursor goes here automatically)
{
}
#endregion

#region Methods
All methods that do not raise or handle events are added to this region. Those methods that are prefixed with On raise events and those with signatures of specific delegates handle them (they are placed in the Event Handlers region). I try to keep all static or utility methods grouped at the top, followed by all initialization-based methods and then finally, arbitrary methods. Depending upon the class, I may also try to group public methods first.
#endregion

#region Events
This region encapsulates all events defined on the type. Since my event snippets serialize the event declaration, invocation method and supporting objects all in one place, each of those components are included here. This region is the only exception to the rule that private fields must be placed in the Private / Protected region. I layout this region based on the position of the cursor in my event snippets, where all supporting fields are grouped first, and then each event/invoker pair follows one after another.
#endregion

#region Event Handlers
All methods that handle events are added to this region. An event-handling method is one that takes the form of: void MethodName(object sender, SomeEventArgs e) { ... } Before I added the Nested region, Event Handlers was the last one in the list because Visual Studio .NET designers were actually smart enough to serialize event handlers into the last region in the code base (or that might have been a bug but I used it to my advantage). VS 2005 doesn't exhibit the same behavior so I just cut and paste designer-serialized code into the proper regions.
#endregion

#region Nested
Nested types are placed here regardless of their accessibility.
#endregion

All interface implementation regions serialized by Visual Studio appear after the last region used by the type.



Not all regions are used in every type that I define. For instance, I usually remove the Nested region immediately after I use the layout snippet. For interfaces, I remove everything except Public Properties, Methods and Events. For stucts I usually remove everything below Methods.  If I predict, for any type, that a particular region will never be used then I'll remove it immediately.

I do not distinguish between members marked as unsafe, fixed, abstract, virtual, override or sealed. However, extern methods are placed in the Methods region below every other method or within a nested region if there are many of them.

When overriding derived methods that raise events (methods beginning with an On prefix), I place them in the Event Handlers region since that is usually the purpose for overriding them in the first place.

The layoutd alternative provides one subtle difference and one obvious difference. The obvious difference is that it serializes a call to InitializeComponent(); within the constructor. When I use layoutd I select the existing constructor that was serialized by the designer and then execute the snippet so that it's completely replaced.  The subtle difference that layoutd provides is that the cursor is no longer placed where the constructor parameters could be entered, but is instead placed within the Private / Protected region on an empty line.

Since I invented the layout snippets I've made it a habit when I open up source code to immediately right-mouse click and select Outlining → Collapse To Definitions from the context menu, making it much easier to navigate my code. This allows me to visualize where code elements are placed instead of blindly using the drop-down lists at the top of the document, which I avoid completely.

Using Snippets

I learned how to write snippets using the documentation on MSDN [1] and I found it to be quite easy, so I'm not going to elaborate here. I'll just provide a synopsis to get snippet-newbs started.

Create a .snippet File

I suggest that you download one of my files as a starting template. You may find the layout template to be useful at first because it's fairly barren. This way you can get an idea about some of the structural xml elements that are supported without the bloat of some of the more advanced concepts such as replaceable tokens. For a more advanced example see eventcg.

I recommend naming the file with the same name as the shortcut that will execute it, with ".snippet" appended to the end (the extension is required by Visual Studio but the name portion seems to be completely ignored). Specify the shortcut in the <Shortcut> element under the <Header> element within the snippet's XML.

Save the snippet file to your "Code Snippets\Visual C#\" folder usually located under "My Documents\Visual Studio 2005\". You don't have to restart Visual Studio. As a matter of fact, you don't even have to close and reopen the current document. Simply save your snippet, go back into VS and press Ctrl+Space to open the IntelliSense list from within a C# code file. Start typing the shortcut for your snippet and you should see it in the list. Pretty cool ;)

Types of Snippets

There are three ways that snippets can be typed: SurroundsWith, Expansion and Refactoring. A custom snippet may be any combination of the first two but not Refactoring. The type of a snippet must be specified by including one <SnippetType> element for each type. They must be placed within the <SnippetTypes> element, which is located in the snippet's <Header> element.

Executing Snippets

Expansion snippets are executed easily by typing their shortcut and pressing tab.

SurroundsWith may be executed using a special key combination (CTRL+K, CTRL+S), by selecting the Edit → IntelliSense → Surround With menu item, or from the context menu by selecting Surround With.

Refactoring snippets are executed by selecting the corresponding menu item from the Refactor menu or context menu. Custom snippets do not support this type.

Using Fields

To fill out a snippet's fields when it's being used you can continue to press tab after the snippet is started, and for each green box that receives focus when you press tab you can type in a new value.  Pressing tab repeatedly will eventually cycle through all of the replaceable tokens in the snippet and start over again from the first token. Note, however, that only the first appearance of a field within the snippet is editable. The remaining instances will take on the same value entered in the first instance.  When you are finished customizing the snippet, make sure that the cursor is within one of the green areas and press Enter or Esc once.

Snippets For Refactoring

Browse to your "Program Files\Microsoft Visual Studio 8\VC#\Snippets\language id\Refactoring" directory and you'll find the snippets used to perform the refactoring techniques in the Refactor menu. Notice that these snippets have a <SnippetType> element with a value of Refactoring, which cannot be specified in custom snippets. If you're going to try modifying these snippets then don't forget to back them up first, otherwise you can take a look at [5] if you mess them up.

Modifying these snippets can be quite useful. For instance, have you noticed that when Visual Studio stubs out an interface implementation, whether implicit or explicit, all methods contain a single line of code that throws a new Exception with the text, "The method or operation is not implemented."? Great, but why not just throw NotImplementedException instead? Well, you can easily adjust MethodStub.snippet to do that by simply replacing global::System.Exception with global::System.NotImplementedException for the Exception field, which uses the SimpleTypeName function.

--
References

[1] Visual C# Application Development, Code Snippets
http://msdn2.microsoft.com/en-gb/library/f7d3wz0k(VS.80).aspx

[2] Visual C# Application Development, Default Code Snippets
http://msdn2.microsoft.com/en-gb/library/z41h7fat(VS.80).aspx

[3] .NET Framework Developer's Guide, Event Design
http://msdn2.microsoft.com/en-us/library/ms229011.aspx

[4] J. Skeet, Delegates and events
http://www.yoda.arachsys.com/csharp/events.html

[5] Visual C# Language Concepts, How to: Restore C# Refactoring Snippets
http://msdn2.microsoft.com/en-gb/library/ms236401(VS.80).aspx

As always, I'd love to hear from anyone that has something to say about this post.  Drop me a comment.