Mar 27 2008

Log in form usability problem

Category: IT RelatedRory Primrose @ 08:37

I've just realised a usability problem with website log in forms. The "remember my password" checkbox is almost always below the log in button. For keyboard support, the sequence goes like this:

  1. Give focus to the username field (any good app would do this for you)
  2. Type in username
  3. Tab to focus on password field
  4. Type in password
  5. Tab to focus on the log in button (with perhaps another tab to get over a cancel button)
  6. Tab to focus on the checkbox
  7. Space to check the checkbox
  8. Shift-Tab to give focus back to the log in button with perhaps another tab if you need to skip over a cancel button
  9. Space to fire the log in button

If the forms where changed to simply have the checkbox above the log in button, then the keyboard sequence would be:

  1. Give focus to the username field (any good app would do this for you)
  2. Type in username
  3. Tab to focus on password field
  4. Type in password
  5. Tab to focus on the checkbox
  6. Space to check the checkbox
  7. Tab to focus on the log in button
  8. Space (to fire the Log in button)

This would read better as the user only traverses down the screen rather than down and then up. It removes unnecessary actions and makes more sense.

Tags:

Mar 26 2008

Xml comments and the include element

Category: Applications | .NetRory Primrose @ 18:57

I have been doing a lot of work over the last week writing xml comment documentation. I have been compiling the xml output into chm files using SandCastle, SHFB and my own SHFB wrapper application. I have been increasingly been finding that I am writing remarks that I want to reuse across different methods and properties of several classes in an assembly. Today, I had a particular property scattered among several data contracts for a WCF service that are used for the same purpose and have the same xml comments.

For several days I have been doing the very bad practice of writing the documentation and copying what I need to the other locations. I remembered reading about the <include /> element for xml comments and read up on it in more detail (see here and here). After a bit of experimentation today, I can say that the <include /> element is very powerful for three reasons:

  1. You can include the entire xml comment for an item
  2. You can include part of the xml comment for an item
  3. You can include include elements and they will be recursively resolved

Lets look some examples. Here is my initial code:

using System.Diagnostics;
 
namespace ClassLibrary1
{
    public class Class1
    {
        /// <summary>
        /// Does something random.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is a common remark.
        /// </para>
        /// </remarks>
        public void SomeRandomMethod()
        {
            Debug.WriteLine("Do something.");    
        }
 
        /// <summary>
        /// Generates a checksum value.
        /// </summary>
        /// <param name="someData">Some data.</param>
        /// <returns>A byte array.</returns>
        /// <remarks>
        /// <para>
        /// This is a paragraph about design considerations related to checksum generation.
        /// </para>
        /// <para>
        /// This is a paragraph about some general information about checksum values.
        /// Lets imagine that this is a really long paragraph that...
        /// </para>
        /// <para>
        /// extends for many
        /// </para>
        /// <para>
        /// many paragraphs.
        /// </para>
        /// <para>
        /// This is a common remark.
        /// </para>
        /// </remarks>
        public byte[] GenerateChecksum(byte[] someData)
        {
            Debug.WriteLine("Generate a checksum using the data supplied.");
 
            return null;
        }
 
        /// <summary>
        /// Gets or sets the checksum.
        /// </summary>
        /// <value>The checksum.</value>
        /// <remarks>
        /// <para>
        /// This is a paragraph about some general information about checksum values.
        /// Lets imagine that this is a really long paragraph that...
        /// </para>
        /// <para>
        /// extends for many
        /// </para>
        /// <para>
        /// many paragraphs.
        /// </para>
        /// </remarks>
        public byte[] Checksum
        {
            get;
            set;
        }
    }
 
    public class Class2
    {
        /// <summary>
        /// Gets or sets the checksum.
        /// </summary>
        /// <value>The checksum.</value>
        /// <remarks>
        /// <para>
        /// This is a paragraph about some general information about checksum values.
        /// Lets imagine that this is a really long paragraph that...
        /// </para>
        /// <para>
        /// extends for many
        /// </para>
        /// <para>
        /// many paragraphs.
        /// </para>
        /// </remarks>
        public byte[] Checksum
        {
            get;
            set;
        }
    }
}

There is some duplication here that we can take care of with <include />.

Reason 1: You can include the entire xml comment for an item

To cover the first reason above, there is duplication of the comment for the Checksum properties in the two classes. The documentation in its entirety is the same. Excellent candidate for pushing out into a common xml file. Add an xml file to the project called something like CommonDocumentation.xml and come up with an appropriate schema. I put together something like this:

<?xml version="1.0" encoding="utf-8" ?>
<CommonDocumentation>
  <Properties>
    <Property name="Checksum">
      <summary>
        Gets or sets the checksum.
      </summary>
      <value>The checksum.</value>
      <remarks>
        <para>
          This is a paragraph about some general information about checksum values.
          Lets imagine that this is a really long paragraph that...
        </para>
        <para>
          extends for many
        </para>
        <para>
          many paragraphs.
        </para>
      </remarks>
    </Property>
  </Properties>
</CommonDocumentation>

We can now update each of those properties to something like this:

<?xml version="1.0" encoding="utf-8" ?>
<CommonDocumentation>
  <Properties>
    <Property name="Checksum">
      <summary>
        Gets or sets the checksum.
      </summary>
      <value>The checksum.</value>
      <remarks>
        <para>
          This is a paragraph about some general information about checksum values.
          Lets imagine that this is a really long paragraph that...
        </para>
        <para>
          extends for many
        </para>
        <para>
          many paragraphs.
        </para>
      </remarks>
    </Property>
  </Properties>
</CommonDocumentation>

 Reason 2: You can include part of the xml comment for an item

You can mix <include /> elements with other xml comments on the same item. This means you are not constrained to having the entire xml comment pushed out to an external file. If there is partial content being duplicated, this works too. Let's update the common documentation file to include the common remarks.

<?xml version="1.0" encoding="utf-8" ?>
<CommonDocumentation>
  <Properties>
    <Property name="Checksum">
      <summary>
        Gets or sets the checksum.
      </summary>
      <value>The checksum.</value>
      <remarks>
        <para>
          This is a paragraph about some general information about checksum values.
          Lets imagine that this is a really long paragraph that...
        </para>
        <para>
          extends for many
        </para>
        <para>
          many paragraphs.
        </para>
      </remarks>
    </Property>
  </Properties>
  <Remarks>
    <Remark name="CommonRemark">
      <para>
        This is a common remark.
      </para>
    </Remark>
  </Remarks>
</CommonDocumentation>

We can now update the SomeRandomMethod() comments to be the following:

/// <summary>
/// Does something random.
/// </summary>
/// <remarks>
/// <include file="CommonDocumentation.xml" path="CommonDocumentation/Remarks/Remark[@name='CommonRemark']/*" />
/// </remarks>
public void SomeRandomMethod()
{
    Debug.WriteLine("Do something.");
}

Reason 3: You can include include elements and they will be recursively resolved

This is where things get really useful. Now that we have pushed out common documentation to an external file, what if there is duplication within that file. No problem. The compiler will recursively resolve all <include /> elements, even if the xml comment itself is in the external file.

In the examples so far, there is an extensive amount of comments in the remarks section of several properties and methods. We have already moved the Checksum comments out to the external file, but we can also remove duplication in the remarks of the GenerateChecksum method which is also common to the Checksum properties. We can update the external xml file to include the duplicate remarks about the checksum and use the same include element against the GenerateChecksum method.

The final xml file is:

<?xml version="1.0" encoding="utf-8" ?>
<CommonDocumentation>
  <Properties>
    <Property name="Checksum">
      <summary>
        Gets or sets the checksum.
      </summary>
      <value>The checksum.</value>
      <remarks>
        <include file="CommonDocumentation.xml"
                path="CommonDocumentation/Remarks/Remark[@name='ChecksumDescription']/*" />
      </remarks>
    </Property>
  </Properties>
  <Remarks>
    <Remark name="CommonRemark">
      <para>
        This is a common remark.
      </para>
    </Remark>
    <Remark name="ChecksumDescription">
      <para>
        This is a paragraph about some general information about checksum values.
        Lets imagine that this is a really long paragraph that...
      </para>
      <para>
        extends for many
      </para>
      <para>
        many paragraphs.
      </para>
    </Remark>
  </Remarks>
</CommonDocumentation>

The final class file is:

using System.Diagnostics;
 
namespace ClassLibrary1
{
    public class Class1
    {
        /// <summary>
        /// Does something random.
        /// </summary>
        /// <remarks>
        /// <include file="CommonDocumentation.xml" path="CommonDocumentation/Remarks/Remark[@name='CommonRemark']/*" />
        /// </remarks>
        public void SomeRandomMethod()
        {
            Debug.WriteLine("Do something.");    
        }
 
        /// <summary>
        /// Generates a checksum value.
        /// </summary>
        /// <param name="someData">Some data.</param>
        /// <returns>A byte array.</returns>
        /// <remarks>
        /// <include file="CommonDocumentation.xml" path="CommonDocumentation/Remarks/Remark[@name='ChecksumDescription']/*" />
        /// <include file="CommonDocumentation.xml" path="CommonDocumentation/Remarks/Remark[@name='CommonRemark']/*" />
        /// </remarks>
        public byte[] GenerateChecksum(byte[] someData)
        {
            Debug.WriteLine("Generate a checksum using the data supplied.");
 
            return null;
        }
 
        /// <include file="CommonDocumentation.xml" path="CommonDocumentation/Properties/Property[@name='Checksum']/*" />
        public byte[] Checksum
        {
            get;
            set;
        }
    }
 
    public class Class2
    {
        /// <include file="CommonDocumentation.xml" path="CommonDocumentation/Properties/Property[@name='Checksum']/*" />
        public byte[] Checksum
        {
            get;
            set;
        }
    }
}

Issues

Now, there are some hazards with this.

The first mistake I made was not putting /* at the end of the xpath queries of the <include /> element. When the compiler runs, it processes the include elements by running the xpath query against the file specified and replaces the include element with the xml elements found. If you don't include /*, the only element found is the container of the external comments, not the comments themselves. This is guaranteed to break your compiled xml file.

The second issue is that because the external xml data is being injected into your file, if the external xml comments refer to a type that exists in a namespace that is not referenced in the class, then the compiler will throw errors. For example, I added the comment <see cref="SHA1CryptoServiceProvider"/> into the external file. When I recompiled, I got the following error (BTW, I treat warnings as errors):

Error    2    Warning as Error: XML comment on 'ClassLibrary1.Class1.GenerateChecksum(byte[])' has cref attribute 'SHA1CryptoServiceProvider' that could not be resolved    C:\Users\[Account]\AppData\Local\Temporary Projects\ClassLibrary1\Class1.Community Server    18    13    ClassLibrary1

The third issue is the complexity of the external xml file. The more include elements you use for the sake of maintainability due to removing duplication, the less maintainable your comments become. So use wisely.

Enjoy.

Tags: , , ,

Mar 25 2008

ReSharper comment token identification

Category: ApplicationsRory Primrose @ 09:33

ReSharper has a handy feature where it will identify tokens in your comments such as todo, note, bug etc. The Visual Studio IDE has a similar feature for TODO where it will identify those comments in the Task List window.

My issue with the ReSharper implementation is that it will identify these tokens even if they are in the middle of the comment rather than just at the start of the comment line. I originally posted an issue into the JetBrains Jira system thinking that this behaviour was not configurable, but then found that ReSharper identifies these tokens with regular expressions. If you open up ReSharper -> Options -> To-do Items, you will see a set of patterns identified.

The patterns defined are Todo, Note and Bug (I added Hack as a duplicate of Bug to support the default Visual Studio HACK token). I modified these regular expressions to ignore words beginning before the token. Instead, I just look for the beginning of the line. For example, the Note pattern was:

(\W|^)(?<TAG>NOTE)(\W|$)(.*)

I have modified this to:

^(?<TAG>NOTE)(\W|$)(.*)

This will now only identify note comments if the NOTE token exists at the beginning of the comment only.

Tags:

Mar 19 2008

SandCastle Builder Support for Namespace Documentation

Category: Applications | .NetRory Primrose @ 05:35

At work we have recently integrated building SandCastle documentation into our TeamBuild process using the SandCastle Help File Builder (SHFB) application. I created a wrapper application to achieve the same thing as a local dev process to help authoring the help contents.

One of the issues we had was how to add namespace documentation. The process we were using was to pass all the required information to SHFB instead of using a project file. The SandCastle project file is where the namespace documentation would normally be stored. Instead, we added the namespace documentation to an xml file stored in the appropriate documented Visual Studio project with a known file name format. The xml file was marked as to be copied to the build directory which would also make it available to team build. This file is then passed to SHFB using the -comment switch.

This may change in the near future as it looks like Eric has checked in a change to the latest SHFB beta that will do the same as the old NDoc model for namespace documentation (see here). Soon we will be able to create an internal class called NamespaceDoc in a namespace and SHFB will pull it out for us. This allows us to have documentation nicely stored in the code along with everything else.

Tags: , , , , ,

Mar 17 2008

Code coverage not available when debugging unit tests

Category: .NetRory Primrose @ 03:54

Yep, this one bit me last week.

I had been writing some unit tests and debugging them. When the tests were finished, I kept wanting to look at the code coverage. All I would see was the message "Code coverage is not enabled for this test run". After trying lots of things and wasting 30 minutes, it turns out that code coverage is not available when debugging unit tests, even though code coverage is enabled through the testrunconfig file and that the build configuration is set Debug.

To avoid this mistake in the future, you can enable a warning message that specifically highlights the problem. Go to Tools, Options, expand the Test Tools node and select Default Dialog Box Action. There is an option called "When starting a remote test run or a run with code coverage under the debugger:". Set this value to "Always prompt". The next time that you run a unit test with the debugger attached, you will get a warning message saying "Debugging tests running on a remote computer or with code coverage enabled is not supported. The tests will run under the debugger locally and without code coverage enabled.".

No more confusion.

Tags:

Mar 17 2008

Using a Vista x64 network printer from Vista x86

Category: IT RelatedRory Primrose @ 03:40

I have installed Vista x64 on my server which has a USB printer attached to it. I have shared this printer so that other machines on the network can use it as a network printer. I found that I could see the printer from my Vista x86 laptop, but it just wouldn't print.

The printer properties on the printer server have some advanced properties where you can specify x86 drivers for the printer for when clients add the network printer. The problem is that the printer drivers come with Vista natively. This means that the manufacturer (HP) doesn't provide the drivers for it as a download. After some research, I found that there is a really easy way around this issue.

The rough steps are:

  1. Add a new printer to your x86 machine
  2. Select Local printer attached to this machine, uncheck automatic detection
  3. Select Create a new port and select Local Port
  4. Enter the address of the printer (\\servername\printername)
  5. Select the printer make and model

Easy

Tags:

Mar 14 2008

Xbox 360: Dead and reborn

Category: PersonalRory Primrose @ 10:07

My Xbox 360 has been slowly dying for a few weeks now. I was getting a single red ring and an E74 error. I called Xbox support but they said that with that error and the box being out of warranty, I would need to spend about $135 getting it fixed.

Luckily I had read some forum posts that indicated that the famous RROD (red ring of death) would be soon to follow. As Microsoft now have an extended three year warranty for RROD cases, I hung on to the box. Sometimes it would take 10 boots to be able to use it, but it wasn't too bad. Gaming graphics were a little off though.

Finally, RROD showed up. I sent the box off last Friday and got is back today under the RROD warranty. I think that is quite quick.

Tags:

Mar 6 2008

Forcing SSL Gracefully on a Site

Category: IT RelatedRory Primrose @ 07:54

This is one to remember. Paul Litwin has posted about forcing SSL gracefully on a site using a post from Paul Wilson as a reference. I'll implement this when I set up some ssl sites that are publicly available.

Tags: