display(new [] {"hello", "world"} );
Enumerable.Range(1, 5)
As you can see, the same basic structure is used whether you pass the object to the display
method or return it as the cell's value.
Similarly to the behavior for IEnumerable
objects, you'll also see table output for dictionaries, but for each value in the dictionary, the key is provided rather than the index within the collection.
var dictionary = new Dictionary<string, int>
{
["zero"] = 0,
["one"] = 1,
["two"] = 2
};
dictionary
The default formatting behavior for other types of objects is to produce a table showing their properties and the values of those properties.
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
display(new Person { FirstName = "Mitch", LastName = "Buchannon", Age = 42} );
When you have a collection of such objects, you can see the values listed for each item in the collection:
var groupOfPeople = new []
{
new Person { FirstName = "Mitch", LastName = "Buchannon", Age = 42 },
new Person { FirstName = "Hobie ", LastName = "Buchannon", Age = 23 },
new Person { FirstName = "Summer", LastName = "Quinn", Age = 25 },
new Person { FirstName = "C.J.", LastName = "Parker", Age = 23 },
};
display(groupOfPeople);
Displaying a dictionary will show the items by key rather than index.
display(groupOfPeople.ToDictionary(p => $"{p.FirstName}"));
Now let's try something a bit more complex. Let's look at a graph of objects.
We'll redefine the Person
class to allow a reference to a collection of other Person
instances.
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public List<Person> Friends { get; } = new List<Person>();
}
var mitch = new Person { FirstName = "Mitch", LastName = "Buchannon", Age = 42 };
var hobie = new Person { FirstName = "Hobie ", LastName = "Buchannon", Age = 23 };
var summer = new Person { FirstName = "Summer", LastName = "Quinn", Age = 25 };
var cj = new Person { FirstName = "C.J.", LastName = "Parker", Age = 23 };
mitch.Friends.AddRange(new [] { hobie, summer, cj });
hobie.Friends.AddRange(new [] { mitch, summer, cj });
summer.Friends.AddRange(new [] { mitch, hobie, cj });
cj.Friends.AddRange(new [] { mitch, hobie, summer });
var groupOfPeople = new List<Person> { mitch, hobie, summer, cj };
display(groupOfPeople);
That's a bit hard to read, right? The defaut formatting behaviors are not always as useful as they might be. In order to give you more control object formatters can be customized from within the .NET notebook.
Let's clean up the output above by customizing the formatter for the Person.Friends
property, which is creating a lot of noise.
The way to do this is to use the Formatter
API. This API lets you customize the formatting for a specific type. For example:
using Microsoft.DotNet.Interactive.Formatting;
Formatter.Register<Person>((person, writer) => {
writer.Write("person");
}, mimeType: "text/plain");
groupOfPeople
With that in mind, we can make it even more concise by registering a good formatter for Person
:
Formatter.ResetToDefault();
Formatter.Register<Person>((person, writer) => {
writer.Write(person.FirstName);
}, mimeType: "text/plain");
groupOfPeople
To replace the default HTML table view, you can register a formatter for the "text/html"
mime type. Let's do that, and write some HTML using PocketView.
using static Microsoft.DotNet.Interactive.Formatting.PocketViewTags;
Formatter.ResetToDefault();
Formatter.Register<List<Person>>((people, writer) =>
{
foreach (var person in people)
{
writer.Write(
span(
b(person.FirstName),
" ",
i($"({person.Age} years old and has {person.Friends.Count} friends)"),
br));
}
}, mimeType: "text/html");
groupOfPeople