Sandcastle, DocProject and creating custom topics

While looking into DocProject, I wanted to see how easy it would be to add custom topics into a project but didn’t get chance to do it in my original post.

One of the nice features of DocProject is that you can add your own code into the build process.  The file BuildProcess.cs has a number of event methods which are called at various points within the build process.  In the How To… there is a a guide to creating custom topics for the webpage, however I wanted to use my existing HTML pages within the help file. It would have been nice just to be able to select these from within the options menu – hint hint Dave.  I am going to create my own for now.

What I want to do, is take a folder and include it within a CHM/Help 1.0 fie.  For this there are two important concepts, one, everything needs to be included in the html folder. Second, the index for the help file is .hhc and .hhk which is loosely based on html file and uses and

  • tags for each entry.  For this you should really use a template for your HTML and add content based on that so all the pages look the same, but I’m just going to look at the core concept.

    Firstly, I created a directory structure in my solution which looks like below.

    • /Root Folder
      • /Custom Topics
        • /test
          • test.htm
          • HtmlPage2.htm
        • HTMLPage1.htm

    This is what I want to appear within my CHM file. However, it’s not as simple as it first seems. I included test.htm to be the default page for when a user clicks the Test namespace, as Test is a folder in the structure, it needs a related page.

    Firstly, I had to hook into the DocProject build process.  This is really nice as Dave has already provided the methods/events there for you, it was just finding where to place the call as when Sandcastle is creating its own files, it will override any changes you have made previously.  The method BeforeExecuteStep is called before every step in the build process, so I simply did a check to see if the I was before the step I was interested in:

    if (step.Name.Equals(“Compile Help 1.x”))
                    ProcessHelpCustomTopics(context, “Html”);

    Now a method is called just before the help file is about to be compiled, this method writes out some information to the trace and calls CustomTopics.Import.  Here, I simply copy all the content from Custom Topics into the html folder, keeping the correct folder structure (Method CopyAllFilesInSourceToTarget).  So now all the files are in the correct place, we need to tell the help compiler about them, now this is where it started to get tricky.

    There are two important files for the help structure, .hhc and .hhk.  Both look very similar but are different. The compiler looks at these files to work out which files to include, and where they should be in the hierarchy of the contents.  Firstly, I thought it would be simple to just create these files automatically based on the directory structure, hence why I wanted to keep the directory structure intact when I copied over the files.  Well, the problem is then you have no control over what order the files should appear in – that’s really not good so I had to come up with a not so great solution.  Ideally, it would be an option within the addin dialog where you could build a custom table of contents, but that would require changing the core code.  I have my own .hhc and .hhk files within Custom Topics which simply outline how the table contents should be for that particular folder. Then, the user has the power to say which files should be where and lay it out correctly. I then just merge the two files with the Sandcastle created files for the table of contents.  The contents of hhc file would for example look like this:

    <li><object type="text/sitemap"> <param name="Name" value="HTMLPage1"> <param name="Local" value="htmlHTMLPage1.htm"> object> <li><object type="text/sitemap"> <param name="Name" value="Test"> <param name="Local" value="htmltesttest.htm"> object> <ul> <li><object type="text/sitemap"> <param name="Name" value="HTMLPage2"> <param name="Local" value="htmltestHTMLPage2.htm"> object> ul>

    It works well, the only problem is that the file format is not the easiest to understand, as its not XML and its not really HTML.  But to be fair, Help 1.0 is from 98, and Help 2.0 does have a XML based TOC file.

    For a simple file to be displayed in the contents, the file hhc file should have a entry like

    <li><object type="text/sitemap"> <param name="Name" value="HTMLPage1"> <param name="Local" value="htmlHTMLPage1.htm"> object>

    Notice, li is not closed. If you wanted a page as a child node of another page, underneath an entry like above, you would add

    <ul> <li><object type="text/sitemap"> <param name="Name" value="HTMLPage2"> <param name="Local" value="htmltestHTMLPage2.htm"> object> ul>

    Adding an entry for each page between the ul tags.

    Within hhk you simple just have a single entry for all of the pages, like below

    <li><object type="text/sitemap"> <param name="Name" value="HTMLPage2"> <param name="Local" value="html/test/HTMLPage2.htm"> object>

    So, its not that difficult, but trying to out it would wasn’t a great experience.

    You can download my code BuildProcess.cs and CustomTopics.cs.  I have also zipped up the complete project for download here

    While, I haven’t implemented the support for Help 2.0, you would simply need to copy all of the html into the Html2 folder and modify the HxT file.  Plus, with this solution it also works with the DocSite web applications.

    One thing to be aware of was that if I changed BuildProcess.cs without changing the target assembly, then it would never build as there was no point because the the source hadn’t changed. That’s fine, unless BuildProcess.cs changes, but selecting ReBuild gets around this and forces a fresh build. 

    Another tip, Sandcastle builds a lot quicker when your laptop isn’t on Power Saving mode…

  • 2 thoughts on “Sandcastle, DocProject and creating custom topics”

    1. Great work Ben! This is a really nice effort. (and I get the hint 🙂

      Others have asked for this functionality too. I’ve already added a work item to DocProject on CodePlex for a “Custom Topic Dialog”. Currently, I’m waiting for Sandcastle to support custom content so that the process is integrated and supported, instead of a hack. In the June ’07 CTP Sandcastle ships with “conceptual” content transformations, which look promising. I’m waiting to here more about them from Anand.

      BTW, DocProject has partial-build support (check out the Build Process wiki), which allows you to rebuild only the HTML Help Workshop files instead of running the entire Sandcastle process for when you only make a change to your custom topics. To trigger a partial build, for example, you can just delete the .chm file and then use the “Build” command instead of using the “Rebuild” command.

    Leave a Reply to Dave Cancel reply

    Your email address will not be published. Required fields are marked *