外汇EA编写教程:在GUI控件中使用布局和容器:cBox类

目录

  • 1。介绍
  • 2。目标
  • 三。CBOX
    3.1类。布局样式3.2。计算控制3.3之间的间隔。对齐3.4。组件渲染3.5.部件尺寸调整3.6.递归呈现
  • 4。在对话框窗口中实现
  • 5。例行程序5.1。程序1:简单点值计算5.2。例程2:控制重新配置示例
  • 6。优缺点
  • 7。结论

1。介绍

在大多数应用程序中,GUI是通过使用对话框窗口中控件绝对定位的直接方法创建的。然而,在某些情况下,这种图形用户界面(GUI)设计是不方便甚至不实用的。本文介绍了一种使用布局管理器cbox类基于布局和容器创建GUI(图形用户界面)的替代方法。

本文中实现和使用的布局管理器大致相当于一些主流编程语言(如Box Dead(Java)和几何管理器包(Python/Tkter))。

2。目标

查看MetaTrader 5中提供的SimplePanel和控制例程,我们可以看到这些面板中的控件是由像素(绝对定位)定位的。创建的每个控件都在工作区中指定了一个特定的位置,并且每个控件都依赖于它之前创建的控件,并附加了一些偏移量。虽然这是一种自然的方法,但在大多数情况下,虽然不需要高精度,但这种方法的使用在许多方面是不利的。

在设计图形用户界面时,任何有经验的程序员都可以使用图形控件的精确像素定位。但是,这有以下缺点:

  • 通常,当修改组件的大小或位置时,它不能阻止其他组件受到影响。
  • 大多数代码是不可重用的-接口中的微小更改可能导致代码发生重大更改。
  • 它可能很耗时,尤其是在设计更复杂的接口时。

这促使我们创建具有以下目标的布局系统:

  • 代码应该是可重用的。
  • 修改接口的一部分应该尽量减少对其他组件的影响。
  • 应自动计算接口中组件的位置。

本文介绍了使用container-cbox类实现这类系统的方法。

三。CBOX

类cbox的一个实例用作容器或框控件-可以将控件添加到框中,cbox自动计算控件在其分配的空间中的位置。典型的cbox类实例应该具有以下布局:

CBox 布局

传奇1。CBox布局

外箱代表集装箱的整体尺寸,内箱代表班轮边界。蓝色区域表示填充空间。剩余的空白是控件可以放置在容器中的空间。

根据面板的复杂性,cbox类可以以不同的方式使用。例如,它可以用作容器(cbox),用一组控件存储其他容器。或者容器包含控件和其他容器。但是,对于给定的父容器,强烈建议使用对等容器。

我们通过扩展cwndclient(不带滚动条)来构建cbox,如下节所示:

#include <Controls/WndClient.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CBox : public CWndClient
  {
public:
                     CBox();
                    ~CBox();   
   virtual bool      Create(const long chart,const string name,const int subwin,
                           const int x1,const int y1,const int x2,const int y2);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CBox::CBox() 
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CBox::~CBox()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CBox::Create(const long chart,const string name,const int subwin,
                  const int x1,const int y1,const int x2,const int y2)
  {
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   if(!CreateBack())
      return(false);
   if(!ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG))
      return(false);
   if(!ColorBorder(clrNONE))
      return(false);
   return(true);
  }
//+------------------------------------------------------------------+

cBox类也可以直接从cwndcontainer继承。但是,这样做将丢失此类中一些有用的函数,如背景和边框。相反,可以直接从扩展的cwndobj实现更简单的版本,但您需要添加carrayobj实例作为其私有成员或受保护成员,并重新创建相关对象的类方法以保存该实例。

3.1。版式风格

cBox有两种布局样式:垂直和水平。

水平样式具有以下基本布局:

CBox 的水平样式

图利2。水平样式(中心)

垂直样式具有以下基本布局:

CBox 的垂直样式

传奇3。垂直样式(中心)

cBox默认为水平样式。

使用这两种布局的组合(可能带有多个容器),您可以重建几乎任何类型的GUI面板设计。此外,在容器中放置控件允许分段设计。也就是说,它允许您自定义给定容器中控件的大小和位置,而不影响其他容器的位置。

为了在cbox中实现水平和垂直样式,我们需要声明一个枚举,然后将其保存为所述类中的成员:

enum LAYOUT_STYLE
  {
   LAYOUT_STYLE_VERTICAL,
   LAYOUT_STYLE_HORIZONTAL
  };


3.2。计算控件之间的间隔

cBox最大化分配给它的可用空间,并均匀地定位控件,如上图所示。

总的来说,我们可以使用以下伪代码导出计算给定cbox容器中控件之间间隔的公式:

对于水平布局:
x 间隔 = ((可用空间 x)-(所有控件的总计 x 大小))/(控件总数 + 1)
y 间隔 = ((可用空间 y)-(y 控件大小))/2

对于垂直布局:
x 间隔 = ((可用空间 x)-(x 控件大小))/2
y 间隔 = ((可用空间 y)-(所有控件的总计 y 大小))/(控件总数 + 1)


3.3。对准

如前一节所述,控件之间的间隔计算仅用于中心对齐。我们希望cbox类能够容纳更多的对齐方式,因此我们需要在计算中做一些小的修改。

对于水平对齐,可用选项为左、右和中心(无边界),但容器居中除外,如下图所示:

水平盒子 - 左对齐

传奇4。水平样式(左对齐)

水平盒子 - 右对齐

传奇5。水平样式(右对齐)

水平盒子 - 居中对齐 (无边)

传奇6。水平样式(集中,无限)

对于水平对齐,除了容器居中之外,可用的选项是顶部、底部、中心和中心(无限),如下图所示:

垂直盒子 - 居顶 垂直盒子 - 居中 (无边) 垂直盒子 - 居底

传奇7。垂直对齐样式:(左)上,(中)中-无边,(右)下

应该注意的是,cBox类应该根据这些对齐设置自动计算控件之间的x和y间隔。所以,最好使用除数。

(控件总数 + 1)

为了得到控制间隔,我们使用控制的总数作为除数,并且(控制的总数-1)作为中心控制,在边界处没有边界。

与布局样式类似,实现CBox类的对齐特性将需要枚举。我们要为每个对齐样式声明一个枚举,如下所示:

enum VERTICAL_ALIGN
  {
   VERTICAL_ALIGN_CENTER,
   VERTICAL_ALIGN_CENTER_NOSIDES,
   VERTICAL_ALIGN_TOP,
   VERTICAL_ALIGN_BOTTOM
  };
enum HORIZONTAL_ALIGN
  {
   HORIZONTAL_ALIGN_CENTER,
   HORIZONTAL_ALIGN_CENTER_NOSIDES,
   HORIZONTAL_ALIGN_LEFT,
   HORIZONTAL_ALIGN_RIGHT
  };


3.4。组件呈现

通常,我们通过指定x1、y1、x2和y2参数来创建控件,例如创建按钮剪辑,如下所示:

CButton m_button;
int x1 = currentX;
int y1 = currentY;
int x2 = currentX+BUTTON_WIDTH; 
int y2 = currentY+BUTTON_HEIGHT
if(!m_button.Create(m_chart_id,m_name+"Button",m_subwin,x1,y1,x2,y2))
      return(false);

这里x2减去x1和y2减去y1分别等于控件的宽度和高度。如果没有,我们可以使用cbox以一种更简单的方式创建相同的按钮,如下节所示:

if(!m_button.Create(m_chart_id,m_name+"Button",m_subwin,0,0,BUTTON_WIDTH,BUTTON_HEIGHT))
      return(false);

类cbox将自动重新定位稍后创建的面板窗口中的小部件。调用方法包()用于重新定位控件和容器,并再次调用render()方法。

bool CBox::Pack(void)
  {
   GetTotalControlsSize();
   return(Render());
  }

方法包()只获取容器的组合大小,然后调用render()方法,在该方法中执行更多操作。以下代码段说明了在实际容器中呈现控件的render()方法:

bool CBox::Render(void)
  {
   int x_space=0,y_space=0;
   if(!GetSpace(x_space,y_space))
      return(false);
   int x=Left()+m_padding_left+
      ((m_horizontal_align==HORIZONTAL_ALIGN_LEFT||m_horizontal_align==HORIZONTAL_ALIGN_CENTER_NOSIDES)?0:x_space);
   int y=Top()+m_padding_top+
      ((m_vertical_align==VERTICAL_ALIGN_TOP||m_vertical_align==VERTICAL_ALIGN_CENTER_NOSIDES)?0:y_space);
   for(int j=0;j<ControlsTotal();j++)
     {
      CWnd *control=Control(j);
      if(control==NULL) 
         continue;
      if(control==GetPointer(m_background)) 
         continue;
      control.Move(x,y);     
      if (j<ControlsTotal()-1)
         Shift(GetPointer(control),x,y,x_space,y_space);      
     }
   return(true);
  }


3.5。部件尺寸调整

当控件大小大于其容器的可用空间时,应调整控件大小以适应可用空间。否则,控件将溢出容器,导致整个面板出现外观问题。当您希望控件最大限度地占用整个客户或容器空间时,这种方法也很方便。如果给定控件的宽度或高度超过容器的宽度或高度减去填充(双边),则控件大小将调整为最大可用宽度或高度。

应注意,当cBox中包含的所有控件的总大小超过可用空间时,将不会调整容器大小。在这种情况下,需要手动调整主对话框窗口(CDialog或CappDialog)或单个控件的大小。


3.6。递归呈现

对于cbox的简单使用,只需调用pack()方法即可。但是,对于嵌套容器,需要调用相同的方法,以便所有容器都可以找到自己的单独控件或容器。我们可以通过向函数中添加方法来防止这种情况,并且我们可以为我们自己的控件实现相同的方法,前提是并且仅当所讨论的控件是cbox类或任何布局类的实例时。为此,我们首先定义一个宏并为其指定一个唯一值:

#define CLASS_LAYOUT 999

然后,我们重写cobject类的type()方法,以便它返回我们刚才准备的宏值:

virtual int       Type() const {return CLASS_LAYOUT;}

最后,在cbox类的pack()方法中,我们将为其子容器执行呈现方法,这些方法是布局类的示例:

for(int j=0;j<ControlsTotal();j++)
     {
      CWnd *control=Control(j);
      if(control==NULL) 
         continue;
      if(control==GetPointer(m_background)) 
         continue;
      control.Move(x,y);

      //如果它是布局类, 调用控件 Pack() 方法
      if(control.Type()==CLASS_LAYOUT)
        {
         CBox *container=control;
         container.Pack();
        }     
   
      if (j<ControlsTotal()-1)
         Shift(GetPointer(control),x,y,x_space,y_space);      
     }

呈现方法在开始时计算容器中控件的可用空间。这些值分别以m_total_x和m_total_y存储。The next task is to calculate the interval between controls based on layout style and alignment. 最后一步是在容器中实现真正的控制重定位。

cBox维护重新定位控件的记录,因为容器中的某些对象不需要重新定位,例如cwndclient本地后台对象或其他可能的cBox扩展控件。

cBox还保留容器中控件的最小大小(背景除外),由m_m in_大小(cSize结构)定义。目标是保持控件在容器中均匀地堆叠,无论是水平还是垂直。它的定义非常违反直觉,因为它实际上是最大的控制大小。但是,这里我们将其定义为最小的,因为cBox假定大小最小,并根据该大小计算可用空间。

注意,shift()方法遵循类似于常规控制定位实现(绝对定位)的路径。渲染方法保留对x和y坐标的引用,并记住在更新cbox时重新定位每个控件。但是,对于cbox,这可以自动完成,让面板开发人员只需设置所使用的每个控件的实际大小。

4。在对话窗口中实现

在使用cbox时,我们几乎要替换cdialog或cappdialog本地客户机区域m_客户机区域的功能,这是cwndclient的一个例子。所以我们现在至少有三个选择:

  1. 扩展/重写cappdialog或cdialog以用cbox替换客户区域。
  2. 使用容器并将其添加到客户区域。
  3. 使用主cbox容器保存其余较小的容器。

使用第一个选项可能需要很多工作,我们需要重写对话框对象以使用新的客户机区域对象。相反,可扩展的对话对象使用自定义容器类,但是给我们留下了一个无用的cwndclient(m_client_area)实例,它占用了不必要的内存空间。

第二种选择也是可行的。我们只需将控件放在cbox容器中,然后使用像素定位将它们添加到客户区域。但是这个选项并没有充分利用cbox类的潜力,也就是说,面板的设计不必被独立控件和容器的位置混淆。

建议使用第三个选项。也就是说,我们创建一个body cbox容器来容纳所有其他较小的容器和控件。主容器将占据整个本地客户区,并且是它添加的唯一子容器。这将使本地客户区有点多余,但至少它仍然可用。此外,我们可以避免使用此选项进行大量编码/重新编码。

5。常规

5.1。程序1:简单点值计算

现在,我们使用cbox类来实现一个简单的面板:点值计算器。“点值计算器”对话框将包含sange cedit类型的字段,称为:

  • 品种名称或金融工具;
  • 输入品种或金融工具的每个点的大小;
  • 输入品种或金融工具的每一点的价值;

这给我们带来了总共七个不同的控件,包括每个字段的标签(clabel)和执行计算的按钮(cButton)。计算器屏幕截图如下图所示:

CBox 布局0

图8。点值计算器

通过查看计算器面板,我们可以使用cbox将其减少到五个不同的容器。每个字段有三个水平容器,另一个作为按钮位于右侧。所有这些容器将封装在一个垂直样式的主容器中。最后,主体容器将附加到cappdialog实例的客户机区域。下图显示了容器的布局。紫色框表示水平线。白色框表示基本控件,包含所有较小框的最大灰色框是主框窗口。

CBox 布局1

图9。点值计算器布局

注意,当使用cbox容器时,我们不能为间隙和缩进声明任何宏。我们只需声明控件大小的宏,配置每个cbox实例,并将它们分配给控件。

为了构建这个面板,我们首先创建一个头文件“pipValueCalculator”。mqh’应该与我们稍后准备的源代码体文件(pipValueCalculator)放在同一个文件夹中。MQ5)。我们在这个文件中包含cbox类的头文件,以及面板所需的其他包含文件。我们还需要csymbolinfo类,我们将实际计算所有给定品种的点值:

#include <Trade/SymbolInfo.mqh>
#include <Layouts/Box.mqh>
#include <Controls/Dialog.mqh>
#include <Controls/Label.mqh>
#include <Controls/Button.mqh>

最后一步是指定要使用的控件的宽度和高度。您可以为每个控件指定特定的大小,但对于此面板,我们将使用通用空间大小。也就是说,所有基本控件的宽度和高度都相同:

#define CONTROL_WIDTH   (100)
#define CONTROL_HEIGHT  (20)

现在,让我们继续创建真正的面板类对象。这可以通过从cappdialog类继承一个新类来实现,方法通常是:

class CPipValueCalculatorDialog : public CAppDialog

类的初始结构如下所示:

class CPipValueCalculatorDialog : public CAppDialog
  {
protected:
//此处是类的保护成员
public:
                     CPipValueCalculatorDialog();
                    ~CPipValueCalculatorDialog();

protected:
//此处是类的保护方法
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPipValueCalculatorDialog::CPipValueCalculatorDialog(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPipValueCalculatorDialog::~CPipValueCalculatorDialog(void)
  {
  }

从上面的代码片段中,我们现在有了点计算器面板类的起始模板(事实上,在创建类似面板时,它可以重用)。现在,让我们继续创建一个主容器类的成员,它将是面板上显示的所有其他cbox容器的父容器:

class CPipValueCalculatorDialog : public CAppDialog
  {
protected:
   CBox              m_main;
//此处更多代码...

我们已经为面板定义了body cbox容器,但是还没有为它创建实际的函数。为此,我们向panel类添加其他类方法,如下所示:

// 开始类定义
// ...
public:
                     CPipValueCalculatorDialog();
                    ~CPipValueCalculatorDialog();
protected:
   virtual bool      CreateMain(const long chart,const string name,const int subwin);
// 其余的定义
// ...

稍后,在类外部,我们定义类方法的实体(类似于如何定义类的构造函数和析构函数):

bool CPipValueCalculatorDialog::CreateMain(const long chart,const string name,const int subwin)
  {   
   //创建主体 CBox 容器
   if(!m_main.Create(chart,name+"main",subwin,0,0,CDialog::m_client_area.Width(),CDialog::m_client_area.Height()))
      return(false);   

   //应用垂直布局
   m_main.LayoutStyle(LAYOUT_STYLE_VERTICAL);
   
   //设置所有边框衬垫 10 个像素
   m_main.Padding(10);
   return(true);
  }

我们使用cdialog::m_客户机_区域。宽度()和cdialog::m_客户机区域。height()指定容器的宽度和高度。也就是说,它占据了面板工作区的整个空间。我们还对容器做了一些修改:应用垂直样式,并将所有边框的填充设置为10像素。这些函数由cbox类提供。

既然我们已经定义了body容器类成员,并且应该创建它,那么我们将为图例9中的每一行创建成员。对于最上面的一行(即变体),我们首先创建容器来声明它们,然后创建包含它们的基本控件,就在上面代码段中显示的主容器类成员的下面:

CBox              m_main;
CBox              m_symbol_row;   //行容器
CLabel            m_symbol_label; //标签控件
CEdit             m_symbol_edit;  //编辑控件

与body容器类似,我们还定义了一个函数来创建实际的行容器:

bool CPipValueCalculatorDialog::CreateSymbolRow(const long chart,const string name,const int subwin)
  {
   //为行创建 CBox 容器 (品种行)
   if(!m_symbol_row.Create(chart,name+"symbol_row",subwin,0,0,CDialog::m_client_area.Width(),CONTROL_HEIGHT*1.5))
      return(false);

   //创建标签控件
   if(!m_symbol_label.Create(chart,name+"symbol_label",subwin,0,0,CONTROL_WIDTH,CONTROL_HEIGHT))
      return(false);
   m_symbol_label.Text("品种");
   
   //创建编辑控件
   if(!m_symbol_edit.Create(chart,name+"symbol_edit",subwin,0,0,CONTROL_WIDTH,CONTROL_HEIGHT))
      return(false);
   m_symbol_edit.Text(m_symbol.Name());

   //添加基本控件至它们的父控件 (行)
   if(!m_symbol_row.Add(m_symbol_label))
      return(false);
   if(!m_symbol_row.Add(m_symbol_edit))
      return(false);
   return(true);
  }

在这个函数中,我们首先创建一个变化行容器。请注意,我们使用工作区的整个宽度作为宽度,同时使其比前面定义的控件的高度高50%。

创建行后,我们创建单独的控件。这一次,它们使用我们前面定义的宽度和高度宏。还要注意我们是如何创建这些控件的:

Create(chart,name+"symbol_edit",subwin,0,0,CONTROL_WIDTH,CONTROL_HEIGHT))

红色值是x1和y1坐标。这意味着创建时,所有控件都放置在图表的左上角。在调用cbox类的pack()方法后,这些方法会立即重新排列。

我们已经创建了行容器。我们还创建了容器中的基本控件。下一步是将刚刚创建的控件添加到行容器中,该容器在函数的最后几行中表示:

if(!m_symbol_row.Add(m_symbol_label))
   return(false);
if(!m_symbol_row.Add(m_symbol_edit))
   return(false);

对于其他行(点大小、点值和按钮行),我们的实现与品种行中的实现大致相同。

当使用cbox类时,需要创建body容器和其他子程序。现在,我们进入了一个熟悉的情况,即创建面板对象本身。这可以通过重写cappDialog类的方法create()来实现(无论是否使用cbox)。我们前面定义的两个方法最终是有意义的,因为我们将在这里称之为:

bool CPipValueCalculatorDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   //创建 CAppDialog 面板
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   
   //使用我们之前定义的函数创建主体 CBox 容器  
   if(!CreateMain(chart,name,subwin))
      return(false);  

   //使用我们之前定义的函数创建品种行 CBox 容器  
   if(!CreateSymbolRow(chart,name,subwin))
      return(false);

   //添加品种行作为主体 CBox 容器的子 CBox 容器
   if(!m_main.Add(m_symbol_row))
      return(false);

   //渲染主体 CBox 容器以及它的所有子容器 (递归)
   if (!m_main.Pack())
      return(false);
   
   //添加主体 CBox 容器作为面板客户区域的仅有子容器
   if (!Add(m_main))
      return(false);
   return(true);
  }

Don’t forget to declare the overridden Create () method in the CPipValueCalculatorDialog class as follows:

public:
                     CPipValueCalculatorDialog();
                    ~CPipValueCalculatorDialog();
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);

如上所示,它应该是类的公共方法,因为我们将从类外部调用它。在主源代码文件pipValueCalculator.mq5中需要更多详细信息:

#include "PipValueCalculator.mqh"
CPipValueCalculatorDialog ExtDialog;
//+------------------------------------------------------------------+
//| 初始函数                                                          |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//--- 创建应用对话框
   if(!ExtDialog.Create(0,"点值计算器",0,50,50,279,250))
      return(INIT_FAILED);
//--- 运行应用
   if(!ExtDialog.Run())
      return(INIT_FAILED);
//--- ok
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 逆初函数                                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| 报价处理函数                                                      |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
    //评论至此, 将在章节稍后继续讨论  
    //ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

除了以下三点外,此代码与我们在面板主体源代码文件中经常看到的非常相似:

  1. 我们添加了“PipValueCalculator”。mqh’头文件,而不是包含来自cappdalog的文件。’PipValueCalculator。mqh’已经包含在头文件中,因此不再需要将其包含在主源代码中。’PipValueCalculator。mqh’还负责包含cbox类的头文件。
  2. 我们将extdialog声明为在“pipValueCalculator”中定义的类的实例。mqh'(PipValueCalculator class).
  3. 我们为在extdialog中定义的面板指定了一个自定义大小。创建()。

仅编译品种线时,面板看起来类似于以下屏幕截图:

CBox 布局2

图10。点值计算器面板
,只有一行

主容器垂直对齐并居中,各种行水平对齐(也水平和垂直对齐)。为了将面板组装成一个图例。8,我们需要添加另外三行,基本上与创建变体行的方式相同。一个例外是按钮行,它只包含一个基本控件(按钮),并且对它是正确的:

m_button_row.HorizontalAlign(HORIZONTAL_ALIGN_RIGHT);

事件处理超出了本文的范围,但是为了这个例程的完整性,我们将简要介绍它。首先,我们为PipValueCalculator类声明一个新的类成员m_符号。我们还包括另外两个成员,m_digits_adjust和m_points_adjust,稍后将用于将点转换为点值。

CSymbolInfo      *m_symbol;
int               m_digits_adjust;
double            m_points_adjust;

使用以下代码在类的构造函数或create()方法中初始化m_符号:

if (m_symbol==NULL)
      m_symbol=new CSymbolInfo();
if(m_symbol!=NULL)
{
   if (!m_symbol.Name(_Symbol))
      return(false);
}   

如果品种号为空,我们将创建一个新的csymbolinfo实例。如果它不是空的,我们为它指定的名称就是图表的名称。

下一步是为按钮定义Click事件处理程序。这可以通过onclickButton()类方法实现。我们将其实体定义如下:

void CPipValueCalculatorDialog::OnClickButton()
  {
   string symbol=m_symbol_edit.Text();
   StringToUpper(symbol);
   if(m_symbol.Name(symbol))
     {
      m_symbol.RefreshRates();
      m_digits_adjust=(m_symbol.Digits()==3 || m_symbol.Digits()==5)?10:1;
      m_points_adjust=m_symbol.Point()*m_digits_adjust;
      m_pip_size_edit.Text((string)m_points_adjust);      
      m_pip_value_edit.Text(DoubleToString(m_symbol.TickValue()*(StringToDouble(m_pip_size_edit.Text()))/m_symbol.TickSize(),2));
     }
   else Print("无效输入");
  }

class方法首先获取m_符号编辑控件的值以计算点值。然后将产品名称传递给CSymbolinfo类的实例。类获取所选品种的报告值,然后乘以某个乘数以调整和计算点值。

最后一步是启用类(也在PipValueCalculator类中)中定义的事件处理程序。在类的公共方法下插入这些代码行:

virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

然后,我们定义类外部的类的方法实体,如下所示:

EVENT_MAP_BEGIN(CPipValueCalculatorDialog)
   ON_EVENT(ON_CLICK,m_button,OnClickButton)
EVENT_MAP_END(CAppDialog)

5.2。例程2:控制重构示例

在安装新的metatrader之后,控制面板的例程将自动安装。在导航窗口中,您可以在专家顾问/示例/控件下找到它。此面板的屏幕截图如下图所示:

CBox 布局3

传奇11。控件对话框(原始)

上面显示的对话框窗口的布局如下图所示。要使用cbox实例重建面板,我们将看到四条主要的水平线(紫色),形成以下一组控件:

  1. 编辑控件;
  2. 三按钮控制;
  3. spinedit和日期选择器;
  4. 组合框、RadioGroup和CheckGroup(第1列)和ListView(第2列)。

最后一个水平容器是一个特殊情况,因为它是一个嵌套的水平容器,可以容纳其他两个容器(绿色的第1列和第2列)。这些容器应垂直排列。

CBox 布局4

传奇12。控件对话框布局

重建对话框控件时,应删除调用add()方法的所有代码,但主容器除外,它将是客户端区域的唯一子角色。同时,其他控件和容器应添加到其设计的父容器中,从最深的容器到主容器,最终添加到本地客户区域。

安装完成后,编译并执行。除了日期选择器的递增、递减和列表按钮将拒绝工作外,其他一切都应该正常。这是由于CDatePicker类的下拉列表是在其他容器的上下文中设置的。要解决此问题,请搜索位于%data folder%/mql5/include/controls/datepicker.mqh中的cdatpicker类文件。找到listshow()方法并在函数开头插入以下代码行:

BringToTop();

重新编译和测试。这会将日期选择器的下拉列表放在前景中,并在显示单击事件时优先处理该事件。以下是完整的函数片段:

bool CDatePicker::ListShow(void)
  {
   BringToTop();
//--- 设置数值   
   m_list.Value(m_value);
//--- 显示列表
   return(m_list.Show());
  }

控制对话框重建的屏幕截图如下图所示:

CBox 布局5

传奇13。控件对话框(使用cbox)

宏观图形与原始图形几乎相同。但是这里也有一个显著的区别,顺便说一下,第1列和第2列是完全对齐的。在原始图像中,我们可以看到checkgroup和listview在底部均匀地堆叠。但是,在顶部,组合框与ListView不对齐。当然,坐标可以在原始面板中重新定位,但这不仅需要调整组合框的像素,还需要调整RadioGroup和三个控件之间的间距。另一方面,使用cbox容器,只需将顶部和底部衬垫设置为零,并使用正确的对齐方式。

然而,这并不意味着使用cbox或布局在细度上更优越。尽管不可否认的是,控制坐标的精度比强制编码要低,但是使用容器和布局仍然可以提供正确的精度水平,同时使GUI设计变得更加容易。

6。优缺点

优势:

  • 代码可重用-您可以在不同的应用程序和对话框中交织和重用CBOX或任何布局类。
  • 可伸缩性-尽管在小型应用程序中使用它可以使源代码更长,但它的好处可以在更复杂的面板和对话框中体现出来。
  • 控件集合分组-它允许您修改一组控件,而不影响其他空间的位置。
  • 自动定位-缩进、间隙和间隔由布局类自动计算,无需手动编码。

缺点:

  • 需要为容器创建其他控件,以及操作它们的其他函数。
  • 低精度-位置受可用布局和对齐选项的限制。
  • 当包含的控件大小不同时,它们可能会变得有问题或复杂——在这种情况下,它们可能被保持在最小值,或者它们可能使用嵌套容器。

7。结论

在本文中,我们研究了在图形面板设计中使用布局和容器的可能性。此方法使我们能够使用布局和对齐样式自动处理不同控件的位置。它可以使图形面板的设计更简单,在某些情况下,减少编码时间。

类cBox类是一个辅助控件,用作GUI面板中基本控件的容器。在本文中,我们已经演示了它的操作以及如何在实际应用中使用它。虽然精度低于绝对定位,但其精度水平在许多应用中仍能得到提高。

由MetaQuotes Software Corp.从英文翻译为
原文。https://www.mql5.com/en/articles/1867

附加文件下载zip box.mqh(11.88 kb)控件2.mq5(2.13 kb)控件对话框2.mqh(20.18 kb)p ipValueCalculator.mq5(1.89 kb)pipValueCalculator.mqh(9.47 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投