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.
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:
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.
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:
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.
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.
public short 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
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.
public PdfImage AddImage(Image 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:
<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:
public sealed class PdfFont
public PdfFontEmbedStyle EmbedStyle
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:
- Link to a method:
- Link to a group of overloaded methods:
- Link to a class:
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
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:
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:
- Download and install Sandcastle.
- Download and install Sandcastle Help File Builder.
- Download and apply Sandcastle Styles patch.
- 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.
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.
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.
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.
Sets the default topic. This topic will be opened when CHM file is opened.
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.
Opens preview of the current topic.
Inserts template of a link to a topic. You should fill the template with topic ID of the target topic.
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:
private void helloWorld()
In order to insert an image to a topic, you should do the following:
- Right-click in the Project Explorer window. Then select Add->Existing Item in context menu. You will be prompted to select an image.
- 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.
To insert an image to topic you should use the
mediaLink tag like this:
<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:
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:
- 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.
- 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:
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.