基于 CChartObject 类设计和实施新 GUI 组件

investeo | 26 十二月, 2013

简介

在我撰写了关于通过 GUI 界面实现半自动“EA 交易”的前作后,结果表明针对更复杂的指标和“EA 交易”,最好使用新的功能来改善界面。在熟悉 MQL5 标准库类后,我实施了一些新的组件。

在本文中,我将介绍将 MQL5 标准库类用于 GUI 对象的过程,以及如何实施派生自 CChartObjectEdit 类的新类:CChartObjectProgressBar、CChartObjectSpinner 和 CChartEditTable。CChartEditTable 类使用动态二维对象数组,这是一个以 MQL5 实施动态二维对象数组的工作示例。

 

1. CChartObject 及其后代

如果我们不使用标准 MQL5 库类,我们必须使用对象函数以创建和维持图表上的任何对象。

对象通过 ObjectCreate() 函数创建,且对象的类型作为一个 ENUM_OBJECT 值传递至 ObjectCreate() 函数。图表上所有的对象都有其自身的属性,可以是整数双精度字符串类型。所有属性均通过专用函数设置和检索:ObjectGetInteger()ObjectSetInteger()ObjectGetDouble()ObjectSetDouble()ObjectGetString()ObjectSetString()。此外还有函数用于在任何指定图表上删除移动统计对象。

得益于 MQL5 的 OOP 范式,各种图表对象的处理可通过使用 CChartObject 类及其后代完成。

CChartObject 类是任何可放置在图表上的图形对象的基类。请观察下面 CChartObject 的基本继承图:

 

CChartObject 类的继承图 

图 1. CChartObject 类的继承图


我们可以看到,部分类在矩形的右下角标注了一个小三角形。

这些类是其他类的父类。基本上,后代类通过添加新的变量和在对象上操作的方法扩展了基类的可能性。它们还可能在 Create() 和 Type() 方法上有所不同,以创建一个派生对象并返回其类型。

下面我通过示例来说明这一点:CChartObjectTrend 类是 CChartObjectTrendByAngleCChartObjectChannelCChartObjectStdDevChannelCChartObjectRegressionCChartObjectPitchfork 类的父类。

CChartObjectTrend 是具有 OBJPROP_RAY_RIGHT 和 OBJPROP_RAY_LEFT 属性的对象的基类,其定义如下所示:

class CChartObjectTrend : public CChartObject
  {
public:
   //--- 访问对象属性的方法
   bool              RayLeft() const;
   bool              RayLeft(bool new_sel);
   bool              RayRight() const;
   bool              RayRight(bool new_sel);
   //--- 创建对象方法
   bool              Create(long chart_id,string name,int window,
                                datetime time1,double price1,datetime time2,double price2);
   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_TREND); }
   //--- 操作文件方法
   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
  };

定义中添加了注释,以区分不同类型的方法。

访问对象属性的方法是 RayLeft() 和 RayRight()。它们的实施是调用 CChartObjectTrend 对象上的 ObjectGetInteger()ObjectSetInteger() 方法。

bool CChartObjectTrend::RayLeft(bool new_ray)
  {
//--- 检查
   if(m_chart_id==-1) return(false);
//---
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_LEFT,new_ray));
  }

Create() 方法用于创建对象和将对象附加至图表。

它调用 ObjectCreate() 方法,并将 OBJ_TREND 作为参数之一:

bool CChartObjectTrend::Create(long chart_id,string name,int window,
                                   datetime time1,double price1,datetime time2,double price2)
  {
   bool result=ObjectCreate(chart_id,name,OBJ_TREND,window,time1,price1,time2,price2);
   if(result) result&=Attach(chart_id,name,window,2);
//---
   return(result);
  }

Save() 和 Load() 方法通过使用 FileWriteInteger()FileLoadInteger() 函数在硬盘上存储和加载对象数据。

bool CChartObjectTrend::Save(int file_handle)
  {
   bool result;
//--- 检查
   if(file_handle<=0) return(false);
   if(m_chart_id==-1) return(false);
//--- 写下
   result=CChartObject::Save(file_handle);
   if(result)
     {
      //--- 写下 "Ray left" 属性值
      if(FileWriteInteger(file_handle,(int) ObjectGetInteger(m_chart_id,m_name,
                                                        OBJPROP_RAY_LEFT),CHAR_VALUE)!=sizeof(char))
      return(false);
      //--- 写下 "Ray right" 属性值
      if(FileWriteInteger(file_handle,(int) ObjectGetInteger(m_chart_id,m_name, 
                                                                OBJPROP_RAY_RIGHT),CHAR_VALUE)!=sizeof(char))
       return(false);
     }
//---
   return(result);
  }

bool CChartObjectTrend::Load(int file_handle)
  {
   bool result;
//--- 检查
   if(file_handle<=0) return(false);
   if(m_chart_id==-1) return(false);
//--- 读取
   result=CChartObject::Load(file_handle);
   if(result)
     {
      //--- 读取 "Ray left" 属性值
      if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_LEFT,
                                                 FileReadInteger(file_handle,CHAR_VALUE)))return(false);
      //--- 读取 "Ray right" 属性值
      if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_RIGHT,
                                                 FileReadInteger(file_handle,CHAR_VALUE))) return(false);
     }
//---
   return(result);
  }

我们快速浏览 CChartObjectTrend 的后代类的定义。

CChartObjectTrendByAngle 类添加了 Angle() 属性修饰符,并返回 OBJ_TRENDBYANGLE 对象类型:

class CChartObjectTrendByAngle : public CChartObjectTrend
  {
public:
   //--- 访问对象属性的方法
   double            Angle() const;
   bool              Angle(double angle);
   //--- 创建对象方法
   bool              Create(long chart_id,string name,int window,datetime time1,double price1,
                               datetime time2,double price2);
   //--- 识别对象方法
   virtual int       Type() { return(OBJ_TRENDBYANGLE); }
  };

CChartObjectChannel 类返回 OBJ_CHANNEL 对象类型,且由于它处理通道,三对价格/日期参数被传递至 Create() 方法:

class CChartObjectChannel : public CChartObjectTrend
  {
public:
   //--- 创建对象方法
   bool              Create(long chart_id,string name,int window,datetime time1,double price1,
                               datetime time2,double price2,datetime time3,double price3);
   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_CHANNEL); }
  };

CChartObjectStdDevChannel 类在 Create() 方法中添加了 Deviations() 属性修饰符和额外的偏差参数:

class CChartObjectStdDevChannel : public CChartObjectTrend
  {
public:
   //--- 访问对象属性的方法
   double            Deviations() const;
   bool              Deviations(double deviation);
   //--- 创建对象方法
   bool              Create(long chart_id,string name,int window,
                           datetime time1,datetime time2,double deviation);
   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_STDDEVCHANNEL); }
   //--- 操作文件方法
   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
  };

CChartObjectRegression 类创建回归趋势线,仅 CChartObjectTrend 类中的 Create() 和 Type() 方法被覆盖。

class CChartObjectRegression : public CChartObjectTrend
  {
public:
   //--- 创建对象方法
   bool              Create(long chart_id,string name,int window,datetime time1,datetime time2);
   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_REGRESSION); }
  };

CChartObjectPitchfork 类处理 pitchfork 类型,同样仅 Create() 和 Type() 方法发生了变化:

class CChartObjectPitchfork : public CChartObjectTrend
  {
public:
   //--- 创建对象方法
   bool              Create(long chart_id,string name,int window,datetime time1,double price1,
                               datetime time2,double price2,datetime time3,double price3);
   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_CHANNEL); }
  };

通过快速浏览,我们可以得出基于其他类编写新的图形对象类时适用的基本规则:

不是所有的规则都必须应用,可以只添加新的访问修饰符或在类中添加新的变量和/或对象。

在我们继续前,我来介绍一下如何在图形对象上使用 CChartObject 方法。

使用对象属性而非 ObjectSet 和 ObjectGet 方法系列足以声明 CChartObject 或后代对象以及调用更改其所需属性的方法。为使其更加简单,我提供了一个普通标签的示例。

我们无需编写:

void OnStart()
  {
//---
   string label_name="my_OBJ_LABEL_object";
   if(ObjectFind(0,label_name)<0)
     {
      Print("对象 ",label_name," 没有找到. 错误代码 = ",GetLastError());
      ObjectCreate(0,label_name,OBJ_LABEL,0,0,0);           
      ObjectSetInteger(0,label_name,OBJPROP_XDISTANCE,200);
      ObjectSetInteger(0,label_name,OBJPROP_YDISTANCE,300);
      ObjectSetInteger(0,label_name,OBJPROP_COLOR,White);
      ObjectSetString(0,label_name,OBJPROP_TEXT,UP);
      ObjectSetString(0,label_name,OBJPROP_FONT,"Wingdings");
      ObjectSetInteger(0,label_name,OBJPROP_FONTSIZE,10);
      ObjectSetDouble(0,label_name,OBJPROP_ANGLE,-45);
      ObjectSetInteger(0,label_name,OBJPROP_SELECTABLE,false);
      ChartRedraw(0);                                      
     }
  }

我们可以使用 OOP 范式来实施它:

1. 声明 CChartObjectLabel 对象:

CChartObjectLabel label;

2. 对象上的操作:

int OnInit()
  {
//---
   label.Create(0, label_name, 0, 0);
   label.X_Distance(200);
   label.Y_Distance(300);
   label.Color(White);
   label.Description(UP);
   label.Font("Wingdings");
   label.FontSize(10);
   label.Angle(-45);
   label.Selectable(false);
//---
   return(0);
  }

如您所见,主要区别在于我们不再对字符串 label_ name 进行操作:

string label_name="my_OBJ_LABEL_object";

并使用 label_name 作为一个参数调用 ObjectSetInteger()ObjectGetInteger()ObjectSetDouble()ObjectGetDouble() 函数,但我们声明 CChartObjectLabel 对象并使用其方法。这不仅更容易记忆、在实施上更符合逻辑,而且编写也更为快速。

当将点 (.) 放置在对象实例后时,MQL5 代码编辑器为我们提供了代码完成功能。没有必要反复遍历 MQL5 文档以查看要放置的 OBJPROP 属性以设置或获取指定属性。

与上文中介绍的 CChartObjectTrend 类相似,获取或设置半直线向左或向右足以声明 CChartObjectTrend 对象和调用 RayRight() 或 RayLeft() 方法:

CChartObjectTrend trendline;
trendline.RayRight(true); 


2. ProgressBar

我们将要实施的首个组件是 ProgressBar。进度条显示部分操作的进度,范围从 0 到 x%。

为使其更加可靠,让我们将最大值限制为任何正整数值,而不是 100。我们需要一个根据进度值改变其大小的彩条。我首先想到的是使用两个矩形,但我采用了另一种方式:使用两个 CChartObjectEdit 对象,其中一个位于另一个的内部,并具有不同的背景颜色。

如此一来就简化了编码和可放入进度条以显示其值的文本的添加。如果我们的进度条可根据需要呈水平或垂直就好了。


2.1. ProgressBar 实施

CChartObjectProgress 类派生自 CChartObjectEdit 类。

我添加了私有内部变量以保存值和值的限制:m_value、m_min、m_max。

进度条的方向设置为整数值并由 m_direction 变量保存。颜色由 m_color 变量保存。Type() 方法返回 OBJ_EDIT 值,因为无论如何,没有识别到符合我们目的的值。读者可能注意到 CChartObjectEdit m_bar 变量位于类定义的内部 - 这是基于 m_value 改变其大小的内部条。其他变量 m_name 和 m_chart 在内部为 m_bar 变量保存值。

class CChartObjectProgressBar : public CChartObjectEdit
  {
private:
   int               m_value;
   int               m_min;
   int               m_max;
   int               m_direction;
   color             m_color;
   CChartObjectEdit  m_bar;
   string            m_name;
   long              m_chart_id;

public:
   int               GetValue();
   int               GetMin();
   int               GetMax();

   void              SetValue(int val);
   void              SetMin(int val);
   void              SetMax(int val);

   void              SetColor(color bgcol,color fgcol);
   bool              Create(long chart_id,string name,int window,int X,int Y,
                           int sizeX,int sizeY,int direction);

   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_EDIT); }
};

Create() 方法创建 ProgressBar 对象并将其附加至图表。

读者可能注意到在绘制竖条时,Y 变量从 sizeY 变量减去,这是因为 CChartObjectEdit 的绘制通常从上至下,而我希望从下至上绘制内部矩形:

bool CChartObjectProgressBar::Create(long chart_id,string name,int window,int X,int Y,
                                          int sizeX,int sizeY,int direction=0)
  {
   bool result=ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0);

   m_name=name;
   m_chart_id=chart_id;
   m_direction=direction;

   if(direction!=0)
     {
      Y=Y-sizeY;
     }

   ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,White);
   ObjectSetInteger(chart_id,name,OBJPROP_COLOR,White);
   ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(chart_id,name,OBJPROP_READONLY,true);

   result&=m_bar.Create(chart_id,name+"m_bar",window,X,Y,sizeX,sizeY);
   m_bar.Color(White);
   m_bar.ReadOnly(true);
   m_bar.Selectable(false);

//---
   if(result) result&=Attach(chart_id,name,window,1);
   result&=X_Distance(X);
   result&=Y_Distance(Y);
   result&=X_Size(sizeX);
   result&=Y_Size(sizeY);
//---
   return(result);
  }

SetColor() 方法为两个矩形设置背景和前景颜色:

void CChartObjectProgressBar::SetColor(color bgCol,color fgCol=White)
  {
   m_color=bgCol;
   m_bar.BackColor(m_color);
   m_bar.Color(fgCol);
  }

SetValue() 方法用于设置 _val 值和重新计算内部矩形对象的大小。

横条和竖条的大小计算是不同的:

void CChartObjectProgressBar::SetValue(int val)
  {
   if(m_direction==0) // 水平进度条
     {
      double sizex=(double)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE,0);

      double stepSize=sizex/(m_max-m_min);

      m_value=val;
      m_bar.Create(m_bar.ChartId(),m_bar.Name(),m_bar.Window(),
                   m_bar.X_Distance(),m_bar.Y_Distance(),(int)MathFloor(stepSize*m_value),m_bar.Y_Size());
        } else {
      double sizey=(double)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE,0);

      double stepSize=sizey/(m_max-m_min);
      m_value=val;
      m_bar.Create(m_bar.ChartId(),m_bar.Name(),m_bar.Window(),
                   m_bar.X_Distance(),(int)(this.Y_Distance()+sizey-MathFloor(stepSize*m_value)),
                   m_bar.X_Size(),(int)MathFloor(stepSize*m_value));

     }

   m_bar.Description(IntegerToString(m_value));
  }


2.2. ProgressBar 演示

既然我们已经实施了 CChartObjectProgressBar 类,是时候看看它在操作中的效果了。

要在图表上放置一个新的进度条,声明 CChartObjectProgressBar 对象并使用 Create() 和适当的属性方法即已足够:

progressBar.Create(0, "progressBar1", 0, 10, 10, 200, 40);
progressBar.SetColor(YellowGreen);
progressBar.SetMin(0);
progressBar.SetMax(100);
progressBar.SetValue(0);

我编写了一个“EA 交易”演示,用于在图表上放置六个不同的进度条,并在点击荧幕上的任何对象后更改它们的值。

本演示和其他演示的完整源代码附于附件中,请观看下面的演示:

 


3. Spinner

Spinner 组件是包含一个字段和两个按钮的组件。通过点击其中一个按钮,它可用于增大或减小编辑字段中的值。

在设计此对象时,我希望它不仅仅是处理整数值,所以将它设计为处理双精度类型。Spinner 还可以定义步幅,即当前值增大或减小的值。它还应具有不能超出的最小和最大值。


3.1. Spinner 实施

在 MQL5 中有 CChartObjectEditCChartObjectButton 类,这两个类可以合并成 CChartObjectSpinner 类。CChartObjectSpinner 继承自 CChartObjectEdit 并包含两个私有成员 CChartObjectButton 对象。

m_value 的最小和最大限值分别存储在 m_min 和 m_max 成员变量中,而 m_precision 变量存储到第 n 个数字值的计算精度。访问值、设置增大和减小步幅以及设置值的方法是必需的。

class CChartObjectSpinner: public CChartObjectEdit
  {

private:
   double            m_value;
   double            m_stepSize;
   double            m_min;
   double            m_max;
   int               m_precision;
   string            m_name;
   long              m_chart_id;
   CChartObjectButton m_up,m_down;

public:
   double            GetValue();
   double            GetMin();
   double            GetMax();

   void              SetValue(double val);
   void              SetMin(double val);
   void              SetMax(double val);

   double            Inc();
   double            Dec();

   bool              Create(long chart_id,string name,int window,int X,int Y,
                               int sizeX,int sizeY,double val,double stepSize,int precision);

   //--- 识别对象方法
   virtual int       Type() const { return(OBJ_EDIT); }
   
  };

Create() 方法创建新的 CChartObjectSpinner 并将其附加至图表。

CChartObjectEdit 的右侧创建有两个 CChartObjectButtons,高度均为 CChartObjectEdit 高度的一半。

增大按钮具有 "+" 符号,减小按钮具有 "-" 符号。

bool CChartObjectSpinner::Create(long chart_id,string name,int window,int X,int Y,
                                     int sizeX,int sizeY,double val=0.0,double stepSize=1.0,int precision=8)
  {
   bool result=ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0);

   m_name=name;
   m_chart_id=chart_id;
   m_value=val;
   m_stepSize=stepSize;
   m_precision=precision;

   ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,White);
   ObjectSetInteger(chart_id,name,OBJPROP_COLOR,Black);
   ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(chart_id,name,OBJPROP_READONLY,true);

   result&=m_up.Create(chart_id, name+"_up", window, X+sizeX, Y, 15, sizeY/2);
   result&=m_down.Create(chart_id, name+"_down", window, X+sizeX, Y+sizeY/2, 15, sizeY/2);
   m_up.Description("+");
   m_down.Description("-");
   ObjectSetString(chart_id,name,OBJPROP_TEXT,0,(DoubleToString(m_value,precision)));

//---
   if(result) result&=Attach(chart_id,name,window,1);
   result&=X_Distance(X);
   result&=Y_Distance(Y);
   result&=X_Size(sizeX);
   result&=Y_Size(sizeY);
//---
   return(result);
  }

SetValue() 方法将私有变量 m_value 设置为双精度值,如果该值位于 <m_min, m_max> 范围中的话。

void CChartObjectSpinner::SetValue(double val)
  {
   if(val>=m_min && val<=m_max) m_value=val;
   this.Description(DoubleToString(m_value));
  }

Inc() 方法以指定的步幅增大值,但不会大于 m_max 值。

请注意我使用了 NormalizeDouble() 函数,以将双精度值与指定精度进行比较。

double CChartObjectSpinner::Inc(void)
  {
   if(NormalizeDouble(m_max-m_value-m_stepSize,m_precision)>0.0) m_value+=m_stepSize;
   else m_value=m_max;
   this.Description(DoubleToString(m_value, m_precision));
   m_up.State(false);
   return m_value;
  }

Dec() 方法以指定的步幅减小值,但不会小于 m_min 值。

double CChartObjectSpinner::Dec(void)
  {
   if(NormalizeDouble(m_value-m_stepSize-m_min,m_precision)>0.0)
      m_value-=m_stepSize; else m_value=m_min;
   this.Description(DoubleToString(m_value,m_precision));
   m_down.State(false);

   return m_value;
  }


3.2. Spinner 演示

是时候测试 Spinner 对象了。为了使用它们,声明 CChartObjectSpinner 对象以及使用 Create()、SetMin() 和 SetMax() 方法即已足够。

   spinner.Create(0, "spinner1", 0, 10, 10, 200, 40, 0.0, 0.4);
   spinner.SetMin(0);
   spinner.SetMax(100);

我准备了一个使用三个 Spinner 组件的演示,在点击任何 Spinner 按钮后将会添加所有的值。

这在 OnChartEvent() 函数内部完成; 

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- 检查点击鼠标按钮事件
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
     
     if (sparam=="spinner1_up") spinner.Inc();
     if (sparam=="spinner1_down") spinner.Dec();
     if (sparam=="spinner2_up") spinner2.Inc();
     if (sparam=="spinner2_down") spinner2.Dec();
     if (sparam=="spinner3_up") spinner3.Inc();
     if (sparam=="spinner3_down") spinner3.Dec();
     
     label.Description(DoubleToString(NormalizeDouble(spinner.GetValue()+spinner2.GetValue()+spinner3.GetValue(),10),10));
   
   ChartRedraw();
     }
  }


 请看随附的演示:

 

 

4. CChartObjectEditTable

在许多多时间表 (MTF)“EA 交易”中,存在为每个时间表单独显示的指标值。

有时候,每个时间表具有不同的指标设置,以不同颜色的矩形和正方形的二维表格的形式显示。通过创建 CChartObjectEditTable 类,我设计了一个此类对象的通用二维表格。由于我使用的是二维动态对象数组,该类可保存任意的行数和列数。

在设计期间,我决定为每个单元格单独定义一种颜色,同时还提供在任何单元格中放置不同文本字符串的可能性。单元格具有相同的大小,但我希望定义单元格的高度、宽度和相邻空间。


4.1. CChartObjectEditTable 实施

CChartObjectEditTable 类保存指向一个二维对象数组的 CArrayObj 指针,m_rows 和 m_columns 成员变量保存表格中的行数和列数。

m_baseName 成员变量保存表格中所有单元格 CChartObjectEdit 对象的前缀。GetColor()、SetColor()、GetText()、SetText() 方法用于为任何所需单元格设置和获取颜色和文本值。Delete() 方法可删除 Create() 方法创建的所有对象。

class CChartObjectEditTable
  {

private:
   CArrayObj        *array2D;
   int               m_rows;
   int               m_cols;
   string            m_baseName;

public:

   bool              Create(long chart_id,string name,int window,int rows,int cols,int startX,int startY,
                                int sizeX,int sizeY,color Bg,int deltaX,int deltaY);
   bool              Delete();
   bool              SetColor(int row,int col,color newColor);
   color             GetColor(int row,int col);
   bool              SetText(int row,int col,string newText);
   string            GetText(int row,int col);


  };

Create() 方法创建 CChartObjectEdit 对象的二维动态表格。

请注意如何以 MQL5 创建二维对象数组:首先,我们声明一个指向二维数组的指针,然后我们使用一些 CArrayObj() 对象填充数组,即,我们在该数组中创建数组。所有数组可看做是表格的列的保存单元。

每个列都包含保存 CChartObjectEdit 对象的行,每个对象是用于显示的一个单元格。

bool CChartObjectEditTable::Create(long chart_id,string name,int window,int rows=1,int cols=1,
                                       int startX=0,int startY=0,int sizeX=15,int sizeY=15,
                                  color Bg=White,int deltaX=5,int deltaY=5)
  {
   m_rows=rows;
   m_cols=cols;
   m_baseName=name;
   int i=0,j=0;

   array2D=new CArrayObj();
   if (array2D==NULL) return false;
   
   for(j=0; j<m_cols; j++)
     {
      CArrayObj *new_array=new CArrayObj();
      if (array2D==NULL) return false;
   
      array2D.Add(new_array);
      for(i=0; i<m_rows; i++)
        {
         CChartObjectEdit *new_edit=new CChartObjectEdit();

         new_edit.Create(chart_id, name+IntegerToString(i)+":"+IntegerToString(j), window, 
                         startX+j*(sizeX+deltaX), startY+i*(sizeY+deltaY), sizeX, sizeY);
         new_edit.BackColor(Bg);
         new_edit.Color(White);
         new_edit.Selectable(false);
         new_edit.ReadOnly(true);
         new_edit.Description("");
         new_array.Add(new_edit);
        }
     }

   return true;
  }

SetColor() 方法用于设置任何单个单元格的颜色。首先,它找到列数组,然后是列数组中的第 n 个元素。

接下来,该元素的颜色值通过调用 BackColor() 方法改变。

bool CChartObjectEditTable::SetColor(int row,int col,color newColor)
  {
   CArrayObj *sub_array;
   CChartObjectEdit *element;

   if((row>=0 && row<m_rows) && (col>=0 && col<m_cols))
     {
      if(array2D!=NULL)
        {
         sub_array=array2D.At(col);
         element=(CChartObjectEdit*)sub_array.At(row);
         element.BackColor(newColor);

         return true;
        }
     }

   return false;
  }

GetColor() 方法采用与 SetColor() 方法相同的算法来定位单元格,但它用于返回任何指定单元格的颜色值。

color CChartObjectEditTable::GetColor(int row,int col)
  {
   CArrayObj *sub_array;
   CChartObjectEdit *element;

   if((row>=0 && row<m_rows) && (col>=0 && col<m_cols))
     {
      if(array2D!=NULL)
        {
         sub_array=array2D.At(col);
         element=(CChartObjectEdit*)sub_array.At(row);
         return element.BackColor();
        }
     }

   return NULL;
  }

SetText() 方法可定位元素,然后通过调用 Description() 方法设置其文本值。

bool CChartObjectEditTable::SetText(int row,int col,string newText)
  {
   CArrayObj *sub_array;
   CChartObjectEdit *element;

   if((row>=0 && row<m_rows) && (col>=0 && col<m_cols))
     {
      if(array2D!=NULL)
        {
         sub_array=array2D.At(col);
         element=(CChartObjectEdit*)sub_array.At(row);
         element.Description(newText);

         return true;
        }
     }

   return false;
  }

Delete() 方法可删除 Create() 方法创建的所有对象。

它先清除所有列数组,然后从内存中删除二维数组对象。

bool CChartObjectEditTable::Delete(void)
  {
   for(int j=0; j<m_cols; j++)
     {
      CArrayObj *column_array=array2D.At(j);
      column_array.Clear();
      delete column_array;
     }
   delete array2D;
   return true;
  }


4.2. CChartObjectEditTable 演示

要使用 CChartObjectEditTable 组件,必须声明 CChartEditTable 对象和使用 Create() 方法,该方法带有表明表格应包含的行数和列数的参数。

然后通过使用属性修饰符,我们可轻易改变任意单元格的颜色和文本。

table.Create(0,"t",0,1,10,10,10,15,15,Yellow);
table.SetColor(2,2,Red);
table.SetText(2,2,"2");

请参见我准备的脚本,该脚本展示了使用 CChartObjectEditTable 对象带来的各种可能性。

脚本的源代码请见附件。

 


总结

我在文中说明和介绍了创建派生自 CChartObject 类的新的图表组件的过程。

使用实施的组件的过程非常直观,且仅需数行代码即可实现。

使用这些组件时,请在“EA 交易”或指标代码中包含 ChartObjectsExtControls.mqh 文件。