目前网上有很多插件来完成图表的制作,但是大部分都是以JS插件的显示实现的,虽然在winform也有MS-chart工具,它功能也是相当的强大,但是随着JS个快速发展,我们就希望能够将html引入winform程序中,在html中使用引用JS插件,通过html展现图表。
现在网上有很多的JS插件完成图表制作,如,JS Chart, highcharts等等,这里我们使用的是另外一个插件flotcharts.我们可以自行去相关官方网站学习在html中如何使用这些插件。
下来我们主要介绍的是在winform中内使用html以及二者之间的相互调用。一般在winform中有两种方法完成,一个是通过SWF,一个是通过webBrowser。在这里我们是通过webBrowser实现,它是winform中提供的一个工具。我们在可以直接将该工具拖到窗体上即可,如下:
上图中空白处就是webBrowser控件。上方的是5个winform按钮,我们在winform中实现按钮的单击事件,在实现的过程中调用html中JS函数,通过JS 方法来改变html的内容,最终呈现在form窗体中。
首先我们要在项目中添加一个html文件,在这个文件中引用相应的JS插件。我的html文件如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>AnalysisChart</title>
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="jquery.js"></script> //必须
<script language="javascript" type="text/javascript" src="jquery.flot.js"></script> //必须
<script language="javascript" type="text/javascript" src="jquery.flot.pie.js"></script>
<script language="javascript" type="text/javascript" src="jquery.flot.categories.js"></script>
<script language="javascript" type="text/javascript" src="jquery.flot.crosshair.js"></script>
<script type="text/javascript">
var timeout;
var opentimer = false;
//设置webBrowser的大小
function IniteContainer(wd, hg) {
// alert(wd+hg);
$("#placeholder").css("height", hg).css("width", wd);
}
function viewChart(dd2) {
var d1 = [];
for (var i = 0; i < 14; i += 0.5) {
d1.push([i, Math.sin(i)]);
}
// var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
var d2 = $.parseJSON(dd2); //jquery.parseJSON()方法解析json序列化数据
// alert(d2);
var d3 = [];
for (var i = 0; i < 14; i += 0.5) {
d3.push([i, Math.cos(i)]);
}
var d4 = [];
for (var i = 0; i < 14; i += 0.1) {
d4.push([i, Math.sqrt(i * 10)]);
}
var d5 = [];
for (var i = 0; i < 14; i += 0.5) {
d5.push([i, Math.sqrt(i)]);
}
var d6 = [];
for (var i = 0; i < 14; i += 0.5 + Math.random()) {
d6.push([i, Math.sqrt(2 * i + Math.sin(i) + 5)]);
}
$.plot("#placeholder", [{
data: d1, //经过json解析的数据是一个数组,可以直接赋值
lines: { show: true, fill: true }
}, {
data: d2,
bars: { show: true }
}, {
data: d3,
points: { show: true }
}, {
data: d4,
lines: { show: true }
}, {
data: d5,
lines: { show: true },
points: { show: true }
}, {
data: d6,
lines: { show: true, steps: true }
}]);
}
//饼状图
function Pie(pieData) {
// alert(pieData);
if (opentimer) {
clearTimeout(timeout);
opentimer = false;
}
var dataRec = $.parseJSON(pieData);
$("#placeholder").unbind();
$.plot("#placeholder", dataRec, {
series: {
pie: {
show: true,
radius: 1,
label: {
show: true,
radius: 3 / 4,
formatter: labelFormatter,
background: {
opacity: 0.5,
color: '#000'
}
}
}
},
legend: {
show: false
}
});
}
function labelFormatter(label, series) {
return "<div style='font-size:8pt; text-align:center; padding:2px; color:white;'>" + label + "<br/>" + Math.round(series.percent) + "%</div>";
}
//动态显示曲线
var upInterval = 40;
var plot;
function wave(internal) {
if (opentimer) {
clearTimeout(timeout);
opentimer = false;
}
var path = window.external.WaveData('hell world');
var data = $.parseJSON(path);
// alert(data);
upInterval = internal;
plot = $.plot("#placeholder", [data], { //注意:虽然经过解析后是一个数组对象,但是此处还是需要添加“[]”
series: {
shadowSize: 0
},
yaxes: {
min: 0,
max: 100
},
xaxes: {
show: false
}
});
update();
}
function update() {
var path = window.external.WaveData('hell world');
var data = $.parseJSON(path);
plot.setData([data]);
plot.draw();
opentimer = true;
timeout = setTimeout(update, upInterval);
}
// 柱状图
function Bar(barData) {
if (opentimer) {
clearTimeout(timeout);
opentimer = false;
}
// alert(barData);
var barRec = $.parseJSON(barData);
// alert(barRec);
$("#placeholder").unbind();
$.plot("#placeholder", [barRec], {
series:
{
bars:
{
show: true,
barWidth: 0.6,
align: "center"
}
},
xaxis:
{
mode: "categories", //必须引用jquery.flot.categories.js
tickLength: 0
}
});
}
//堆积图
function stacking(dataone, datatwo, datathr) {
if (opentimer) {
clearTimeout(timeout);
opentimer = false;
}
var dtOne = $.parseJSON(dataone);
var dttwo = $.parseJSON(datatwo);
var dtthr = $.parseJSON(datathr);
$("#placeholder").unbind();
$.plot("#placeholder", [dtOne, dttwo, dtthr], {
series: {
stack: true,
lines:
{
show: false,
fill: true,
steps: false
},
bars:
{
show: true,
barWidth: 0.6
}
}
});
}
//实时显示曲线值
function Tracking(dataSin, dataCos) {
var dtsin = $.parseJSON(dataSin);
var dtcos = $.parseJSON(dataCos);
plot = $.plot("#placeholder", [
{ data: dtsin, label: "sin(x) = -0.00" },
{ data: dtcos, label: "cos(x) = -0.00" }
], {
series: {
lines: {
show: true
}
},
crosshair: {
mode: "x"
},
grid: {
hoverable: true,
autoHighlight: false
},
yaxis: {
min: -1.2,
max: 1.2
}
});
var legends = $("#placeholder .legendLabel");
legends.each(function () {
// fix the widths so they don't jump around
$(this).css('width', $(this).width());
});
var updateLegendTimeout = null;
var latestPosition = null;
function updateLegend() {
updateLegendTimeout = null;
var pos = latestPosition;
var axes = plot.getAxes();
if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
return;
}
var i, j, dataset = plot.getData();
for (i = 0; i < dataset.length; ++i) {
var series = dataset[i];
// Find the nearest points, x-wise
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] > pos.x) {
break;
}
}
// Now Interpolate
var y,
p1 = series.data[j - 1],
p2 = series.data[j];
if (p1 == null) {
y = p2[1];
} else if (p2 == null) {
y = p1[1];
} else {
y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);
}
legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
}
}
$("#placeholder").bind("plothover", function (event, pos, item) {
latestPosition = pos;
if (!updateLegendTimeout) {
updateLegendTimeout = setTimeout(updateLegend, 50);
}
});
}
</script>
</head>
<body>
<div id="content">
<div class="demo-container">
<div id="placeholder" class="demo-placeholder" style="height: 100">
</div>
</div>
</div>
</body>
</html>
以上已经准备好了html文件,接下来看看怎么在winform中的单击事件中调用对应的js,以及从winform传递参数到JS和从JS传递参数到winform的函数中。
- 从winform到JS
在winform中调用JS,首先要在winform中添加如下语句,来使得winform可以和html相互交互。
在form中需要注意的是,我们在html中并没有设置图标绘制的DIV的大小,因此在我们需要在加载完成html之后调用JS函数,完成设置容器的大小。我们在winform中获取获取webBrowser的大小,然后传递到JS中,我们如可完成调用JS的函数呢?主要是通过webBrowser提供的调用,如下:
webBrowser.Document.InvokeScript("IniteContainer", new object[] { this.webBrowser.Width - 50, this.webBrowser.Height - 50 });
注:上述函数的添加是在webBrowser的
DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)事件中完成的。另外还需要添加如下的代码,使得JS可以访问form:
webBrowser.ObjectForScripting = this; //使得js可以访问form中的方法
其中,第一个参数是JS函数的名称,第二参数是传递的参数值,参数的类型是object,在JS中接收到这个参数,根据需要决定是否进行相应的转换或者解析等等。下面是InitContainer的实现:
function IniteContainer(wd, hg) {
// alert(wd+hg);
$("#placeholder").css("height", hg).css("width", wd);
}
使用同样的方法,将在winform中获取的数据转换为JSON格式,当做JS的参数传递给JS。这里的数据可以是我们从数据库中获取的,也可以是XML文件中的数据,也可以是其他形式的数据,最终封装序列化为JSON格式的数据,这里我使用的是Newtonsoft工具进行JSON格式数据的转换,在html中通过JQuery提供的parseJSON方法来解析。在form中的数据必须是数组形式的,这是因为FLot图表插件要求的 数据格式。下面的是form的一个按钮的单击事件:
private void btPie_Click(object sender, EventArgs e)
{
List<FlotData> piedata = new List<FlotData>();
piedata.Add(new FlotData() { label = "serials 1", data = new object[] { new object[] { 1, 10 } } });
piedata.Add(new FlotData() { label = "serials 2", data = new object[] { new object[] { 1, 20 } } });
piedata.Add(new FlotData() { label = "serials 3", data = new object[] { new object[] { 1, 15 } } });
piedata.Add(new FlotData() { label = "serials 4", data = new object[] { new object[] { 1, 50 } } });
webBrowser.Document.InvokeScript("Pie", new object[] { JsonConvert.SerializeObject(piedata) });
}
下面是另外一个实现:
private void btBar_Click(object sender, EventArgs e)
{
List<object[]> lsBar = new List<object[]>();
lsBar.Add(new object[] { "January",10});
lsBar.Add(new object[] { "February", 10 });
lsBar.Add(new object[] { "March", 10 });
lsBar.Add(new object[] { "April", 10 });
lsBar.Add(new object[] { "May", 17 });
lsBar.Add(new object[] { "Jun", 9 });
string tt = JsonConvert.SerializeObject(lsBar);
webBrowser.Document.InvokeScript("Bar", new object[] { tt });
}
- 从JS到winform
在JS中调用form中函数,目前还没有研究怎么在JS中调用form中的事件,这里只是调用form自定义的函数。我们想实现下面这样的功能:当在form中单击一个按钮产生click事件后,调用html中的JS完成实时动态的显示,类似于医院的心电图等,但是这个数据是在winform中产生的。也就是说我们要在html启动一个定时器,在一定间隔后调用form中函数获取数据,然后更新JS绘图容器的数据源重新进行绘制。
在JS中是怎么调用winform中的函数呢?主要是通过JS提供的对象window.external.**实现的其中“**”就是我们在winform总定义的函数,也包括函数,如下就是在html的JS调用winform的自定义函数:
下面是在form中定义的函数的代码:
public string WaveData(string mes)
{
int divWidth = this.webBrowser.Width - 10;
int divHeight = this.webBrowser.Height - 10;
List<object[]> lsData = new List<object[]>();
Random rd=new Random();
for (int i = 0; i < divWidth;i++ )
{
lsData.Add(new object[] { i, rd.Next(0, divHeight) });
}
return JsonConvert.SerializeObject(lsData);
}
关于在form中如何经行实时显示的,可以到官方网站看相关的例子。
下面是程序运行的截图: