图形对象事件
对于图表上的 图形对象 ,终端会生成多种专门的事件。其中大部分事件适用于任何类型的对象。输入字段中的文本编辑结束事件 CHARTEVENT_OBJECT_ENDEDIT 仅针对 OBJ_EDIT 类型的对象生成。
对象点击事件 (CHARTEVENT_OBJECT_CLICK)、鼠标拖动事件 (CHARTEVENT_OBJECT_DRAG) 和对象特性更改事件 (CHARTEVENT_OBJECT_CHANGE) 始终处于激活状态,而对象创建事件 (CHARTEVENT_OBJECT_CREATE) 和对象删除事件 (CHARTEVENT_OBJECT_DELETE) 则需要通过设置图表的相关特性来显式启用:CHART_EVENT_OBJECT_CREATE 和 CH ART_EVENT_OBJECT_DELETE。
当手动重命名对象时(通过特性对话框),终端会依次生成事件序列:CHARTEVENT_OBJECT_DELETE、CHARTEVENT_OBJECT_CREATE、CHARTEVENT_OBJECT_CHANGE。通过编程方式重命名对象时,不会生成这些事件。
所有对象事件都会在 OnChartEvent函数的 sparam 参数中携带关联对象的名称。
此外,CHARTEVENT_OBJECT_CLICK 事件会传递点击坐标:X 存储在 lparam参数中,Y 存储在dparam 参数中。坐标在整个图表中通用(包括子窗口)。
点击不同类型的对象时,其工作机制会有所不同。对于部分对象(如椭圆),光标必须位于任意锚点上方。对于其他对象(如三角形、矩形、线条),光标可以位于对象周边区域(不仅限于点)。在所有这些情况下,当鼠标光标悬停在对象的交互式区域时,将显示包含对象名称的工具提示。
与屏幕坐标绑定的对象(可用于构建程序图形界面,例如按钮、输入字段和矩形面板),鼠标点击对象内部任意位置时将生成事件。
如果光标所在处有多个对象,则仅对 Z 轴优先级最高的对象生成事件。如果多个对象优先级相同,则事件将被分配给创建时间较晚的对象(这与其视觉显示行为一致,即后创建的对象会覆盖更早创建的对象)。
指标新版本EventAllObjects.mq5可用于检测对象事件。我们将使用已熟悉的多对象 Object Selector类创建并配置该指标,然后在 OnChartEvent 处理程序中捕捉其特征事件。
#include <MQL5Book/ObjectMonitor.mqh>
class ObjectBuilder: public ObjectSelector
{
protected:
const ENUM_OBJECT type;
const int window;
public:
ObjectBuilder(const string _id, const ENUM_OBJECT _type,
const long _chart = 0, const int _win = 0):
ObjectSelector(_id, _chart), type(_type), window(_win)
{
ObjectCreate(host, id, type, window, 0, 0);
}
};
|
首先,在OnInit中创建一个按钮对象和一条垂直线。针对该线条,我们将追踪其移动(拖拽)事件,当用户按下按钮时,我们将创建一个输入字段,并对输入的文本进行校验。
const string ObjNamePrefix = "EventShow-";
const string ButtonName = ObjNamePrefix + "Button";
const string EditBoxName = ObjNamePrefix + "EditBox";
const string VLineName = ObjNamePrefix + "VLine";
bool objectCreate, objectDelete;
void OnInit()
{
// remember the original settings to restore in OnDeinit
objectCreate = ChartGetInteger(0, CHART_EVENT_OBJECT_CREATE);
objectDelete = ChartGetInteger(0, CHART_EVENT_OBJECT_DELETE);
// set new properties
ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
ObjectBuilder button(ButtonName, OBJ_BUTTON);
button.set(OBJPROP_XDISTANCE, 100).set(OBJPROP_YDISTANCE, 100)
.set(OBJPROP_XSIZE, 200).set(OBJPROP_TEXT, "Click Me");
ObjectBuilder line(VLineName, OBJ_VLINE);
line.set(OBJPROP_TIME, iTime(NULL, 0, 0))
.set(OBJPROP_SELECTABLE, true).set(OBJPROP_SELECTED, true)
.set(OBJPROP_TEXT, "Drag Me").set(OBJPROP_TOOLTIP, "Drag Me");
ChartRedraw();
}
|
在此过程中,不要忘记将图表特性 CHART_EVENT_OBJECT_CREATE 和 CHART_EVENT_OBJECT_DELETE 设置为 true ,以便在对象集合发生更改时获得通知。
在 OnChartEvent函数中,我们将对必要事件提供额外响应:拖拽操作完成后,我们将在日志中显示线条的新位置,并在输入字段的文本编辑完成后,显示其内容。
void OnChartEvent(const int id,
const long &lparam, const double &dparam, const string &sparam)
{
ENUM_CHART_EVENT evt = (ENUM_CHART_EVENT)id;
PrintFormat("%s %lld %f '%s'", EnumToString(evt), lparam, dparam, sparam);
if(id == CHARTEVENT_OBJECT_CLICK && sparam == ButtonName)
{
if(ObjectGetInteger(0, ButtonName, OBJPROP_STATE))
{
ObjectBuilder edit(EditBoxName, OBJ_EDIT);
edit.set(OBJPROP_XDISTANCE, 100).set(OBJPROP_YDISTANCE, 150)
.set(OBJPROP_BGCOLOR, clrWhite)
.set(OBJPROP_XSIZE, 200).set(OBJPROP_TEXT, "Edit Me");
}
else
{
ObjectDelete(0, EditBoxName);
}
ChartRedraw();
}
else if(id == CHARTEVENT_OBJECT_ENDEDIT && sparam == EditBoxName)
{
Print(ObjectGetString(0, EditBoxName, OBJPROP_TEXT));
}
else if(id == CHARTEVENT_OBJECT_DRAG && sparam == VLineName)
{
Print(TimeToString((datetime)ObjectGetInteger(0, VLineName, OBJPROP_TIME)));
}
}
|
请注意,当首次按下按钮时,其状态将从释放转变为按下,我们将针对这一状态变化创建一个输入字段。如果再次点击按钮,其状态会恢复原状,输入字段将从图表中移除。
以下是指标运行时的图表图像。

由 OnChartEvent 事件处理程序控制的对象
指标启动后,日志中会立即显示以下内容:
CHARTEVENT_OBJECT_CREATE 0 0.000000 'EventShow-Button'
CHARTEVENT_OBJECT_CREATE 0 0.000000 'EventShow-VLine'
CHARTEVENT_CHART_CHANGE 0 0.000000 ''
|
如果我们用鼠标拖拽线条,将看到类似如下的信息:
CHARTEVENT_OBJECT_DRAG 0 0.000000 'EventShow-VLine'
2022.01.05 10:00
|
接下来,你可以点击按钮,并在新创建的输入字段中编辑文本(编辑完成后按Enter键或点击输入字段外部)。这将在日志中生成以下条目(坐标和消息文本可能不同,此处输入的文本为 "new message"):
CHARTEVENT_OBJECT_CLICK 181 113.000000 'EventShow-Button'
CHARTEVENT_CLICK 181 113.000000 ''
CHARTEVENT_OBJECT_CREATE 0 0.000000 'EventShow-EditBox'
CHARTEVENT_OBJECT_CLICK 152 160.000000 'EventShow-EditBox'
CHARTEVENT_CLICK 152 160.000000 ''
CHARTEVENT_OBJECT_ENDEDIT 0 0.000000 'EventShow-EditBox'
new message
|
如果随后松开按钮,输入字段将被删除。
CHARTEVENT_OBJECT_CLICK 162 109.000000 'EventShow-Button'
CHARTEVENT_CLICK 162 109.000000 ''
CHARTEVENT_OBJECT_DELETE 0 0.000000 'EventShow-EditBox'
|
值得注意的是,该按钮默认为双位开关,即每次鼠标点击会使其在按下和释放状态之间交替切换。对于普通按钮,这种行为是多余的:如果仅需追踪按钮的按下操作,应在事件处理过程中通过调用 ObjectSetInteger(0, ButtonName, OBJPROP_STATE, false)将其恢复为释放状态。