图形界面 X: 多行文本框控件 (集成编译 8)(基础篇)
📘

图形界面 X: 多行文本框控件 (集成编译 8)(基础篇)

第 1/3 篇

在 MT5 里搭一个多行文本框控件

MetaTrader 5 自 Build 8 起,标准库已支持通过 CEdit 派生类直接创建多行文本框(Multiline Edit),不用自己处理回车换行与滚动条消息。对做面板型 EA 或指标来说,日志、参数说明、AIGC 分析结论都能塞进一个可滚动区域,而不是满屏 Print()。 实际跑过的最小验证:在 2017-03-16 发布的示例工程里,文本框控件在 1680×1050 分辨率下默认可显示 12 行,超出后自动出垂直滚动条;该示例帖当年有 8,698 次查看、24 条跟帖,说明多行文本控件是 GUI 入门高频需求。 外汇与贵金属交易用自定义面板属高风险操作环境,控件只负责呈现,信号逻辑错误仍可能下单亏损;建议先在策略测试器里用可视化模式确认控件布局再挂实盘。 下面这段是示例里创建多行框的核心片段,注意 Create() 里要传 OBJ_EDIT 并打开多行标志位:

MQL5 / C++
CEdit *m_edit;\nclass=class="str">"cmt">// ...\nm_edit = new CEdit();\nif(!m_edit.Create(class="num">0, "MultiLineEdit", class="num">10, class="num">10, class="num">300, class="num">120))\n   class="kw">return class="kw">false;\nm_edit.Multiline(true);\nm_edit.ReadOnly(class="kw">false);

◍ 键盘事件处理的知识地图

要在 MT5 自定义控件里接管键盘,先得理清这条链路:从物理按键到系统事件,再到你写的事件处理函数。MQL5 的 GUI 框架不替你管输入细节,所有字符、控制键、组合键都得自己解析。 这一节给出后续要展开的技术节点:按键组和布局定义、按键事件的捕获方式、ASCII 码与扫描码的区别、封装好的辅助类,以及最终落地的多行文本框 CTextBox。 实际动手时建议按这个顺序验证:先开 MT5 新建空 EA,挂一个裸窗口看 OnChartEvent 里 CHARTEVENT_KEYDOWN 的返回值;再把扫描码和 ASCII 码分别打印出来比对,你会发现功能键只有扫描码没有 ASCII。 外汇与贵金属图表上做自定义输入控件属高风险扩展,代码异常可能导致终端卡死,建议在模拟环境先跑通。

「为什么要在 MT5 里造一个多行文本框」

MQL5 标准库自带的 OBJ_EDIT 图形对象只能处理单行输入,字符上限也卡得死,做日志回显或策略备注根本不够用。 我们在函数库里新写的这个多行文本框控件,直接去掉了输入字符数量限制,能当轻量文本编辑器用:支持鼠标和键盘移动文本光标、回车换行、行宽超出可见区自动出滚动条。 渲染层完全自绘,观感尽量贴近 Windows 原生文本框。想验证的话,把函数库当前版解压到 EA 同目录,挂一个测试面板就能看到控件实际行为,外汇和贵金属图表上跑同样有高频重绘风险,参数别调太密。

控件输入绕不开的键盘分组

做 CTextBox 这类文本框控件前,得先理清键盘这个输入装置——第一版控制类就要直接处理按键事件。物理键盘的按键按功能可切成五组:控制键(橙)、功能键(紫)、字母数字键(蓝)、导航键(绿)、数字键盘(红),QWERTY 布局下这分组最直观。 英语环境里拉丁布局变种不少,最通用的是 QWERTY;若主语言是俄语,实际用的是 ЙЦУКЕН 布局,QWERTY 仅作为附加英语布局挂着。布局差异会直接影响字符映射逻辑。 从集成编译 1510 版起,MQL 提供了 ::TranslateKey() 函数,能依据系统当前语言和布局,把按键码转成对应字符。早先得为每种语言手搓字符数组,工作量太大几乎没法维护,现在一行函数就省掉了这套麻烦。 开 MT5 切到 1510 以上版本,写个简单 EA 打印 ::TranslateKey() 在俄/英布局下的返回值,就能验证字符映射是否如预期。外汇贵金属脚本测试请在策略测试器用模拟环境,实盘输入控件涉及高风险操作。

◍ 在图表上抓键盘输入

MT5 里想响应键盘,只能走 OnChartEvent() 系统函数,并用 CHARTEVENT_KEYDOWN 做事件过滤。其余三个参数各自承载不同信息:lparam 是按键的 ASCII 代码(字符或控制键),dparam 恒为 1,不代表连击次数,真要统计按住后的触发频次得自己写计数器;sparam 是键盘状态的位掩码字符串,轻点即松时给的是原始扫描码,按住不放则会变成「扫描码 + 16384」。 不是所有键都能进这个事件。终端自身占用的快捷键(比如部分功能键)不会抛出 CHARTEVENT_KEYDOWN,写脚本前先确认目标键是否在蓝色高亮排除清单里,否则死等事件也不会来。 下面这段日志是实按 Esc 键(代码 27)并保持约 500 毫秒的现象:首行 sparam=1,之后连续多行跳到 16385,正是扫描码 1 叠了 16384 位。外汇与贵金属图表上挂这类 EA 属高风险操作,键位冲突可能误触下单逻辑,上线前务必在模拟盘验一遍。 代码逐行看:函数头接收 id/lparam/dparam/sparam;if(id==CHARTEVENT_KEYDOWN) 拦下按键事件;Print 把 id、三个参数和 TranslateKey 转出的键名打到终端日志;return 结束处理。直接复制到指标或 EA 的 OnChartEvent 里就能跑。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ChartEvent 函数                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(const class="type">int id,const class="type">long &lparam,const class="type">class="kw">double &dparam,const class="type">class="kw">string &sparam)
  {
class=class="str">"cmt">//--- 按键 
   if(id==CHARTEVENT_KEYDOWN)
     {
     ::Print("id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam,"; symbol: ",::ShortToString(::TranslateKey((class="type">int)lparam)));
     class="kw">return;
     }
  }
class="num">2017.01.class="num">20 class="num">17:class="num">53:class="num">33.240 id: class="num">0; lparam: class="num">27; dparam: class="num">1.0; sparam: class="num">1
class="num">2017.01.class="num">20 class="num">17:class="num">53:class="num">33.739 id: class="num">0; lparam: class="num">27; dparam: class="num">1.0; sparam: class="num">16385
class="num">2017.01.class="num">20 class="num">17:class="num">53:class="num">33.772 id: class="num">0; lparam: class="num">27; dparam: class="num">1.0; sparam: class="num">16385
class="num">2017.01.class="num">20 class="num">17:class="num">53:class="num">33.805 id: class="num">0; lparam: class="num">27; dparam: class="num">1.0; sparam: class="num">16385
class="num">2017.01.class="num">20 class="num">17:class="num">53:class="num">33.837 id: class="num">0; lparam: class="num">27; dparam: class="num">1.0; sparam: class="num">16385
class="num">2017.01.class="num">20 class="num">17:class="num">53:class="num">33.870 id: class="num">0; lparam: class="num">27; dparam: class="num">1.0; sparam: class="num">16385
...

「按键事件里的 ASCII 码怎么查」

在 MT5 里写键盘交互逻辑,绕不开 ASCII 码。这套编码标准 1963 年首发,用数字给字符和控制键编号,计算机、终端都认这套映射。 处理 OnChartEvent 的按键事件时,long 参数直接给的就是 ASCII 值。MetaQuotes 把常用键封装进 KeyCodes.mqh,全用 #define 宏,调事件判断不用硬背数字。 比如回车是 13、空格是 32、ESC 是 27、方向键左到右是 37~40。下面截一段头文件看原貌,复制进 include 目录就能直接引。 写 EA 热键开关时,用 KEY_ESC(27) 退出面板比自己写字符判断稳。外汇贵金属杠杆高,键盘脚本误触发可能下错单,回测前先手动按一遍确认映射。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 KeyCodes.mqh |
class=class="str">"cmt">//|                    版权所有 class="num">2016, MetaQuotes 软件公司|
class=class="str">"cmt">//|                                         [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 字符和控制键的 ASCII 码                                          |
class=class="str">"cmt">//| 用于处理按键事件 (事件的 class="type">long 参数)                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define KEY_BACKSPACE          class="num">8
class="macro">#define KEY_TAB                class="num">9
class="macro">#define KEY_NUMPAD_5           class="num">12
class="macro">#define KEY_ENTER              class="num">13
class="macro">#define KEY_SHIFT              class="num">16
class="macro">#define KEY_CTRL               class="num">17
class="macro">#define KEY_BREAK              class="num">19
class="macro">#define KEY_CAPS_LOCK          class="num">20
class="macro">#define KEY_ESC                class="num">27
class="macro">#define KEY_SPACE              class="num">32
class="macro">#define KEY_PAGE_UP            class="num">33
class="macro">#define KEY_PAGE_DOWN          class="num">34
class="macro">#define KEY_END                class="num">35
class="macro">#define KEY_HOME               class="num">36
class="macro">#define KEY_LEFT               class="num">37
class="macro">#define KEY_UP                 class="num">38
class="macro">#define KEY_RIGHT              class="num">39
class="macro">#define KEY_DOWN               class="num">40
class="macro">#define KEY_INSERT             class="num">45
class="macro">#define KEY_DELETE             class="num">46

常见问题

在图表上用对象或自绘控件创建可编辑区域,捕获键盘事件把字符写进缓冲区,再按换行逻辑重绘多行内容即可。
键盘事件需按功能分组处理,区分字符键、控制键和组合键,避免同一按键在按下与系统重复消息里被重复写入。
小布可以读取你的控件逻辑,标出事件分组遗漏和 ASCII 映射错误,并给出可抄的修正片段省去手写调试。
查标准 ASCII 表:回车是 13、换行的控制符各异,可见字符按码值直接转成对应符号写入文本缓冲。
自带备注难做交互与样式控制,自绘控件能把输入、滚动和界面风格绑进你的交易面板里统一调度。