Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Creating Documentation for a .NET Component with Sandcastle Help File Builder

4.91/5 (30 votes)
1 Jan 2011CPOL14 min read 101.3K  
This article shows how to create documentation within your code, write additional topics and compile the help file using Sandcastle Help File Builder.

Contents

Introduction

Open notebook Good documentation is an important part of a successful product. Creating full and comprehensive description of functions and capabilities of a software product or component takes time and patience. In this article, I will discuss some practical aspects of creating documentation for .NET components.

Let's assume that we have finished or almost finished creating a .NET developer library (developers are end users in this case). Library's API is perfect, the number of bugs impressively small, and indeed it is not a library, but simply a storehouse of perfect code. One little thing is left to do. We need to explain to users how to use this wonderful product.

There are different approaches to writing documentation. Some teams prefer to start producing documentation at the time of inception of the product. Others postpone writing manuals to a later time. In some teams, there are people who go from developer to developer and from manager to manager asking questions and accumulating knowledge about the product. These people are responsible for writing documentation for the product. In many small teams, there are no such people and the documentation is often written by a product developer or developers. Some teams use third-party tools like Help & Manual, which, as a full-fledged text processor, can be used to create documentation with a very complex layout of the pages. Such tools are also good for producing documentation in a variety of formats like PDF, CHM etc. Many people use a different approach, widely advocated in recent years - they write documentation directly in the code of the product.

I have used third-party tools and wrote documentation directly in the code, tried to start writing documentation before and after development is done. In the end, I decided for myself that it's better to postpone writing manuals to the second half of a product development cycle. The closer to completion the more stable the API, feature set, etc., and less adjustments to the documentation will be needed. Writing documentation directly in the code eventually proved to be more convenient than using third-party tools, although at first it seemed quite the opposite. This article is just about how to write documentation directly in your code and what you can do with the written one later.

Documenting the API

The C# and VB.NET compilers are able to recognize comments, decorated in a special way (XML comments) and, if necessary, create an XML file which can then be used to generate the documentation. In order to create documentation this way, it's needed to describe all the public code entities (classes, interfaces, methods, etc.) using XML comments. The XML comments start with three forward slashes. It looks like this:

C#
/// <summary>
/// Gets the R component from ABGR value returned by 
/// <see cref="O:BitMiracle.LibTiff.Classic.Tiff.ReadRGBAImage">ReadRGBAImage</see>.
/// </summary>
/// <param name="abgr">The ABGR value.</param>
/// <returns>The R component from ABGR value.</returns>
public static int GetR(int abgr)
{
	return (abgr & 0xff);
} 

The creation of an XML file from the comments is disabled by default. It should be enabled in the project properties on Build tab.

XML comments

As a result, an XML file will be created upon each build of your executable or assembly. That file will contain all the XML comments from the code, including comments for all non-public entities. This file is useful in itself because when you put it next to the assembly, the IntelliSense feature in Visual Studio will use information from this file to display descriptions for the methods, properties and parameters of the assembly. Here is an example of how it will look for a function GetR shown above:

Intellisense for LibTiff.Net

However, in most cases, the generated XML file will contain comments for the internal entities that users should not see. Later in this article, I'll show how to automatically remove information for non-public entities from the XML file.

I'm not going to describe all possible XML comment tags, but will try to briefly describe the most commonly used ones.

The summary tag is used for a description of the class, interface, enumeration, methods and properties of a class or interface, and members of the enumeration. The param tag is used to describe a parameter for a method. This tag should be used for each method parameter. The returns tag is used to describe method return value (if any). The value tag is useful to describe the value that a property accepts or returns. In a sense, the value tag is an analog of the returns tag but is used for properties instead of methods.

C#
/// <summary>
/// Gets the font ascent.
/// </summary>
/// <value>The font ascent.</value>
/// <remarks>Ascent is the maximum height above the baseline reached
/// by glyphs in this font, excluding the height of glyphs for
/// accented characters.</remarks>
public short Ascent
{
    get
    {
        return Impl.Ascent;
    }
}    

Very useful and, unfortunately, often overlooked is the remarks tag, which allows you to specify remarks for a code entity. This tag can be used in comments for almost any code entity except enumeration values. In fact, you can use remarks tag for enum values, but the compiled documentation (CHM file) produced from XML comments using default style (vs2005 style) won't contain such remarks. This default behavior obviously reduces the usefulness of remarks for enum values.

Here are some more practical observations and recommendations.

I recommend you to download and install the GhostDoc plug-in for Visual Studio. This plug-in is compatible with all versions of Visual Studio newer than Visual Studio 2005 and greatly simplifies the authoring of XML comments. GhostDoc generates and inserts XML comment for a method or property near the cursor position when you press Ctrl-Shift-D. The plug-in inserts all the necessary tags and generates text for them based on types, names and other contextual information. Often you will only need to correct and complete the generated text.

The biggest drawback of writing documentation directly in the code is that the comments sometimes take more space than the code itself. This may make the code difficult to read. To circumvent the problem, it's very convenient to separate public interface and its implementation completely.

Compiled documentation will contain a separate page for each group of overloaded methods. You can take a look at such page in Docotic.Pdf library help. For a text you want to be shown above the Overload List section on such pages the overloads tag should be used.

C#
/// <summary>
/// Adds the new image with the data and properties of the specified
/// <see cref="Image"/> to the end of the collection of document images.
/// </summary>
/// <param name="image">The existing <see cref="Image"/> from which
/// to create the <see cref="PdfImage"/>.</param>
/// <returns>The newly added <see cref="PdfImage"/> for the first
/// frame (page) found in the existing <see cref="Image"/>.</returns>
/// <overloads>Adds the new image to the end of the collection of
/// document images.</overloads>
/// <remarks>This method adds a new image for each frame (page) found
/// in the existing <see cref="Image"/>.</remarks>
public PdfImage AddImage(Image image)
{
	return impl.AddImage(image);
}  

You may want to provide a link to another method or type in an XML comment. For such a link, you should use a text like this:

HTML
<see cref="X:MEMBER">link text</see>

In a code above X is an optional prefix denoting an entity type and MEMBER is a full or partial specification of the entity. Type prefixes are as following: T for a class, M for a method, P for a property, O for a group of overloaded methods. You can use partial specification and omit the prefix for links between the two methods of the same class or between two entities of the same namespace.

The following code shows partial specification used to link to PdfFontEmbedStyle enum from description of a property in PdfFont class. Both the enum and the class are contained in the same namespace:

C#
public sealed class PdfFont
{
    ...
    /// <summary>
    /// Gets or sets the <see cref="PdfFontEmbedStyle"/> value that specifies
    /// how this font is embedded into the document.
    /// </summary>
    /// <value>The <see cref="PdfFontEmbedStyle"/> value that specifies
    /// how this font is embedded into the document.</value>
    public PdfFontEmbedStyle EmbedStyle
    {
        get
        {
            return Impl.EmbedStyle;
        }
        set
        {
            Impl.EmbedStyle = value;
        }
    }
} 

If you link to an entity in another namespace, to a group of overloaded methods or to a specific method in a group of overloaded methods, you should always use full specification or your link may be broken in compiled documentation. Examples of links with full specification:

  • Link to a property:
    HTML
    <see cref="P:System.Exception.HResult"/>
  • Link to a method:
    HTML
    <see cref="M:BitMiracle.LibTiff.Classic.Tiff.GetR(System.Int32)"/>
  • Link to a group of overloaded methods:
    HTML
    <see cref="O:BitMiracle.LibTiff.Classic.Tiff.PrintDirectory"/>
  • Link to a class:
    HTML
    <see cref="T:BitMiracle.LibTiff.Classic.TiffTagMethods"/>

As you can see, any full specification contains method parameters. This helps to resolve the link but complicates the link text. You can save manual labor by copying the full specifications from a previously produced XML comments file.

There is an annoyance associated with links to a group of overloaded methods. Visual Studio requires such references to be specified as O:XXX.YYY, and the Sandcastle Help File Builder (this tool I will use later to compile help file) requires such references to be specified as Overload:XXX.YYY. To solve this problem, I use a simple script that gets called on Post-build event and replaces all occurrences of O: in produced XML comments file with Overload:.

For links to some external, not related to the description of the API, page of your documentation or a resource on the Internet, use good old <a> tag with href attribute. For example, <a href = "54cbd23d-dc55-44b9-921f-3a06efc2f6ce.htm">link text</a> or <a href = "http://site.com/page.html">another link text</a>. In the first example, the href attribute contains string formatted as "TOPIC_ID.htm". What is the TOPIC_ID and where you can get one will be described further.

You can read more about XML comments in the following articles:

Compiling Help File

Once the XML comments for your component are ready, you can generate documentation file from them. I prefer to use Sandcastle and Sandcastle Help File Builder (SHFB) for this task. Some people prefer DocProject for this. You may want to read a discussion on Stack Overflow about SHFB and DocProject. If you decide to use SHFB then you should:

  1. Download and install Sandcastle.
  2. Download and install Sandcastle Help File Builder.
  3. Download and apply Sandcastle Styles patch.
  4. If you have any problems building the documentation in HTML Help format, you will need to verify that itircl.dll exists and registered in your copy of Windows. Typically this DLL can be found in the System32 folder and it must be registered using regsvr32. You can read more about this in Rick Stone's Tips 'n Tricks.

Let's build the documentation in CHM format. To do this, run Sandcastle Help File Builder and configure Project Properties. You can configure additional components used by SHFB during the build using "ComponentConfigurations" property. If you do not know what components you may need, you can select all the components. In any case, I recommend that you always use IntelliSense Component, as it automatically creates a copy of the input XML file, cleansed of all comments for non-public code entities. You should provide your users with an XML file produced by IntelliSense Component and not the XML file which was created during build of your component.

Docotic.Pdf in SHFB

Also, I recommend changing the following properties:

  • in Build section: FrameworkVersion
  • in Help File section: CopyrightHref, CopyrightText, FeedbackEMailAddress, FeedbackEMailLinkText, HelpTitle, HtmlHelpName
  • in Paths section: OutputPath

Next, specify Documentation Sources in the Project Explorer window. I recommend you to specify the DLL and XML files produced during the build of your component as documentation sources, not your project file. If you specify your project file as documentation source you may face a problem: changes in the XML comments are not always reflected in the documentation. Even after rebuild of the documentation. Who is to blame for this problem, I do not know.

Another important step is to describe the namespaces in SHFB. Your code can not have XML comments for namespace so you need to do specify such comments manually in SHFB. The Comments section and NamespaceSummaries property in the section should be used for the task. You can use standard HTML tags for namespace comments.

The documentation project is set up, it's time to build CHM file. We should execute Documentation->Build Project command and if everything is done right then we get a nice MSDN-style help file built.

For more information about Sandcastle Help File Builder take a look at this article.

Writing additional topics

Description of classes, methods and other code entities is important part of the documentation, but not sufficient one. Good documentation usually includes additional articles, examples, FAQ, etc. Let's see how you can add topics to your help file.

First, you need to open Content Layout window. To do so, right-click in Project Explorer window and select Add->New Item->Content Layout in context menu. Topics should be added to documentation by means of Content Layout window. You can also specify order of topics, default topic, etc.

Content Layout

The markup language for topics is MAML. It's an XML-based format. Topics are stored in *.aml files. Sandcastle Help File Builder contains a set of topic templates which is quite handy. For me, most useful templates are Conceptual and Walkthrough.

Each new topic gets a topic ID. Later, this ID will be used to generate a name for an HTML file produced from topic markup. A produced HTML file will be included in compiled help file. The topic ID is also used to refer to a topic from other topics or from XML comments in the code of your component.

When you create a new topic, SHFB will prompt you to save the topic in a file. A default name for the file is "TOPIC_ID.aml". You may and, probably, should change default name to something like "Topic Title.aml".

Let's look at some GUI elements in SHFB, which are useful when editing topics.

SHFB Content Layout
Default Topic icon

Sets the default topic. This topic will be opened when CHM file is opened.

Api Insertion Point icon

Sets the API insertion point. Depending on what option is selected, the pages generated from XML comments in your code will be inserted either before or after or as child elements of an element marked as API insertion point.

Preview Topic icon

Opens preview of the current topic.

Topic Link icon

Inserts template of a link to a topic. You should fill the template with topic ID of the target topic.

MAML Tags icon

Inserts a MAML tag. You can select a tag to insert from dropdown list.

You can use the Entity Reference window (in the picture above it's shown on the right) to insert code entity references into currently open topic. But I think that this window is not very convenient because in order to insert a link at cursor position we need to first open the topic in editor, then open the Entity Reference window, then write a part or full name of the entity, then find an entity in search results and then double-click on it. I prefer to insert references by hand. To do so, I find a text for the references in previous build log.

You should use <code> tag, if you need to insert a code snippet to a topic. Example:

C#
<code language="cs">
private void helloWorld()
{
    Console.WriteLine("Hello World!");
}
</code>

In order to insert an image to a topic, you should do the following:

  1. Right-click in the Project Explorer window. Then select Add->Existing Item in context menu. You will be prompted to select an image.

    add-image.png

  2. Select image in the Project Explorer window. Then change BuildAction property value to Image and ImageId property value to an ID of your choice. This ID will be used in links to this image.

    image-properties.png

To insert an image to topic you should use the mediaLink tag like this:

XML
<mediaLink><image xlink:href="ImageId" placement="center" /></mediaLink>

Unfortunately, the current version of built-in editor in SHFB is far from perfect. For example, the tags are not closed automatically, too many actions have no associated hotkeys, and some standard MAML tags cannot be inserted by means of the toolbar. It's weird, but I found that most of topic authoring tasks are easier to perform with Visual Studio XML editor. Of course, you can use any other XML editor when writing topics.

That was my description of how to perform basic topic authoring tasks. I recommend you the following links if you want more information:

Integrating into Build Process

You can include a Sandcastle Help File Builder project file (*.shfbproj) into a Visual Studio solution, but it won't be added as full-blown project. That is, the project will only be added to the Solution Items group and you won't be able to see its contents.

To include a SHFB project into a Visual Studio solution, do the following:

  1. Right-click on a solution root and select Add->Existing Item… in context menu, then select a SHFB project. The project will be added to the Solution Items group.

    SHFB project in Visual Studio

  2. Right-click on a SHFB project name in Solution Items group and select "Open With..." in context menu, then click Add to add a new program to the list. Enter the path to SandcastleBuilderGUI.exe and enter "Sandcastle Help File Builder GUI" for the friendly name. Click OK to save it, and then click "Set as Default" button.

After that, you'll be able to open help project from within Visual Studio using double click on a help project name.

I think that ability to build help file from the command line is more useful. You can build your help file on Post-Build event, for example. Here is a command you can use to build a Sandcastle Help File Builder project from the command line:

%SystemRoot%\Microsoft.NET\Framework\v3.5\MSBuild.exe" 
/p:Configuration=Release Help.shfbproj

In a line above Help.shfbproj is the filename of the SHFB project to be built.

Hopefully, this article will help you start writing the documentation for your projects. Good luck with authoring your help files!

P.S.: You can always download the source code package of LibTiff.Net, if you want to see a sample of the SHFB project and topic files.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)