下载MetaTrader 5

图形界面 X: 文本编辑框, 图片滑块和简单控件 (构建 5)

19 一月 2017, 08:40
Anatoli Kazharski
0
2 317

内容

概论

为了更加理解此函数库的目的, 请阅读首篇文章: 图形界面 I: 准备函数库结构 (第 1 章)。您在每一章节的末尾可找到文章清单的链接。您也可以从那里下载当前正在开发阶段的函数库完整版。这些文件必须按照与存档相同的目录存放。

本文研究新的控件: 文本编辑框, 图片滑块, 以及其它的简单控件: 文本标签和图片, 这些在各种情况下都很有用。函数库正在持续增长, 并引入了一些其它的新控件, 以前创建的那些也有所改进。由于函数库目前被许多用户使用, 从他们那里收到了许多评论和建议。这些请求的大部分已在新版函数库中实现。外加, 某些算法已经优化。这将降低 CPU 负载。有关这方面的更多细节会在文章中进一步阐述。

 

文本编辑框控件

迄今为止, 已开发的函数库包含了一个编辑框控件 (CSpinEdit 类), 但它只用于输入数值。现在函数库将补充另一个控件, 它将允许输入任何文本到字段。在不同的情况下也许都需要文本编辑框控件。例如, 可以在终端 "沙盒" 的文件中创建字符串搜索。另一个选项是为 MQL 应用程序的最终用户提供输入交易品种数组的能力。简言之, 它可以是任何处理所需的文本数据。

我们来枚举文本编辑框控件的所有组件:

  1. 背景
  2. 图标
  3. 描述
  4. 编辑框

 

图例. 1. 文本编辑框控件组件。


我们来就近看看这个控件的类。

 

创建文本编辑框控件的类

利用 CTextEdit 类创建 TextEdit.mqh 文件, 其标准方法可用于所有控件, 且已包含在函数库引擎 (WndContainer.mqh 文件)。以下是供用户自定义控件的属性:

  • 控件背景色
  • 控件激活或是阻塞状态的图标
  • 图标沿两条数轴 (x, y) 的边距
  • 控件的描述文本
  • 文本标签沿两条数轴 (x, y) 的边距
  • 控件不同状态的文本颜色
  • 编辑框大小
  • 文本编辑框沿两条数轴 (x, y) 的边距
  • 文本编辑框和文本在不同状态的颜色
  • 编辑框中的文本对齐 (左/右/中心)
  • 文本选择光标的显示模式
  • 在编辑框中重置数值的模式
//+------------------------------------------------------------------+
//| 创建文本编辑框的类                                                                |
//+------------------------------------------------------------------+
class CTextEdit : public CElement
  {
private:
   //--- 控件背景色
   color             m_area_color;
   //--- 控件激活或是阻塞状态的图标
   string            m_icon_file_on;
   string            m_icon_file_off;
   //--- 图标边距
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- 编辑框描述文本
   string            m_label_text;
   //--- 文本标签边距
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- 不同状态的文本颜色
   color             m_label_color;
   color             m_label_color_hover;
   color             m_label_color_locked;
   color             m_label_color_array[];
   //--- 编辑框当前数值
   string            m_edit_value;
   //--- 编辑框大小
   int               m_edit_x_size;
   int               m_edit_y_size;
   //--- 编辑框边距
   int               m_edit_x_gap;
   int               m_edit_y_gap;
   //--- 不同状态的编辑框颜色和文本
   color             m_edit_color;
   color             m_edit_color_locked;
   color             m_edit_text_color;
   color             m_edit_text_color_locked;
   color             m_edit_text_color_highlight;
   //--- 不同状态的编辑框边框颜色
   color             m_edit_border_color;
   color             m_edit_border_color_hover;
   color             m_edit_border_color_locked;
   color             m_edit_border_color_array[];
   //--- 重置数值的模式 (空字符串)
   bool              m_reset_mode;
   //--- 文本选择指针的显示模式
   bool              m_show_text_pointer_mode;
   //--- 文本对齐模式
   ENUM_ALIGN_MODE   m_align_mode;
   //---
public:
   //--- 图标边距
   void              IconXGap(const int x_gap)                      { m_icon_x_gap=x_gap;                 }
   void              IconYGap(const int y_gap)                      { m_icon_y_gap=y_gap;                 }
   //--- (1) 背景色, (2) 编辑框描述文本, (3) 文本标签边距
   void              AreaColor(const color clr)                     { m_area_color=clr;                   }
   string            LabelText(void)                          const { return(m_label.Description());      }
   void              LabelText(const string text)                   { m_label.Description(text);          }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                }
   //--- 不同状态的文本标签颜色
   void              LabelColor(const color clr)                    { m_label_color=clr;                  }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;            }
   void              LabelColorLocked(const color clr)              { m_label_color_locked=clr;           }
   //--- (1) 编辑框大小, (2) 编辑框距右侧距离
   void              EditXSize(const int x_size)                    { m_edit_x_size=x_size;               }
   void              EditYSize(const int y_size)                    { m_edit_y_size=y_size;               }
   //--- 编辑框边距
   void              EditXGap(const int x_gap)                      { m_edit_x_gap=x_gap;                 }
   void              EditYGap(const int y_gap)                      { m_edit_y_gap=y_gap;                 }
   //--- 不同状态的编辑框颜色
   void              EditColor(const color clr)                     { m_edit_color=clr;                   }
   void              EditColorLocked(const color clr)               { m_edit_color_locked=clr;            }
   //--- 不同状态的编辑框文本颜色
   void              EditTextColor(const color clr)                 { m_edit_text_color=clr;              }
   void              EditTextColorLocked(const color clr)           { m_edit_text_color_locked=clr;       }
   void              EditTextColorHighlight(const color clr)        { m_edit_text_color_highlight=clr;    }
   //--- 不同状态的编辑框边框颜色
   void              EditBorderColor(const color clr)               { m_edit_border_color=clr;            }
   void              EditBorderColorHover(const color clr)          { m_edit_border_color_hover=clr;      }
   void              EditBorderColorLocked(const color clr)         { m_edit_border_color_locked=clr;     }
   //--- (1) 处理文本标签时重置模式, (2) 文本选择指针的显示模式
   bool              ResetMode(void)                                { return(m_reset_mode);               }
   void              ResetMode(const bool mode)                     { m_reset_mode=mode;                  }
   void              ShowTextPointerMode(const bool mode)           { m_show_text_pointer_mode=mode;      }
   //--- 文本对齐模式
   void              AlignMode(ENUM_ALIGN_MODE mode)                { m_align_mode=mode;                  }
   //--- 设置按钮激活或是阻塞状态的图标
   void              IconFileOn(const string file_path);
   void              IconFileOff(const string file_path);
  };

文本选择指针的显示模式意味着当鼠标光标悬浮在编辑框上时, 将用一个附加图标作为鼠标光标的补充, 表示可以在那里输入文本。为此作用, 已在 ENUM_MOUSE_POINTER 枚举中多加了一个标识符 (MP_TEXT_SELECT)。

//+------------------------------------------------------------------+
//| 指针类型的枚举 |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_POINTER
  {
   MP_CUSTOM      =0,
   MP_X_RESIZE    =1,
   MP_Y_RESIZE    =2,
   MP_XY1_RESIZE  =3,
   MP_XY2_RESIZE  =4,
   MP_X_SCROLL    =5,
   MP_Y_SCROLL    =6,
   MP_TEXT_SELECT =7
  };

CPointer 类中也相应地添加 (参阅以下代码)。文本选择时光标图标的图像也在文章末尾的存档里提供。 

//+------------------------------------------------------------------+
//|                                                      Pointer.mqh |
//|                        版权所有 2015, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- 资源
...
#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp"

//+------------------------------------------------------------------+
//| 基于光标类型设置光标图标                                                     |
//+------------------------------------------------------------------+
void CPointer::SetPointerBmp(void)
  {
   switch(m_type)
     {
      case MP_X_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs.bmp";
         break;
      case MP_Y_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs.bmp";
         break;
      case MP_XY1_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs.bmp";
         break;
      case MP_XY2_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs.bmp";
         break;
      case MP_X_SCROLL :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp";
         break;
      case MP_Y_SCROLL :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll.bmp";
         break;
      case MP_TEXT_SELECT :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
         break;

     }
//--- 如果已指定光标类型 (MP_CUSTOM)
   if(m_file_on=="" || m_file_off=="")
      ::Print(__FUNCTION__," > 必须为光标设置两个图标!");
  }

为了创建文本编辑框控件, 需要五个 private 和一个 public 方法: 

class CTextEdit : public CElement
  {
private:
   //--- 创建编辑框的对象
   CRectLabel        m_area;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CEdit             m_edit;
   CPointer          m_text_select;
   //---
public:
   //--- 创建文本编辑框的方法
   bool              CreateTextEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateEdit(void);
   bool              CreateTextSelectPointer(void);
  };


CTextEdit 的其余部分不包含任何本系列之前文章未研究的内容。因此, 您可以自行试验它的能力。当前版本的文本编辑框限制在 63 个字符。 

 

图片滑块控件

图片滑块属于图形界面的信息控件。当创建快速参考指南时非常有用, 其中插图在价格图表上显示某些状况, 或有关所用 MQL 应用程序中特定图形界面控件用途的简要说明。 

我们来枚举图片滑块控件的所有组件:

  1. 背景
  2. 滑块箭头按钮
  3. 单选按钮组
  4. 与单选按钮组相关联的图像组


 

图例. 2. 图片滑块控件的组件。

 

创建图片滑块控件的类

利用其它类中存在的标准方法创建 PicturesSlider.mqh 文件, 并将其包含在 WndContainer.mqh 文件中。以下是可由函数库用户自定义的控件属性。

  • 控件背景色
  • 控件背景框颜色
  • 图片沿 Y 轴的边距
  • 滑块箭头按钮沿两个轴 (x, y) 的边距
  • 单选按钮沿两个轴 (x, y) 的边距
  • 单选按钮之间的边距
//+------------------------------------------------------------------+
//| 创建图片滑块类                                                                        |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
  {
private:
   //--- 控件背景框颜色
   color             m_area_color;
   color             m_area_border_color;
   //--- 图片沿 Y 轴的边距
   int               m_pictures_y_gap;
   //--- 按钮边距
   int               m_arrows_x_gap;
   int               m_arrows_y_gap;
   //--- 单选按钮边距
   int               m_radio_buttons_x_gap;
   int               m_radio_buttons_y_gap;
   int               m_radio_buttons_x_offset;
   //---
public:
   //--- (1) 背景以及 (2) 背景框颜色
   void              AreaColor(const color clr)              { m_area_color=clr;                      }
   void              AreaBorderColor(const color clr)        { m_area_border_color=clr;               }
   //--- 箭头按钮边距
   void              ArrowsXGap(const int x_gap)             { m_arrows_x_gap=x_gap;                  }
   void              ArrowsYGap(const int y_gap)             { m_arrows_y_gap=y_gap;                  }
   //--- 图片沿 Y 轴边距
   void              PictureYGap(const int y_gap)            { m_pictures_y_gap=y_gap;                }
   //--- (1) 单选按钮边距, (2) 单选按钮之间的距离
   void              RadioButtonsXGap(const int x_gap)       { m_radio_buttons_x_gap=x_gap;           }
   void              RadioButtonsYGap(const int y_gap)       { m_radio_buttons_y_gap=y_gap;           }
   void              RadioButtonsXOffset(const int x_offset) { m_radio_buttons_x_offset=x_offset;     }
  };

 为了创建图片滑块控件, 需要五个 private 和一个 public 方法:

class CPicturesSlider : public CElement
  {
private:
   //--- 创建控件的对象
   CRectLabel        m_area;
   CBmpLabel         m_pictures[];
   CRadioButtons     m_radio_buttons;
   CIconButton       m_left_arrow;
   CIconButton       m_right_arrow;
   //---
public:
   //--- 创建图片滑块方法
   bool              CreatePicturesSlider(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreatePictures(void);
   bool              CreateRadioButtons(void);
   bool              CreateLeftArrow(void);
   bool              CreateRightArrow(void);
  };

控件的宽度将会依照用户定义的参数自动计算。这些参数包括单选按钮组距控件左边缘的边距, 相对于此的图片滑块右箭头按钮的坐标也一并计算。控件高度取决于图片的尺寸。假定图像尺寸相同, 因此计算时使用组中第一副图像的大小。

调用创建控件的主方法之前, 必须使用 CPicturesSlider::AddPicture() 方法将图片加入数组。方法唯一的参数就是图片路径, 若未设置的话将采用 省缺 路径。  

//+------------------------------------------------------------------+
//|                                               PicturesSlider.mqh |
//|                        版权所有 2016, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
//--- 省缺图片
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| 创建图片滑块类                                                                         |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
  {
private:
   //--- 图片数组 (图片路径)
   string            m_file_path[];
   //--- 图片的省缺路径
   string            m_default_path;
   //---
public:
   //--- 添加图片
   void              AddPicture(const string file_path="");
  };
//+------------------------------------------------------------------+
//| 构造器                                                                                        |
//+------------------------------------------------------------------+
CPicturesSlider::CPicturesSlider(void) : m_default_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"),
                                         m_area_color(clrNONE),
                                         m_area_border_color(clrNONE),
                                         m_arrows_x_gap(2),
                                         m_arrows_y_gap(2),
                                         m_radio_button_width(12),
                                         m_radio_buttons_x_gap(25),
                                         m_radio_buttons_y_gap(1),
                                         m_radio_buttons_x_offset(20),
                                         m_pictures_y_gap(25)
  {
//--- 在基类中存储控件的名称
   CElement::ClassName(CLASS_NAME);
//--- 设置鼠标左键点击优先权
   m_zorder=0;
  }
//+------------------------------------------------------------------+
//| 添加图片                                                                                    |
//+------------------------------------------------------------------+
void CPicturesSlider::AddPicture(const string file_path="")
  {
//--- 为数组增加一个元素的尺寸
   int array_size=::ArraySize(m_pictures);
   int new_size=array_size+1;
   ::ArrayResize(m_pictures,new_size);
   ::ArrayResize(m_file_path,new_size);
//--- 保存传递参数的值
   m_file_path[array_size]=(file_path=="")?m_default_path : file_path;
  }

若要显示来自组中的图片, 使用 CPicturesSlider::SelectPicture() 方法。此方法会在处理箭头按钮和单选按钮的处理器类 CPicturesSlider 中调用。  

class CPicturesSlider : public CElement
  {
public:
   //--- 切换指定索引处的图片
   void              SelectPicture(const uint index);
  };
//+------------------------------------------------------------------+
//| 指定要显示的图片                                                                     |
//+------------------------------------------------------------------+
void CPicturesSlider::SelectPicture(const uint index)
  {
//--- 获取图片数量
   uint pictures_total=PicturesTotal();
//--- 如果组内无图片, 报告
   if(pictures_total<1)
     {
      ::Print(__FUNCTION__," > 方法被调用, "
              "如果组内至少包含一副图片!使用 CPicturesSlider::AddPicture() 方法");
      return;
     }
//--- 如果超出数组范围, 调整索引
   uint correct_index=(index>=pictures_total)?pictures_total-1 : index;
//--- 在此索引处选择单选按钮
   m_radio_buttons.SelectRadioButton(correct_index);
//--- 切换图片
   for(uint i=0; i<pictures_total; i++)
     {
      if(i==correct_index)
         m_pictures[i].Timeframes(OBJ_ALL_PERIODS);
      else
         m_pictures[i].Timeframes(OBJ_NO_PERIODS);
     }
  }

当处理箭头按钮时, 控件的事件处理器调用 CPicturesSlider::OnClickLeftArrow() 和 CPicturesSlider::OnClickRightArrow() 方法。下列清单显示了鼠标左键方法的代码。如果必要, 点击图片滑块按钮的事件 可在 MQL 应用程序的自定义类中跟踪。 

class CPicturesSlider : public CElement
  {
public:
private:
   //--- 点击左按钮处理器
   bool              OnClickLeftArrow(const string clicked_object);
   //--- 点击右按钮处理器
   bool              OnClickRightArrow(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| 点击鼠标左键                                                                             |
//+------------------------------------------------------------------+
bool CPicturesSlider::OnClickLeftArrow(const string clicked_object)
  {
//--- 如果无按钮点击, 离开
   if(::StringFind(clicked_object,CElement::ProgramName()+"_icon_button_",0)<0)
      return(false);
//--- 从对象名获取控件标识符
   int id=CElement::IdFromObjectName(clicked_object);
//--- 从对象名获取控件索引
   int index=CElement::IndexFromObjectName(clicked_object);
//--- 如果控件标识符不匹配, 离开
   if(id!=CElement::Id())
      return(false);
//--- 如果控件索引不匹配, 离开
   if(index!=0)
      return(false);
//--- 获取当前选中单选按钮的索引
   int selected_radio_button=m_radio_buttons.SelectedButtonIndex();
//--- 切换图片
   SelectPicture(--selected_radio_button);
//--- 发送有关消息
   ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),CElement::Index(),"");
   return(true);
  }

下列清单显示图片滑块事件处理程序的缩减代码。显然, 此处也跟踪滑块单选按钮的点击事件。可以理解, 点击一个本地组当中的单选按钮使用控件的标识符, 其 等于图片滑块标识符。 

//+------------------------------------------------------------------+
//| 事件处理器                                                                                |
//+------------------------------------------------------------------+
void CPicturesSlider::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 光标移动事件处理器
...
//--- 单选按钮点击事件处理器
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      //--- 如果这是滑块的单选按钮, 切换图片
      if(lparam==CElement::Id())
         SelectPicture(m_radio_buttons.SelectedButtonIndex());
      //---
      return;
     }
//--- 在对象上点击鼠标左键处理器
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- 如果点击滑块的箭头按钮, 切换图片
      if(OnClickLeftArrow(sparam))
         return;
      if(OnClickRightArrow(sparam))
         return;
      //---
      return;
     }
  }

 

文本标签和图片控件

作为补充, 函数库现在包括两个新的 CTextLabelCPicture 类用于创建简单文本标签和图片控件。它们可作为单独对象, 无需将它们与任何其它控件绑定。它们的内容很简单。在 CPicture 类中, 用户仅可改变一个属性 - 图片路径。方法 CPicture::Path() 已为此目的而实现。除非未指定自定义路径, 否则将使用 省缺图片。即使在创建 MQL 应用程序的图形界面之后, 也可以随时以编程方式更改图片。

//+------------------------------------------------------------------+
//|                                                      Picture.mqh |
//|                        版权所有 2016, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//--- 资源
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| 创建图片的类                                                                            |
//+------------------------------------------------------------------+
class CPicture : public CElement
  {
private:
   //--- 图片路径
   string            m_path;
   //---
public:
   //--- 图片路径取值/赋值
   string            Path(void)               const { return(m_path);             }
   void              Path(const string path);
  };
//+------------------------------------------------------------------+
//| 构造器                                                                                        |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp")

  {
//--- 在基类中保存控件类的名字
   CElement::ClassName(CLASS_NAME);
//--- 设置鼠标左键点击优先权
   m_zorder=0;
  }
//+------------------------------------------------------------------+
//| 设置图片                                                                                    |
//+------------------------------------------------------------------+
void CPicture::Path(const string path)
  {
   m_path=path;
   m_picture.BmpFileOn("::"+path);
   m_picture.BmpFileOff("::"+path);
  }

对于文本标签控件, 一切都很简单, 只有四个属性可以由用户定义:

  • 标签的文本
  • 文本颜色
  • 字体
  • 字号
//+------------------------------------------------------------------+
//| 创建文本标签的类                                                                    |
//+------------------------------------------------------------------+
class CTextLabel : public CElement
  {
public:
   //--- 标签文本取值/赋值
   string            LabelText(void)             const { return(m_label.Description()); }
   void              LabelText(const string text)      { m_label.Description(text);     }
   //--- 设置文本标签的 (1) 颜色, (2) 字体和 (3) 字号
   void              LabelColor(const color clr)       { m_label.Color(clr);            }
   void              LabelFont(const string font)      { m_label.Font(font);            }
   void              LabelFontSize(const int size)     { m_label.FontSize(size);        }
  };

 

处理字体的 CFonts 类

为了方便字体选择, 已实现了另一个 CFonts 类。它包括 187 种字体。这些是终端的系统字体, 您可能已经在某些图形对象的设置中看到过清单。

 图例. 3. 终端的系统字体。

图例. 3. 终端的系统字体。


字体文件 (Fonts.mqh) 位于 "MetaTrader 5\MQL5\Include\EasyAndFastGUI\Fonts.mqh" 目录。它已经被包含在 Objects.mqh 文件中, 函数库的整个方案当中随处均可完全访问。

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        版权所有 2015, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include "..\Fonts.mqh"
#include "..\Canvas\Charts\LineChart.mqh"
#include <ChartObjects\ChartObjectSubChart.mqh>
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>

CFonts 仅包括两个公共方法 来获取字体数组大小 以及 按照索引获取字体名称。字体数组 应在类的构造器中初始化。 

//+------------------------------------------------------------------+
//|                                                        Fonts.mqh |
//|                        版权所有 2016, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 操纵字体的类                                                                            |
//+------------------------------------------------------------------+
class CFonts
  {
private:
   //--- 字体数组
   string            m_fonts[];
   //---
public:
                     CFonts(void);
                    ~CFonts(void);
   //--- 返回字体数量
   int               FontsTotal(void) const { return(::ArraySize(m_fonts)); }
   //--- 按照索引返回字体
   string            FontsByIndex(const uint index);
   //---
private:
   //--- 初始化字体数组
   void              InitializeFontsArray(void);
  };
//+------------------------------------------------------------------+
//| 构造器                                                                                         |
//+------------------------------------------------------------------+
CFonts::CFonts(void)
  {
//--- 初始化字体数组
   InitializeFontsArray();
  }
//+------------------------------------------------------------------+
//| 析构器                                                                                         |
//+------------------------------------------------------------------+
CFonts::~CFonts(void)
  {
   ::ArrayFree(m_fonts);
  }

调用 CFonts::FontsByIndex() 方法进行 调整防止超出数组范围:

//+------------------------------------------------------------------+
//| 按照索引返回                                                                            |
//+------------------------------------------------------------------+
string CFonts::FontsByIndex(const uint index)
  {
//--- 数组大小
   uint array_size=FontsTotal();
//--- 范围超出的情况下进行调整
   uint i=(index>=array_size)?array_size-1 : index;
//--- 返回字体
   return(m_fonts[i]);
  }

 

其它的函数库更新清单

1. 修复了对话框中不正确的工具提示显示。现在, 主窗口中工具提示按钮的按下状态通用于图形界面的所有窗口。按下按钮会生成一条带有新 ON_WINDOW_TOOLTIPS 事件标识符的消息 (参见 Defines.mqh 文件)。

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        版权所有 2015, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
#define ON_WINDOW_TOOLTIPS         (29) // 在工具提示按钮上点击

相应地, 方法 OnClickTooltipsButton() 已被添加到 CWindow 类中来处理工具提示按钮: 

//+------------------------------------------------------------------+
//| 创建控件表单的类                                                                     |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
private:
   //--- 处理点击工具按钮事件
   bool              OnClickTooltipsButton(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| 图表事件处理器                                                                        |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 处理对象点击事件
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- 如果工具按钮已被点击
      if(OnClickTooltipsButton(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| 处理点击工具按钮事件                                                            |
//+------------------------------------------------------------------+
bool CWindow::OnClickTooltipsButton(const string clicked_object)
  {
//--- 如果窗口是一个对话框, 则无需此按钮
   if(m_window_type==W_DIALOG)
      return(false);
//--- 如果无按钮点击, 离开
   if(::StringFind(clicked_object,CElement::ProgramName()+"_window_tooltip_",0)<0)
      return(false);
//--- 从对象名获取控件标识符
   int id=CElement::IdFromObjectName(clicked_object);
//--- 如果控件标识符不匹配, 离开
   if(id!=CElement::Id())
      return(false);
//--- 在类字段中保存状态
   m_tooltips_button_state=m_button_tooltip.State();
//--- 发送有关消息
   ::EventChartCustom(m_chart_id,ON_WINDOW_TOOLTIPS,CElement::Id(),CElement::Index(),"");
   return(true);
  }

为了所有这些能在函数库引擎中工作 (CWndEvents 类), 已加入了处理 ON_WINDOW_TOOLTIPS 标识符的 OnWindowTooltips() 方法: 

class CWndEvents : public CWndContainer
  {
private:
   //--- 启用/禁用工具提示
   bool              OnWindowTooltips(void);
  };
//+------------------------------------------------------------------+
//| CHARTEVENT_CUSTOM 事件                                               |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
  {
//--- 如果信号是表单最小化
//--- 如果信号是表单最大化
//--- 如果信号是控件沿 X 轴调整大小
//--- 如果信号是控件沿 Y 轴调整大小
//--- 如果信号是启用/禁用工具提示
   if(OnWindowTooltips())
      return;

//--- 如果信号是隐藏初始项目下的上下文菜单
//--- 如果信号是隐藏所有上下文菜单
//--- 如果信号是打开对话窗口
//--- 如果信号是关闭对话窗口
//--- 如果信号是清除指定表单上所有元素的颜色
//--- 如果信号是重设鼠标左键点击的优先权
//--- 如果信号是恢复鼠标左键点击的优先权
  }
//+------------------------------------------------------------------+
//| ON_WINDOW_TOOLTIPS 事件                                        |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowTooltips(void)
  {
//--- 如果信号是 "启用/禁用工具提示"
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_TOOLTIPS)
      return(false);
//--- 如果窗口标识符匹配
   if(m_lparam!=m_windows[0].Id())
      return(true);
//--- 在所有窗口中同步工具提示模式
   int windows_total=WindowsTotal();
   for(int w=0; w<windows_total; w++)
     {
      if(w>0)
         m_windows[w].TooltipButtonState(m_windows[0].TooltipButtonState());
     }
//---
   return(true);
  }

2. 以下控件创建后为其添加修改描述文本的能力: 

 

图例. 4. 控件创建后有能力更改文本的列表。


3. 现在可在所有控件中设置图标, 这可能是必要的 (请参阅下表)。此外, 添加了在控件图标创建后更改控件图标的功能:


 

图例. 5. 控件创建后有能力更改图标的列表。

 

若要替换上表所列所有控件中的图标, 存在 IconFileOn() 和 IconFileOff() 方法。


4. 添加了在按钮和选项卡创建之后以编程方式管理所有类型状态 (按下/释放) 的能力。下表显示了包含此添加的控件:

 

图例. 6. 控件创建后有能力更改状态 (按下/释放) 的列表。


5. 优化当光标悬停在以下控件时高亮显示项目的算法:

 

图例. 7. 带有优化算法的控件, 高亮显示控件项目。

 

以前, 程序遍历上述控件列表中的所有项目, 检查鼠标光标在它们上方的位置。因此, 光标下的项目以不同的颜色高亮显示, 而默认设置的颜色则用于其余项目。但此方法属于资源密集型, 所以需要优化。现在, 无需遍历整个数组, 只有两个项目参与颜色变化。仅在项目转换时进行周期搜索, 即焦点已经改变。 

进而, 作为示例, 考虑如何在 CListView 类中实现。实现上述需要添加 (1) m_prev_item_index_focus 类字段来保存最后关注项目的索引, (2) CListView::CheckItemFocus() 方法来检查关注项目, 以及 (3) 修改在 CListView::ChangeItemsColor() 方法中的算法。  

//+------------------------------------------------------------------+
//| 用于创建列表视图的类                                                            |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
private:
   //--- 判断鼠标光标从一个项目转换到另一个项目的时刻
   int               m_prev_item_index_focus;
   //---
private:
   //--- 当光标悬停在列表视图项目上时更改列表视图项目的颜色
   void              ChangeItemsColor(void);
   //--- 当光标悬停时, 检查列表视图项的焦点
   void              CheckItemFocus(void);
  };

方法 CListView::CheckItemFocus() 仅在鼠标进入控件区域时才被调用 (在此情况下 – CListView), 以及鼠标从一个项目移动到另一个项目时 (参见以下代码)。一旦发现鼠标悬停的项目, 其 索引被保存。 

//+------------------------------------------------------------------+
//| 当光标悬停时, 检查列表视图项目的焦点                              |
//+------------------------------------------------------------------+
void CListView::CheckItemFocus(void)
  {
//--- 获取滚动条滑块的当前位置
   int v=m_scrollv.CurrentPos();
//--- 标识光标悬停所在项目, 并高亮显示它
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- 如果未超出列表视图范围, 计数器增长
      if(v>=0 && v<m_items_total)
         v++;
      //--- 跳过所选项目
      if(m_selected_item_index==v-1)
        {
         m_items[i].BackColor(m_item_color_selected);
         m_items[i].Color(m_item_text_color_selected);
         continue;
        }
      //--- 如果光标悬停于此项目, 将之高亮
      if(m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
         m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2())
        {
         m_items[i].BackColor(m_item_color_hover);
         m_items[i].Color(m_item_text_color_hover);
         //--- 记住此项目
         m_prev_item_index_focus=i;
         break;
        }
     }
  }

方法 CListView::CheckItemFocus() 的调用来自 CListView::ChangeItemsColor() 方法之内, 如同以前段落所描述的那样 (参见以下代码清单):

//+------------------------------------------------------------------+
//| 当光标悬停时更改列表视图项的颜色                                    |
//+------------------------------------------------------------------+
void CListView::ChangeItemsColor(void)
  {
//--- 若光标悬停禁用或滚动条已激活, 离开
   if(!m_lights_hover || m_scrollv.ScrollState())
      return;
//--- 如果不是下拉元素, 并且表单被阻塞, 离开
   if(!CElement::IsDropdown() && m_wnd.IsLocked())
      return;
//--- 如果再次进入列表视图
   if(m_prev_item_index_focus==WRONG_VALUE)
     {
      //--- 检查当前项目的焦点
      CheckItemFocus();
     }
   else
     {
      //--- 检查当前行的焦点
      int i=m_prev_item_index_focus;
      bool condition=m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
                     m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2();
      //--- 如果移至另一个项目
      if(!condition)
        {
         //--- 重置前次项目的颜色
         m_items[i].BackColor(m_item_color);
         m_items[i].Color(m_item_text_color);
         m_prev_item_index_focus=WRONG_VALUE;
         //--- 检查当前项目的焦点
         CheckItemFocus();
        }
     }
  }

CListView::OnEvent() 事件处理器中, 方法 CListView::ChangeItemsColor() 仅当鼠标光标位于控件区域内才会调用。一旦光标离开控件区域, 将设置默认颜色, 并重置 项目索引值。事件处理程序的缩短版本如下所示。 

//+------------------------------------------------------------------+
//| 事件处理器                                                                                |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 光标移动事件处理器
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- 如果元素隐藏, 离开
      //--- 如果子窗口编号不匹配, 离开
      //--- 检查聚焦元素
      //--- 如果是下拉列表且已按下鼠标按键
      //--- 如果滑块管理启用则移动列表

      //--- 如果未聚焦, 重置元素颜色
      if(!CElement::MouseFocus())
        {
         //--- 如果项目已聚焦
         if(m_prev_item_index_focus!=WRONG_VALUE)
           {
            //--- 重置列表视图颜色
            ResetColors();
            m_prev_item_index_focus=WRONG_VALUE;
           }
         return;
        }
      //--- 当光标悬停于列表视图的项目上时, 更改列表视图项目的颜色
      ChangeItemsColor();
      return;
     }
  }

相同的原理已在 CTable, CCalendarCTreeView 类中实现, 但考虑到每个控件的特性略有某些差异。

6. 在双状态模式下按下 CIconButton 类型的按钮 (在单击后不释放按钮) 会显示不同的图标, 如果已设置。已按下按钮的图表可使用 CIconButton::IconFilePressedOn()CIconButton::IconFilePressedOff() 方法来设置。 

//+------------------------------------------------------------------+
//| 创建图标按钮的类 |
//+------------------------------------------------------------------+
class CIconButton : public CElement
  {
private:
   //--- 按钮激活图标, 阻塞且按下状态
   string            m_icon_file_on;
   string            m_icon_file_off;
   string            m_icon_file_pressed_on;
   string            m_icon_file_pressed_off;

   //---
public:
   //--- 设置按钮释放图标, 激活且阻塞状态
   void              IconFileOn(const string file_path);
   void              IconFileOff(const string file_path);
   void              IconFilePressedOn(const string file_path);
   void              IconFilePressedOff(const string file_path);

  };
//+------------------------------------------------------------------+
//| 设置已按下 "ON" 状态的图标                                                 |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOn(const string file_path)
  {
//--- 如果按钮的双态模式禁用, 离开
   if(!m_two_state)
      return;
//--- 保存图片路径
   m_icon_file_pressed_on=file_path;
//--- 立刻判断按钮是否已按下
   if(m_button.State())
      m_icon.BmpFileOn("::"+file_path);
  }
//+------------------------------------------------------------------+
//| 设置已按下 "OFF" 状态的图标                                                |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOff(const string file_path)
  {
//--- 如果按钮的双态模式禁用, 离开
   if(!m_two_state)
      return;
//--- 保存图片路径
   m_icon_file_pressed_off=file_path;
//--- 立刻判断按钮是否已按下
   if(m_button.State())
      m_icon.BmpFileOff("::"+file_path);
  }

7. 添加了在表中 (CTable) 以编程方式选择行的能力。为此, 使用 CTable::SelectRow() 方法。指定已选择行的索引并取消选择。 

//+------------------------------------------------------------------+
//| 创建编辑框表单的类 |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
public:
   //--- 选择指定的表单行
   void              SelectRow(const uint row_index);
  };
//+------------------------------------------------------------------+
//| 选择指定的表单行                                                                    |
//+------------------------------------------------------------------+
void CTable::SelectRow(const uint row_index)
  {
//--- 在超出范围的情况下进行调整
   uint index=(row_index>=(uint)m_rows_total)?m_rows_total-1 : row_index;
//--- 如果已经选择此行, 则取消选择
   bool is_selected=(index==m_selected_item);
//--- 保存行索引
   m_selected_item=(is_selected)?WRONG_VALUE : (int)index;
//--- 保存单元行
   m_selected_item_text=(is_selected)?"" : m_vcolumns[0].m_vrows[index];
//--- 生成单元参数的字符串
   string cell_params=string(0)+"_"+string(index)+"_"+m_vcolumns[0].m_vrows[index];
//--- 重置焦点
   m_prev_item_index_focus=WRONG_VALUE;
//--- 更新表格
   UpdateTable();
//--- 高亮已选行
   HighlightSelectedItem();
  }

8. 修复了 CIconTabs 控件中选定选项卡显示元素的问题。问题会发生在使用此类型的选项卡打开并最大化表单时。 

 

测试控件的应用程序

我们来编写一个测试应用程序, 在此您可亲身参与所有新控件的测试, 评估它们的不同模式。应用程序的图形界面中,创建一个 Tabs 控件 (CTabs 类), 其内包括含有以下内容的四个选卡:

1. 第一个选卡:

  • 进度条 (CProgressBar)。
  • 文本编辑框 (CTextEdit)。
  • 带有一个下拉列表的复合选择框 (CCombobox)。
  • 轮转数值编辑框 (CSpinEdit)。
  • 调用选色器的按钮 (CColorButton)。
  • 文本标签 (CTextLabel)。

为所有控件设置图标, 文本标签除外。进度条和文本编辑框控件的描述将以固定的时间间隔变更, 以便演示此类现在可用的功能。在复合框列表中放置来自 CFonts 类的名称。MQL 测试应用程序的事件模型将以这样的方式构建, 即从组合框中选择一种字体, 并将之反映在文本标签中。类似地, 文本标签的字号将被绑定到轮转数字编辑框以改变字体大小, 并在选择器中选择颜色。 

用于管理文本标签控件参数的事件处理程序将如下所示:

//+------------------------------------------------------------------+
//| 图表事件处理器                                                                        |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 从复选框列表中进行选择的事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_combobox1.Id())
        {
         //--- 改变字体
         m_text_label1.LabelFont(m_combobox1.ButtonText());
        }
      //---
      return;
     }
//--- 点击轮转编辑框按钮的事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC ||
      id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_spin_edit1.Id())
        {
         //--- 改变字号
         m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
        }
      //---
      return;
     }
//--- 使用择色器变更颜色的事件
   if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_spin_edit1.Id())
        {
         //--- 改变字号
         m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
        }
      //---
      return;
     }
//--- 使用择色器变更颜色的事件
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE_COLOR)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_color_picker.Id())
        {
         //--- 如果从第一个按钮得到响应
         if(sparam==m_color_button1.LabelText())
           {
            //--- 改变对象颜色
            m_text_label1.LabelColor(m_color_button1.CurrentColor());
            return;
           }
        }
      return;
     }
//--- 按钮按下的事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      //--- 如果调用颜色选择器的第一个按钮按下
      if(sparam==m_color_button1.LabelText())
        {
         //--- 传递按钮指针, 使用颜色选择器自动打开窗口
         m_color_picker.ColorButtonPointer(m_color_button1);
         return;
        }
      //---
      return;
     }
  }

下面的屏幕截图显示了如何使用图形界面配置文本显示。

图例. 8. 第一个选项卡上的控件组。 

图例. 8. 第一个选项卡上的控件组。


2. 仅有一个控件 – 图片滑块 (CPicturesSlider 类) 将会放置在第二个选卡上。只有三个省缺图片将被添加到组, 以便您可以自行快速测试此控件。若要令控件正常工作, 请准备相同尺寸的图片。

图例. 9. 第二个选项卡上的图片滑块控件。 

图例. 9. 第二个选项卡上的图片滑块控件。


若要以编程方式切换图片, 请使用 CPicturesSlider::SelectPicture() 方法。


3. 第三个选项卡将包含 CTable 类型的表格。若要以编程方式选择一行, 请使用 CTable::SelectRow() 方法。 

 图例. 10. 第三个选项卡上的表格控件。

图例. 10. 第三个选项卡上的表格控件。


4. 三个控件将位于第四选项卡上: (1) 日历, (2) 下拉日历, 和 (3) 带有按压/释放两个不同状态图标的按钮。

图例. 11. 第四个选项卡上的控件组。 

图例. 11. 第四个选项卡上的控件组。

 

在此文章中介绍的测试应用程序可从以下链接下载, 以供进一步学习。 


结论

用于创建图形界面的函数库, 当前开发阶段如下图所示。

 图例. 12. 当前开发阶段的函数库结构。

图例. 12. 当前开发阶段的函数库结构。


在下一个版本中, 函数库将使用其它控件进行扩展。此外, 现有的控件将会进一步开发并扩展新的功能。

如果您在使用这些文件所提供的素材时有任何疑问, 可以参考函数库开发系列文章之一的详细描述, 或在本文的评论中提出您的问题。 

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/2829

附加的文件 |
带有图形用户界面的通用震荡指标 带有图形用户界面的通用震荡指标

本文描述了创建基于终端中所有震荡指标的通用指标的过程,并且指标中还带有自身的图形界面。该图形界面(GUI)使用户可以简单快速地直接在图表窗口中修改每个震荡指标的设置(不需要打开它的属性), 以及比较它们的数值和为特定的任务选取最佳的选项。

通用的之字转向指标 通用的之字转向指标

之字转向指标(ZigZag)是在 MetaTrader 5 用户中最流行的指标之一,本文分析了创建各种版本的之字转向指标的可能性,结果是一个可以使用各种方法扩展其功能的通用指标,它对EA交易和其他指标的开发会非常有用。

直方图形式的统计分布, 无需指标缓冲区和数组 直方图形式的统计分布, 无需指标缓冲区和数组

本文讨论当绘制市场条件的统计分布直方图时利用图形存储器的可能性, 而无需指标缓冲区和数组。描述了样本直方图的细节, 并展示了 MQL5 图形对象的 "隐藏" 功能。

交易货币篮子时可用的形态 交易货币篮子时可用的形态

跟随我们以前关于货币篮子交易原理的文章, 这里我们将分析交易者可以检测的形态。我们还将研究每种形态的优点和缺点, 并就其使用提供一些建议。基于威廉姆斯振荡器的指标将用作分析工具。