1.引言
由于工作原因,需要对数据进行图表展示,.NET默认的控件库或者DotNetBar的控件库又不能满足需求。去网上找到了这个开源的ZedGraph绘制2D图表的程序集,之所以选择这个ZedGraph程序集,一来它能够满足业务需求,二来它网上相关资料很多,三来它是开源代码。本篇中主要是给大家介绍几个我在开发过程中遇到的一些稍微棘手的问题,或者说是在网上资源较少的问题。在面对这些问题时,对于刚刚接触这个*.dll的我来说还是花了一些时间的,故而总结成篇。
2.相关资料
相信认真参研上面的几篇博文,尤其是第一个之后,你对ZedGraph的入门使用会了解许多。如果你想更多的了解ZedGraph,可以Down下源码,仔细研究。ZedGraph的相对来说还是比较简单的,虽然说界面不甚美观,但其功能还算完善,我是做GIS二次开发的,它的几个基本功能:图表漫游、放大、缩小、上一视图等与GIS软件不谋而合。
3.ZedGraph节点Symbol问题
ZedGraph中有个枚举列举了节点的所有常见Symbol(如下),但是有些时候特殊问题特殊处理,我们需要用不同的symbol来表示节点,怎么办?
public enum SymbolType { Square, Diamond, Triangle, Circle, XCross, Plus, Star, TriangleDown, HDash, VDash, UserDefined,//用户自定义 Default, None }
参考如下Demo:
4.ZedGraph上下文菜单问题
在开发过程中,如果需要实现图表的上下文菜单,需要将ZedGraphControl的IsShowContextMenu属性设置为true才可显示,当然你也可以实现自己的自定义上下文菜单。这里需要注意的是,有些你下载的dll可能上下菜单是中文,而有些却是英文并且有可能"缺损"了那么几项,造成这个问题的主要是由于ZedGraph开源,网友很有可能是改完源码之后上传的,这就造成你Down下来的dll不尽相同。现在的问题是:怎么修改ZedGraph的上下文菜单,英文<->中文?隐藏某一选项?代码如下:
private void zGCDateChart_ContextMenuBuilder(ZedGraphControl sender, ContextMenuStrip menuStrip, Point mousePt, ZedGraphControl.ContextMenuObjectState objState) { try { //每次循环只能遍历一个键 foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "copy") { item.Text = "复制"; item.Visible = true; break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "save_as") { item.Text = "另存图表"; item.Visible = true; break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "show_val") { item.Text = "显示XY值"; item.Visible = true; break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "unzoom") { item.Text = "上一视图"; item.Visible = true; break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "undo_all") { item.Text = "还原缩放/移动"; item.Visible = true; break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "print") { menuStrip.Items.Remove(item); item.Visible = false; //不显示 break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "page_setup") { menuStrip.Items.Remove(item);//移除菜单项 item.Visible = false; //不显示 break; } } foreach (ToolStripMenuItem item in menuStrip.Items) { if ((string)item.Tag == "set_default") { menuStrip.Items.Remove(item);//移除菜单项 item.Visible = false; //不显示 break; } } } catch (System.Exception ex) { MessageBox.Show("初始化右键菜单错误" + ex.ToString()); } }
这里需要注意几点:1.事件为ContextMenuBuilder2.每次foreach循环只能遍历一个键,因为你改变了枚举项。
5.添加文本问题
有些时候需要你在图表是添加一些文本,比如说:
TextObj text_obs = new TextObj("缩放:鼠标滚轮\n移动:鼠标中键\n菜单:鼠标右键", 0.899f, 0.001f, CoordType.ChartFraction, AlignH.Center, AlignV.Bottom); text_obs.FontSpec.StringAlignment = StringAlignment.Center; text_obs.FontSpec.Fill = new Fill(Color.LightBlue); text_obs.FontSpec.FontColor = Color.Black; myPane_obs.GraphObjList.Add(text_obs);
double xVal = Math.Round(list[i].X, 2); double yVal = Math.Round(list[i].Y, 2); string lab = string.Format("高潮位\r\n时间:{0}\r\n潮位:{1}", GetStrTime(xVal), yVal); TextObj text = new TextObj(lab, (float)xVal, (float)yVal * 1.01);//设置一个偏移量 text.FontSpec.StringAlignment = StringAlignment.Near; text.Location.CoordinateFrame = CoordType.AxisXYScale;//很重要,设置参考系,或者说参考坐标 text.FontSpec.Size = 10; text.FontSpec.FontColor = Color.Blue; text.Location.AlignH = AlignH.Center; text.Location.AlignV = AlignV.Bottom; text.FontSpec.Border.IsVisible = false; text.FontSpec.Angle = 0; text.FontSpec.Fill.IsVisible = false; myPane.GraphObjList.Add(text);
6.节点显示悬停提示和悬停提示闪烁问题
当你需要鼠标悬停在图表节点上提示节点的位置值,或者其他的一些内容时,你需要首先将ZedGraphControl的IsShowPointValues属性设置为true,然后关联事件PointValueEvent。
private string zGCDateChart_PointValueEvent(ZedGraphControl sender, GraphPane pane, CurveItem curve, int iPt) { PointPair pt = curve[iPt]; return "时间:" + pt.X.ToString() + " \n潮位:" + pt.Y.ToString(); }
如果这样就把问题解决了,那固然好,可我在做的过程中发现的一个问题,或者说是不尽如人意的地方:当鼠标悬停在节点上时,内容是显示出来了,没有问题,但是悬停提示框就是一直在闪烁,这个问题困扰了我很久,解决方法如下:找来ZedGraph的源码,在ZedGraphControl.Event.cs中找到了下面这块代码:
private Point HandlePointValues( Point mousePt ) { int iPt; GraphPane pane; object nearestObj; using ( Graphics g = this.CreateGraphics() ) { if ( _masterPane.FindNearestPaneObject( mousePt,g, out pane, out nearestObj, out iPt ) ) { if ( nearestObj is CurveItem && iPt >= 0 ) { //................ //code to show the tooltip } else this.pointToolTip.Active = false; } else this.pointToolTip.Active = false; } return mousePt; }
分析后发现,原来这段代码不论鼠标移动的范围大小,甚至乎不论鼠标是否移动,都要刷新一次Tooltip,所以才会导致CPU爆满。解决方案并不复杂,包括三个步骤:1、创建私有变量lastObj用以保存上次为之显示Tooltip的对象;2、显示Tooltip前判断对象是否就是lastObj;3、将新获得的对象保存到lastObj中。修改后的代码结构如下:
private object lastObj; private Point HandlePointValues( Point mousePt ) { int iPt; GraphPane pane; object nearestObj; using ( Graphics g = this.CreateGraphics() ) { if ( _masterPane.FindNearestPaneObject( mousePtg, out pane, out nearestObj, out iPt ) ) { if (nearestObj is CurveItem && iPt >= 0 && !object.Equals(nearestObj, lastObj)) { //................ //code to show the tooltip } //else // this.pointToolTip.Active = false; } else this.pointToolTip.Active = false; } lastObj = nearestObj; return mousePt; }
7.X轴刻度重写(X轴刻度循环问题)
简单的X、Y轴刻度刻度形式可以通过设置以下属性来实现:
myPane_obs.XAxis.Scale.Format = ""; myPane_obs.YAxis.Scale.Format = "";
本次开发中碰到的是关于X轴循环问题,X轴是一根绝对的数轴,但是现实的数值代表时间,并且时间从0—23时不断重复循环,也就是说不会出现负数和大于23的整数,具体解决方案如下:
myPane_obs.XAxis.Scale.Min = 0; myPane_obs.XAxis.Scale.Max = 24; myPane_obs.XAxis.Scale.MajorStep = 4; myPane_obs.XAxis.Scale.MinorStep = 1;
////// X轴刻度重定义 /// /// /// /// /// ///string XAxis_ScaleFormatEvent(GraphPane pane, Axis axis, double val, int index) { if (val >= 0 && val < 24) return val.ToString(); else if (val >= 24) return (val % 24).ToString(); else return (24-Math.Abs(val % 24)).ToString(); }