简介
第一篇文章“创建一个简单的控件”讲述的是图形控件的创建原则,而且还提供了一个逐步的示例,生动阐释简单控件的创建。接下来的文章“控件库”则列举了一组现成的控件。还有另一种非常重要的图形界面组件 – 表单。
表单就是屏幕上被特别指定的某个矩形部分,控件依此显示。此外,表单也是一种容器,允许同时管理其中包含的所有控件:一同隐藏、显示和移动。
而这篇收尾文章,则会阐明表单的创建及其同控件的搭配使用。
使用表单的类已被添加到此前文章使用的IncGUI.mqh文件中(新的文件名为 IncGUI_v3.mqh)。除使用表单的类之外,还有一些类被添加到该文件,而且更新了 CHMenu (水平菜单)与 CVMenu (垂直菜单),并修复了几个错误。
添加的类:
- CFrame 类 – 框架。此类与通过 CWorkPiece 类的 Frame 方法创建的框架完全相同。
- CButton 类 – 按钮。一种常规按钮 (OBJ_BUTTON)。
- CLabel 类 – 标签。除了像通过图形对象 “Label” (OBJ_LABEL) 一样显示标签之外,此类还允许分行显示文本,行与行之间由 “/n” 符号隔开。
添加新类的结果之一,就是 CColorSchemes 类被如下修改:新控件的颜色已添加。
CHMenu 与 CVMenu 类中的变更: 应用的变更允许利用下拉选项卡创建两级菜单。下文的“创建主菜单”章节中将对此有所详述。
错误:CWorkPiece 类 Frame() 方法中的一种纠正 – 某矩形标签的某个子窗口编号不能设置。CListMS 类 – Selected() 与 SetSelected() 方法 – 正在检验数组大小,否则不能使用空列表。
1. 表单
表单基于图形对象“矩形标签” OBJ_RECTANGLE_LABEL 搭配使用多个按钮 OBJ_BUTTON。外表看,表单呈矩形(图 1),顶部有一个显示表单名称与控件按钮的栏。
左侧是一个移动表单的按钮(有一个手状图像),右侧则有一个最小化按钮(有一个矩形图像)和关闭按钮(有一个叉号图像)。
图 1. 表单
欲移动表单,则按下移动按钮(按钮会变成推进位置),再点击图表中表单想要移往的任何位置。如此一来,移动按钮就会弹起,而表单就会移往指定位置(其左上角即位于点击的点位)。
如果指定的位置处于图表的边缘,而表单必须置于图表边框之外,则会对表单的位置进行调整,使其完全显示于图表当中。
可以创建多种表单类型:
- 0 型 (图 1);
- 1 型 – 带有附加的 “Cancel” (取消)与 “Apply” (应用)按钮(图 2);
- 2 型 – 带有附加的 “Close” (关闭)按钮(图 3);
图 2. 1 型表单(带有 “Cancel” 与 “Apply” 按钮)
图 3. 2 型表单(带有 “Close” 按钮)
使用表单的编程手段集中体现于两个类:CFormBase 和 CFormTemplate。
CFormBase 类是一种基类,而 CFormTemplate 则是 CFormBase 类的一个子类。在效果与原则(其中包括应用原则)方面,CFormBase 类与之前讲过的其它控件完全一样。
CFormBase 类拥有 Init() 控件编写方法、用于显示表单的 SetPos() 与 Show() 方法(呈现表单的图形对象的创建)、用于隐藏的 Hide() 方法、用于刷新显示的 Refresh() 方法、用于事件处理的 Event() 方法,以及与所有其它控件中相同的其它方法。
表单的应用及控件的使用,都从调用 Init() 方法开始,接下来是使用 SetPos() 方法设定一个位置,再利用 Show() 方法或单纯使用带参数的 Show() 方法启用显示,还可以采用 Hide() 方法将表单隐藏起来。
已向OnChartEvent()函数中添加了一个Event()方法调用,以确保表单控件的相关功能(移动、最小化、关闭等)。在需要确保表单于子窗口操作的位置,调用Init()方法之后立即使用SetSubWindow()方法调用的内容,以及CHARTEVENT_CHART_CHANGE事件后OnChartEvent()函数当中。所有一切都与其它控件中对应相同。
尽管标准控件是独立的(它们无需关联其它控件),但表单蕴含着与其它控件某种强制关联的存在,目的是确保随同表单的隐藏或显示,同时显示或隐藏表单内的控件。因此,应确保表单及关联控件 Init()、Show() 及 Hide() 方法的同步操作。
只调用(比如调用表单的 Show() 方法时)与此表单相关的所有控件的 Show() 方法当然可以,但如果程序中使用了多个表单,这种方式就会导致代码非结构化且不方便理解、调试和更新。
可以为每份表单制作一个该表单类的副本,再将带有控件的工作代码加入其中;但这样又会导致非结构化代码(其中的表单代码不会与控件操作的相关代码区分开来)、代码过度重复(其中只有负责表单设计与功能的代码部分会被复制)。所有这些,都会加大任何表单功能修改应用的复杂程度,因为每一个类副本都要进行更改。
基类与子类的划分,有助于解决创建多个表单、表单与关联控件同步显示、以及清楚区分关联不同表单的代码的问题。除了解决这些基本问题之外,类与子类的使用,还允许基类的未来升级,且无需对已经完成的工作做出任何改动。
为实现控件与表单Init()、Show() 及 Hide() 方法的同步调用,与标准的控件类相比,CFormBase 类做出了一些区分:某受保护分区中的几个虚拟方法:
virtual void MainProperties() {} virtual void OnInitEvent() {} virtual void OnShowEvent() {} virtual void OnHideEvent() {} virtual void EventsHandler(const int id, const long& lparam, const double& dparam, const string& sparam){} virtual void OnWindowChangeEvent(int aSubWindow) {} virtual bool OnCancelEvent() {return(true);} virtual bool OnApplyEvent() {return(true);}
这些方法都是虚拟的,也就是说,重定向到对应的真实CFormTemplate子类方法,要通过它们来实现。
CFormTemplate子类是一种模板,不单独使用。想要创建一个新表单,制作一份具有独特名称的 CFormTemplate 类的副本(只需复制 CFormTemplate 类的代码后重命名,每个表单都重复此流程)。复制子类即可区分开不同表单关联的代码。
虚拟方法位于受保护分区中,因此调用不可访问,甚至也无需访问——因为已经根据不同的表单事件从基类调用 – 您只需对应这些事件,为控件的操作添加代码即可。
下图展示的是“EA 交易”函数与标准控件类方法之间的相互作用(图 4),以及作为对比的、与表单类方法的相互作用(图 5)。
图 4. “EA 交易”函数与控件方法间的相互作用
图 5. “EA 交易”函数与表单类方法间的相互作用
我们详细地查看一下每种方法的目的:
-
MainProperties() 与 OnInitEvent() 方法由表单的 Init() 方法调用。MainProperties() 方法设置表单的外观(表单类型,有无移动、最小化及关闭按钮)。OnInitEvent() 方法会调用所有控件的 Init() 方法,并为控件设置默认值。
-
OnShowEvent()方法由 Show() 方法调用;所有控件的 Show() 方法的调用代码都向其添加。
-
OnHideEvent()方法由 Hide() 方法调用;所有控件的 Hide() 方法的调用代码都向其添加。
-
EventsHandler()方法等同于“EA 交易”的 OnChartEvent() 函数。表单的 Event() 方法由“EA 交易”的 OnChartEvent() 函数调用,而且所有的图表事件都是简单地重定向到该方法。所有控件Event()方法的调用代码以及相应的事件处理,均添加到 EventsHandler()。
-
OnWindowChangeEvent() 方法会在更换子窗口时由 SetSubWindow() 方法调用。所有控件 SetSubWindow() 方法的调用代码都向此添加。此方法拥有一个参数,可传送所有控件内待设定的新子窗口编号。
-
OnCancelEvent() 方法会在关闭表单(点击叉形图像、”Cancel” 或 “Close” 按钮)时执行。可在表单关闭处添加一些检查代码,比如调用一个确认窗口以确保真的需要关闭该表单。如果此方法返回 True,则关闭表单的代码完成执行,表单关闭;如果此方法返回 False,则关闭此表单的代码会中断运行,表单仍保持打开状态。
-
OnApplyEvent() 方法会在点击表单 “Apply” 按钮时执行。可于此(使用与 OnCancelEvent() 相同的方法)执行一些检查,再看看此方法返回的是 True 还是 False,并由此允许或不允许表单关闭。此方法亦可纳入保存控件值的代码,以使“EA 交易”下一次启动处的控件值与之前关闭程序的相同。为此,保存值应载入 OnInitEvent() 方法并完成控件设置。
2. 创建新表单的逐步流程
创建一个新表单的逐步流程如下:
1. 包含IncGUI_v3.mqh文件。
#include <IncGUI_v3.mqh>
2. 由IncGUI_v3.mqh文件将CFormTemplate类代码复制到您“EA 交易”的某个文件(此子类位于文件末尾)并重命名(库中使用此子类的示例的命名为 CForm)。
class CFormTemplate: public CFormBase{ public: protected: void MainProperties() { m_Name = "Form"; // 表单名. 表单的所有控件名必须有这个前缀. m_Width = 200; // 表单宽度 m_Height = 150; // 表单高度 m_Type = 1; // 表单类型: 0 - 无按钮, 1 - 带 "应用" 和 "取消" 按钮, 2 - 带 "关闭" 按钮 m_Caption = "FormCaption"; // 表单标题 m_Movable = true; // 可移动表单 (带手势图像按钮,显示在左上角) m_Resizable = true; // 表单最小/最大允许 (带正方形图像按钮 // 显示在右上角) m_CloseButton = true; // 表单关闭允许 (带交叉图像按钮显示在右上角) } void OnInitEvent() {} void OnShowEvent(int aLeft, int aTop) {} void OnHideEvent() {} void OnWindowChangeEvent(int aSubWindow) {} void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam){} bool OnApplyEvent() {return(true);} bool OnCancelEvent() {return(true);} };
可为表单代码创建一个独立的包含文件,将其纳入“EA 交易”,将CFormTemplate子类的代码复制进去,再编写此文件中与此表单操作关联的剩余代码。
与使用可视编辑器做一个类比,此步骤几乎类似于点击按钮创建一个新表单,之后项目中就会出现一个带有各种表单事件功能的新表单文件。
3.设置表单的主要属性。此步骤于MainProperties()方法中执行——将值指派给基类中声明的变量。此模板中提供有关所有变量目的的详细注释。您可能会注意到此步骤与可视编辑器(比如 Excel 中的 VBA)中使用表单属性的相似性。
void MainProperties() { m_Name = "Form"; // 表单名. 表单的所有控件名必须有这个前缀. m_Width = 200; // 表单宽度 m_Height = 150; // 表单高度 m_Type = 1; // 表单类型: 0 - 无按钮, 1 - 带 "应用" 和 "取消" 按钮, 2 - 带 "关闭" 按钮 m_Caption = "FormCaption"; // 表单标题 m_Movable = true; // 可移动表单 (带手势图像按钮,显示在左上角) m_Resizable = true; // 表单最小/最大允许 (带正方形图像按钮 // 显示在右上角) m_CloseButton = true; // 表单关闭允许 (带交叉图像按钮显示在右上角) }
有人可能会注意到表单与其它控件的另一个区别-表单名称是在MainProperties()方法中设定,而不是作为 Init() 方法参数传递。
表单上的主体工作都在子类中完成;表单的每个子类只声明一次,所以在调用表单一个子类的 Init() 方法时无需指定不同的名称。
唯一可以传递到 Init() 方法的参数是 State (状态)参数,但即便是这个参数也并非强制。“状态”参数的值可确定表单的初始显示状态:最大化还是最小化。如果此值为 1,则表单最大化(图 1、2、3),如果此值为 2,则表单最小化(图 6)。
图 6. 最小化的表单
4. 声明一个类。
CForm frm;
5.为控件使用制定标准化步骤:由“EA交易”的OnInit()函数调用表单的Init()方法,由OnDeinit()函数调用Deinit()方法,由OnChartEvent()函数调用Event()方法,以及必要情况下,CHARTEVENT_CHART_CHANGE事件时调用 SetSubWindow() 方法。
随后图表中即会出现表单,对移动、最小化、关闭按钮等动作作出应对。
之后,我们再继续向表单添加控件。
3. 向表单添加控件的逐步流程
-
声明控件。 如您需要确保在表单以外使用控件方法,则应在公用分区中声明控件类;如果所有工作都能在表单类中完成,则应于受保护分区中声明控件类。
-
声明变量以恢复控件值。 可选步骤。如果您要使用带有 “Cancel” 和 “Apply” 按钮的表单,即应向每个控件添加一个变量,以将表单打开时的控件值存储起来,从而在用户更改了控件值却按下了 “Cancel” 按钮的情况下恢复该值。
-
为所有控件调用 Int() 方法。 此步骤于 OnInitEvent() 方法中执行。
-
载入保存的数据。 可选步骤。此步骤于 OnInitEvent() 方法中执行。如有必要在重启“EA 交易”、终端或计算机后令控件仍保留其值,则执行此步骤。数据保存于步骤 12 中完成。
-
设定默认值。 在此步骤中,所有控件都会完成相应值的设定——这些设定值会在“EA 交易”启动后首次打开表单时于控件中出现(直到用户再行更改)。如果已在步骤 4 中载入了数据,则采用载入数据。控件的进一步编写工作亦于此步骤内完成,比如说,列表和菜单项就是在此添加。
-
为所有控件调用 Show() 方法(带参数版本),都是在 OnShowEvent() 方法中执行。OnShowEvent() 方法有两个参数 (int aLeft, int aTop),它们会传送表单工作区左上角的坐标。而控件位置的设定,则服从于此类变量的值(值应添加)。
-
为表单所有控件调用 Hide() 方法,都是在 OnHideEvent() 方法中执行。
-
调用SetSubWindow()方法。 可选步骤。如有必要在子窗口中显示表单,则为所有控件调用 SetSubWindow() 方法。此步骤于 OnWindowChangeEvent() 方法中执行,其中包含一个传送新子窗口编号的参数 (int aSubWindow)。
-
调用所有控件的 Event() 方法。此步骤于 EventsHandler() 方法中执行。
-
处理控件事件。 可选步骤。如果要求确保事件发生时控件之间的相互作用,就要在此步骤处理事件并执行恰当的动作。此步骤于 EventsHandler() 方法中执行。
-
应用新值。 可选步骤,利用 “Apply” 按钮执行。此步骤于 OnApplyEvent() 方法中执行。在此步骤中,检查控件值是否准确;如发现任何错误值,则必须通知用户,此方法也会返回 False,表单亦仍保持打开状态。如果各值均正确,则会返回 True 以关闭表单。
-
数据保存。 可选步骤。此步骤于 OnApplyEvent() 方法中使用 “Apply” 按钮执行。根据目标和目的,数据可保存于文件、全局变量以及图表的可见物件等中。
-
于OnCancelEvent()方法中检查。可选步骤。如果OnCancelEvent()方法返回 False,则表单仍会保持打开状态,即,关闭按钮没有反应。想要关闭表单,此方法须返回 True。
使用表单的示例请见随附的 eIncGUI_v3_Test_Form.mq5 文件。带有不同控件的主表单会在启动时打开(图 7)。
注意此表单没有关闭按钮;此为主表单,图表中不可或缺,否则就必须重启“EA 交易”以令该表单重新出现。
图 7. eIncGUI_v3_Test_Form.mq5 文件中带有打开主菜单选项卡示例的主表单
利用主菜单命令,您可以打开表单的另两个变量。
主菜单 – 表单类型 – 1 型会打开一个 1 型表单(带有 “Cancel” 与 “Apply” 按钮)。
主菜单 – 表单类型 – 2 型会打开一个 2 型表单(带有 “Close” 按钮)。
两个表单都要求关闭时确认(利用 MessageBox() 函数实现)。1 型表单有一个输入框;点击 “Apply” 按钮即可勾选一个输入值。2 型表单有一个新的 “Label” (CLabel 类)控件,还有几个测试按钮(CButton 类)。
现在,我们回顾一下简介中提到的 CHMenu 与 CVMenu 类中的变更,以创建主菜单为例。
4. 创建主菜单
首先,我想说的是:只能创建两级菜单:一个水平条(基于 CHMenu 类)和选项卡(基于 CVMenu 类)。原则上讲,创建更多的层级可能实现,但过程会非常复杂艰难,所以不建议如此,亦不会再深究。
实际上,甚至都没有为表单创建主菜单的计划,CHMenu 与 CVMenu 类的本义即是分别独立使用,用于启用/禁用各种函数与工具。但是,对 CHMenu 和 CVMenu 类做出的一项细微改进使其能够获取主菜单,而这对于大多数情况来讲,已经足够了。
想创建一个完备的多级菜单,必须采用一种稍有不同的方法(创建一种树状数据结构),而且要能充当某完整独立文章的主题,所以,我们当前止步于现有的方式。
我们首先回顾一下应用于 CHMenu 与 CVMenu 类的所有更改与修正。
CVMenu 类中,已修复点击图形对象 “Label” 时的一个反应(用于显示对勾符号)。相应地,在 LastClickedX()、LastClickedY() 及 LastClickedQuarter() 方法的操作中也有变更。现在点击标签时,会返回与文本框中相同的值。在 CHMenu 类与 LastClickedX()、LastClickedY()、LastClickedQuarter() 以及 LastClickedW() 方法中亦有类似更正。
两个类都通过添加 SolvePosX() 和 SolvePosY() 方法进行了更新,而这两种方法旨在计算菜单命令下显示的某图形对象的坐标。显示对象的宽度被传递至 SolvePosX() 方法,而显示对象的高度则被传递至 SolvePosY() 方法,两者再分别返回显示对象的 X- 或 Y- 坐标。现在已经不再需要使用 LastClickedX()、LastClickedY()、LastClickedQuarter() 和 LastClickedW() 方法了。
两个类都通过添加 LastClickedName1() 和 LastClickedName2() 方法进行了更新。这两种方法会返回构建上一次点击菜单项目的图形对象名称。LastClickedName1() 会返回文本框的名称,而 LastClickedName2() 则返回对勾符号标签的名称。
ToggleNameAdd() 与 ToggleNamesClear() 方法已添加至 CVMenu。ToggleNameAdd() 方法用于创建 “toggle” (切换)名称列表,而 ToggleNamesClear() 方法则用于清除该列表。使用 ToggleNameAdd() 方法(向列表添加名称)时,除构建菜单的图形对象事件和使用 ToggleNameAdd() 方法添加名称的图形对象事件之外,如有任何图表事件,菜单都会自动关闭。使用 ToggleNamesClear() 方法清除列表后,菜单会返回常规操作模式。
此两级菜单的操作如下:通过 LastClickedName1() 和 LastClickedName2() 方法定义点击水平菜单项目的事件时,我们就会得到该项目的对象名称,再将其传递至垂直菜单的 ToggleNameAdd()。
此后,除垂直菜单事件和一种水平菜单项目事件(垂直菜单要通过它才能打开)之外,垂直菜单会在任何图表事件时隐藏。
如有用户选定了垂直菜单的一个项目,则垂直菜单应关闭,并执行此项目对应的动作。如有用户重复点击同一个水平菜单项目(垂直菜单要通过它才能打开),则垂直菜单应隐藏。
我们来看一个菜单创建示例。
主菜单有三个项目,所以我们需要三个垂直菜单。在表单子类的公用分区中声明一个水平菜单类和三个垂直菜单类(下方示例以与 eIncGUI_v3_Test_Form.mq5 示例中完全相同的方式予以呈现):
class CForm: public CFormBase{ public: CHMenu m_hm; CVMenu m_vm1; CVMenu m_vm2; CVMenu m_vm3;
在OnInitEvent()方法中完成菜单类的初始化,并添加菜单项目:
// 水平菜单 m_hm.Init(m_Name+"_HM",m_Width,2); // 加入水平菜单项目 m_hm.AddItem("Form types"); m_hm.AddItem("Item-2"); m_hm.AddItem("Item-3"); // 垂直菜单 1 m_vm1.Init(m_Name+"_VM1",70,10); // 加入项目至垂直菜单 1 m_vm1.AddItem("Type-1"); m_vm1.AddItem("Type-2"); // 垂直菜单 2 m_vm2.Init(m_Name+"_VM2",70,3); // 加入项目至垂直菜单 2 m_vm2.AddItem("Item-2-1"); m_vm2.AddItem("Item-2-2"); m_vm2.AddItem("Item-2-3"); m_vm2.AddItem("Item-2-4"); m_vm2.AddItem("Item-2-5"); // 垂直菜单 3 m_vm3.Init(m_Name+"_VM3",70,3); // 加入项目至垂直菜单 3 m_vm3.AddItem("Item-3-1"); m_vm3.AddItem("Item-3-2"); m_vm3.AddItem("Item-3-3"); m_vm3.AddItem("Item-3-4"); m_vm3.AddItem("Item-3-5");
于 OnHideEvent() 方法中隐藏菜单:
void OnHideEvent()
{
m_hm.Hide();
m_vm1.Hide();
m_vm2.Hide();
m_vm3.Hide();
}
于OnShowEvent()方法中显示水平菜单:
void OnShowEvent(int aLeft,int aTop) { m_hm.Show(aLeft,aTop); }
最后是EventsHandler()方法中的主体工作。
调用所有菜单的Event()方法:
void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { int m_event0=m_hm.Event(id,lparam,dparam,sparam); int m_event1=m_vm1.Event(id,lparam,dparam,sparam); int m_event2=m_vm2.Event(id,lparam,dparam,sparam); int m_event3=m_vm3.Event(id,lparam,dparam,sparam);
根据 m_event0 的值(水平菜单项目索引),使用合适的垂直菜单(比如搭配项目 0 和垂直菜单 1):
if(m_event0==0) { // 点击项目 0 if(m_vm1.Visible()) { m_vm1.Hide(); // 如果垂直菜单打开, 关闭它 } else{ // 如果垂直菜单关闭 m_vm1.ToggleNamesClear(); // 清除"切换"的名字列表 // 为表单的项目加入图形对象名 // 水平菜单的最后事件 // 水平菜单 m_vm1.ToggleNameAdd(m_hm.LastClickedName1()); m_vm1.ToggleNameAdd(m_hm.LastClickedName2()); // 显示垂直菜单 m_vm1.Show(m_hm.SolvePosLeft(m_vm1.Width()),m_hm.SolvePosTop(m_vm1.Height())); } }
应为打开垂直菜单的所有水平菜单项目执行上述动作。如果发生垂直菜单事件,则关闭菜单。
根据点击项目的索引,如下执行:
if(m_event1>=0) { // 垂直菜单事件 1 m_vm1.Hide(); // 隐藏菜单 if(m_event1==0) { // 点击项目 0 //... } if(m_event1==1) { // 点击项目 1 //... } }
应为所有垂直菜单及其所有项目执行上述动作。
现在,主菜单创建已经完成了。
总结
本文是专门讲解图形界面创建系列文章的终结篇。
经过努力,我们已经为快速创建一个多功能且易用的图形界面准备了数量相当可观的方法。这与现在对于图形界面的要求相当一致:
- 采用说明形式的框架,以及可以最小化、最大化、移动的表单;
- 代码区清晰指定与某个表单或其它表单的关联;
- 控件的显示/隐藏与表单的显示/隐藏同步。
所有控件和表单均有通用的设计和配色方案,而且可以快速变更。
我们一起重复创建一个表单的流程。
创建一个表单的简要流程:
-
包含 IncGUI_v3.mqh 文件。
-
制作一份具有独特名称的 CFormTemplate 类的副本,并声明此类。
-
在子类副本的 MainProperties() 方法中设定表单属性。
-
由“EA 交易”的 OnInit()、OnDeinit() 和 OnChartEvent() 函数分别调用 Init()、Hide() 和 Event() 方法。必要时由“EA 交易”的 OnInit() 函数调用 Show() 方法。
-
使用这些控件。于子类副本中声明控件类。由子类副本的OnInitEvent()、OnShowEvent()、OnHideEvent() 和 EventHandler() 方法调用控件的 Init()、Show()、Hide()和Event() 方法。
附录
附录文件清单:
- IncGUI_v3.mqh – 一个包含文件,其中包含创建图形界面所需的所有类。该文件应置于 MQL5/Include directory of the Terminal Data Directory (终端数据目录的 MQL5/Include 目录)中。
- eIncGUI_v3_Test_Form.mq5 – 使用表单示例。该文件应置于 MQL5/Experts directory of the Terminal Data Directory (终端数据目录的 MQL5/Experts 目录)。
- IncGUIv3mqh.chm – 于 IncGUI_v3.mqh 归档的文件材料。
本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/322
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。