⚠️ Warning: This website is still being developed. Code examples, videos, and links may be broken while I continue to work on it. -- Scott, May 10, 2020
Resources for visualizing data using C# and the .NET platform

Interactive Data Display

Interactive Data Display is a set of WPF controls developed by Microsoft for interactively displaying data in WPF applications. Reviewing its GitHub page shows its last commit was in 2018 (and the package on NuGet is from 2017), so the future of this project is uncertain.

Controls

  • left-click-drag to pan
  • left-click-drag on an axis to pan just that axis
  • scroll-wheel to zoom
  • double-click to fit axis limits to data

Platform Support

The NuGet package (version 1.0.0) does not support .NET Core. More specifically, the NuGet package has complex antiquated dependencies and the official NuGet package only works out of the box on .NET Framework 4.5.2

The issues page links to a fork by Jeff Mastry that supports .NET Core, but it's not available on NuGet and Microsoft hasn't responded to the issue it originally was posted nearly a year ago.

Code

XAML

<d3:Chart Name="myChart">
    <Grid Name="myGrid"/>
</d3:Chart>

Generate Sample Data

This code generates random data we can practice plotting

private Random rand = new Random(0);
private double[] RandomWalk(int points = 5, double start = 100, double mult = 50)
{
    // return an array of difting random numbers
    double[] values = new double[points];
    values[0] = start;
    for (int i = 1; i < points; i++)
        values[i] = values[i - 1] + (rand.NextDouble() - .5) * mult;
    return values;
}
private double[] Consecutive(int points, double offset = 0, double stepSize = 1)
{
    // return an array of ascending numbers starting at 1
    double[] values = new double[points];
    for (int i = 0; i < points; i++)
        values[i] = i * stepSize + 1 + offset;
    return values;
}

Bar Graph

// generate some random Y data
int pointCount = 5;
double[] xs1 = Consecutive(pointCount, offset: 0);
double[] xs2 = Consecutive(pointCount, offset: .4);
double[] ys1 = RandomWalk(pointCount);
double[] ys2 = RandomWalk(pointCount);

// create the series and describe their styling
var bar1 = new InteractiveDataDisplay.WPF.BarGraph()
{
    Color = Brushes.Blue,
    Description = "Group A",
    BarsWidth = .35,
};

var bar2 = new InteractiveDataDisplay.WPF.BarGraph()
{
    Color = Brushes.Red,
    Description = "Group B",
    BarsWidth = .35,
};

// load data into each series
bar1.PlotBars(xs1, ys1);
bar2.PlotBars(xs2, ys2);

// add the series to the grid
myGrid.Children.Clear();
myGrid.Children.Add(bar1);
myGrid.Children.Add(bar2);

// customize styling
myChart.Title = $"Bar Graph";
myChart.BottomTitle = $"Horizontal Axis Label";
myChart.LeftTitle = $"Vertical Axis Label";
myChart.IsAutoFitEnabled = false;
myChart.LegendVisibility = Visibility.Visible;

// set axis limits manually
myChart.PlotOriginX = .5;
myChart.PlotWidth = 5.5;
myChart.PlotOriginY = 0;
myChart.PlotHeight = 200;

Scatter Plot

// generate some random X and Y data
int pointCount = 500;
double[] xs1 = RandomWalk(pointCount);
double[] ys1 = RandomWalk(pointCount);
double[] xs2 = RandomWalk(pointCount);
double[] ys2 = RandomWalk(pointCount);
double[] sizes = Consecutive(pointCount, 10, 0);

// create the lines and describe their styling
var line1 = new InteractiveDataDisplay.WPF.CircleMarkerGraph()
{
    Color = new SolidColorBrush(Colors.Blue),
    Description = "Group A",
    StrokeThickness = 1
};

var line2 = new InteractiveDataDisplay.WPF.CircleMarkerGraph()
{
    Color = new SolidColorBrush(Colors.Red),
    Description = "Group B",
    StrokeThickness = 1
};

// load data into the lines
line1.PlotSize(xs1, ys1, sizes);
line2.PlotSize(xs2, ys2, sizes);

// add lines into the grid
myGrid.Children.Clear();
myGrid.Children.Add(line1);
myGrid.Children.Add(line2);

// customize styling
myChart.Title = $"Line Plot ({pointCount:n0} points each)";
myChart.BottomTitle = $"Horizontal Axis Label";
myChart.LeftTitle = $"Vertical Axis Label";
myChart.IsAutoFitEnabled = true;
myChart.LegendVisibility = Visibility.Hidden;

Line Plot

I can display lines with about 100 thousand lines points performance starts to greatly suffer.

int pointCount = 10_000;
double[] xs = Consecutive(pointCount);
double[] ys1 = RandomWalk(pointCount);
double[] ys2 = RandomWalk(pointCount);

// create the lines and describe their styling
var line1 = new InteractiveDataDisplay.WPF.LineGraph
{
    Stroke = new SolidColorBrush(Colors.Blue),
    Description = "Line A",
    StrokeThickness = 2
};

var line2 = new InteractiveDataDisplay.WPF.LineGraph
{
    Stroke = new SolidColorBrush(Colors.Red),
    Description = "Line B",
    StrokeThickness = 2
};

// load data into the lines
line1.Plot(xs, ys1);
line2.Plot(xs, ys2);

// add lines into the grid
myGrid.Children.Clear();
myGrid.Children.Add(line1);
myGrid.Children.Add(line2);

// customize styling
myChart.Title = $"Line Plot ({pointCount:n0} points each)";
myChart.BottomTitle = $"Horizontal Axis Label";
myChart.LeftTitle = $"Vertical Axis Label";
myChart.IsAutoFitEnabled = true;
myChart.LegendVisibility = Visibility.Visible;

Resources

Source Code

Source code for the example program shown on this page is on the C# Data visualization GitHub page:

If you find it helpful, give the project a star!