背景
Echarts 是百度推出的一个使用 JavaScript 实现的开源可视化库。 该库提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
既然 Echarts
提供了丰富的图形,所以咱们有必要把它封装起来,以便让其支持 Windows 窗体应用程序。
技术分析
整体的技术方案就是做一个自定义控件,该控件中包含 WebBrowser 浏览器控件,通过该浏览器控件显示指定位置的网页。就像咱们直接通过 Web 浏览器网页一样。具体的步骤如下:
首先,创建一个在 Windows 窗体应用程序中使用的控件项目 LSGO.Core.ECharts
。
其次,在该控件项目的设计器中,拖入一个 WebBrowser 控件,并设置其 Dock
属性为 Fill
,即让 WebBrowser 充满整个容器。
接着,写一个 InitialECharts
方法,加载指定目录的网页.\assets\echarts.html
,让该网页在 WebBrowser 中打开。
当该网页加载完成后,触发 WebBrowser 的 WebBrowserDocumentCompletedEventHandler
事件,在该事件注册的方法中调用该网页中用 JS 写的 showChart
方法,则在该网页中显示图形。
当窗体控件的尺寸发生变化后,触发 WebBrowser 的 SizeChanged
事件,在该事件注册的方法中调用该网页中用 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.html 在 WebBrowser 内加载完成之后执行的方法</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>对 ECharts 中 yAxis 结构的封装</u>。 public class YAxis { /// <summary> /// 坐标轴类型 /// </summary> public string type { get; set; } = "value"; } <u>对 ECharts 中 series 集合元素的封装</u>。 public class SeriesItem { /// <summary> /// 每个系列通过 type 决定自己的图表类型 /// </summary> public string type { get; set; } /// <summary> /// 系列中的数据内容数组 /// </summary> public List<int> data { get; set; } } <u>对 ECharts 中 option 结构的封装</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 并非心血来潮,学习任何技术的目的都要应用于实际,去体现技术的价值。