如何利用 C# + Echarts 绘制 Bar Simple

背景

Echarts 是百度推出的一个使用 JavaScript 实现的开源可视化库。 该库提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。

柱状图
散点图
关系图
三维图

既然 Echarts 提供了丰富的图形,所以咱们有必要把它封装起来,以便让其支持 Windows 窗体应用程序。


技术分析

整体的技术方案就是做一个自定义控件,该控件中包含 WebBrowser 浏览器控件,通过该浏览器控件显示指定位置的网页。就像咱们直接通过 Web 浏览器网页一样。具体的步骤如下:

首先,创建一个在 Windows 窗体应用程序中使用的控件项目 LSGO.Core.ECharts

其次,在该控件项目的设计器中,拖入一个 WebBrowser 控件,并设置其 Dock 属性为 Fill,即让 WebBrowser 充满整个容器。

接着,写一个 InitialECharts 方法,加载指定目录的网页.\assets\echarts.html,让该网页在 WebBrowser 中打开。

当该网页加载完成后,触发 WebBrowserWebBrowserDocumentCompletedEventHandler 事件,在该事件注册的方法中调用该网页中用 JS 写的 showChart 方法,则在该网页中显示图形。

当窗体控件的尺寸发生变化后,触发 WebBrowserSizeChanged 事件,在该事件注册的方法中调用该网页中用 JS 写的 setPosition 方法,则重新调整显示图形的布局,以满足新的尺寸。

WebBrowser 类的常用属性、事件与方法

<u>属性</u>

/// <summary>
/// 获取或设置一个对象,该对象可由显示在 WebBrowser 控件中的网页所包含的脚本代码访问。
/// </summary>
/// <returns>
/// 可用于脚本代码的对象。
/// </returns>
public object ObjectForScripting { get; set; }

/// <returns>
/// 表示当前页的 HtmlDocument,如果未加载任何页,则为 null。
/// </returns>
public HtmlDocument Document { get; }

<u>事件</u>

/// <summary>
/// 在 WebBrowser 控件完成加载文档时发生。
/// </summary>
public event WebBrowserDocumentCompletedEventHandler DocumentCompleted;

/// <summary>
/// 在 Control.Size 属性值更改时发生。
/// </summary>
public event EventHandler SizeChanged;

<u>方法</u>

/// <summary>
/// 将指定的统一资源定位器 (URL) 处的文档加载到 WebBrowser 控件中,替换上一个文档。
/// </summary>
/// <param name="urlString">要加载的文档的 URL。</param>
public void Navigate(string urlString);

HtmlDocument 类的常用方法

/// <returns>
/// 活动脚本调用所返回的对象。
/// </returns>
/// <param name="scriptName">要调用的脚本方法的名称。</param>
/// <param name="args">要传递给脚本方法的参数。</param>
public object InvokeScript(string scriptName, object[] args);

代码实现

Step1:创建一个用于显示图形的网页

<u>初始显示的网页 echarts.html</u>

<!DOCTYPE html>
<html lang="en-US">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="./bootstrap/css/bootstrap.min.css" />
<script src="./jquery-1.11.2.min.js"></script>
<script src="./bootstrap/js/bootstrap.min.js"></script>
<script src="./json2.js"></script>
<head>
  <title></title>
</head>
<body>
    <div class="container-fluid">
        <div id="main" style="height:350px;"></div>
    </div>
    <script src="./echarts.js"></script>
    <script>
        var myChart = echarts.init(document.getElementById('main'));

        // 指定图表的配置项和数据
        var option = {
            title: {
                text: 'ECharts 入门示例'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
            },
            yAxis: {},
            series: [{
                name: '销量',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };
        myChart.setOption(option);
    </script>    
</body>
</html>

<u>显示图形时调用的 JS 代码 showChart</u>

function showChart(option) {
    myChart.clear();
    var op = JSON.parse(option);
    myChart.setOption(op);
}

<u>当控件的尺寸发生变化时调用的 JS 代码 setPosition</u>

function setPosition(height) {
    var divMain = document.getElementById("main");
    divMain.style.height = height + "px";
    window.onresize = myChart.resize();
}

Step2:创建自定义控件 Echarts

<u>初始化 Echarts 控件的方法</u>

public object Option { get; set; }

public void InitialECharts(Option option)
{
    if (option == null)
        throw new ArgumentNullException();

    Option = JsonConvert.SerializeObject(option);
    string strHtml = Application.StartupPath + @"\assets\echarts.html";
    if (File.Exists(strHtml))
    {
        webBrowserMain.Navigate(strHtml);
        webBrowserMain.ObjectForScripting = this;
    }
}

<u>当 echarts.htmlWebBrowser 内加载完成之后执行的方法</u>。

private void webBrowserMain_DocumentCompleted(object sender,
    WebBrowserDocumentCompletedEventArgs e)
{
    object[] objArray = new object[] {Option};
    HtmlDocument htmlDocument = webBrowserMain.Document;
    if (htmlDocument != null)
    {
        htmlDocument.InvokeScript("showChart", objArray);
        objArray[0] = Height;
        htmlDocument.InvokeScript("setPosition", objArray);
        _isDocumentLoaded = true;
    }
}

<u>当控件 Echarts 尺寸发生变化之后执行的方法</u>。

private void webBrowserMain_SizeChanged(object sender, EventArgs e)
{
    if (_isDocumentLoaded)
    {
        object[] objArray = new object[] {Height};
        HtmlDocument htmlDocument = webBrowserMain.Document;
        if (htmlDocument != null)
        {
            htmlDocument.InvokeScript("setPosition", objArray);
        }
    }
}

Step3:对百度 Echarts 组件的封装

<u>对 ECharts 中的 xAxis 结构的封装</u>。

public class XAxis
{
    /// <summary>
    /// 坐标轴类型
    /// </summary>
    public string type { get; set; } = "category";

    /// <summary>
    /// 类目数据
    /// </summary>
    public List<string> data { get; set; }
}

<u>对 EChartsyAxis 结构的封装</u>。

public class YAxis
{
    /// <summary>
    /// 坐标轴类型
    /// </summary>
    public string type { get; set; } = "value";
}

<u>对 EChartsseries 集合元素的封装</u>。

public class SeriesItem
{
    /// <summary>
    /// 每个系列通过 type 决定自己的图表类型
    /// </summary>
    public string type { get; set; }
    
    /// <summary>
    /// 系列中的数据内容数组
    /// </summary>
    public List<int> data { get; set; }
}

<u>对 EChartsoption 结构的封装</u>。

public class Option
{
    /// <summary>
    /// x轴
    /// </summary>
    public XAxis xAxis { get; set; }
    
    /// <summary>
    /// y轴
    /// </summary>
    public YAxis yAxis { get; set; }

    /// <summary>
    /// 数据
    /// </summary>
    public List<SeriesItem> series { get; set; }
}

总结

百度示例的代码:

option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [120, 200, 150, 80, 70, 110, 130],
        type: 'bar'
    }]
};

封装成控件之后的调用代码:

private List<string> GetXAxisData()
{
    List<string> reslt = new List<string>
    {
        "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
    };
    return reslt;
}

private List<SeriesItem> GetSeriesData()
{
    List<SeriesItem> result = new List<SeriesItem>();
    SeriesItem item = new SeriesItem
    {
        type = "bar",
        data = new List<double>
        {
            120, 200, 150, 80, 70, 110, 130
        }
    };
    result.Add(item);
    return result;
}

private void FormMain_Load(object sender, EventArgs e)
{
    Option option = new Option
    {
        title = new Title
        {
            text= "ECharts 入门示例",
        },
        xAxis = new XAxis
        {
            type = "category",
            data = GetXAxisData()
        },
        yAxis = new YAxis
        {
            type = "value"
        },
        series = GetSeriesData()
    };
    echartsMain.InitialECharts(option);
}

图形显示如下:

图形显示

当然,咱们封装百度的 Echarts 并非心血来潮,学习任何技术的目的都要应用于实际,去体现技术的价值。

应用01
应用02

好了,今天就到这里吧!See You!


相关图文

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容