用XAML来为C1Chart 创建背景高亮的”Alarm Zone”区域

发布时间:2013/11/12 00:11 发布者:Roger

返回博客中心

本文在C1Chart实现一个自定义的序列,它用来背景高亮呈现颜色带---Alarm Zone区域。  用途适用于需要用背景颜色区分的场景,本文适用于WPF、SilverLight,且代码可用于Windows Store和Windows Phone apps上。

什么是Alarm Zone区域?

用数据绘制的Alarm Zone区域定义: 放在后面的一系列带或形状,同时,可作为前面的图表背景。 Alarm Zone也被称为更简单的彩色条纹或地区。

Chart_AlarmZone5

 

自定义的实现

虽然在XAML C1Chart的并没有内建支持Alarm Zone, 但完成同样的功能是有办法的:每个XAML版本的C1Chart支持多边形的图表类型,它们允许你在一个单一的场景下结合任何数量的图表类型。 如,可以很容易地创建一个Alarm Zone,通过直接添加一个你喜欢PolygonFilled图表。

 

  1: <c1:C1Chart Name="c1Chart1"
  2:             ChartType="XYPlot"
  3:             Palette="Standard">
  4:     <c1:C1Chart.Data>
  5:         <c1:ChartData>
  6:             <c1:XYDataSeries ChartType="PolygonFilled"
  7:                              XValues="3 3 7 6 5"
  8:                              Values="8 17 17 15 8"/>
  9:             <c1:XYDataSeries Label="S1"
 10:                              Values="5 12 18 14 6 7 21 15 12 19"
 11:                              XValues="5 4 3 1 2 6 7 9 8 5" />
 12:         </c1:ChartData>
 13:     </c1:C1Chart.Data>
 14:     <c1:C1ChartLegend />
 15: </c1:C1Chart>

 

Chart_PolygonSeries

 

问题是,你必须在你的脑海绘制出的X,Y坐标的每个点。另外,如果你想要一个区域扩展到“无限”,你必须知道的轴的最大值是什么。  为此,我们可以按写一个定制的的类,AlarmZone,它继承自XYDataSeries类。

请看看下面的AlarmZone.cs的WPF定义。在这篇博客文章的底部,可下载完整的demo。

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using System.Windows.Media;
  6: using System.Windows.Controls;
  7: using C1.WPF.C1Chart;
  8:  
  9: namespace Chart_AlarmZones_WPF_CS
 10: {
 11:     public class AlarmZone : XYDataSeries
 12:     {
 13:         #region "constructor"
 14:  
 15:         public AlarmZone()
 16:         {
 17:             this.LayoutUpdated += new EventHandler(AlarmZone_LayoutUpdated);
 18:             this.ChartType = C1.WPF.C1Chart.ChartType.PolygonFilled;
 19:             this.XValues = new DoubleCollection();
 20:             this.Values = new DoubleCollection();
 21:             UpdateLegend();
 22:             Update();
 23:         }
 24:  
 25:         #endregion
 26:  
 27:         #region "members"
 28:  
 29:         private C1Chart _chart;
 30:         public C1Chart Chart
 31:         {
 32:             get { return _chart; }
 33:             set { _chart = value; }
 34:         }
 35:  
 36:         private double? _lowerExtent = null;
 37:         public double? LowerExtent
 38:         {
 39:             get { return _lowerExtent; }
 40:             set
 41:             {
 42:                 _lowerExtent = value;
 43:                 Update();
 44:             }
 45:         }
 46:  
 47:         private double? _upperExtent = null;
 48:         public double? UpperExtent
 49:         {
 50:             get { return _upperExtent; }
 51:             set
 52:             {
 53:                 _upperExtent = value;
 54:                 Update();
 55:             }
 56:         }
 57:  
 58:         private double? _near = null;
 59:         public double? Near
 60:         {
 61:             get { return _near; }
 62:             set
 63:             {
 64:                 _near = value;
 65:                 Update();
 66:             }
 67:         }
 68:  
 69:         private double? _far = null;
 70:         public double? Far
 71:         {
 72:             get { return _far; }
 73:             set
 74:             {
 75:                 _far = value;
 76:                 Update();
 77:             }
 78:         }
 79:  
 80:         private bool _showInLegend = false;
 81:         public bool ShowInLegend
 82:         {
 83:             get { return _showInLegend; }
 84:             set
 85:             {
 86:                 _showInLegend = value;
 87:                 UpdateLegend();
 88:             }
 89:         }
 90:  
 91:         #endregion
 92:  
 93:         #region "implementation"
 94:  
 95:         public void Update()
 96:         {
 97:             if (_near != null && _far != null)
 98:             {
 99:                 this.XValues.Clear();
100:                 this.XValues.Add((double)_near);
101:                 this.XValues.Add((double)_far);
102:                 this.XValues.Add((double)_far);
103:                 this.XValues.Add((double)_near);
104:             }
105:  
106:             if(_lowerExtent != null && _upperExtent != null)
107:             {
108:                 this.Values.Clear();
109:                 this.Values.Add((double)_lowerExtent);
110:                 this.Values.Add((double)_lowerExtent);
111:                 this.Values.Add((double)_upperExtent);
112:                 this.Values.Add((double)_upperExtent);
113:             }
114:         }
115:  
116:         public void UpdateLegend()
117:         {
118:             if (_showInLegend)
119:             {
120:                 this.Display = SeriesDisplay.SkipNaN;
121:             }
122:             else
123:             {
124:                 this.Display = SeriesDisplay.HideLegend;
125:             }
126:         }
127:  
128:         private void chart_LayoutUpdated(object sender, EventArgs e)
129:         {
130:             if (this.Chart != null)
131:             {
132:                 if (this.Chart.View != null)
133:                 {
134:                     // if extent is null, set to axis bounds
135:                     if (_near == null)
136:                     {
137:                         _near = Chart.View.AxisX.ActualMin;
138:                         Update();
139:                     }
140:                     if (_far == null)
141:                     {
142:                         _far = Chart.View.AxisX.ActualMax;
143:                         Update();
144:                     }
145:                     if (_upperExtent == null)
146:                     {
147:                         _upperExtent = Chart.View.AxisY.ActualMax;
148:                         Update();
149:                     }
150:                     if (_lowerExtent == null)
151:                     {
152:                         _lowerExtent = Chart.View.AxisY.ActualMin;
153:                         Update();
154:                     }
155:  
156:                 }
157:             }
158:         }
159:  
160:         // obtains the parent chart control so we can later get axis bounds
161:         void AlarmZone_LayoutUpdated(object sender, EventArgs e)
162:         {
163:             if (this.Parent != null && this.Chart == null)
164:             {
165:                 Canvas c = this.Parent as Canvas;
166:                 if (c != null)
167:                 {
168:                     Canvas cv = c.Parent as Canvas;
169:                     if (cv != null)
170:                     {
171:                         Border b = cv.Parent as Border;
172:                         if (b != null)
173:                         {
174:                             Grid g = b.Parent as Grid;
175:                             if (g != null)
176:                             {
177:                                 C1Chart chart = g.Parent as C1Chart;
178:                                 if (chart != null)
179:                                 {
180:                                     this.Chart = chart;
181:                                     this.Chart.LayoutUpdated += chart_LayoutUpdated;
182:                                 }
183:                             }
184:                         }
185:  
186:                     }
187:                 }
188:  
189:             }
190:         }
191:  
192:         #endregion
193:  
194:     }
195: }

 

AlarmZone类公开4个关键的属性:Near, Far, UpperExtent,LowerExtent。 这些属性换算成多边形系列的数据点。 正如你可以看到下图中,这4个属性,形成我们的Alarm Zone的边界。

Chart_AlarmZone2

我们的区域的颜色,是由从XYDataSeries继承的ConnectionFill属性设置的。我们可以复用很多基类的东西,如外观和图例属性。

下列代码添加到您的项目的AlarmZone类,建立本地XML命名空间,并添加到您的XAML文件。

  1: xmlns:local="clr-namespace:<Project Namespace>"

 

现在你可以增加一些AlarmZones,如数据序列将它们添加到您的图表。 创建一个红块,像在上面的图表,你将所有四个的属性,Near, Far, LowerExtent, UpperExtent的,如在下面的设置。

  1: <c1:C1Chart Name="c1Chart1"
  2:             ChartType="XYPlot"
  3:             Palette="Standard">
  4:     <c1:C1Chart.Data>
  5:         <c1:ChartData>
  6:             <local:AlarmZone Near="3"
  7:                              Far="7"
  8:                              LowerExtent="10"
  9:                              UpperExtent="18"
 10:                              ShowInLegend="True"
 11:                              Label="Alarm"
 12:                              ConnectionFill="#79FF0000"/>
 13:             <c1:XYDataSeries Label="S1"
 14:                              Values="5 12 18 14 6 7 21 15 12 19"
 15:                              XValues="5 4 3 1 2 6 7 9 8 5" />
 16:         </c1:ChartData>
 17:     </c1:C1Chart.Data>
 18:     <c1:C1ChartLegend />
 19: </c1:C1Chart>

 

我们还增加了ShowInLegend的属性,可让您在图例标记区域。此属性只是显示属性XYDataSeries序列在图例中显示。

这个自定义AlarmZone实现的是,它也可以扩展区轴的界限,不管实际的图表中的数据多少。如,你已经注意到,Near, Far, LowerExtent, UpperExtent的属性默认都为空值(如果没有明确设置这些值), 区域将延伸到在该方向的轴边界。例如,要创建一些垂直条纹延伸至底部的Y轴的顶端,你可以只是简单地为每个区域设置了Near, Far属性。

  1: <c1:C1Chart Name="c1Chart1" ChartType="XYPlot">
  2:     <c1:C1Chart.Data>
  3:         <c1:ChartData>
  4:             <local:AlarmZone Far="3"
  5:                              ConnectionFill="#96FF0000"
  6:                              ShowInLegend="True"
  7:                              Label="Poor"/>
  8:             <local:AlarmZone Near="3"
  9:                              Far="6"
 10:                              ConnectionFill="#96FFFF00"
 11:                              ShowInLegend="True"
 12:                              Label="Medium"/>
 13:             <local:AlarmZone Near="6"
 14:                              ConnectionFill="#96008000"
 15:                              ShowInLegend="True"
 16:                              Label="Good"/>
 17:             <c1:XYDataSeries Label="S1"
 18:                              Values="5 12 18 14 6 7 21 15 12 19"
 19:                              XValues="5 4 3 1 2 6 7 9 8 5" />
 20:         </c1:ChartData>
 21:     </c1:C1Chart.Data>
 22:     <c1:C1ChartLegend />
 23: </c1:C1Chart>

 

Chart_AlarmZone1

如不设置UpperExtent LowerExtent属性,区域将延长。请注意,我们甚至没有设置近属性为红色区域,绿色区域或Far属性,因为这些将沿x轴延伸到“无限”。

如果任何这些值是null,Alarm Zone从父图表得到实际轴的最小值或最大值,并使用该值。(AlarmZone的初始化,可获得父图表对象。)

  1: if (_upperExtent == null)
  2: {
  3:     _upperExtent = Chart.View.AxisY.ActualMax;
  4:     Update();
  5: }

 

这是其中的代码之间稍有不同,因为的WPF和其他XAML平台的可视化树层次结构是一个有点不同。因此,请确保使用正确版本的AlarmZone。

 

结论

此示例演示,在C1Chart--WPF、SilverLight下,我们如何能够按使用现有的功能扩展和添加这些缺少的功能。通过使用多边形数据为基础,我们可以创建我们自己的'Alarm Zone定义, 可以添加到任何现有图表解决方案系列。

 

 

 

 


关于葡萄城

赋能开发者!葡萄城是专业的集开发工具、商业智能解决方案、低代码开发平台于一身的软件和服务提供商,为超过 75% 的全球财富 500 强企业提供服务。葡萄城专注控件软件领域30年,希望通过模块化的开发控件、灵活的低代码应用开发平台等一系列开发工具、解决方案和服务,帮助开发者快速响应复杂多变的业务需求,最大程度地发挥开发者的才智和潜能,让开发者的 IT 人生更从容更美好。

了解详情,请访问葡萄城官网