开发环境: VS2003 + Windows XP SP2

测试环境: Windows XP SP2

Demo截图

构建仪表、图表控件的绘制框架_控件

 

编写图形相关的控件需要完成两部分:1 绘制;2 与窗口类(泛指)集成使之成为控件。本文重点在于“绘制”部分,提出一个较灵活的框架。用VC的GDI+实现框架,并在Demo中简单封装成圆表和直表控件。图一是要实现的目标(圆表、直表等仪表,指示灯,图表,旋钮,滑块等),由这些目标,经需求分析后,设计出框架。

构建仪表、图表控件的绘制框架_控件_02

 

图一

框架的建立

“如果说我比别人看得更远些,那是因为我站在了巨人的肩上”。.Net上开源图表控件比较多,在这里我们分析一下MS Graph Demo。图二 是其绘制Pie图表的类图。

构建仪表、图表控件的绘制框架_ide_03

 

图二 MS Graph Demo绘制Pie图表的结构

PieSlice表示Pie中的单个扇形,_value用来计算百分比,_color是此扇形的填充颜色。

PieSliceCollection是PieSlice集合。

PieGraph类内置一个PieSliceCollection对象,类中其它的数据是描述Pie整体的数据(宽高,边框宽度,背景颜色……)。

PieGraphRenderer类通过 DrawGraph函数绘制Pie图,类内置一个PieGraph对象。

此结构有诸如数据结构和算法的分离等的优点,但也有它的局限性。1 它的框架是建立在功能分类上的,如,Pie图、Bar图。PieSlice中不仅有功能数据_value还有外观数据_color。假如不再填充纯色,而是填充图像,那么就必须修改PieSlice,增加_image:Image属性; PieGraphRenderer也需相应的修改。假设将上述修改扩展到Pie图表以外的其它类型的图表,如:Bar图表、A图表、B图表,那么BarSlice、ASlice、BSlice,及三者相应的Renderer类也必须修改,工作量多且重复。2 在应用过程中,当需要把Pie图对象转换成Bar图对象时,MS Graph Demo的现有框架便不能实现。

由此可见,MS Graph Demo的框架不够灵活,原因在于功能数据和外观数据没有分离。

图三是我所设计的框架,用一句话概述:由YFillBase填充形状(YShapeBase),逻辑(YPaintBase)负责把形状组合起来。

构建仪表、图表控件的绘制框架_控件

 

图三

框架由三个基础类YPaintBase,YShapeBase和YFillBase组成。其中YFillBase是填充基础类(简称“填充”),它负责对象颜色、图像的填充,边框等。YShapeBase是基本图形基础类(简称“形状”),由此类派生出简单的基本图形,如:圆,三角,五角星,特殊指针样式……。YPaintBase是逻辑组合基础类(简称“逻辑”),由YPaintBase把YShapeBase和YFillBase进行组合,构建出复杂图形,而复杂图形可由YPaintBase的派生类再次组合。

图四演示了应用框架构建仪表控件的背景和指针。

构建仪表、图表控件的绘制框架_ide_05

图四

由YShapeBase派生出,将YPointer对象的pShape指向YRectangle对象,就可以得到矩形指针。同理将YBackground对象的pShape指向YRectangle对象,就得到了矩形背景(图五)。

构建仪表、图表控件的绘制框架_控件_06

 

图五

如果需要升级,添加新的形状如三角形YTriangle(图六),也就相应得到了三角型指针和三角形背景。

构建仪表、图表控件的绘制框架_数据_07

图六

同样由YFillBase派生一种特殊的图片填充YFillImage(图七),其它地方的代码不用修改,就可以得到用这种填充的任意形状指针和背景。

构建仪表、图表控件的绘制框架_控件_08

图七

在实际项目中,类似指针,背景的元素很多,应用此框架可以使编码减少,功能倍增,易于升级、维护。

构建仪表、图表控件的绘制框架_控件_09

图九

下图演示应用框架构建类似MS Graph Demo的Pie图表和Bar图表,是不是很容易加入填充图像和在Pie图Bar图间转换^_^。

构建仪表、图表控件的绘制框架_ide_10

图十

框架的在绘制仪表控件中的应用

1、YFillBase和YShapeBase的配合使用:

构建仪表、图表控件的绘制框架_数据_11

 


​1.​​​ ​​YFillGradient fill;​


​2.​​  


​3.​​​ ​​YEllipse shape;​


​4.​​  


​5.​​​ ​​shape.SetFill(&fill);​


​6.​​  


​7.​​​ ​​shape.Draw(g);​

2、透明仪表罩的绘制

表罩由2个YRange和1个YEllipes,YRange和YEllipes用特定填充。

构建仪表、图表控件的绘制框架_ide_12


​01.​​​ ​​FillGradient glassFill;​


​02.​​​ ​​glassFill.Border.Hide();​


​03.​​​ ​​glassFill.Background.Show();​


​04.​​​ ​​glassFill.Background.FillColor.SetColor(20,240,240,240);​


​05.​​​ ​​glassFill.Background.FillEndColor.SetColor(180,120,120,120);​


​06.​​​ ​​glassFill.Background.SetGradientType(YGradientType_ForwardDiagonal);​


​07.​​​ ​​YEllipse glassBK;​


​08.​​​ ​​glassBK.SetFill(&glassFill);​


​09.​​​ ​​glassBK.Draw(g);​


​10.​​  


​11.​​​ ​​//反光​


​12.​​​ ​​YFillGradient lightFill;​


​13.​​​ ​​lightFill.Border.Hide();​


​14.​​​ ​​lightFill.Background.FillColor.SetColor(210,255,255,255);​


​15.​​​ ​​lightFill.Background.FillEndColor.SetColor(210,255,255,255);​


​16.​​​ ​​YRange range;​


​17.​​​ ​​range.SetStartWidth(width);​


​18.​​​ ​​range.SetEndWidth(width);​


​19.​​​ ​​range.SetFill(&lightFill);​


​20.​​​ ​​range.SetPlacement(Inside);​


​21.​​​ ​​range.SetSweepAngle(24);​


​22.​​​ ​​range.SetStartAngle(110);​


​23.​​​ ​​range.Draw(g);​


​24.​​​ ​​range.SetStartAngle(136);​


​25.​​​ ​​range.Draw(g);​

3、刻度的绘制

由于框架的原因,可以非常方便的更改刻度的形状和填充。(加入一个最基本的刻度)

构建仪表、图表控件的绘制框架_控件_13

 

YScaleTextCircular是环绕文字。

YScaleCircular是环形刻度。

YScaleXY是线型刻度。

构建仪表、图表控件的绘制框架_ide_05

 

4、圆表,直表与刻度对应的文字

针对圆表的刻度文字的种类和位置定义。

构建仪表、图表控件的绘制框架_ide_15

 

图 环绕排列文字的四种方式

环绕文字与环形刻度一般同时出现,这就要求环绕文字必须遵循某种规则,使文字和刻度不重叠上,且很自然。

以下两图展示了这种规则的定义。

构建仪表、图表控件的绘制框架_控件_16

 

“向下”文字位置的定义

构建仪表、图表控件的绘制框架_ide_17

 

图 "向心"文字位置定义 

构建仪表、图表控件的绘制框架_数据_18


​01.​​​ ​​YScaleTextCircular ScaletextCircular;​


​02.​​​ ​​YTextHelper* pScaletextHelper = NULL;​


​03.​​​ ​​pScaletextHelper = ​​​​new​​​ ​​YTextHelper;​


​04.​​​ ​​pScaletextHelper->SetSize(10.f);​


​05.​​​ ​​pScaletextHelper->FontColor.SetColor(128,128,128);​


​06.​​​ ​​ScaletextCircular.AddText(pScaletextHelper);​


​07.​​​ ​​pScaletextHelper = ​​​​new​​​ ​​YTextHelper;​


​08.​​​ ​​pScaletextHelper->SetBold(TRUE);​


​09.​​​ ​​pScaletextHelper->SetSize(12.f);​


​10.​​​ ​​pScaletextHelper->SetFontName(​​​​"黑体"​​​​);​


​11.​​​ ​​pScaletextHelper->FontColor.SetColor(255,44,44);​


​12.​​​ ​​ScaletextCircular.AddText(pScaletextHelper);​


​13.​​​ ​​pScaletextHelper = ​​​​new​​​ ​​YTextHelper;​


​14.​​​ ​​pScaletextHelper->FontColor.SetColor(51,51,255);​


​15.​​​ ​​ScaletextCircular.AddText(pScaletextHelper);​


​16.​​​ ​​ScaletextCircular.SetType(1);​


​17.​​​ ​​ScaletextCircular.AddText(​​​​"你"​​​​);​


​18.​​​ ​​ScaletextCircular.AddText(​​​​"有没有"​​​​);​


​19.​​​ ​​ScaletextCircular.AddText(​​​​"想过"​​​​);​


​20.​​​ ​​ScaletextCircular.AddText(​​​​"罐头"​​​​);​


​21.​​​ ​​ScaletextCircular.AddText(​​​​"的"​​​​);​


​22.​​​ ​​ScaletextCircular.AddText(​​​​"感受"​​​​);​


​23.​​​ ​​ScaletextCircular.AddText(​​​​"?"​​​​);​


​24.​​​ ​​ScaletextCircular.SetDefault(FALSE);​


​25.​​​ ​​ScaletextCircular.SetOrigin(point_this.X,point_this.Y);​


​26.​​​ ​​ScaletextCircular.SetRadius((​​​​int​​​​)(cs_this.Width*0.3f));​


​27.​​​ ​​ScaletextCircular.SetPlacement(Inside);​


​28.​​​ ​​ScaletextCircular.Draw(g);​


​29.​​​ ​​pScaletextHelper = NULL;​

绘制线型文字

构建仪表、图表控件的绘制框架_数据_19


​01.​​​ ​​水平文字​


​02.​​​ ​​//     ∧​


​03.​​​ ​​//     │​


​04.​​​ ​​//  Outside │  Inside​


​05.​​​ ​​//     │​


​06.​​​ ​​//     │ Inside​


​07.​​​ ​​//     └───────>​


​08.​​​ ​​//        Outside ​


​09.​​​ ​​//​


​10.​​​ ​​YScaleTextXY m_ScaleText;​


​11.​​​ ​​YTextHelper* pScaletext_helper = NULL;​


​12.​​​ ​​pScaletext_helper = ​​​​new​​​ ​​YTextHelper;​


​13.​​​ ​​pScaletext_helper->SetSize(8.5f);​


​14.​​​ ​​pScaletext_helper->SetAngle(-30);​


​15.​​​ ​​pScaletext_helper->SetHorizontal(StringAlignmentFar);​


​16.​​​ ​​m_ScaleText.AddText(pScaletext_helper);​


​17.​​​ ​​m_ScaleText.SetMin(0);​


​18.​​​ ​​m_ScaleText.SetMax(600);​


​19.​​​ ​​m_ScaleText.SetBoolY(FALSE);​


​20.​​​ ​​m_ScaleText.SetOrientation(TRUE);​


​21.​​​ ​​m_ScaleText.SetOrigin(30.f,50.f);​


​22.​​​ ​​m_ScaleText.SetLength(380.f);​


​23.​​​ ​​m_ScaleText.Draw(g);​


​24.​​​ ​​m_ScaleText.AddText(​​​​"一月"​​​​);​


​25.​​​ ​​m_ScaleText.AddText(​​​​"二月"​​​​);​


​26.​​​ ​​m_ScaleText.AddText(​​​​"三月"​​​​);​


​27.​​​ ​​m_ScaleText.AddText(​​​​"四月"​​​​);​


​28.​​​ ​​m_ScaleText.AddText(​​​​"五月"​​​​);​


​29.​​​ ​​m_ScaleText.AddText(​​​​"六月"​​​​);​


​30.​​​ ​​m_ScaleText.AddText(​​​​"七月"​​​​);​


​31.​​​ ​​m_ScaleText.SetDefault(FALSE);​


​32.​​​ ​​m_ScaleText.SetOrigin(30.f,80.f);​


​33.​​​ ​​m_ScaleText.Draw(g);​


​34.​​​ ​​pScaletext_helper = NULL;​