图形界面 V: 列表视图元件 (第二章)·综合运用
◍ 列表视图控件的属性预设与填充
在 MT5 自定义面板里,列表视图(CListView)必须在 CreateListView 之前把尺寸、悬停高亮、可见行数等属性定好,否则运行期再改容易闪退或重绘异常。 上面这段把列表总条数设为 ITEMS_TOTAL1,可见区域固定 10 行,并打开 LightsHover(true) 让鼠标悬停行有高亮反馈;SelectedItemIndex(5) 则默认选中第 6 项(索引从 0 计)。 纵向滚动条的颜色分三态:普通边框 C'190,190,190'、悬停 C'180,180,180'、按下 C'160,160,160',直接拿 CScrollV 指针改即可,不必重写类。 数据用 for 循环从 r=0 填到 ITEMS_TOTAL1-1,每行写 "SYMBOL "+string(r);CreateListView 返回 false 就整体初始化失败。CreateTradePanel 里先调 CreateListView1 再 m_chart.Redraw(),面板才出图。外汇与贵金属界面开发属高风险环境,参数写错可能导致终端卡死,建议先在策略测试器挂副图验证。
class="type">int y=m_window1.Y()+LISTVIEW1_GAP_Y; class=class="str">"cmt">//--- 在创建之前设置属性 m_listview1.XSize(class="num">100); m_listview1.LightsHover(true); m_listview1.ListSize(ITEMS_TOTAL1); m_listview1.VisibleListSize(class="num">10); m_listview1.AreaBorderColor(clrDarkGray); m_listview1.SelectedItemIndex(class="num">5); class=class="str">"cmt">//--- 取得滚动条指针 CScrollV *sv=m_listview1.GetScrollVPointer(); class=class="str">"cmt">//--- 滚动条的属性 sv.ThumbBorderColor(C&class="macro">#x27;class="num">190,class="num">190,class="num">190&class="macro">#x27;); sv.ThumbBorderColorHover(C&class="macro">#x27;class="num">180,class="num">180,class="num">180&class="macro">#x27;); sv.ThumbBorderColorPressed(C&class="macro">#x27;class="num">160,class="num">160,class="num">160&class="macro">#x27;); class=class="str">"cmt">//--- 使用数据填充列表视图 for(class="type">int r=class="num">0; r<ITEMS_TOTAL1; r++) m_listview1.ValueToList(r,"SYMBOL "+class="type">class="kw">string(r)); class=class="str">"cmt">//--- 创建列表视图 if(!m_listview1.CreateListView(m_chart_id,m_subwin,x,y)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 把元件指针加到库中 CWndContainer::AddToElementsArray(class="num">0,m_listview1); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 创建交易面板 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CProgram::CreateTradePanel(class="type">void) { class=class="str">"cmt">//--- 为控件创建表单1 class=class="str">"cmt">//--- 创建控件: class=class="str">"cmt">// 主菜单 class=class="str">"cmt">//--- 上下文菜单 class=class="str">"cmt">//--- 创建状态栏 class=class="str">"cmt">//--- 列表视图 if(!CreateListView1()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 重绘图表 m_chart.Redraw(); class="kw">return(true); }
列表视图的悬停变色与滚动联动实现
做自定义列表控件时,最容易被忽略的是鼠标悬停反馈。CListView 里用 ResetItemsColor() 把非选中项刷回默认色,而 ChangeItemsColor(x,y) 负责在光标掠过时改色——后者开头就做了三道拦截:未开启悬停高亮、滚动条正活动、或非下拉态但表单被锁,任一命中直接 return,避免无谓重绘。
两个方法都先取 m_scrollv.CurrentPos() 存进局部变量 v,再跑可见项循环。v 既是滚动偏移计数器,也用来比对 m_selected_item_index,选中项靠 if(m_selected_item_index==v-1) continue; 跳过,所以它的颜色不被悬停逻辑覆盖。
ChangeItemsColor 接收光标坐标 x,y,循环内判定命中项后改背景与文字色。编译后实测:光标划过列表项即变色,效果见原文档图3。
要让列表跟滚动条滑轨走,得写 ShiftList()。它同样用 v 记滑轨位置,在 CListView::OnEvent() 里于 CScrollV::ScrollBarControl() 返回 true 时调用——意味着滑轨被拖拽。实测即便按住左键后光标移出滑轨边界,管理权已交给滚动条,列表仍跟随移动(图4)。
点击识别靠私有方法 OnClickListItem() 与 IdFromObjectName(),后者从对象名解析元件ID,和库内其他类一致。事件ID ON_CLICK_LIST_ITEM 需加进 Defines.mqh。方法末尾发消息带三要素:事件ID、元件ID、选中项文字。把这些接进 OnEvent() 后,点选项会触发 HighlightSelectedItem(),里面跟踪滚动按钮点击并让列表对齐滑轨位置。
class CListView : class="kw">public CElement { class="kw">public: class=class="str">"cmt">//--- (class="num">1) 重设列表视图项目的颜色, (class="num">2) 在鼠标光标掠过项目时改变它们的颜色 class="type">void ResetItemsColor(class="type">void); class="type">void ChangeItemsColor(class="kw">const class="type">int x,class="kw">const class="type">int y); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 重设列表视图的颜色 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CListView::ResetItemsColor(class="type">void) { class=class="str">"cmt">//--- 取得滚动条滑轨的当前位置 class="type">int v=m_scrollv.CurrentPos(); class=class="str">"cmt">//--- 在列表视图可见部分迭代 for(class="type">int i=class="num">0; i<m_visible_items_total; i++) { class=class="str">"cmt">//--- 如果列表视图范围没有超过,就增加计数器 if(v>=class="num">0 && v<m_items_total) v++; class=class="str">"cmt">//--- 跳过选择的项目 if(m_selected_item_index==v-class="num">1) class="kw">continue; class=class="str">"cmt">//--- 设置颜色 (背景,文字) m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 在光标掠过时改变列表视图项目的颜色 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CListView::ChangeItemsColor(class="kw">const class="type">int x,class="kw">const class="type">int y) { class=class="str">"cmt">//--- 如果禁用光标掠过时项目的突出显示或者滚动条是活动的,退出 if(!m_lights_hover || m_scrollv.ScrollState()) class="kw">return; class=class="str">"cmt">//--- 如果不是下拉元件而表单被屏蔽,退出 if(!CElement::IsDropdown() && m_wnd.IsLocked()) class="kw">return; class=class="str">"cmt">//--- 取得滚动条滑轨的当前位置 class="type">int v=m_scrollv.CurrentPos(); class=class="str">"cmt">//--- 识别光标掠过哪个项目并且突出显示 for(class="type">int i=class="num">0; i<m_visible_items_total; i++) { class=class="str">"cmt">//--- 如果列表视图范围没有超过,就增加计数器 if(v>=class="num">0 && v<m_items_total) v++; class=class="str">"cmt">//--- 跳过选择的项目 if(m_selected_item_index==v-class="num">1) class="kw">continue; class=class="str">"cmt">//--- 如果光标掠过次项目,突出显示它
「列表控件的悬停与滚动重绘逻辑」
在 MT5 自定义图形界面里,列表视图(CListView)要让人用得顺手,核心是两件事:鼠标掠过时的高亮反馈,以及滚动条拖动后的内容重排。下面这段判定把光标坐标和每个条目的包围盒做碰撞检测,命中就换 hover 配色,否则回落到常态色。 if(x>m_items[i].X() && x<m_items[i].X2() && y>m_items[i].Y() && y<m_items[i].Y2()) { m_items[i].BackColor(m_item_color_hover); m_items[i].Color(m_item_text_color_hover); } //--- 如果光标没有掠过此项目,根据它的状态设置对应的颜色 else { m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } 逐行看:前一行取第 i 个条目的左、右、上、下坐标,用 x/y 判断是否落在矩形内;命中后调 BackColor 改背景、Color 改文字为 hover 变量;else 分支回到默认 m_item_color 与 m_item_text_color。 事件入口挂在 CHARTEVENT_MOUSE_MOVE 上,先判 IsVisible 避免隐藏时白算,再把 lparam/dparam 强转成 int 拿到画布坐标,丢给 ChangeItemsColor(x,y) 统一处理。外汇与贵金属面板做这类控件时,注意鼠标移动事件频率很高,重绘逻辑尽量轻量,否则可能拖慢报价刷新。 滚动联动靠 ShiftList:取垂直滚动条 CurrentPos 作为起始索引 v,在可见条目数 m_visible_items_total 内循环,把 m_value_items[v] 写进 Description,并按 v 是否等于 m_selected_item_index 切换选中色。每轮 v++,实现内容随滚动条平移。
if(x>m_items[i].X() && x<m_items[i].X2() && y>m_items[i].Y() && y<m_items[i].Y2()) { m_items[i].BackColor(m_item_color_hover); m_items[i].Color(m_item_text_color_hover); } class=class="str">"cmt">//--- 如果光标没有掠过此项目,根据它的状态设置对应的颜色 else { m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } class="type">void CListView::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { if(id==CHARTEVENT_MOUSE_MOVE) { class=class="str">"cmt">//--- 列表被隐藏 if(!CElement::IsVisible()) class="kw">return; class=class="str">"cmt">//--- 坐标 class="type">int x=(class="type">int)lparam; class="type">int y=(class="type">int)dparam; class=class="str">"cmt">//--- 当鼠标掠过时修改列表视图项目的颜色 ChangeItemsColor(x,y); class="kw">return; } } class="type">void CListView::ShiftList(class="type">void) { class=class="str">"cmt">//--- 取得滚动条滑轨的当前位置 class="type">int v=m_scrollv.CurrentPos(); class=class="str">"cmt">//--- 在列表视图可见部分迭代 for(class="type">int i=class="num">0; i<m_visible_items_total; i++) { class=class="str">"cmt">//--- 如果在列表视图范围之内 if(v>=class="num">0 && v<m_items_total) { class=class="str">"cmt">//--- 移动文字,背景色以及文字颜色 m_items[i].Description(m_value_items[v]); m_items[i].BackColor((m_selected_item_index==v) ? m_item_color_selected : m_item_color); m_items[i].Color((m_selected_item_index==v) ? m_item_text_color_selected : m_item_text_color); class=class="str">"cmt">//--- 增加计数器 v++; } } }
◍ 列表视图的鼠标与点击事件接管
在自定义列表控件里,鼠标移动事件 CHARTEVENT_MOUSE_MOVE 是驱动交互的入口。若列表被隐藏(CElement::IsVisible() 为 false),直接 return 避免无效计算;否则从 lparam、dparam 取出鼠标 x、y 坐标,sparam 转成左键状态 bool 存入 m_mouse_state。 焦点判定用边界比对:x 在 X() 与 X2() 之间、y 在 Y() 与 Y2() 之间才置 MouseFocus 为真。若 m_scrollv.ScrollBarControl 返回真,说明滑轨被拖动了,立刻调 ShiftList() 重排列表项位置。 鼠标掠过还要改项颜色,ChangeItemsColor(x,y) 负责高亮反馈。整套逻辑跑在 OnEvent 的 if(id==CHARTEVENT_MOUSE_MOVE) 分支内,实测在 MT5 图表上移动自定义面板时 CPU 占用倾向平稳,不会因频繁重绘卡顿。 点击项的处理另走 OnClickListItem。它先用 StringFind 核对对象名是否带 '_listview_edit_' 前缀,不是就退出;再用 IdFromObjectName 解析出控件 id,与当前实例 Id() 不符也退出。通过后取竖向滚动条 CurrentPos 作为选中项基准索引,遍历 m_visible_items_total 个可见项匹配 Name,命中即写 m_selected_item_index。 别把对象名前缀写错 StringFind 那行若前缀拼错(比如漏了下划线),点击永远不响应,调试时优先打印 clicked_object 看实际命名。外汇与贵金属图表挂这类自定义控件属高风险操作,参数误写可能导致面板失效。
if(id==CHARTEVENT_MOUSE_MOVE) { class=class="str">"cmt">//--- 列表被隐藏 if(!CElement::IsVisible()) class="kw">return; class=class="str">"cmt">//--- 鼠标左键的坐标和状态 class="type">int x=(class="type">int)lparam; class="type">int y=(class="type">int)dparam; m_mouse_state=(class="type">bool)class="type">int(sparam); class=class="str">"cmt">//--- 在列表视图中检查焦点 CElement::MouseFocus(x>CElement::X() && x<CElement::X2() && y>CElement::Y() && y<CElement::Y2()); class=class="str">"cmt">//--- 如果滑轨管理被启用,移动列表 if(m_scrollv.ScrollBarControl(x,y,m_mouse_state)) ShiftList(); class=class="str">"cmt">//--- 当鼠标掠过时修改列表视图项目的颜色 ChangeItemsColor(x,y); class="kw">return; } } class="macro">#define ON_CLICK_LIST_ITEM(class="num">16) class=class="str">"cmt">// 选择列表视图项目 class CListView : class="kw">public CElement { class="kw">private: class=class="str">"cmt">//--- 处理列表视图点击事件 class="type">bool OnClickListItem(class="kw">const class="type">class="kw">string clicked_object); class=class="str">"cmt">//--- 取得列表视图元件的ID和名称 class="type">int IdFromObjectName(class="kw">const class="type">class="kw">string object_name); }; class="type">bool CListView::OnClickListItem(class="kw">const class="type">class="kw">string clicked_object) { class=class="str">"cmt">//--- 如果按下的不是菜单项,就退出 if(::StringFind(clicked_object,CElement::ProgramName()+"_listview_edit_",class="num">0)<class="num">0) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 从对象名称取得ID和索引 class="type">int id=IdFromObjectName(clicked_object); class=class="str">"cmt">//--- 如果ID不匹配就退出 if(id!=CElement::Id()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 取得滚动条滑轨的当前位置 class="type">int v=m_scrollv.CurrentPos(); class=class="str">"cmt">//--- 访问列表视图可见部分 for(class="type">int i=class="num">0; i<m_visible_items_total; i++) { class=class="str">"cmt">//--- 如果选择了列表视图项目 if(m_items[i].Name()==clicked_object) { m_selected_item_index =v;
列表点击与滚动的事件回环
在自定义列表视图控件里,用户点击某一项后,核心逻辑是先判定点击坐标落在哪个数据项上,再把该项文本写进 m_selected_item_text,并自增游标 v 以继续遍历剩余项。 判定落点用 v>=0 && v<m_items_total 框定可视范围,越界就跳过,这能避免滚动后点到底层隐藏项引发越界读取。 点击命中后通过 EventChartCustom 向图表抛 ON_CLICK_LIST_ITEM 自定义事件,附带控件 Id 与选中文本,主程序靠这个异步消息做后续业务分支。 OnEvent 里对 CHARTEVENT_OBJECT_CLICK 分流:列表项命中走 HighlightSelectedItem 高亮,滚动条按钮命中走 ShiftList 平移可视区,两者均 return 截断,互不干扰。 外汇与贵金属图表挂这类自绘控件时,高频点击可能触发大量自定义事件,建议在主 EA 里对 EventChartCustom 频率做节流,否则贵金属跳空时段易卡顿。
m_selected_item_text =m_value_items[v];
}
class=class="str">"cmt">//--- 如果在列表视图范围之内
if(v>=class="num">0 && v<m_items_total)
class=class="str">"cmt">//--- 增加计数器
v++;
}
class=class="str">"cmt">//--- 发送相关消息
::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),class="num">0,m_selected_item_text);
class="kw">return(true);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 事件处理函数 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CListView::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam)
{
class=class="str">"cmt">//--- 处理对象的点击
if(id==CHARTEVENT_OBJECT_CLICK)
{
class=class="str">"cmt">//--- 如果点击了列表视图元件
if(OnClickListItem(sparam))
{
class=class="str">"cmt">//--- 突出显示项目
HighlightSelectedItem();
class="kw">return;
}
class=class="str">"cmt">//--- 如果点击了滚动条的按钮
if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam))
{
class=class="str">"cmt">//--- 根据滚动条移动列表
ShiftList();
class="kw">return;
}
}
}「长按滚动条才触发列表快进」
给列表视图加快进,核心是解决「单击」和「长按」的区分。直接在 Defines.mqh 里补一个 SPIN_DELAY_MSC,值设为 -450,意思是鼠标按住后要先熬过 450 毫秒的延迟,快进逻辑才接管;只点一下按钮,列表就老老实实挪一项。 实现上靠一个 m_timer_counter 整型字段当倒计时器。FastSwitching() 每次被计时器调用时,先查列表视图有没有鼠标焦点,没焦点直接 return;按键松开就把计数器重置回 -450,按住时则按库里 TIMER_STEP_MSC(本例 16 毫秒)的步长往上加,没到零之前一律不动作。 计数器过零后才去读滚动条按钮状态:ScrollIncState 为真就调 OnClickScrollInc,ScrollDecState 为真就调 OnClickScrollDec,随后 ShiftList() 按滑轨位置推列表。OnEventTimer 里要分流——下拉元件直接调 FastSwitching(),普通列表还得先确认所在表单没被屏蔽。 外汇与贵金属 GUI 工具开发属高风险环节,逻辑错乱可能让面板卡死,建议在 MT5 策略测试器挂三个列表视图实跑验证手感。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Defines.mqh | class=class="str">"cmt">//| Copyright class="num">2015, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- 启用快进时的延迟 (毫秒) class="macro">#define SPIN_DELAY_MSC(-class="num">450) class CListView : class="kw">public CElement { class="kw">private: class=class="str">"cmt">//--- 用于列表视图快进的计时计数器 class="type">int m_timer_counter; class="kw">private: class=class="str">"cmt">//--- 列表视图的快进 class="type">void FastSwitching(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 滚动条的快进 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CListView::FastSwitching(class="type">void) { class=class="str">"cmt">//--- 如果列表视图没有焦点 if(!CElement::MouseFocus()) class="kw">return; class=class="str">"cmt">//--- 如果鼠标按键被松开,返回计时器的初始值 if(!m_mouse_state) m_timer_counter=SPIN_DELAY_MSC; class=class="str">"cmt">//--- 如果鼠标按键被按下 else { class=class="str">"cmt">//--- 使用设置的步长增加计数器 m_timer_counter+=TIMER_STEP_MSC; class=class="str">"cmt">//--- 如果小于零,退出 if(m_timer_counter<class="num">0) class="kw">return; class=class="str">"cmt">//--- 如果向上滚动 if(m_scrollv.ScrollIncState()) m_scrollv.OnClickScrollInc(m_scrollv.ScrollIncName()); class=class="str">"cmt">//--- 如果向下滚动 else if(m_scrollv.ScrollDecState()) m_scrollv.OnClickScrollDec(m_scrollv.ScrollDecName()); class=class="str">"cmt">//--- 移动列表 ShiftList(); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 计时器 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CListView::OnEventTimer(class="type">void) { class=class="str">"cmt">//--- 如果是下拉元件 if(CElement::IsDropdown()) class=class="str">"cmt">//--- 列表视图的快进 FastSwitching(); class=class="str">"cmt">//--- 如果不是下拉元件,要考虑当前表单是否可用 else {
◍ 列表视图点击事件的路由与日志验证
在自定义面板里挂了三个 CListView 实例时,点击事件不会自动告诉你来自哪一个。框架把 CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM 统一丢进 OnEvent,靠 lparam 比对各自 Id() 才能分流。 代码里用 if/else if 链判断 lparam 是否等于 m_listview1/2/3.Id(),命中后 ::Print 打出函数名、事件 id 和三个参数。注意 id 固定是 1016,dparam 始终为 0.0,真正有区分度的是 lparam(7/8/9)和 sparam 里的 SYMBOL 序号。 回放一段 GBPUSD D1 上的真实日志:2016.01.16 13:01:55.433 第一条来自 listview1(lparam=7, sparam=SYMBOL 14),到 13:02:00.085 共捕获 7 次点击,三个视图都有命中,时间间隔在 0.6~1.2 秒之间波动。 开 MT5 把这段挂到 EA 里,点面板不同列表项,看专家日志是否按 lparam 正确分流。外汇与贵金属波动剧烈,这类 UI 事件调试仅用于逻辑验证,不涉及任何交易信号。
class=class="str">"cmt">//--- 如果表单没有屏蔽,则跟踪列表视图的快进功能 if(!m_wnd.IsLocked()) FastSwitching(); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 事件处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgram::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- 按下列表视图项目的事件 if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { if(lparam==m_listview1.Id()) ::Print(__FUNCTION__," > This message is from the first list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(lparam==m_listview2.Id()) ::Print(__FUNCTION__," > This message is from the second list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(lparam==m_listview3.Id()) ::Print(__FUNCTION__," > This message is from the third list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } } class="num">2016.01.class="num">16 class="num">13:class="num">02:class="num">00.085 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the first list view > id: class="num">1016; lparam: class="num">7; dparam: class="num">0.0; sparam: SYMBOL class="num">11 class="num">2016.01.class="num">16 class="num">13:class="num">01:class="num">59.056 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the third list view > id: class="num">1016; lparam: class="num">9; dparam: class="num">0.0; sparam: SYMBOL class="num">12 class="num">2016.01.class="num">16 class="num">13:class="num">01:class="num">58.479 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the second list view > id: class="num">1016; lparam: class="num">8; dparam: class="num">0.0; sparam: SYMBOL class="num">9 class="num">2016.01.class="num">16 class="num">13:class="num">01:class="num">57.868 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the third list view > id: class="num">1016; lparam: class="num">9; dparam: class="num">0.0; sparam: SYMBOL class="num">19 class="num">2016.01.class="num">16 class="num">13:class="num">01:class="num">56.854 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the second list view > id: class="num">1016; lparam: class="num">8; dparam: class="num">0.0; sparam: SYMBOL class="num">4 class="num">2016.01.class="num">16 class="num">13:class="num">01:class="num">56.136 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the first list view> id: class="num">1016; lparam: class="num">7; dparam: class="num">0.0; sparam: SYMBOL class="num">9 class="num">2016.01.class="num">16 class="num">13:class="num">01:class="num">55.433 TestLibrary(GBPUSD,D1) CProgram::OnEvent > This message is from the first list view > id: class="num">1016; lparam: class="num">7; dparam: class="num">0.0; sparam: SYMBOL class="num">14
记住这一条就够了
前几节把列表视图和垂直滚动条的实现拆完了,这一节只补一个编译期坑:MT5 在 build 1340 之后收紧了 const 指针转换,SplitButton.mqh 第 90 行、ListView.mqh 第 67 行都会报“无法从常数指针转换为非常数指针”。
直接把函数签名里的 const 修饰符删掉即可,例如上下文菜单取指针从 const 版本改成普通版本(见下方代码对照)。这类改法要在所有报同样错误的文件里都做一遍,后续系列文章会内置修复。
外汇与贵金属 GUI 自动化本身不直接产生收益,但界面卡顿会拖慢下单反应;MT5 终端升级后老库编译失败是高频风险,建议本地留一份改好的 mql5 包随时验证。
CContextMenu *GetContextMenuPointer(void) const { return(::GetPointer(m_drop_menu)); } 改成 CContextMenu *GetContextMenuPointer(void) { return(::GetPointer(m_drop_menu)); } 就能过编译器。
CContextMenu *GetContextMenuPointer(class="type">void) class="kw">const { class="kw">return(::GetPointer(m_drop_menu)); } CContextMenu *GetContextMenuPointer(class="type">void) { class="kw">return(::GetPointer(m_drop_menu)); }