Custom formatters - arklumpus/TreeViewer GitHub Wiki

Custom formatters are implemented as static methods that accept a single argument of type object and return a value of the appropriate type - string for a String formatter, double? for a Number formatter and Colour? for a Colour formatter.

String formatters

In general, code for a string formatter looks like the following:

public static string Format(object attribute)
{
    //...
}

The code within the curly braces somehow converts the value of attribute (which is an object) into a string. This can be done in multiple ways; if the attribute is expected to already be a string (e.g. because we are formatting the Name attribute), converting it is as simple as using the following code:

public static string Format(object attribute)
{
    return attribute as string;
}

If attribute is a string, this method returns its value; otherwise it returns null. Alternatively, if the attribute is expected to be a Number (which translates to the double C# type - such as the Length attribute), the first step is to unbox it back into a double:

public static string Format(object attribute)
{
    if (attribute is double attributeValue)
    {
        //...
    }
    else
    {
        return null;
    }
}

This checks if attribute is actually a double: if it is, it stores its value in a variable called attributeValue; if it is not, it immediately returns null. Once we have converted the attribute value into a double, we can manipulate it before finally converting it into a string:

public static string Format(object attribute)
{
    if (attribute is double attributeValue)
    {
        attributeValue = attributeValue * 100;
        return attributeValue.ToString();
    }
    else
    {
        return null;
    }
}

This code multiplies the attribute value by 100 before returning its string representation. Of course, there are countless variations on this theme; one could check whether the attribute value is a valid number before returning it, or if it is greater than 0, and so on.

To get started in writing a custom formatter, it might be a good idea to try different settings in the formatter window, to see how they affect the generated code. It should then be clear which parts of the code need to be modified to change the results obtained.

Number formatters

Number formatters are similar to String formatters; they are implemented as a method with an argument of type object and returning a value of type double?. The ? is important, because it allows formatters to return null if the attribute value is invalid. The method signature for a Number formatter is the following:

public static double? Format(object attribute)
{
    //...
}

If the attribute is expected to be of type Number (i.e. double, like the Length or Support attributes), we can directly convert it:

public static double? Format(object attribute)
{
    return attribute as double;
}

We could also manipulate the value, e.g.:

public static double? Format(object attribute)
{
    return (attribute as double) * 4;
}

This code returns the value of the attribute multiplied by 4.

If the attribute is instead a string, it needs to be unboxed first:

public static double? Format(object attribute)
{
    if (attribute is string attributeValue)
    {
        //...
    }
    else
    {
        return null;
    }
}

As in the case of the String formatter, this code attempts to unbox the attribute into a string variable called attributeValue; if this does not succeed, the method returns null. Otherwise, attributeValue needs to be converted into a double. This can be achieved e.g. by parsing the string value (which would for example convert the string "1.23" into the number 1.23):

public static double? Format(object attribute)
{
    if (attribute is string attributeValue)
    {
        return double.Parse(attributeValue);
    }
    else
    {
        return null;
    }
}

Again, fiddling with the default settings of the formatter is a good way of understanding which parts of the code need to be changed to achieve different results.

Colour formatters

Colour formatters are similar to String and Number formatters; they are implemented as a method with an argument of type object and returning a value of type Colour?. The ? is important, because it allows formatters to return null if the attribute value is invalid. The method signature for a Colour formatter is the following:

public static Colour? Format(object attribute)
{
    //...
}

If the attribute is expected to be a string, one approach could be to parse it as a CSS colour string:

public static Colour? Format(object attribute)
{
    if (attribute is string colour)
    {
        return Colour.FromCSSString(colour);
    }
    else
    {
        return null;
    }
}

This code attempts to convert the attribute into a string called colour. If this succeeds, it interprets the string as a CSS colour and returns the resulting colour; otherwise it returns null. This makes it possible to directly convert attribute values such as "red", "#FF0000", or "rgb(255, 0, 0)" into Colours.

An alternative approach would be to return different colours depending on the value of the attribute. For example, the following code associates different colours to the attribute values "large", "medium" and "small":

public static Colour? Format(object attribute)
{
    if (attribute is string size)
    {
        switch (size)
        {
            case "large":
                return Colours.Red;
            
            case "medium":
                return Colours.Green;

            case "small":
                return Colours.Blue;
        }
    }
    else
    {
        return null;
    }
}

Colour objects can be initialised in multiple ways, including the Colour.FromRGB and Colour.FromRGBA methods (which create Colour objects from RGB values), the Colour.FromCSSString method (which creates the colour from a CSS colour string), and more. All methods to create colours are in the form of Colour.FromXXX; therefore, if you start to type this in the code editor window, you can use the autocomplete feature to see a list of all the available possibilities.

In addition, the CSS named colours are defined in the Colours static class and can be obtained (like in the code above) by using a syntax like Colours.Red or Colours.CornflowerBlue.

If the attribute is expected to be a double, there are again multiple ways in which it could be converted into a Colour; for example, the following code returns a red colour if the attribute value is lower than 0.5, a yellow colour if it is between 0.5 and 0.95 and a green colour if it is greater than or equal to 0.95:

public static Colour? Format(object attribute)
{
    if (attribute is double support)
    {
        if (support < 0.5)
        {
            return Colour.FromRgb(255, 0, 0);
        }
        else if (support < 0.95)
        {
            return Colours.Yellow;
        }
        else // support >= 0.95
        {
            return Colours.FromCSSString("green");
        }
    }
    else
    {
        return null;
    }
}

You can fiddle with the settings for the default colour formatter to get additional ideas, e.g. on how to convert a number into a colour from a gradient.