The personal website of Scott W Harden

Reflection and XML Documentation in C#

In C#, you can document your code using XML directly before code blocks. This XML documentation is used by Visual Studio to display tooltips and provide autocomplete suggestions.

/// <summary>
///  This method performs an important function.
/// </summary>
public void MyMethod() {}

To enable automatic generation of XML documentation on every build, add the following to your csproj file:

<PropertyGroup>
  <DocumentationFile>MyProgram.xml</DocumentationFile>
</PropertyGroup>

However, XML documentation is not actually metadata, so it is not available in the compiled assembly. In this post I'll show how you can use System.Reflection to gather information about methods and combine it with documentation from an XML file read with System.Xml.Linq.XDocument.

I find this useful for writing code which automatically generates documentation. Reflection alone cannot provide comments, and XML documentation alone cannot provide parameter names (just their types). By combining reflection with XML documentation, we can more completely describe methods in C# programs.

⚠️ WARNING: These code examples are intentionally simple. They only demonstrate how to read summary comments from XML documentation for methods found using reflection. Additional functionality can be added as needed, and my intent here is to provide a simple starting point rather than overwhelmingly complex examples that support all features and corner cases.

💡 Source code can be downloaded at the bottom of this article

✔️ The "hack" described on this page is aimed at overcoming limitations of partially-documented XML. A better solution is to fully document your code, in which case the XML document is self-sufficient. The primary goal of these efforts is to use XML documentation where it is present, but use Reflection to fill in the blanks about methods which are undocumented or only partially documented. Perhaps a better strategy would be to have a fully documented code base, with an XML file containing <summary>, <returns>, and <param> for every parameter. Tests can ensure this is and remains the case.

What does the XML Documentation look like?

There are some nuances here you might not expect, especially related to arrays, generics, and nullables. Let's start with a demo class with documented summaries. Keep in mind that the goal of this project is to help use Reflection to fill in the blanks about undocumented or partially-documented code, so this example will only add a <summary> but no <param> descriptions.

DemoClass.cs

/// <summary>
/// Display a name
/// </summary>
public static void ShowName(string name)
{
    Console.WriteLine($"Hi {name}");
}

/// <summary>
/// Display a name a certain number of times
/// </summary>
public static void ShowName(string name, byte repeats)
{
    for (int i = 0; i < repeats; i++)
        Console.WriteLine($"Hi {name}");
}

/// <summary>
/// Display the type of the variable passed in
/// </summary>
public static void ShowGenericType<T>(T myVar)
{
    Console.WriteLine($"Generic type {myVar.GetType()}");
}

/// <summary>
/// Display the value of a nullable integer
/// </summary>
public static void ShowNullableInt(int? myInt)
{
    Console.WriteLine(myInt);
}

XML Documentation File

There are a few important points to notice here:

  • Each method is a member with a name starting with M:
  • Parameter types are in the member name, but not parameter names!
  • Parameters might be listed in the XML, but they will be missing if only <summary> was added in code
  • 💡 The key step required to connect a reflected method with its XML documentation is being able to determine the XML method name of that method. How to do this is discussed below...
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>XmlDocDemo</name>
    </assembly>
    <members>
        <member name="M:XmlDocDemo.DemoClass.ShowName(System.String)">
            <summary>
            Display a name
            </summary>
        </member>
        <member name="M:XmlDocDemo.DemoClass.ShowName(System.String,System.Byte)">
            <summary>
            Display a name a certain number of times
            </summary>
        </member>
        <member name="M:XmlDocDemo.DemoClass.ShowGenericType``1(``0)">
            <summary>
            Display the type of the variable passed in
            </summary>
        </member>
        <member name="M:XmlDocDemo.DemoClass.ShowNullableInt(System.Nullable{System.Int32})">
            <summary>
            Display the value of a nullable integer
            </summary>
        </member>
    </members>
</doc>

XML Name Details

  • Generics from types have a single ` character
  • Generics from methods have double `` characters
  • If the parameter is by "ref" then you need to pre-pend the @ character
  • If the parameter is a pointer you need to pre-pend it with the * character
  • If the parameter is an array, you need to add [] characters and the appropriate number of commas
  • If the parameter is Nullable it will be wrapped in System.Nullable{}
  • If the method is MethodInfo is a casing operator, then you need to pre-pend it with ~ character

Thanks Zachary Patten for sharing these details in an MSDN article and e-mail correspondence

Read XML Documentation File

This code reads the XML documentation file (using the modern XDocument) and stores method summaries in a Dictionary using the XML method name as a key. This dictionary will be accessed later to look-up documentation for methods found using Reflection.

private readonly Dictionary<string, string> MethodSummaries = new Dictionary<string, string>();

public XmlDoc(string xmlFile)
{
    XDocument doc = XDocument.Load(xmlFile);
    foreach (XElement element in doc.Element("doc").Element("members").Elements())
    {
        string xmlName = element.Attribute("name").Value;
        string xmlSummary = element.Element("summary").Value.Trim();
        MethodSummaries[xmlName] = xmlSummary;
    }
}

Determine XML Method Name for a Reflected Method

This example code returns the XML member name for a method found by reflection. This is the key step required to connect reflected methods with their descriptions in XML documentation files.

⚠️ Warning: This code sample may not support all corner-cases, but in practice I found it supports all of the ones I typically encounter in my code bases and it's a pretty good balance between functionality and simplicity.

public static string GetXmlName(MethodInfo info)
{
    string declaringTypeName = info.DeclaringType.FullName;

    if (declaringTypeName is null)
        throw new NotImplementedException("inherited classes are not supported");

    string xmlName = "M:" + declaringTypeName + "." + info.Name;
    xmlName = string.Join("", xmlName.Split(']').Select(x => x.Split('[')[0]));
    xmlName = xmlName.Replace(",", "");

    if (info.IsGenericMethod)
        xmlName += "``#";

    int genericParameterCount = 0;
    List<string> paramNames = new List<string>();
    foreach (var parameter in info.GetParameters())
    {
        Type paramType = parameter.ParameterType;
        string paramName = GetXmlNameForMethodParameter(paramType);
        if (paramName.Contains("#"))
            paramName = paramName.Replace("#", (genericParameterCount++).ToString());
        paramNames.Add(paramName);
    }
    xmlName = xmlName.Replace("#", genericParameterCount.ToString());

    if (paramNames.Any())
        xmlName += "(" + string.Join(",", paramNames) + ")";

    return xmlName;
}

private static string GetXmlNameForMethodParameter(Type type)
{
    string xmlName = type.FullName ?? type.BaseType.FullName;
    bool isNullable = xmlName.StartsWith("System.Nullable");
    Type nullableType = isNullable ? type.GetGenericArguments()[0] : null;

    // special formatting for generics (also Func, Nullable, and ValueTulpe)
    if (type.IsGenericType)
    {
        var genericNames = type.GetGenericArguments().Select(x => GetXmlNameForMethodParameter(x));
        var typeName = type.FullName.Split('`')[0];
        xmlName = typeName + "{" + string.Join(",", genericNames) + "}";
    }

    // special case for generic nullables
    if (type.IsGenericType && isNullable && type.IsArray == false)
        xmlName = "System.Nullable{" + nullableType.FullName + "}";

    // special case for multidimensional arrays
    if (type.IsArray && (type.GetArrayRank() > 1))
    {
        string arrayName = type.FullName.Split('[')[0].Split('`')[0];
        if (isNullable)
            arrayName += "{" + nullableType.FullName + "}";
        string arrayContents = string.Join(",", Enumerable.Repeat("0:", type.GetArrayRank()));
        xmlName = arrayName + "[" + arrayContents + "]";
    }

    // special case for generic arrays
    if (type.IsArray && type.FullName is null)
        xmlName = "``#[]";

    // special case for value types
    if (xmlName.Contains("System.ValueType"))
        xmlName = "`#";

    return xmlName;
}

Get XML Documentation for a Reflected Method

Now that we have XmlName(), we can easily iterate through reflected methods and get their XML documentation.

// use Reflection to get info from custom methods
var infos = typeof(DemoClass).GetMethods()
                             .Where(x => x.DeclaringType.FullName != "System.Object")
                             .ToArray();

// display XML info about each reflected method
foreach (MethodInfo mi in infos)
{
    string xmlName = XmlName(mi);
    Console.WriteLine("");
    Console.WriteLine("Method: " + XmlDoc.MethodSignature(mi));
    Console.WriteLine("XML Name: " + xmlName);
    Console.WriteLine("XML Summary: " + MethodSummaries[xmlName]);
}

Output

Method: XmlDocDemo.DemoClass.ShowName(string name)
XML Name: M:XmlDocDemo.DemoClass.ShowName(System.String)
XML Summary: Display a name

Method: XmlDocDemo.DemoClass.ShowName(string name, byte repeats)
XML Name: M:XmlDocDemo.DemoClass.ShowName(System.String,System.Byte)
XML Summary: Display a name a certain number of times

Method: XmlDocDemo.DemoClass.ShowGenericType<T>(T myVar)
XML Name: M:XmlDocDemo.DemoClass.ShowGenericType``1(``0)
XML Summary: Display the type of the variable passed in

Method: XmlDocDemo.DemoClass.ShowNullableInt(int? myInt)
XML Name: M:XmlDocDemo.DemoClass.ShowNullableInt(System.Nullable{System.Int32})
XML Summary: Display the value of a nullable integer

Resources

Source Code

A simple-case working demo of these concepts can be downloaded here:

Documentation Generators

  • DocFX - An extensible and scalable static documentation generator.

  • Sandcastle - Sandcastle Help File Builder (SHFB). A standalone GUI, Visual Studio integration package, and MSBuild tasks providing full configuration and extensibility for building help files with the Sandcastle tools.

Zachary Patten's Useful Article

There is an extensive article on this topic in the October 2019 issue of MSDN Magazine, Accessing XML Documentation via Reflection by Zachary Patten. The code examples there provide a lot of advanced features, but are technically incomplete and some critical components are only shown using pseudocode. The reader is told that full code is available as part of the author's library Towel, but this library is extensive and provides many functions unrelated to reflection and XML documentation making it difficult to navigate. The method to convert a method to its XML documentation name is Towel/Meta.cs#L1026-L1092, but it's coupled to other code which requires hash maps to be pre-formed in order to use it. My post here is intended to be self-contained simple reference for how to combine XML documentation with Reflection, but users interested in reading further on this topic are encouraged to read Zachary's article.

Update: Potentially Useful Libraries

Update (Feb 21, 2021): I continued to do research on this topic. I thought I'd find a "golden bullet" library that could help me do this perfectly. The code above does a pretty good job, but I would feel more confident using something designed/tested specifically for this task. I looked and found some helpful libraries, but none of them met all me needs. For my projects, I decided just to use the code above.

DocXml

DocXml is a small .NET standard 2.0 library of helper classes and methods for compiler-generated XML documentation retrieval. Its API is very simple and easy to use with a predictive IDE. Out of the box though it was unable to properly identify the XML name of one of my functions. I think it got stuck on the generic method with a multi-dimensional generic array as an argument, but don't recall for sure. For basic code bases, this looks like a fantastic library.

NuDoq

NuDoq (previously NuDoc?) is a standalone API to read and write .NET XML documentation files and optionally augment it with reflection information. According to the releases it was actively worked on around 2014, then rested quietly for a few years and new releases began in 2021. NuDoq looks quite extensive, but takes some studying before it can be used effectively. "Given the main API to traverse and act on the documentation elements is through the visitor pattern, the most important part of the API is knowing the types of nodes/elements in the visitable model."

Towel

Towel is a .NET library intended to add core functionality and make advanced topics as clean and simple as possible. Towel has tools for working with data structures, algorithms, mathematics, metadata, extensions, console, and more. Towel favors newer coding practices over maintaining backwards compatibility, and at the time of writing Towel only supports .NET 5 and newer. One of Towel's Meta module has methods to get XML names for reflected objects. It's perfect, but requires .NET 5.0 or newer so I could not use it in my project.

Washcloth

I tried to create a version of Towel that isolated just the XML documentation reflection module so I could back-port it to .NET Standard. I created Washcloth which largely achieved this and wrapped Towel behind a simple API. This task proved extremely difficult to accomplish cleanly though, because most modules in the Towel code base are highly coupled to one another so it was very difficult to isolate the Meta module and I never achieved this goal to my satisfaction. I named the project Washcloth because a washcloth is really just a small towel with less functionality. Washcloth technically works (and can be used in projects back to .NET Core 2.0 and .NET Framework 4.6.1), but so much coupled code from the rest of Towel came along with it that I decided not to use this project for my application.

Final Thoughts

After a month poking around at this, here's where I landed:

  • Reading XML documentation is easy with System.Xml.Linq.XDocument

  • Getting XML names for fields, properties, classes, constructors, and enumerations is easy

  • Getting XML names for methods can be very hard

  • If you're creating something special, consider a custom solution like that shown above (~50 lines and you're done).

  • If you can target the latest .NET platform, consider the Meta module in Towel

  • If you want a package that targets .NET Standard, consider DocXml (simple) or NuDoq (complex)

Markdown source code last modified on February 22nd, 2021
---
Title: Reflection and XML Documentation in C# 
Description: How to access XML documentation from within a C# application
Date: 2021-01-31 1:57pm
Tags: csharp
---

# Reflection and XML Documentation in C# 

**In C#, you can document your code using XML directly before code blocks.** This XML documentation is used by Visual Studio to display tooltips and provide autocomplete suggestions. 

```cs
/// <summary>
///  This method performs an important function.
/// </summary>
public void MyMethod() {}
```

**To enable automatic generation of XML documentation** on every build, add the following to your csproj file:

```xml
<PropertyGroup>
  <DocumentationFile>MyProgram.xml</DocumentationFile>
</PropertyGroup>
```

**However, XML documentation is not actually metadata, so it is not available in the compiled assembly.** In this post I'll show how you can use `System.Reflection` to gather information about methods and combine it with documentation from an XML file read with `System.Xml.Linq.XDocument`. 

**I find this useful for writing code which automatically generates documentation.** Reflection alone cannot provide comments, and XML documentation alone cannot provide parameter names (just their types). By combining reflection with XML documentation, we can more completely describe methods in C# programs.

> **⚠️ WARNING: These code examples are intentionally simple.** They only demonstrate how to read summary comments from XML documentation for methods found using reflection. Additional functionality can be added as needed, and my intent here is to provide a simple starting point rather than overwhelmingly complex examples that support all features and corner cases.

> **💡 Source code** can be downloaded at the bottom of this article

> **✔️ The "hack" described on this page is aimed at overcoming limitations of partially-documented XML. A better solution is to fully document your code, in which case the XML document is self-sufficient.** The primary goal of these efforts is to use XML documentation where it is present, but use Reflection to fill in the blanks about methods which are undocumented or only partially documented. Perhaps a better strategy would be to have a fully documented code base, with an XML file containing `<summary>`, `<returns>`, and `<param>` for every parameter. Tests can ensure this is and remains the case.

## What does the XML Documentation look like?

There are some nuances here you might not expect, especially related to arrays, generics, and nullables. Let's start with a demo class with documented summaries. Keep in mind that the goal of this project is to help use Reflection to fill in the blanks about undocumented or partially-documented code, so this example will only add a `<summary>` but no `<param>` descriptions.

### DemoClass.cs

```cs
/// <summary>
/// Display a name
/// </summary>
public static void ShowName(string name)
{
    Console.WriteLine($"Hi {name}");
}

/// <summary>
/// Display a name a certain number of times
/// </summary>
public static void ShowName(string name, byte repeats)
{
    for (int i = 0; i < repeats; i++)
        Console.WriteLine($"Hi {name}");
}

/// <summary>
/// Display the type of the variable passed in
/// </summary>
public static void ShowGenericType<T>(T myVar)
{
    Console.WriteLine($"Generic type {myVar.GetType()}");
}

/// <summary>
/// Display the value of a nullable integer
/// </summary>
public static void ShowNullableInt(int? myInt)
{
    Console.WriteLine(myInt);
}
```

### XML Documentation File

There are a few important points to notice here:

* Each method is a `member` with a `name` starting with `M:`
* Parameter _types_ are in the member name, but not parameter _names_!
* Parameters might be listed in the XML, but they will be missing if only `<summary>` was added in code
* 💡 The key step required to connect a reflected method with its XML documentation is being able to determine the XML method name of that method. How to do this is discussed below...

```xml
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>XmlDocDemo</name>
    </assembly>
    <members>
        <member name="M:XmlDocDemo.DemoClass.ShowName(System.String)">
            <summary>
            Display a name
            </summary>
        </member>
        <member name="M:XmlDocDemo.DemoClass.ShowName(System.String,System.Byte)">
            <summary>
            Display a name a certain number of times
            </summary>
        </member>
        <member name="M:XmlDocDemo.DemoClass.ShowGenericType``1(``0)">
            <summary>
            Display the type of the variable passed in
            </summary>
        </member>
        <member name="M:XmlDocDemo.DemoClass.ShowNullableInt(System.Nullable{System.Int32})">
            <summary>
            Display the value of a nullable integer
            </summary>
        </member>
    </members>
</doc>
```

### XML Name Details

* Generics from types have a single <code>`</code> character 
* Generics from methods have double <code>``</code> characters
* If the parameter is by "ref" then you need to pre-pend the `@` character
* If the parameter is a pointer you need to pre-pend it with the <code>*</code> character
* If the parameter is an array, you need to add `[]` characters and the appropriate number of commas
* If the parameter is Nullable it will be wrapped in `System.Nullable{}`
* If the method is MethodInfo is a casing operator, then you need to pre-pend it with `~` character

_Thanks [Zachary Patten](https://github.com/ZacharyPatten) for sharing these details in an [MSDN article](https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection) and e-mail correspondence_

## Read XML Documentation File

This code reads the XML documentation file (using the modern [XDocument](https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument)) and stores method summaries in a Dictionary using the XML method name as a key. This dictionary will be accessed later to look-up documentation for methods found using Reflection.

```cs
private readonly Dictionary<string, string> MethodSummaries = new Dictionary<string, string>();

public XmlDoc(string xmlFile)
{
    XDocument doc = XDocument.Load(xmlFile);
    foreach (XElement element in doc.Element("doc").Element("members").Elements())
    {
        string xmlName = element.Attribute("name").Value;
        string xmlSummary = element.Element("summary").Value.Trim();
        MethodSummaries[xmlName] = xmlSummary;
    }
}
```

## Determine XML Method Name for a Reflected Method

This example code returns the XML member name for a method found by reflection. **This is the key step** required to connect reflected methods with their descriptions in XML documentation files.

**⚠️ Warning: This code sample may not support all corner-cases**, but in practice I found it supports all of the ones I typically encounter in my code bases and it's a pretty good balance between functionality and simplicity.

```cs
public static string GetXmlName(MethodInfo info)
{
	string declaringTypeName = info.DeclaringType.FullName;

	if (declaringTypeName is null)
		throw new NotImplementedException("inherited classes are not supported");

	string xmlName = "M:" + declaringTypeName + "." + info.Name;
	xmlName = string.Join("", xmlName.Split(']').Select(x => x.Split('[')[0]));
	xmlName = xmlName.Replace(",", "");

	if (info.IsGenericMethod)
		xmlName += "``#";

	int genericParameterCount = 0;
	List<string> paramNames = new List<string>();
	foreach (var parameter in info.GetParameters())
	{
		Type paramType = parameter.ParameterType;
		string paramName = GetXmlNameForMethodParameter(paramType);
		if (paramName.Contains("#"))
			paramName = paramName.Replace("#", (genericParameterCount++).ToString());
		paramNames.Add(paramName);
	}
	xmlName = xmlName.Replace("#", genericParameterCount.ToString());

	if (paramNames.Any())
		xmlName += "(" + string.Join(",", paramNames) + ")";

	return xmlName;
}

private static string GetXmlNameForMethodParameter(Type type)
{
	string xmlName = type.FullName ?? type.BaseType.FullName;
	bool isNullable = xmlName.StartsWith("System.Nullable");
	Type nullableType = isNullable ? type.GetGenericArguments()[0] : null;

	// special formatting for generics (also Func, Nullable, and ValueTulpe)
	if (type.IsGenericType)
	{
		var genericNames = type.GetGenericArguments().Select(x => GetXmlNameForMethodParameter(x));
		var typeName = type.FullName.Split('`')[0];
		xmlName = typeName + "{" + string.Join(",", genericNames) + "}";
	}

	// special case for generic nullables
	if (type.IsGenericType && isNullable && type.IsArray == false)
		xmlName = "System.Nullable{" + nullableType.FullName + "}";

	// special case for multidimensional arrays
	if (type.IsArray && (type.GetArrayRank() > 1))
	{
		string arrayName = type.FullName.Split('[')[0].Split('`')[0];
		if (isNullable)
			arrayName += "{" + nullableType.FullName + "}";
		string arrayContents = string.Join(",", Enumerable.Repeat("0:", type.GetArrayRank()));
		xmlName = arrayName + "[" + arrayContents + "]";
	}

	// special case for generic arrays
	if (type.IsArray && type.FullName is null)
		xmlName = "``#[]";

	// special case for value types
	if (xmlName.Contains("System.ValueType"))
		xmlName = "`#";

	return xmlName;
}
```

## Get XML Documentation for a Reflected Method

Now that we have `XmlName()`, we can easily iterate through reflected methods and get their XML documentation.

```cs
// use Reflection to get info from custom methods
var infos = typeof(DemoClass).GetMethods()
                             .Where(x => x.DeclaringType.FullName != "System.Object")
                             .ToArray();

// display XML info about each reflected method
foreach (MethodInfo mi in infos)
{
    string xmlName = XmlName(mi);
    Console.WriteLine("");
    Console.WriteLine("Method: " + XmlDoc.MethodSignature(mi));
    Console.WriteLine("XML Name: " + xmlName);
    Console.WriteLine("XML Summary: " + MethodSummaries[xmlName]);
}
```

### Output

```
Method: XmlDocDemo.DemoClass.ShowName(string name)
XML Name: M:XmlDocDemo.DemoClass.ShowName(System.String)
XML Summary: Display a name

Method: XmlDocDemo.DemoClass.ShowName(string name, byte repeats)
XML Name: M:XmlDocDemo.DemoClass.ShowName(System.String,System.Byte)
XML Summary: Display a name a certain number of times

Method: XmlDocDemo.DemoClass.ShowGenericType<T>(T myVar)
XML Name: M:XmlDocDemo.DemoClass.ShowGenericType``1(``0)
XML Summary: Display the type of the variable passed in

Method: XmlDocDemo.DemoClass.ShowNullableInt(int? myInt)
XML Name: M:XmlDocDemo.DemoClass.ShowNullableInt(System.Nullable{System.Int32})
XML Summary: Display the value of a nullable integer
```

## Resources

### Source Code

A simple-case working demo of these concepts can be downloaded here:

* [**XmlDocDemo.zip**](XmlDocDemo.zip) (4kb)

### Documentation Generators

* [DocFX](https://dotnet.github.io/docfx/) - An extensible and scalable static documentation generator.

* [Sandcastle](https://github.com/EWSoftware/SHFB) - Sandcastle Help File Builder (SHFB). A standalone GUI, Visual Studio integration package, and MSBuild tasks providing full configuration and extensibility for building help files with the Sandcastle tools.

### Zachary Patten's Useful Article

There is an extensive article on this topic in the October 2019 issue of MSDN Magazine, [Accessing XML Documentation via Reflection](https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection) by [Zachary Patten](https://github.com/ZacharyPatten). The code examples there provide a lot of advanced features, but are technically incomplete and some critical components are only shown using pseudocode. The reader is told that full code is available as part of the author's library [Towel](https://github.com/ZacharyPatten/Towel), but this library is extensive and provides many functions unrelated to reflection and XML documentation making it difficult to navigate. The method to convert a method to its XML documentation name is [Towel/Meta.cs#L1026-L1092](https://github.com/ZacharyPatten/Towel/blob/360b4ae695c5f95ca9b8e1ec3c466092eeff972e/Sources/Towel/Meta.cs#L1026-L1092), but it's coupled to other code which requires hash maps to be pre-formed in order to use it. My post here is intended to be self-contained simple reference for how to combine XML documentation with Reflection, but users interested in reading further on this topic are encouraged to read Zachary's article.

## Update: Potentially Useful Libraries

**Update (Feb 21, 2021):** I continued to do research on this topic. I thought I'd find a "golden bullet" library that could help me do this perfectly. The code above does a pretty good job, but I would feel more confident using something designed/tested specifically for this task. I looked and found some helpful libraries, but none of them met all me needs. For my projects, I decided just to use the code above.

### DocXml

[DocXml](https://github.com/loxsmoke/DocXml) is a small .NET standard 2.0 library of helper classes and methods for compiler-generated XML documentation retrieval. Its API is very simple and easy to use with a predictive IDE. Out of the box though it was unable to properly identify the XML name of one of my functions. I think it got stuck on the generic method with a multi-dimensional generic array as an argument, but don't recall for sure. For basic code bases, this looks like a fantastic library.

### NuDoq

[NuDoq](https://github.com/devlooped/NuDoq) (previously NuDoc?) is a standalone API to read and write .NET XML documentation files and optionally augment it with reflection information. According to the [releases](https://github.com/devlooped/NuDoq/releases) it was actively worked on around 2014, then rested quietly for a few years and new releases began in 2021. NuDoq looks quite extensive, but takes some studying before it can be used effectively. "Given the main API to traverse and act on the documentation elements is through the visitor pattern, the most important part of the API is knowing the types of nodes/elements in the visitable model."

### Towel

[Towel](https://github.com/ZacharyPatten/Towel) is a .NET library intended to add core functionality and make advanced topics as clean and simple as possible. Towel has tools for working with data structures, algorithms, mathematics, metadata, extensions, console, and more. Towel favors newer coding practices over maintaining backwards compatibility, and at the time of writing Towel only supports .NET 5 and newer. One of Towel's `Meta` module has methods to get XML names for reflected objects. It's perfect, but requires .NET 5.0 or newer so I could not use it in my project.

### Washcloth

I tried to create a version of Towel that isolated just the XML documentation reflection module so I could back-port it to .NET Standard. I created [Washcloth](https://github.com/swharden/Washcloth) which largely achieved this and wrapped Towel behind a simple API. This task proved extremely difficult to accomplish cleanly though, because most modules in the Towel code base are highly coupled to one another so it was very difficult to isolate the `Meta` module and I never achieved this goal to my satisfaction. I named the project Washcloth because a washcloth is really just a small towel with less functionality. Washcloth technically works (and can be used in projects back to .NET Core 2.0 and .NET Framework 4.6.1), but so much coupled code from the rest of Towel came along with it that I decided not to use this project for my application.

## Final Thoughts

After a month poking around at this, here's where I landed:

* Reading XML documentation is easy with `System.Xml.Linq.XDocument`

* Getting XML names for fields, properties, classes, constructors, and enumerations is easy

* Getting XML names for methods can be very hard

* If you're creating something special, consider a custom solution like that shown above (~50 lines and you're done).

* If you can target the latest .NET platform, consider the `Meta` module in [Towel](https://github.com/ZacharyPatten/Towel)

* If you want a package that targets .NET Standard, consider [DocXml](https://github.com/loxsmoke/DocXml) (simple) or [NuDoq](https://github.com/devlooped/NuDoq) (complex)

Google Charts in Blazor

Google Charts is a free interactive JavaScript charting framework. The Google Chart Gallery showcases many of the available chart types and options. This project shows how to create data from C# functions in Blazor, then use JavaScript interop to pass arrays into JavaScript for display with Google Charts.

index.razor

This page contains a div where the chart will be displayed, controls for customizing settings, and a few functions in the code-behind to call a JavaScript function that updates the chart.

@page "/"
@inject IJSRuntime JsRuntime

<!-- this is where the Google Chart is displayed -->
<div id="chart_div" style="height: 400px;"></div>

<!-- buttons call C# functions -->
<button @onclick="PlotSin">Sin</button>
<button @onclick="PlotRandom">Random</button>
<button @onclick="PlotWalk">Walk</button>
<button @onclick="PlotRandomXY">RandomXY</button>

<!-- a slider bound to a C# field -->
<input type="range" @bind="PointCount" @bind:event="oninput">
@code{
    private int PointCount = 123;
    Random Rand = new Random();

    private void PlotData(double[] xs, double[] ys)
    {
        // This function calls a JavaScript function to update the chart.
        // Notice how multiple parameters are passed in.
        JsRuntime.InvokeVoidAsync("createNewChart", new { xs, ys });
    }

    private void PlotSin()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => Math.Sin(x / 10)).ToArray();
        PlotData(xs, ys);
    }

    private void PlotRandom()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => (Rand.NextDouble() - .5) * 1000).ToArray();
        PlotData(xs, ys);
    }

    private void PlotWalk()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = new double[PointCount];
        for (int i = 1; i < ys.Length; i++)
            ys[i] = ys[i - 1] + Rand.NextDouble() - .5;
        PlotData(xs, ys);
    }

    private void PlotRandomXY()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        double[] ys = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        PlotData(xs, ys);
    }
}

index.html

These JavaScript blocks were added just before the closing </body> tag

<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>

<script>
    google.charts.load('current', { packages: ['corechart', 'line'] });

    // draw an empty chart when the page first loads
    google.charts.setOnLoadCallback(initChart);
    function initChart() {
        var xs = [];
        var ys = [];
        window.createNewChart({xs, ys});
    }

    // draw a new chart given X/Y values
    window.createNewChart = (params) => {
        var xs = params.xs;
        var ys = params.ys;

        var data = new google.visualization.DataTable();
        data.addColumn('number', 'X');
        data.addColumn('number', 'Y');

        for (var i = 0; i < ys.length; i++) {
            data.addRow([xs[i], ys[i]]);
        }

        var options = {
            hAxis: { title: 'Horizontal Axis Label' },
            vAxis: { title: 'Vertical Axis Label' },
            title: 'This is a Google Chart in Blazor',
            legend: { position: 'none' },
        };

        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));

        chart.draw(data, options);
    };

</script>

Automatic Resizing

Plots look bad when the window is resized because Google Charts adopts a fixed size on each render. To give the appearance of fluid charts (that resize to fit their container as its size changes), add some JavaScript to re-render on every resize.

window.onresize = function () { initChart(); };

We can't call our existing createNewChart() method because that expects data (from C#/Blazor) passed-in as a parameter. To support this type of resizing, the call from C# must be modified to store data arrays at the window level so they can be later accessed when the chart is plotted again. This would take some re-structuring of this project, but it's possible.

Conclusions

  • Google Charts is a straightforward way to display data in mouse-interactive graphs on the web.

  • JS interop allows Blazor to pass data to a JavaScript function that can plot it on a Google Chart.

  • Extra steps are required to automatically resize a Google Chart when its container size changes.

  • For interactive graphs in desktop applications, check out ScottPlot (an open-source plotting library for .NET that makes it easy to interactively display large datasets).

Resources

Markdown source code last modified on January 18th, 2021
---
title: Google Charts in Blazor
date: 2021-01-12 19:15:00
tags: blazor, csharp
---

# Google Charts in Blazor

**Google Charts is a free interactive JavaScript charting framework.** The [Google Chart Gallery](https://developers.google.com/chart/interactive/docs/gallery) showcases many of the available chart types and options. This project shows how to create data from C# functions in Blazor, then use JavaScript interop to pass arrays into JavaScript for display with Google Charts.

<div class="text-center">

[![](blazor-google-charts.jpg)](app)

</div>

* [Google Charts with Blazor Demo](app) 👈 Try it live!

* Source code: [blazor-google-charts.zip](blazor-google-charts.zip)

## index.razor

This page contains a `div` where the chart will be displayed, controls for customizing settings, and a few functions in the code-behind to call a JavaScript function that updates the chart.

```html
@page "/"
@inject IJSRuntime JsRuntime

<!-- this is where the Google Chart is displayed -->
<div id="chart_div" style="height: 400px;"></div>

<!-- buttons call C# functions -->
<button @onclick="PlotSin">Sin</button>
<button @onclick="PlotRandom">Random</button>
<button @onclick="PlotWalk">Walk</button>
<button @onclick="PlotRandomXY">RandomXY</button>

<!-- a slider bound to a C# field -->
<input type="range" @bind="PointCount" @bind:event="oninput">
```

```cs
@code{
    private int PointCount = 123;
    Random Rand = new Random();

    private void PlotData(double[] xs, double[] ys)
    {
        // This function calls a JavaScript function to update the chart.
		// Notice how multiple parameters are passed in.
        JsRuntime.InvokeVoidAsync("createNewChart", new { xs, ys });
    }

    private void PlotSin()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => Math.Sin(x / 10)).ToArray();
        PlotData(xs, ys);
    }

    private void PlotRandom()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => (Rand.NextDouble() - .5) * 1000).ToArray();
        PlotData(xs, ys);
    }

    private void PlotWalk()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = new double[PointCount];
        for (int i = 1; i < ys.Length; i++)
            ys[i] = ys[i - 1] + Rand.NextDouble() - .5;
        PlotData(xs, ys);
    }

    private void PlotRandomXY()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        double[] ys = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        PlotData(xs, ys);
    }
}
```

## index.html

These JavaScript blocks were added just before the closing `</body>` tag

```js
<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>

<script>
    google.charts.load('current', { packages: ['corechart', 'line'] });

    // draw an empty chart when the page first loads
    google.charts.setOnLoadCallback(initChart);
    function initChart() {
        var xs = [];
        var ys = [];
        window.createNewChart({xs, ys});
    }

    // draw a new chart given X/Y values
    window.createNewChart = (params) => {
        var xs = params.xs;
        var ys = params.ys;

        var data = new google.visualization.DataTable();
        data.addColumn('number', 'X');
        data.addColumn('number', 'Y');

        for (var i = 0; i < ys.length; i++) {
            data.addRow([xs[i], ys[i]]);
        }

        var options = {
            hAxis: { title: 'Horizontal Axis Label' },
            vAxis: { title: 'Vertical Axis Label' },
            title: 'This is a Google Chart in Blazor',
            legend: { position: 'none' },
        };

        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));

        chart.draw(data, options);
    };

</script>
```

## Automatic Resizing

Plots look bad when the window is resized because Google Charts adopts a fixed size on each render. To give the appearance of fluid charts (that resize to fit their container as its size changes), add some JavaScript to re-render on every resize.

```js
window.onresize = function () { initChart(); };
```

We can't call our existing `createNewChart()` method because that expects data (from C#/Blazor) passed-in as a parameter. To support this type of resizing, the call from C# must be modified to _store_ data arrays at the window level so they can be later accessed when the chart is plotted again. This would take some re-structuring of this project, but it's possible.

## Conclusions

* Google Charts is a straightforward way to display data in mouse-interactive graphs on the web. 

* JS interop allows Blazor to pass data to a JavaScript function that can plot it on a Google Chart.

* Extra steps are required to automatically resize a Google Chart when its container size changes.

* For interactive graphs in desktop applications, check out [ScottPlot](https://swharden.com/scottplot) (an open-source plotting library for .NET that makes it easy to interactively display large datasets).

## Resources

* Live demo: [Google Charts with Blazor Demo](app)

* Source code: [blazor-google-charts.zip](blazor-google-charts.zip)

* Source code on GitHub: [/examples/2021-01-10-blazor-google-charts](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-10-blazor-google-charts)

* [C# Data Visualization](https://github.com/swharden/Csharp-Data-Visualization) on GitHub

Mystify your Browser with Blazor

The project simulates the classic Mystify your Mind screensaver from Windows 3 using client-side Blazor. The graphics model logic is entirely C# and the web interface has user-facing options (razor components) to customize its behavior in real time. To render a frame JavaScript calls a C# function that returns JSON containing an array of line details (colors and coordinates) so a JavaScript render function can draw them on a HTML canvas. While .NET APIs for drawing on canvases exist, I find this method to be a bit more versatile because it does not require any external libraries, and it only makes a single JS interop call on each render so it is appreciably faster.

Live Demo

The C# Graphics Model

The point of this article is to discuss strategies for rendering graphics using client-side Blazor and WebAssembly, so I won't detail how the polygons are tracked here. Source code for these classes can be viewed on GitHub (MystifyBlazor/Models) or downloaded with this project (blazor-mystify.zip).

  • Models/Color.cs - Represents a color and has methods to help with hue-shifting

  • Models/Corner.cs - Represents a corner of a polygon (position, direction, and velocity) and has logic to move forward in time and bounce off walls

  • Models/Polygon.cs - A polygon is just a color and a list of corners that are connected by a single line.

  • Models/Shape.cs - A Shape is a moving polygon, and this class contains a list of polygons (a historical record of a single polygon's configuration as it changed over time).

  • Models/Field.cs - Represents the rectangular render area and holds multiple shapes.

    • This is the only class Blazor directly interacts with3
    • Configuration options (like rainbow mode) are public fields that can be bound to inputs on Razor pages
    • This class can return rendering instructions as JSON. This is just an array of lines, each with a color and an array of X/Y points.

index.razor

Canvas and Holder

This is the HTML that holds the HTML canvas. By using CSS to make the canvas background black I don't have to concern myself with manually drawing a black background on every new frame.

Note that I'm using Bootstrap 5 (not the Bootstrap 4 that currently comes with new Blazor projects), so the classes may be slightly different.

@page "/"

<div class="bg-white shadow mt-3" id="myCanvasHolder">
    <canvas id="myCanvas" style="background-color: black;"></canvas>
</div>

Code-Behind

The only goals here are to start the JavaScript render loop and provide a method JavaScript can call to get line data in JSON format. Notice the method called by JavaScript accepts width and height arguments so the data model can properly adjust to canvases that change size as the browser window changes dimensions.

@inject IJSRuntime JsRuntime

@code
{
    Models.Field MystifyField = new Models.Field();

    protected override void OnInitialized()
    {
        JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        MystifyField.Advance(width, height);
        return MystifyField.GetJSON();
    }
}

Data Binding

User-facing configuration is achieved by binding public fields of the graphics model directly to input elements.

<input type="range" 
    @bind="MystifyField.Speed" 
    @bind:event="oninput">

<input type="range" min="1"max="20"
    @bind="MystifyField.ShapeCount" 
    @bind:event="oninput">

<input type="range" min="3" max="20"
    @bind="MystifyField.CornerCount" 
    @bind:event="oninput" >

<input type="range" min="1" max="50"
    @bind="MystifyField.HistoryCount"
    @bind:event="oninput">

<button @onclick="MystifyField.RandomizeColors">
    Random Colors
</button>

<input type="checkbox" @bind="MystifyField.Rainbow">

index.html

This file contains JavaScript to start the renderer (an infinite loop) and request line data as JSON from a C# function in each render.

Notice that the aspect ratio of the canvas is maintained by setting its with to be a fraction of its height. The canvas width and height are passed as arguments to the C# function which updates the model.

<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('myCanvasHolder');
        var canvas = document.getElementById('myCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = canvas.width * .6;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var dataString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var polys = JSON.parse(dataString);

        // render each polygon
        var lineCount = 0;
        var ctx = document.getElementById('myCanvas').getContext('2d');
        ctx.lineWidth = 2;
        for (var i = 0; i < polys.length; i++) {
            var poly = polys[i];
            var color = poly.shift();

            ctx.beginPath();
            for (var j = 0; j < poly.length; j++) {
                x = parseFloat(poly[j][0]);
                y = parseFloat(poly[j][1]);
                if (j == 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            ctx.strokeStyle = color;
            ctx.closePath();
            ctx.stroke();
            lineCount += 1;
        }

        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>

JSON

The JSON that C# passes to the JavaScript renderer is just an array of lines, each with a color and an array of X/Y points. This is an example JSON packet to render a single frame:

["#00FFB4",[665.26,767.31], [1098.58,250.94], [1159.48,206.49], [717.52,194.45]],
["#00FFB4",[660.94,757.61], [1089.18,257.06], [1166.11,203.76], [720.90,201.92]],
["#00FFB4",[656.62,747.92], [1079.78,263.18], [1172.73,201.03], [724.29,209.40]],
["#00FFB4",[652.30,738.22], [1070.38,269.31], [1179.36,198.30], [727.67,216.88]],
["#00FFB4",[647.98,728.53], [1060.98,275.43], [1185.99,195.57], [731.05,224.36]],
["#0049FF",[1219.26,656.86], [5.34,599.35], [454.87,716.81], [276.93,416.92]],
["#0049FF",[1224.78,648.28], [0.00,602.83], [455.97,708.21], [286.48,408.14]],
["#0049FF",[1230.30,639.69], [6.55,606.32], [457.08,699.61], [296.03,399.35]],
["#0049FF",[1235.81,631.11], [13.10,609.80], [458.19,691.02], [305.58,390.57]],
["#0049FF",[1241.33,622.53], [19.65,613.28], [459.29,682.42], [315.13,381.79]]

Alternatively you can Render from C#

There are wrappers to let you draw on a HTML canvas from .NET, allowing you to write your render function in C# instead of JavaScript. It is a bit of added complexity (requires a package) and is a bit slower (additional JavaScript interop calls), so I did not choose to use it for this project.

See my earlier article Draw Animated Graphics in the Browser with Blazor WebAssembly for an example of how to draw on a canvas from C# using the Blazor.Extensions.Canvas package.

Performance Notes

Vanilla JavaScript will probably always be faster at rendering graphics in the browser than Blazor. If your application requires fast graphics, you'll have to write all your graphics model logic and render system in JavaScript. The great advantage of Blazor is that existing C# code that has already been developed (and extensively tested) can be used in the browser. At the time of writing, Speed is not Blazor's strong suit.

  • This system starts to choke when the model represents more than a few lines. The bottleneck seems to be encoding all the data as JSON on the C# side.

  • I tried using StringBuilder everywhere and also using int instead of float, but it did not produce large improvements.

  • I also tired using the Blazor.Extensions.Canvas package so I could make all the rendering calls from C# (eliminating the conversion to JSON step). Because this required so many individual interop calls (even with batching), it was about twice as slow as the JS renderer implemented in the code above.

Resources

Markdown source code last modified on January 18th, 2021
---
title: Mystify your Browser with Blazor
date: 2021-01-09 22:16:00
tags: blazor, csharp
---

# Mystify your Browser with Blazor

**The project simulates the classic _Mystify your Mind_ screensaver from Windows 3 using client-side Blazor.** The graphics model logic is entirely C# and the web interface has user-facing options (razor components) to customize its behavior in real time. To render a frame JavaScript calls a C# function that returns JSON containing an array of line details (colors and coordinates) so a JavaScript render function can draw them on a HTML canvas. While [.NET APIs for drawing on canvases](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics/) exist, I find this method to be a bit more versatile because it does not require any external libraries, and it only makes a single JS interop call on each render so it is appreciably faster.

<div class="text-center">

[![](blazor-mystify.gif)](app)

</div>

### Live Demo

* [Mystify your Browser with Blazor](app/) 👈 ***Launch the demo***

## The C# Graphics Model

The point of this article is to discuss strategies for rendering graphics using client-side Blazor and WebAssembly, so I won't detail how the polygons are tracked here. Source code for these classes can be viewed on GitHub ([MystifyBlazor/Models](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models)) or downloaded with this project ([blazor-mystify.zip](blazor-mystify.zip)).

* [`Models/Color.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Color.cs) - Represents a color and has methods to help with hue-shifting

* [`Models/Corner.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Corner.cs) - Represents a corner of a polygon (position, direction, and velocity) and has logic to move forward in time and bounce off walls

* [`Models/Polygon.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Polygon.cs) - A polygon is just a color and a list of corners that are connected by a single line.

* [`Models/Shape.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Shape.cs) - A Shape is a moving polygon, and this class contains a list of polygons (a historical record of a single polygon's configuration as it changed over time).

* [`Models/Field.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Field.cs) - Represents the rectangular render area and holds multiple shapes. 
  * This is the only class Blazor directly interacts with3
  * Configuration options (like rainbow mode) are public fields that can be bound to inputs on Razor pages
  * This class can return rendering instructions as JSON. This is just an array of lines, each with a color and an array of X/Y points.

## index.razor

### Canvas and Holder

This is the HTML that holds the HTML canvas. By using CSS to make the canvas background black I don't have to concern myself with manually drawing a black background on every new frame.

Note that I'm using [Bootstrap 5](https://getbootstrap.com/) (not the Bootstrap 4 that currently comes with new Blazor projects), so the classes may be slightly different.

```html
@page "/"

<div class="bg-white shadow mt-3" id="myCanvasHolder">
    <canvas id="myCanvas" style="background-color: black;"></canvas>
</div>
```

### Code-Behind

The only goals here are to start the JavaScript render loop and provide a method JavaScript can call to get line data in JSON format. Notice the method called by JavaScript accepts `width` and `height` arguments so the data model can properly adjust to canvases that change size as the browser window changes dimensions.

```cs
@inject IJSRuntime JsRuntime

@code
{
    Models.Field MystifyField = new Models.Field();

    protected override void OnInitialized()
    {
        JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        MystifyField.Advance(width, height);
        return MystifyField.GetJSON();
    }
}
```

### Data Binding

User-facing configuration is achieved by binding public fields of the graphics model directly to input elements.

```html
<input type="range" 
    @bind="MystifyField.Speed" 
    @bind:event="oninput">

<input type="range" min="1"max="20"
    @bind="MystifyField.ShapeCount" 
    @bind:event="oninput">
    
<input type="range" min="3" max="20"
    @bind="MystifyField.CornerCount" 
    @bind:event="oninput" >
    
<input type="range" min="1" max="50"
    @bind="MystifyField.HistoryCount"
    @bind:event="oninput">

<button @onclick="MystifyField.RandomizeColors">
    Random Colors
</button>

<input type="checkbox" @bind="MystifyField.Rainbow">
```

## index.html

This file contains JavaScript to start the renderer (an infinite loop) and request line data as JSON from a C# function in each render.

Notice that the aspect ratio of the canvas is maintained by setting its with to be a fraction of its height. The canvas width and height are passed as arguments to the C# function which updates the model.

```html
<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('myCanvasHolder');
        var canvas = document.getElementById('myCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = canvas.width * .6;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var dataString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var polys = JSON.parse(dataString);

        // render each polygon
        var lineCount = 0;
        var ctx = document.getElementById('myCanvas').getContext('2d');
        ctx.lineWidth = 2;
        for (var i = 0; i < polys.length; i++) {
            var poly = polys[i];
            var color = poly.shift();

            ctx.beginPath();
            for (var j = 0; j < poly.length; j++) {
                x = parseFloat(poly[j][0]);
                y = parseFloat(poly[j][1]);
                if (j == 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            ctx.strokeStyle = color;
            ctx.closePath();
            ctx.stroke();
            lineCount += 1;
        }

        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>
```

## JSON

The JSON that C# passes to the JavaScript renderer is just an array of lines, each with a color and an array of X/Y points. This is an example JSON packet to render a single frame:

```
["#00FFB4",[665.26,767.31], [1098.58,250.94], [1159.48,206.49], [717.52,194.45]],
["#00FFB4",[660.94,757.61], [1089.18,257.06], [1166.11,203.76], [720.90,201.92]],
["#00FFB4",[656.62,747.92], [1079.78,263.18], [1172.73,201.03], [724.29,209.40]],
["#00FFB4",[652.30,738.22], [1070.38,269.31], [1179.36,198.30], [727.67,216.88]],
["#00FFB4",[647.98,728.53], [1060.98,275.43], [1185.99,195.57], [731.05,224.36]],
["#0049FF",[1219.26,656.86], [5.34,599.35], [454.87,716.81], [276.93,416.92]],
["#0049FF",[1224.78,648.28], [0.00,602.83], [455.97,708.21], [286.48,408.14]],
["#0049FF",[1230.30,639.69], [6.55,606.32], [457.08,699.61], [296.03,399.35]],
["#0049FF",[1235.81,631.11], [13.10,609.80], [458.19,691.02], [305.58,390.57]],
["#0049FF",[1241.33,622.53], [19.65,613.28], [459.29,682.42], [315.13,381.79]]
```

## Alternatively you can Render from C# 

There are wrappers to let you draw on a HTML canvas from .NET, allowing you to write your render function in C# instead of JavaScript. It is a bit of added complexity (requires a package) and is a bit slower (additional JavaScript interop calls), so I did not choose to use it for this project. 

See my earlier article [_Draw Animated Graphics in the Browser with Blazor WebAssembly_](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics) for an example of how to draw on a canvas from C# using the [`Blazor.Extensions.Canvas`](https://github.com/BlazorExtensions/Canvas) package.

## Performance Notes

Vanilla JavaScript will probably always be faster at rendering graphics in the browser than Blazor. If your application requires fast graphics, you'll have to write all your graphics model logic and render system in JavaScript. The great advantage of Blazor is that existing C# code that has already been developed (and extensively tested) can be used in the browser. At the time of writing, Speed is not Blazor's strong suit.

* This system starts to choke when the model represents more than a few lines. The bottleneck seems to be encoding all the data as JSON on the C# side.

* I tried using StringBuilder everywhere and also using `int` instead of `float`, but it did not produce large improvements.

* I also tired using the [`Blazor.Extensions.Canvas`](https://github.com/BlazorExtensions/Canvas) package so I could make all the rendering calls from C# (eliminating the conversion to JSON step). Because this required so many individual interop calls (even with batching), it was about twice as slow as the JS renderer implemented in the code above.

## Resources

* Download this project: [blazor-mystify.zip](blazor-mystify.zip)

* Launch the demo: [Mystify your Browser with Blazor](app/)

* Browse source on GitHub: [C# Data Visualization: MystifyBlazor](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-09-blazor-mystify/)

Boids in your Browser with Blazor

This project implements the Boids flocking algorithm in C# and uses Blazor WebAssembly to render it in the browser. Drone birds (bird-oids, or boids) follow a simple set of rules to determine their angle and velocity. When many boids are placed in a field together, interesting group behaviors emerge. Details about the boid flocking algorithm are discussed in depth elsewhere. This article summarizes how to configure a Blazor application to model graphics with C# and render them with JavaScript.

Live Demo

  • Boids with default settings: app/

  • Boids with custom settings: app/?boids=100&predators=5

  • Pro tip: resize your window really small and watch what happens!

The C# Boids Model

The code that manages the field of boids is entirely written in C#. It tracks the positions of boids and advances their positions and directions over time, but it does not manage rendering. Briefly,

  • A BoidField has a width, height, and array of Boids
  • Each Boid has a position, direction, and velocity
  • Each Boid can advance itself by considering the positions of all the other boids:
    • Rule 1: boids steer toward the center of mass of nearby boids
    • Rule 2: boids adjust direction to match nearby boids
    • Rule 3: boids adjust speed to match a match a target
    • Rule 4: boids steer away from very close boids
    • Rule 5: boids steer away from boids marked as predators
    • Rule 6: boids are repelled by the edge of the window

Model with C#, Render with JavaScript

At the time of writing, the most performant way to render high frame rates in the browser is to write your model and business logic entirely in JavaScript. The purpose of this project is not to make the most performant model possible, but rather to explore what can be done with client-side Blazor and WebAssembly. There is a strong advantage to being able to keep keeping your graphics model and business logic in C#, especially if it is already written and extensively tested.

In my Draw Animated Graphics in the Browser with Blazor WebAssembly article we used Blazor.Extensions.Canvas to draw on a HTML Canvas from C# code. Although this worked, it was limited by the fact that interop calls can be slow. Even though they can be batched, they still represent a significant bottleneck when thousands of calls need to be made on every frame.

In this project I used C# to model the field of boids, converted the field to JSON (with each boid having a X/Y position and a rotation), and had JavaScript parse the boid array and render each boid using a rendering function inside JavaScript.

Source Code

The full source code can be downloaded here (blazor-boids.zip) or navigated on GitHub C# Data Visualization: Blazor Boids. The C# code for Boid and BoidField can be found there. What I'll focus on here is the key Blazor code required to manage the model and render it using JavaScript.

index.razor

There are a lot of different things going on here. Most of them can be figured out with a little visual inspection. Here are the highlights:

  • A canvas is created inside a div, each with a unique ID so they can be referenced from JavaScript.

  • A IJSRuntime is injected so JS functions can be called from C#. Specifically, the initRenderJS is called when the component is first rendered.

  • A NavigationManager is injected to let us easily work with query strings (Note that QueryHelpers requires the Microsoft.AspNetCore.WebUtilities NuGet package).

  • A boidField graphics model is created as a private field and initialized OnAfterRender using query strings to customize the initializer parameters.

  • The UpdateModel() method is decorated with JSInvokable so it can be called from JavaScript. It accepts a width and height (which may change if the browser size changes) and can resize the boidField as needed. It advances the model then converts the positions and directions of all boids to JSON and returns it as a string.

@page "/"
@using System.Text;
@inject IJSRuntime JsRuntime
@inject NavigationManager NavManager
@using Microsoft.AspNetCore.WebUtilities

<div id="boidsHolder" style="position: fixed; width: 100%; height: 100%">
    <canvas id="boidsCanvas"></canvas>
</div>

@code{
    private Random rand = new Random();
    private Models.Field boidField;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // call the initialization JavaScript function
        await JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
        await base.OnInitializedAsync();

        // use a query string to customize the number of boids
        int boidCount = 75;
        var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("boids", out var boidCountString))
            if (int.TryParse(boidCountString, out int parsedBoidCount))
                boidCount = parsedBoidCount;

        // use a query string to customize the number of predators
        int predatorCount = 3;
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("predators", out var predatorCountString))
            if (int.TryParse(predatorCountString, out int parsedPredatorCount))
                predatorCount = parsedPredatorCount;

        // create the model using the custom settings
        boidField = new Models.Field(800, 600, boidCount, predatorCount);
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        if (boidField is null)
            return "";

        boidField.Resize(width, height);
        boidField.Advance(bounceOffWalls: true, wrapAroundEdges: false);

        StringBuilder sb = new StringBuilder("[");
        foreach (var boid in boidField.Boids)
        {
            double x = boid.X;
            double y = boid.Y;
            double r = boid.GetAngle() / 360;
            sb.Append($"[{x:0.0},{y:0.0},{r:0.000}],");
        }

        sb.Append(boidField.PredatorCount.ToString());
        sb.Append("]");
        return sb.ToString();
    }
}

index.html

Two JavaScript functions are added to the HTML:

  • renderJS() - this function calls C# to update the model and request the latest data, then calls itself to create an infinite render loop. Each time it executes it:
    • resizes the canvas to fit the div it's inside of
    • calls the C# UpdateModel() method (passing in the latest canvas size)
    • parses the JSON the C# method returns to obtain an array of boid details
    • clears the canvas by filling it with a blue color
    • renders each boid (by translating and rotating the canvas, not the boid)
    • requests itself be rendered again so rendering continues infinitely
  • initRenderJS - this function is called from Blazor so the running instance can be referenced in the future. It also starts the infinite render loop.
<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('boidsHolder');
        var canvas = document.getElementById('boidsCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = holder.clientHeight;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var boidsString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var boids = JSON.parse(boidsString);
        var predatorCount = boids.pop();

        // render each boid
        var ctx = document.getElementById('boidsCanvas').getContext('2d');
        for (var i = 0; i < boids.length; i++) {
            var predator = i < predatorCount;
            var boid = boids[i];
            var x = boid[0];
            var y = boid[1];
            var rotation = boid[2];
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(rotation * 2 * Math.PI);
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(4, -2);
            ctx.lineTo(0, 10);
            ctx.lineTo(-4, -2);
            ctx.lineTo(0, 0);
            ctx.closePath();
            ctx.fillStyle = predator ? '#FFFF00' : '#FFFFFF';
            ctx.fill();
            ctx.restore();
        }

        // call this same function to render the next frame
        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>

Resources

Blazor Boids

Blazor Source Code

Boids in C# (Windows Application)

  • Boids in C# - a Windows application that runs much faster than this Blazor app. It has controls to customize flocking parameters. This was the source of the C# model I used for this Blazor app.

JavaScript Boids Simulators

Obviously a native JavaScript Boids simulator will be much faster. Implementing this Blazor app in JavaScript would have meant translating the model from C# to JavaScript. For performance-critical rendering-intensive applications, this is the way to go at the time of writing.

Literature

Markdown source code last modified on January 18th, 2021
---
title: Boids in your Browser with Blazor
date: 2021-01-08 22:58:00
tags: blazor, csharp
---

# Boids in your Browser with Blazor

**This project implements the [Boids flocking algorithm](https://en.wikipedia.org/wiki/Boids) in C# and uses Blazor WebAssembly to render it in the browser.** Drone birds (bird-oids, or boids) follow a simple set of rules to determine their angle and velocity. When many boids are placed in a field together, interesting group behaviors emerge. Details about the boid flocking algorithm are discussed in depth elsewhere. This article summarizes how to configure a Blazor application to _model_ graphics with C# and _render_ them with JavaScript.

<div class="text-center">

[![](blazor-boids.gif)](app)

</div>

### Live Demo

* Boids with default settings: [app/](app/)

* Boids with custom settings: [app/?boids=100&predators=5](app/?boids=100&predators=5)

* Pro tip: resize your window really small and watch what happens!

## The C# Boids Model

The code that manages the field of boids is entirely written in C#. It tracks the positions of boids and advances their positions and directions over time, but it does not manage rendering. Briefly, 

* A `BoidField` has a width, height, and array of Boids
* Each `Boid` has a position, direction, and velocity
* Each `Boid` can advance itself by considering the positions of all the other boids:
  * Rule 1: boids steer toward the center of mass of nearby boids
  * Rule 2: boids adjust direction to match nearby boids
  * Rule 3: boids adjust speed to match a match a target
  * Rule 4: boids steer away from very close boids
  * Rule 5: boids steer away from boids marked as predators
  * Rule 6: boids are repelled by the edge of the window

## Model with C#, Render with JavaScript

At the time of writing, the most performant way to render high frame rates in the browser is to write your model and business logic entirely in JavaScript. The purpose of this project is _not_ to make the most performant model possible, but rather to explore what can be done with client-side Blazor and WebAssembly. There is a strong advantage to being able to keep keeping your graphics model and business logic in C#, especially if it is already written and extensively tested.

In my _[Draw Animated Graphics in the Browser with Blazor WebAssembly](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics/)_ article we used `Blazor.Extensions.Canvas` to draw on a HTML Canvas from C# code. Although this worked, it was limited by the fact that interop calls can be slow. Even though they can be batched, they still represent a significant bottleneck when thousands of calls need to be made on every frame.

In this project I used C# to model the field of boids, converted the field to JSON (with each boid having a X/Y position and a rotation), and had JavaScript parse the boid array and render each boid using a rendering function inside JavaScript.

## Source Code

The full source code can be downloaded here ([blazor-boids.zip](blazor-boids.zip)) or navigated on GitHub [C# Data Visualization: Blazor Boids](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-08-blazor-boids/BlazorBoids). The C# code for `Boid` and `BoidField` can be found there. What I'll focus on here is the key Blazor code required to manage the model and render it using JavaScript.

### index.razor

There are a lot of different things going on here. Most of them can be figured out with a little visual inspection. Here are the highlights:

* A canvas is created inside a div, each with a unique ID so they can be referenced from JavaScript.

* A `IJSRuntime` is injected so JS functions can be called from C#. Specifically, the `initRenderJS` is called when the component is first rendered.

* A `NavigationManager` is injected to let us easily work with query strings (Note that `QueryHelpers` requires the `Microsoft.AspNetCore.WebUtilities` NuGet package).

* A `boidField` graphics model is created as a private field and initialized `OnAfterRender` using query strings to customize the initializer parameters.

* The `UpdateModel()` method is decorated with `JSInvokable` so it can be called from JavaScript. It accepts a width and height (which may change if the browser size changes) and can resize the `boidField` as needed. It advances the model then converts the positions and directions of all boids to JSON and returns it as a string.

```cs
@page "/"
@using System.Text;
@inject IJSRuntime JsRuntime
@inject NavigationManager NavManager
@using Microsoft.AspNetCore.WebUtilities

<div id="boidsHolder" style="position: fixed; width: 100%; height: 100%">
    <canvas id="boidsCanvas"></canvas>
</div>

@code{
    private Random rand = new Random();
    private Models.Field boidField;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // call the initialization JavaScript function
        await JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
        await base.OnInitializedAsync();

        // use a query string to customize the number of boids
        int boidCount = 75;
        var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("boids", out var boidCountString))
            if (int.TryParse(boidCountString, out int parsedBoidCount))
                boidCount = parsedBoidCount;

        // use a query string to customize the number of predators
        int predatorCount = 3;
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("predators", out var predatorCountString))
            if (int.TryParse(predatorCountString, out int parsedPredatorCount))
                predatorCount = parsedPredatorCount;

        // create the model using the custom settings
        boidField = new Models.Field(800, 600, boidCount, predatorCount);
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        if (boidField is null)
            return "";

        boidField.Resize(width, height);
        boidField.Advance(bounceOffWalls: true, wrapAroundEdges: false);

        StringBuilder sb = new StringBuilder("[");
        foreach (var boid in boidField.Boids)
        {
            double x = boid.X;
            double y = boid.Y;
            double r = boid.GetAngle() / 360;
            sb.Append($"[{x:0.0},{y:0.0},{r:0.000}],");
        }

        sb.Append(boidField.PredatorCount.ToString());
        sb.Append("]");
        return sb.ToString();
    }
}
```

### index.html

Two JavaScript functions are added to the HTML:

* `renderJS()` - this function calls C# to update the model and request the latest data, then calls itself to create an infinite render loop. Each time it executes it:
  * resizes the canvas to fit the div it's inside of
  * calls the C# `UpdateModel()` method (passing in the latest canvas size)
  * parses the JSON the C# method returns to obtain an array of boid details
  * clears the canvas by filling it with a blue color
  * renders each boid (by translating and rotating the canvas, not the boid)
  * requests itself be rendered again so rendering continues infinitely
* `initRenderJS` - this function is called from Blazor so the running instance can be referenced in the future. It also starts the infinite render loop.

```html
<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('boidsHolder');
        var canvas = document.getElementById('boidsCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = holder.clientHeight;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var boidsString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var boids = JSON.parse(boidsString);
        var predatorCount = boids.pop();

        // render each boid
		var ctx = document.getElementById('boidsCanvas').getContext('2d');
        for (var i = 0; i < boids.length; i++) {
            var predator = i < predatorCount;
            var boid = boids[i];
            var x = boid[0];
            var y = boid[1];
            var rotation = boid[2];
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(rotation * 2 * Math.PI);
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(4, -2);
            ctx.lineTo(0, 10);
            ctx.lineTo(-4, -2);
            ctx.lineTo(0, 0);
            ctx.closePath();
            ctx.fillStyle = predator ? '#FFFF00' : '#FFFFFF';
            ctx.fill();
            ctx.restore();
        }

        // call this same function to render the next frame
        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>
```

## Resources

### Blazor Boids

* Launch _Blazor Boids_ with default settings: [app/](app/)

* Launch _Blazor Boids_ with custom settings: [app/?boids=100&predators=5](app/?boids=100&predators=5)

### Blazor Source Code

* Download source: [blazor-boids.zip](blazor-boids.zip)

* View source on GitHub: [C# Data Visualization: Blazor Boids](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-08-blazor-boids/BlazorBoids)

* [Draw Animated Graphics in the Browser with Blazor WebAssembly](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics/) uses a render loop written in C# (instead of JavaScript) using `Blazor.Extensions.Canvas`. It's a bit slower as a result, but for simple models it has the advantage of not having to write a JavaScript rendering method.

### Boids in C# (Windows Application)

* [Boids in C#](https://swharden.com/CsharpDataVis/boids/boids.md.html) - a Windows application that runs much faster than this Blazor app. It has controls to customize flocking parameters. This was the source of the C# model I used for this Blazor app.

<div class="text-center">

[![](boids-csharp-opengl.png)](https://swharden.com/CsharpDataVis/boids/boids.md.html)

</div>

### JavaScript Boids Simulators

**Obviously a native JavaScript Boids simulator will be much faster.** Implementing this Blazor app in JavaScript would have meant translating the model from C# to JavaScript. For performance-critical rendering-intensive applications, this is the way to go at the time of writing.

* [Boids algorithm demonstration](https://eater.net/boids) by Ben Eater (featured in SmarterEveryDay's [YouTube Video](https://youtu.be/4LWmRuB-uNU?t=187) about flocking birds)
* [Boids: Flocking made simple](http://www.harmendeweerd.nl/boids/) by Harmen de Weerd
* [Flocking](https://processing.org/examples/flocking.html) by Daniel Shiffman
* [Flocking Simulation](http://www.emergentmind.com/boids) by Matt Mazur
* [Simulate How Birds Flock in Processing](https://medium.com/swlh/boids-a-simple-way-to-simulate-how-birds-flock-in-processing-69057930c229) by Takuma Kakehi

### Literature
* [Boids: Background and Update](https://www.red3d.com/cwr/boids/) by Craig Reynolds (the inventor of boids)
* [Boids on Wikipedia](https://en.wikipedia.org/wiki/Boids)
* [3 Simple Rules of Flocking Behaviors: Alignment, Cohesion, and Separation](https://gamedevelopment.tutsplus.com/tutorials/3-simple-rules-of-flocking-behaviors-alignment-cohesion-and-separation--gamedev-3444)
* [Boids Code Golf](https://codegolf.stackexchange.com/questions/154277/implement-the-boids-algorithm)

Draw Animated Graphics in the Browser with Blazor WebAssembly

Client-side Blazor allows C# graphics models to be rendered in the browser. This means .NET developers can create web apps with business logic written (and tested) in C# instead of being forced to write their business logic in a totally different language (JavaScript) just to get it to run in the browser. In this project we'll create an interactive graphics model (a field of balls that bounce off the edge of the screen) entirely in C#, and draw the model on the screen using an API that allows us to interact with a HTML5 Canvas.

Strategy

At the time of this writing Blazor WebAssembly can't directly paint on the screen, so JavaScript is required somewhere to make this happen. The Blazor.Extensions.Canvas package has a Canvas component and provides a C# API for all of its JavaScript methods, allowing you to draw on the canvas entirely from C#.

Rendering gets a little slower every time a JavaScript function is called from Blazor. If a large number of shapes are required (a lot of JavaScript calls), performance may be limited compared to a similar application written entirely in JavaScript. For high performance rendering of many objects, a rendering loop written entirely in JavaScript may be required.

This method is good for simple models with a limited number of shapes. It allows the business logic (and tests) to remain entirely in C#. The same graphics model code could be displayed in the browser (using HTML canvas) or on the desktop in WPF and WinForms apps (using System.Drawing or SkiaSharp).

Step 1: Create a Pure C# Graphics Model

I'm using graphics model to describe the state of the field of balls and logic required to move each ball around. Ideally this logic would be isolated in a separate library. At the time of writing a .NET Standard C# library seems like a good idea, so the same graphics model could be used in .NET Framework and .NET Core environments.

Models/Ball.cs

public class Ball
{
    public double X { get; private set; }
    public double Y { get; private set; }
    public double XVel { get; private set; }
    public double YVel { get; private set; }
    public double Radius { get; private set; }
    public string Color { get; private set; }

    public Ball(double x, double y, double xVel, double yVel, double radius, string color)
    {
        (X, Y, XVel, YVel, R, Color) = (x, y, xVel, yVel, radius, color);
    }

    public void StepForward(double width, double height)
    {
        X += XVel;
        Y += YVel;
        if (X < 0 || X > width)
            XVel *= -1;
        if (Y < 0 || Y > height)
            YVel *= -1;

        if (X < 0)
            X += 0 - X;
        else if (X > width)
            X -= X - width;

        if (Y < 0)
            Y += 0 - Y;
        if (Y > height)
            Y -= Y - height;
    }
}

Models/Field.cs

public class Field
{
    public readonly List<Ball> Balls = new List<Ball>();
    public double Width { get; private set; }
    public double Height { get; private set; }

    public void Resize(double width, double height) =>
        (Width, Height) = (width, height);

    public void StepForward()
    {
        foreach (Ball ball in Balls)
            ball.StepForward(Width, Height);
    }

    private double RandomVelocity(Random rand, double min, double max)
    {
        double v = min + (max - min) * rand.NextDouble();
        if (rand.NextDouble() > .5)
            v *= -1;
        return v;
    }


    private string RandomColor(Random rand) => 
        string.Format("#{0:X6}", rand.Next(0xFFFFFF));

    public void AddRandomBalls(int count = 10)
    {
        double minSpeed = .5;
        double maxSpeed = 5;
        double radius = 10;
        Random rand = new Random();

        for (int i = 0; i < count; i++)
        {
            Balls.Add(
                new Ball(
                    x: Width * rand.NextDouble(),
                    y: Height * rand.NextDouble(),
                    xVel: RandomVelocity(rand, minSpeed, maxSpeed),
                    yVel: RandomVelocity(rand, minSpeed, maxSpeed),
                    radius: radius,
                    color: RandomColor(rand);
                )
            );
        }
    }
}

Step 2: Get the Blazor.Extensions.Canvas Package

Use NuGet to install Blazor.Extensions.Canvas

Install-Package Blazor.Extensions.Canvas

Step 3: Add a script to index.html

This JavaScript sets-up the render loop which automatically calls RenderInBlazor C# method on every frame. It also calls the ResizeInBlazor C# function whenever the canvas is resized so the graphics model's dimensions can be updated. You can place it just before the closing </body> tag.

This code will automatically resize the canvas and graphics model to fit the window, but for a fixed-size canvas you can omit the addEventListener and resizeCanvasToFitWindow calls.

<script src='_content/Blazor.Extensions.Canvas/blazor.extensions.canvas.js'></script>
<script>
    function renderJS(timeStamp) {
        theInstance.invokeMethodAsync('RenderInBlazor', timeStamp);
        window.requestAnimationFrame(renderJS);
    }

    function resizeCanvasToFitWindow() {
        var holder = document.getElementById('canvasHolder');
        var canvas = holder.querySelector('canvas');
        if (canvas) {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
            theInstance.invokeMethodAsync('ResizeInBlazor', canvas.width, canvas.height);
        }
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.addEventListener("resize", resizeCanvasToFitWindow);
        resizeCanvasToFitWindow();
        window.requestAnimationFrame(renderJS);
    };
</script>

Step 4: Create a page for the Canvas and Render code

This step ties everything together:

  • The graphics model: a private class stored at this level
  • The canvas component: a protected field
  • The canvas: a Razor component referencing the canvas component
  • The init code: which tells JavaScipt to start the render loop
  • The render method: C# function called from the JavaScript render loop when a frame is to be drawn
  • The resize method: C# function called from JavaScript to update the model when the canvas size changes
  • JavaScript runtime injection: This allows Blazor/JavaScript interoperability (JS interop)

For simplicity it's demonstrated here using a code-behind, but a clearer strategy would be to move the render logic into its own class/file.

@page "/"

@using Blazor.Extensions
@using Blazor.Extensions.Canvas
@using Blazor.Extensions.Canvas.Canvas2D
@inject IJSRuntime JsRuntime;

<div id="canvasHolder" style="position: fixed; width: 100%; height: 100%">
    <BECanvas Width="600" Height="400" @ref="CanvasRef"></BECanvas>
</div>

@code{
    private Models.Field BallField = new Models.Field();
    private Canvas2DContext ctx;
    protected BECanvasComponent CanvasRef;
    private DateTime LastRender;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        this.ctx = await CanvasRef.CreateCanvas2DAsync();
        await JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
        await base.OnInitializedAsync();
    }

    [JSInvokable]
    public void ResizeInBlazor(double width, double height) => BallField.Resize(width, height);

    [JSInvokable]
    public async ValueTask RenderInBlazor(float timeStamp)
    {
        if (BallField.Balls.Count == 0)
            BallField.AddRandomBalls(50);
        BallField.StepForward();

        double fps = 1.0 / (DateTime.Now - LastRender).TotalSeconds;
        LastRender = DateTime.Now;

        await this.ctx.BeginBatchAsync();
        await this.ctx.ClearRectAsync(0, 0, BallField.Width, BallField.Height);
        await this.ctx.SetFillStyleAsync("#003366");
        await this.ctx.FillRectAsync(0, 0, BallField.Width, BallField.Height);
        await this.ctx.SetFontAsync("26px Segoe UI");
        await this.ctx.SetFillStyleAsync("#FFFFFF");
        await this.ctx.FillTextAsync("Blazor WebAssembly + HTML Canvas", 10, 30);
        await this.ctx.SetFontAsync("16px consolas");
        await this.ctx.FillTextAsync($"FPS: {fps:0.000}", 10, 50);
        await this.ctx.SetStrokeStyleAsync("#FFFFFF");
        foreach (var ball in BallField.Balls)
        {
            await this.ctx.BeginPathAsync();
            await this.ctx.ArcAsync(ball.X, ball.Y, ball.Radius, 0, 2 * Math.PI, false);
            await this.ctx.SetFillStyleAsync(ball.Color);
            await this.ctx.FillAsync();
            await this.ctx.StrokeAsync();
        }
        await this.ctx.EndBatchAsync();
    }
}

Notice how the JavaScript calls are wrapped in BeginBatchAsync and EndBatchAsync. This allows the calls between these two statements to be bundled into a single call. Since Blazor/JavaScript interop calls are the primary bottleneck in this system, limiting the number of individual JavaScript calls has a large influence on final performance.

Notes and References

💡 I observed a strong performance increase when upgrading from .NET Core 3.1 to .NET 5. If you can, Migrate your Blazor project to .NET 5 or newer

Markdown source code last modified on January 18th, 2021
---
title: Draw Animated Graphics in the Browser with Blazor WebAssembly
date: 2021-01-07 21:46:00
tags: blazor, csharp
---

# Draw Animated Graphics in the Browser with Blazor WebAssembly

**Client-side Blazor allows C# graphics models to be rendered in the browser.** This means .NET developers can create web apps with business logic written (and tested) in C# instead of being forced to write their business logic in a totally different language (JavaScript) just to get it to run in the browser. In this project we'll create an interactive graphics model (a field of balls that bounce off the edge of the screen) entirely in C#, and draw the model on the screen using an API that allows us to interact with a HTML5 Canvas.

<div class="text-center">

[![](blazor-canvas-demo.gif)](app)

</div>

* [**View the live demo**](app)
* [**Download the source code**](blazor-canvas.zip)

## Strategy

**At the time of this writing Blazor WebAssembly can't directly paint on the screen, so JavaScript is required somewhere to make this happen.** The [`Blazor.Extensions.Canvas`](https://github.com/BlazorExtensions/Canvas) package has a Canvas component and provides a C# API for all of its JavaScript methods, allowing you to draw on the canvas entirely from C#. 

**Rendering gets a little slower every time a JavaScript function is called from Blazor.** If a large number of shapes are required (a lot of JavaScript calls), performance may be limited compared to a similar application written entirely in JavaScript. For high performance rendering of many objects, a rendering loop written entirely in JavaScript may be required.

**This method is good for simple models with a limited number of shapes.** It allows the business logic (and tests) to remain entirely in C#. The same graphics model code could be displayed in the browser (using HTML canvas) or on the desktop in WPF and WinForms apps (using System.Drawing or SkiaSharp).

## Step 1: Create a Pure C# Graphics Model

**I'm using _graphics model_ to describe the state of the field of balls and logic required to move each ball around.** Ideally this logic would be isolated in a separate library. At the time of writing a .NET Standard C# library seems like a good idea, so the same graphics model could be used in .NET Framework and .NET Core environments.

### Models/Ball.cs

```cs
public class Ball
{
    public double X { get; private set; }
    public double Y { get; private set; }
    public double XVel { get; private set; }
    public double YVel { get; private set; }
    public double Radius { get; private set; }
    public string Color { get; private set; }

    public Ball(double x, double y, double xVel, double yVel, double radius, string color)
    {
        (X, Y, XVel, YVel, R, Color) = (x, y, xVel, yVel, radius, color);
    }

    public void StepForward(double width, double height)
    {
        X += XVel;
        Y += YVel;
        if (X < 0 || X > width)
            XVel *= -1;
        if (Y < 0 || Y > height)
            YVel *= -1;

        if (X < 0)
            X += 0 - X;
        else if (X > width)
            X -= X - width;

        if (Y < 0)
            Y += 0 - Y;
        if (Y > height)
            Y -= Y - height;
    }
}
```

### Models/Field.cs

```cs
public class Field
{
    public readonly List<Ball> Balls = new List<Ball>();
    public double Width { get; private set; }
    public double Height { get; private set; }

    public void Resize(double width, double height) =>
        (Width, Height) = (width, height);

    public void StepForward()
    {
        foreach (Ball ball in Balls)
            ball.StepForward(Width, Height);
    }

    private double RandomVelocity(Random rand, double min, double max)
    {
        double v = min + (max - min) * rand.NextDouble();
        if (rand.NextDouble() > .5)
            v *= -1;
        return v;
    }


    private string RandomColor(Random rand) => 
	    string.Format("#{0:X6}", rand.Next(0xFFFFFF));
	
    public void AddRandomBalls(int count = 10)
    {
        double minSpeed = .5;
        double maxSpeed = 5;
        double radius = 10;
        Random rand = new Random();

        for (int i = 0; i < count; i++)
        {
            Balls.Add(
                new Ball(
                    x: Width * rand.NextDouble(),
                    y: Height * rand.NextDouble(),
                    xVel: RandomVelocity(rand, minSpeed, maxSpeed),
                    yVel: RandomVelocity(rand, minSpeed, maxSpeed),
                    radius: radius,
                    color: RandomColor(rand);
                )
            );
        }
    }
}
```

## Step 2: Get the Blazor.Extensions.Canvas Package

Use NuGet to install [Blazor.Extensions.Canvas](https://github.com/BlazorExtensions/Canvas)

```cs
Install-Package Blazor.Extensions.Canvas
```

## Step 3: Add a script to index.html

**This JavaScript sets-up the render loop** which automatically calls `RenderInBlazor` C# method on every frame. It also calls the `ResizeInBlazor` C# function whenever the canvas is resized so the graphics model's dimensions can be updated. You can place it just before the closing `</body>` tag.

**This code will automatically resize the canvas and graphics model to fit the window,** but for a fixed-size canvas you can omit the `addEventListener` and `resizeCanvasToFitWindow` calls.

```html
<script src='_content/Blazor.Extensions.Canvas/blazor.extensions.canvas.js'></script>
<script>
    function renderJS(timeStamp) {
        theInstance.invokeMethodAsync('RenderInBlazor', timeStamp);
        window.requestAnimationFrame(renderJS);
    }

    function resizeCanvasToFitWindow() {
        var holder = document.getElementById('canvasHolder');
        var canvas = holder.querySelector('canvas');
        if (canvas) {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
            theInstance.invokeMethodAsync('ResizeInBlazor', canvas.width, canvas.height);
        }
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.addEventListener("resize", resizeCanvasToFitWindow);
        resizeCanvasToFitWindow();
        window.requestAnimationFrame(renderJS);
    };
</script>
```

## Step 4: Create a page for the Canvas and Render code

This step ties everything together:
 * **The graphics model**: a private class stored at this level
 * **The canvas component**: a protected field
 * **The canvas**: a Razor component referencing the canvas component
 * **The init code**: which tells JavaScipt to start the render loop
 * **The render method**: C# function called from the JavaScript render loop when a frame is to be drawn
 * **The resize method**: C# function called from JavaScript to update the model when the canvas size changes
* **JavaScript runtime injection**: This allows Blazor/JavaScript interoperability (JS interop)

For simplicity it's demonstrated here using a code-behind, but a clearer strategy would be to move the render logic into its own class/file.

```cs
@page "/"

@using Blazor.Extensions
@using Blazor.Extensions.Canvas
@using Blazor.Extensions.Canvas.Canvas2D
@inject IJSRuntime JsRuntime;

<div id="canvasHolder" style="position: fixed; width: 100%; height: 100%">
    <BECanvas Width="600" Height="400" @ref="CanvasRef"></BECanvas>
</div>

@code{
    private Models.Field BallField = new Models.Field();
    private Canvas2DContext ctx;
    protected BECanvasComponent CanvasRef;
    private DateTime LastRender;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        this.ctx = await CanvasRef.CreateCanvas2DAsync();
        await JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
        await base.OnInitializedAsync();
    }

    [JSInvokable]
    public void ResizeInBlazor(double width, double height) => BallField.Resize(width, height);

    [JSInvokable]
    public async ValueTask RenderInBlazor(float timeStamp)
    {
        if (BallField.Balls.Count == 0)
            BallField.AddRandomBalls(50);
        BallField.StepForward();

        double fps = 1.0 / (DateTime.Now - LastRender).TotalSeconds;
        LastRender = DateTime.Now;

        await this.ctx.BeginBatchAsync();
        await this.ctx.ClearRectAsync(0, 0, BallField.Width, BallField.Height);
        await this.ctx.SetFillStyleAsync("#003366");
        await this.ctx.FillRectAsync(0, 0, BallField.Width, BallField.Height);
        await this.ctx.SetFontAsync("26px Segoe UI");
        await this.ctx.SetFillStyleAsync("#FFFFFF");
        await this.ctx.FillTextAsync("Blazor WebAssembly + HTML Canvas", 10, 30);
        await this.ctx.SetFontAsync("16px consolas");
        await this.ctx.FillTextAsync($"FPS: {fps:0.000}", 10, 50);
        await this.ctx.SetStrokeStyleAsync("#FFFFFF");
        foreach (var ball in BallField.Balls)
        {
            await this.ctx.BeginPathAsync();
            await this.ctx.ArcAsync(ball.X, ball.Y, ball.Radius, 0, 2 * Math.PI, false);
            await this.ctx.SetFillStyleAsync(ball.Color);
            await this.ctx.FillAsync();
            await this.ctx.StrokeAsync();
        }
        await this.ctx.EndBatchAsync();
    }
}
```

**Notice how the JavaScript calls are wrapped in `BeginBatchAsync` and `EndBatchAsync`.** This allows the calls between these two statements to be bundled into a single call. Since Blazor/JavaScript interop calls are the primary bottleneck in this system, limiting the number of individual JavaScript calls has a large influence on final performance.

## Notes and References

> **💡 I observed a strong performance increase when upgrading from .NET Core 3.1 to .NET 5.** If you can, [Migrate your Blazor project to .NET 5](https://docs.microsoft.com/en-us/aspnet/core/migration/31-to-50#update-blazor-webassembly-projects) or newer

* **A hybrid JavaScript/C# architecture is be possible** involving exchange of graphics model _data_, whereby JavaScript is used to render a model but C# is used to advanced the model. This could be achieved with a single interop call passing model data as JSON. This is explored in the later blog posts [Mystify your Browser with Blazor](https://swharden.com/blog/2021-01-09-blazor-mystify/) and [Boids in your Browser with Blazor](https://swharden.com/blog/2021-01-08-blazor-boids/).

* **Live demo of this project**: [Ball field demo](app)

* **Source code for this project**: [blazor-canvas.zip](app)

* **C# Data Visualization:** https://github.com/swharden/Csharp-Data-Visualization
Pages