的)时填充。 m_pLineSeries,m_pPointsSeries和m_ChartCtrl是CMyClass类的成员变量。
void CMyClass::Init() {
.... // SNIP: Creation of the axes in the chart. This MUST be done before. m_pLineSeries = m_ChartCtrl.CreateLineSerie(); m_pPointsSeries = m_ChartCtrl.CreatePointsSerie();
double YValues[10]; for (inti=0;i<10;i++)
XValues[i] = YValues[i] = i;
m_pLineSerie->SetPoints(XValues,YValues,10); }
void CMyClass::OnDataReceived(double X, double Y) {
m_pPointsSeries->AddPoint(X, Y); }
所有系列类继承自同一抽象基类:CChartSerie。该类处理所有系列通用的功能,但对具体的数据点没有任何处理功能。点的概念在子类CChartSerieBase中引入,它是一个模板类,模板参数是要操作为点的数据类型。这很重要,因为序列可能必须处理不同的数据类型:例如点序列操作具有X和Y值的点,但是K线图系列操纵具有5个值(打开,关闭,高,低和时间值)的点。其他系列继承自CChartSerieBase并提供他们操作的数据类型。 CChartSerieBase类已经处理了大多数数据管理,并通过纯虚函数将渲染委托给子类。每个系列在创建时也会分配一个Id。此标识可通过CChartSerie :: GetSerieId()检索,并可用于从图表中删除该系列。
该系列的一个重要特征是控制点的顺序:该系列中的所有点将根据它们的值重新排序。 默认情况下,点是基于它们的X值排序的,但您可以通过对它们的Y值排序或不对它们进行排序来改变这种行为(在这种情况下,系列保持将点添加到系列中的顺序 )。 对点进行排序会对性能产生影响:如果点是有序的,则控件能够从完整系列中检索第一个和最后一个可见点,并且仅绘制两个点之间的点。 另一方面,你将不能绘制像椭圆形的曲线。 您可以通过调用CChartSerieBase :: SetSeriesOrdering来更改点的顺序。
控件中的不同系列的功能通常是不言自明的。 然而,柱状图系列需要一些解释。
柱状图系列
这个系列有点特别,如果其中几个在同一个控件上绘制在一起,他们将互相影响。 目的是能够绘制多个条形图系列,而不会重叠:它们是彼此相邻绘制的。 为此,您需要指定每个所属的组(一个简单的整数标识符)。 同一组的系列彼此相邻地绘制(或者对于水平条在彼此的顶部):参见两个图形的示例。 设置组ID是通过SetGroupId函数完成的。
您还可以通过调用SetInterSpace静态函数来控制所有柱形图之间剩余的空间的宽度。 这将为所有系列设置以像素为单位的空间(因此,如果显示多于两个系列,则在任何位置使用相同的空间)。 注意,您可以通过调用SetBarWidth单独设置柱状图系列的宽度。
在点上添加标签
一旦使用数据填充您的系列,您还可以在系列的特定点上添加标签:这个标签始终附加到特定点。 现在,只提供一种类型的标签,气泡标签:包含文本的圆角矩形并用线连接到特定点上。 当然,如果需要,您也可以提供自己的自定义标签(参见“扩展功能”一节)。
有两种方式创建文本标签:静态创建标签时,或动态注册一个对象,当标签请求时,它将提供文本。 第一种方法是最简单的,但也不太灵活。 下面是一个代码片段,显示如何做(假设m_pSeries已经创建并填充足够的数据):
void CMyClass::Init() {
// SNIP...
m_pSeries->CreateBalloonLabel(5,_T(\}
此调用将创建一个带有“This is a simple label”文本的标签,并将其附加到带索引为5的点。该函数返回一个指向新创建的标签的指针,以便您可以修改其某些属性或存储以供以后使用。
第二种方法有点复杂,但提供了更多的灵活性:例如,您可以以更方便的方式在标签中显示点属性(例如X值,Y值,?)。 为此,您必须创建一个继承自CChartLabelProvider
class CCustomLabelProvider : public CChartLabelProvider
public:
TChartString GetText(CChartSerieBase
TChartStringStream ssText;
SChartXYPoint Point = pSeries->GetPoint(uPtIndex); ssText << _T(\ << Point.X; return ssText.str(); } };
此代码段显示如何将其与标签一起使用。 注意m_pSeries应该是一个操作SChartXYPoint点(点,线,面或者柱系列)的系列。 如果不是这样,你的代码将给出一个编译错误。
void CMyClass::Init() {
// SNIP...
m_pLabelProvider = new CCustomLabelProvider(); m_pSeries->CreateBalloonLabel(5, m_pLabelProvider); }
控件不获取指针的所有权,因此,当你不再需要时,你有责任删除它。 在上面的例子中,它通常会在CMyClass析构函数中被删除。 在上面的示例中,您可以为所有要添加的标签地方重复使用相同的标签类, 这也带来另一个优点:如果你想在运行时改变标签的格式,你只需要在CustomLabelProvider中添加代码。 不需要遍历所有现有标签并更改其文本。 当然,在这种情况下,需要刷新控件,因为必须重新绘制标签。 还要注意TChartStringStream类的用法,TChartStringStream类是由控件提供的别名(类似于TChartString)。 当UNICODE被定义时,它解析为std :: wstringstream,当未定义UNICODE时,解析为std :: stringstream。
对轴的操作
轴是图表的一个重要特征,因为它们控制不同系列在控制中的显示方式。 控件中最多可使用四个轴:底部,顶部,左侧和右侧。 控件的每个系列必须和一个水平轴和一个垂直轴相连接。 在图表中添加系列时指定这些轴。 底部和左侧轴是主轴,顶部和右侧轴是辅助轴(您将在控件的某些功能中遇到此问题)。 现在有三种类型的轴供选择:标准轴,对数轴和日期/时间轴。 您可以在不同位置选用不同类型的轴。
一旦您选择了在不同位置使用哪些轴,您需要先创建它们,然后才能向控件添加任何数据。 为此,通过指定轴附加在哪个位置,简单地调用CreateStandardAxis,CreateLogarithmicAxis或CreateDateTimeAxis。 如果已经在该位置创建了轴,则控件将销毁它并且用新的轴替换它。 这里有一个简单的代码片段,显示如何在底部创建日期/时间,在左侧创建一个标准轴:
void CMyClass::Init()
{
CChartStandardAxis* pBottomAxis =
m_ChartCtrl.CreateStandardAxis(CChartCtrl::BottomAxis); CChartLogarithmicAxis* pLeftAxis =
m_ChartCtrl.CreateLogarithmicAxis(CChartCtrl::LeftAxis); }
一旦创建了这些轴,就可以对它们设置一些属性。 大多数属性在所有轴类型之间共享(例如自动模式,最小值和最大值,轴标签,?)。 轴可以设置为三种“自动”模式:全自动,屏幕自动和手动模式。
全自动模式基于附加到该轴的所有系列计算轴最小值和最大值(所有系列的所有点的最小值用作轴的最小值,并使用所有系列的所有点的最大值 作为轴的最大值)。
屏幕自动模式基于与该轴相关的所有系列的所有可见点计算轴最小值和最大值。 例如,如果图表仅显示连接到手动底部轴和屏幕自动左侧轴的一个系列,则左侧轴将自适应于当前可见的点,并且不考虑这些点有可能超过底轴的范围(在全自动模式下,底轴外部的点将被考虑)。 警告:如果系列的两个轴都处于屏幕自动模式,则结果未定义。
在手动模式下,轴最小和最大值由用户设置,不由控件计算。
在使用自动轴模式下,如果将数据动态添加到控件,如果新的数据点位于轴的范围之外,那么控件将自动刷新。 这里是一个代码片段(继续前一个代码段),显示一个全自动轴(底部轴)和一个手动轴(左轴,它是一个对数轴):
void CMyClass::Init() {
// SNIP ...
pBottomAxis->SetAutomaticMode(CChartAxis::FullAutomatic); // The call to SetAutomaticMode(CChartAxis::NotAutomatic) is not // really needed because this is the default.
pLeftAxis->SetAutomaticMode(CChartAxis::NotAutomatic); pLeftAxis->SetMinMax(0.01,1000); }
处于离散模式下的轴
轴有一个模式是离散模式(默认禁用)。此模式指定轴不显示连续值,而只显示离散值,这些值是轴上刻度指定的值,而轴将不显示其他的值。尝试绘制不同于显示的节拍值的值是不可能的。让我们举一个例子:假设你有一个底部标准轴,间隔为1.0(所以,显示的蜱是1,2,3等等)。尝试绘制X值为0.5的点将在相同位置显示该点,就好像它的值为1.0。事实上,你可以认为两个刻度之间的区域是一个常量值。这就是为什么刻度标签显示在两个刻度的中间,而不是刻度本身。
这里有一个小代码片段,显示离散轴对系列显示方式的影响。代码片段下的两个图像显示启用离散模式(第一个图像)或禁用(第二个图像)的结果。
void CMyClass::Init()
{
CChartStandardAxis* pBottomAxis =
m_ChartCtrl.CreateStandardAxis(CChartCtrl::BottomAxis); pBottomAxis->SetMinMax(0, 10); CChartStandardAxis* pLeftAxis =
m_ChartCtrl.CreateStandardAxis(CChartCtrl::LeftAxis); pLeftAxis->SetMinMax(0, 10);
pBottomAxis->SetTickIncrement(false, 1.0); pBottomAxis->SetDiscrete(true);
CChartLineSerie* pSeries = m_ChartCtrl.CreateLineSerie(); double XVal[20]; double YVal[20];
for (int i=0; i<20; i++) {
XVal[i] = YVal[i] = i/2.0; }
pSeries->SetPoints(XVal,YVal,20); }
使用日期/时间轴
使用日期/时间轴有点特别,下面是如何利用这个功能的解释。要了解日期/时间轴的重要一点是它们在COleDateTime对象内部工作。原因很简单:COleDateTime中有DATE类型的类,DATE类型是一个双精度型。由于图表中的点表示为双精度值,因此它非常适合:使用标准点(非日期/时间)和日期/时间点之间没有差异,这使得后者的使用不太复杂。所有点仍然存储为双精度型,无论是否是日期/时间。
创建日期/时间轴后,可以在控件中填充数据。为此目的,没有改变:你必须从CChartSerie类调用void AddPoint(double X,double Y)或void SetPoints(double * X,double * Y,int Count)。 CChartCtrl类提供了两个静态函数,让你从COleDateTime转换为双精度,反之亦然:
double DateToValue(const COleDateTime& Date) COleDateTime ValueToDate(double Value)
如果您有另一种格式的日期(例如time_t或SYSTEMTIME),这不是一个问题,因为