内容
- 概论
- 文本编辑框控件
- 创建文本编辑框控件的类
- 图片滑块控件
- 创建图片滑块控件的类
- 文本标签和图片控件
- 处理字体的 CFonts 类
- 其它的函数库更新清单
- 测试控件的应用程序
- 结论
概论
为了更加理解此函数库的目的, 请阅读首篇文章: 图形界面 I: 准备函数库结构 (第 1 章)。您在每一章节的末尾可找到文章清单的链接。您也可以从那里下载当前正在开发阶段的函数库完整版。这些文件必须按照与存档相同的目录存放。
本文研究新的控件: 文本编辑框, 图片滑块, 以及其它的简单控件: 文本标签和图片, 这些在各种情况下都很有用。函数库正在持续增长, 并引入了一些其它的新控件, 以前创建的那些也有所改进。由于函数库目前被许多用户使用, 从他们那里收到了许多评论和建议。这些请求的大部分已在新版函数库中实现。外加, 某些算法已经优化。这将降低 CPU 负载。有关这方面的更多细节会在文章中进一步阐述。
文本编辑框控件
迄今为止, 已开发的函数库包含了一个编辑框控件 (CSpinEdit 类), 但它只用于输入数值。现在函数库将补充另一个控件, 它将允许输入任何文本到字段。在不同的情况下也许都需要文本编辑框控件。例如, 可以在终端 "沙盒" 的文件中创建字符串搜索。另一个选项是为 MQL 应用程序的最终用户提供输入交易品种数组的能力。简言之, 它可以是任何处理所需的文本数据。
我们来枚举文本编辑框控件的所有组件:
- 背景
- 图标
- 描述
- 编辑框
图例. 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 方法:
{
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 应用程序中特定图形界面控件用途的简要说明。
我们来枚举图片滑块控件的所有组件:
- 背景
- 滑块箭头按钮
- 单选按钮组
- 与单选按钮组相关联的图像组
图例. 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 方法:
{
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 中调用。
{
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 应用程序的自定义类中跟踪。
{
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;
}
}
文本标签和图片控件
作为补充, 函数库现在包括两个新的 CTextLabel 和 CPicture 类用于创建简单文本标签和图片控件。它们可作为单独对象, 无需将它们与任何其它控件绑定。它们的内容很简单。在 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. 终端的系统字体。
字体文件 (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() 方法:
{
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, CCalendar 和 CTreeView 类中实现, 但考虑到每个控件的特性略有某些差异。
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. 第一个选项卡上的控件组。
2. 仅有一个控件 – 图片滑块 (CPicturesSlider 类) 将会放置在第二个选卡上。只有三个省缺图片将被添加到组, 以便您可以自行快速测试此控件。若要令控件正常工作, 请准备相同尺寸的图片。
图例. 9. 第二个选项卡上的图片滑块控件。
若要以编程方式切换图片, 请使用 CPicturesSlider::SelectPicture() 方法。
3. 第三个选项卡将包含 CTable 类型的表格。若要以编程方式选择一行, 请使用 CTable::SelectRow() 方法。
图例. 10. 第三个选项卡上的表格控件。
4. 三个控件将位于第四选项卡上: (1) 日历, (2) 下拉日历, 和 (3) 带有按压/释放两个不同状态图标的按钮。
图例. 11. 第四个选项卡上的控件组。
在此文章中介绍的测试应用程序可从以下链接下载, 以供进一步学习。
结论
用于创建图形界面的函数库, 当前开发阶段如下图所示。
图例. 12. 当前开发阶段的函数库结构。
在下一个版本中, 函数库将使用其它控件进行扩展。此外, 现有的控件将会进一步开发并扩展新的功能。
如果您在使用这些文件所提供的素材时有任何疑问, 可以参考函数库开发系列文章之一的详细描述, 或在本文的评论中提出您的问题。