How to integrate d3.js chart in C# application?

I am a big fan of Mike Bostock's d3.js chart library: d3js.org.

I would like to use it to display charts in a C# .Net application, but I don't know if it is possible.

It may be possible by generating HTM+JS codes and rendering it in a webbrowser window. However, I understood d3.js library cannot be used locally without a webserver (However I did not understood what works without a webserver and what requires a webserver), therefore a simple solution does not work.

Has anybody tried to develop that kind of deployment of d3.js charts? Do you have an idea on where to start in order to have the most simple solution?

Answers


A web server definitely isn't required to use a client side JavaScript library like d3.js.

For C#, you'll need to embed a web browser control (in either WindowsForms or WPF).

You'll need to make sure that the browser is working in IE9 Standards mode as shown here.

Create your web pages as you would normally. Navigate to them using webbrowser.navigate (as just files on the file system.)

This should work.


Necromancing.

You can do this with C# and .NET-Core on all operating systems using nodeJS. Absolutely no browser-control required. Just install JavaScript-Services with nuget, then install d3, jsdom and svg2png in nodejs:

npm install –save svg2png
npm install –save jsdom
npm install –save d3

then in Startup.cs, add NodeServices in ConfigureServices

using Microsoft.AspNetCore.NodeServices;

public void ConfigureServices(IServiceCollection services)
{
      // services.AddMvc();

      // https://geeks.ms/clanderas/2016/10/18/asp-net-core-node-services-to-execute-your-nodejs-scripts/
      // https://blogs.msdn.microsoft.com/webdev/2017/02/14/building-single-page-applications-on-asp-net-core-with-javascriptservices/
      services.AddNodeServices( options => {
      // options.DebuggingPort 
      });
}

Add an output handler:

public class AgeInfo
{

    public string age;
    public int population;

    public AgeInfo(string prmAge, int prmPop)
    {
        this.age = prmAge;
        this.population = prmPop;
    }

}


// http://gunnarpeipman.com/2017/10/aspnet-core-node-d3js/
public async Task<IActionResult> Chart([FromServices] INodeServices nodeServices)
{
    var options = new { width = 400, height = 200 };

    var data = new[] {
        new { label = "Abulia", count = 10 },
        new { label = "Betelgeuse", count = 20 },
        new { label = "Cantaloupe", count = 30 },
        new { label = "Dijkstra", count = 40 }
    };

    List<AgeInfo> ls = new List<AgeInfo>();
    ls.Add( new AgeInfo("<5", 2704659));
    ls.Add( new AgeInfo("5-13", 4499890));
    ls.Add( new AgeInfo("14-17", 2159981));
    ls.Add( new AgeInfo("18-24", 3853788));
    ls.Add( new AgeInfo("25-44", 14106543));
    ls.Add( new AgeInfo("45-64", 8819342));
    ls.Add( new AgeInfo("≥65", 612463));


    // string markup = await nodeServices.InvokeAsync<string>("Node/d3Pie.js", options, data);

    string markup = await nodeServices.InvokeAsync<string>("Node/d3chart.js", options, ls);

    string html = @"<!DOCTYPE html>
<html>
<head><meta charset=""utf-8"" />
<style type=""text/css"">
.arc text 
{
  font: 10px sans-serif;
  text-anchor: middle;
}
.arc path 
{
  stroke: #fff;
}
</style>
</head>
<body>
    <img src=""" + markup + @""" />
</body>
</html>";

    return Content(html, "text/html");
}

And then add the JavaScript

// Include all modules we need
const svg2png = require("svg2png");
const { JSDOM } = require("jsdom");
const d3 = require('d3');



// https://bl.ocks.org/mbostock/3887235
module.exports = function (callback, options, data) {


    var dom = new JSDOM('<!DOCTYPE html><html><head><meta charset="utf-8" /></head><body><svg width="960" height="500"></svg></body></html>');
    var document = dom.window.document;
    dom.window.d3 = d3.select(dom.window.document);


    // callback(null, dom.window.document.body.innerHTML);


    var svg = dom.window.d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    radius = Math.min(width, height) / 2,
    g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");



    var color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

    var pie = d3.pie()
        .sort(null)
        .value(function (d) {
            return d.population;
        });


    var path = d3.arc()
        .outerRadius(radius - 10)
        .innerRadius(0);

    var label = d3.arc()
        .outerRadius(radius - 40)
        .innerRadius(radius - 40);

    /*
    var dataaa =
        [
            {
                age: "<5",
                population: 2704659
            },
            {
                age: "5-13",
                population: 4499890
            },
            {
                age: "14-17",
                population: 2159981
            },
            {
                age: "18-24",
                population: 3853788
            },
            {
                age: "25-44",
                population: 14106543
            }
            ,
            {
                age: "45-64",
                population: 8819342
            }
            ,
            {
                age: "≥65",
                population: 612463
            }
        ];
    */

        var arc = g.selectAll(".arc")
        .data(pie(data))
        .enter().append("g")
        .attr("class", "arc");

    arc.append("path")
        .attr("d", path)
        .attr("fill", function (d) {
            return color(d.data.age);
        });

    arc.append("text")
        .attr("transform", function (d) {
            return "translate(" + label.centroid(d) + ")";
        })
        .attr("dy", "0.35em")
        .text(function (d) {
            return d.data.age;
        });
    //});

    // var svgText = dom.window.document.body.outerHTML;
    // var svgText = dom.window.document.body.innerHTML;
    var svgText = dom.window.document.body.querySelector("svg").outerHTML
    // callback(null, svgText);

    // var svgText = dom.window.d3.select("svg").html();
    // svgText=process.version; // v8.6.0
    // svgText= JSON.stringify(process.versions); //
    // var pjson = require('./package.json'); svgText = pjson.version;
    // callback(null, svgText);
    // callback(null, JSON.stringify(  { width: width, height: height } ));

    // var buf = Buffer.from(svgText);
    // callback(null, JSON.stringify( buf ));
    // var output = svg2png.sync(buf, { width: width, height: height } );
    // callback(null, JSON.stringify( output ));
    //callback(null,  svgText);
    // callback(null,  'data:image/svg+xml;base64,' + Buffer.from(svgText).toString('base64'));


    svg2png(Buffer.from(svgText), { width: width, height: height })
        .then(buffer => 'data:image/png;base64,' + buffer.toString('base64') )
        .then(buffer => callback(null, buffer));

}

That should give you a the required d3-chart without needing a svg-compatible browser.

You might need to update npm before you can (successfully) install any nodeJS modules.

npm install -g npm

You can also do this in a command-line application, just that then you need to set up your own DI contrainer.


Was looking for the same solution and stumbled on edge.js. It can be run from any .NET 4.5 application and installed via Nuget. It doesn't require node.js installed but you will need node to install D3 using NPM. Make sure package-lock.json and the node_modules folder is in the same directory as the edge.js file at runtime, or installed globally. I just ran npm i d3-array from inside the \edge folder, then included the *.json and *.js files, set them to content and copy to output directory. Then its as simple as:

class Program {
    public static async Task Start(int[] someArray) {
        var func = Edge.Func(@"
            var d3 = require('d3-array')
            return function (data, callback) {
                callback(null, 'D3 says: ' + d3.min(data));
            }
        ");
        Console.WriteLine(await func(someArray));
    }

    static void Main(string[] args) {
        Start(new int[] { 1, 2, 3, 4 }).Wait();
    }
} 

Outputs: D3 says: 1

NOTE: In .Net7.2 i had to install the Edge.Js.Binaries NuGet package to install the edge folder in the project root. The Edge.Js package didn't do this, in 4.5 it did.


Need Your Help

CSS Selector for <input type="?"

css internet-explorer css-selectors

Is there any way with CSS to target all inputs based on their type? I have a disabled class I use on various disabled form elements, and I'm setting the background color for text boxes, but I don't...

Remove all multiple spaces in Javascript and replace with single space

javascript jquery replace

How can I automatically replace all instances of multiple spaces, with a single space, in Javascript?