English Русский Español Deutsch 日本語 Português
preview
DoEasy. 控件 (第 8 部分): 基准 WinForms 对象类别,GroupBox 和 CheckBox 控件

DoEasy. 控件 (第 8 部分): 基准 WinForms 对象类别,GroupBox 和 CheckBox 控件

MetaTrader 5示例 | 5 九月 2022, 14:15
677 0
Artyom Trishkin
Artyom Trishkin

内容


概述

我们创建的 WinForms 对象的分类与 MS Visual Studio 非常相似:

  • 标准控件
  • 容器
  • 菜单和工具条
  • 数据
  • 组件
  • 打印
  • 对话窗口

所有类别的对象都继承自公共 CWinFormBase 基准对象。 此外,属于同一类别的对象在其类别内的功能也会有重叠。 因此,值得将属于一个类别的对象的所有类似属性和方法合并到同一类别的公共类之中。 换句话说,我们需要创建一个单独的基类,供所有自 WinForms 对象基类继承的每个对象公用。 在此只是简单地为每个类别中的新对象编写代码。

在本文中,我们将创建两个这样的对象 — 容器对象和标准控件对象。 一般来说,为了理解哪些属性和方法可由一个类别的对象公共,我们至少需要在该类别中创建两个对象。

我们在容器类别中创建 GroupBox 对象类 — 这是一个容器,在视觉上由其内的若干个对象组合而成。
与 Panel 对象(也是一个容器)不同,GroupBox 的功能较少,只是一个容器,可把对象可视化地组合到一个公共组中。

在标准控件类别中,创建 CheckBox 对象。 这是一个带有标签的复选框,可以有三种状态:选中、未选中、和未定义。 由于对象拥有文本标签,因此它继承自标签对象。 不过,将分阶段加入在不同状态下绘制复选框(验证标志)的功能。

所有已创建对象仍然是静态的,即,它们无法与鼠标交互。 我将在完成计划创建开发的大多数 WinForms 对象之后,再来实现必要的功能。


改进库类

对于所创建的对象,我们需要在创建时为某些属性值添加默认分配值。

在 \MQL5\Include\DoEasy\Defines.mqh 中,为这些值添加新的宏替换

//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define CLR_CANV_NULL                  (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define CLR_DEF_FORE_COLOR             (C'0x2D,0x43,0x48')        // Default color for texts of objects on canvas
#define CLR_DEF_FORE_COLOR_OPACITY     (255)                      // Default color non-transparency for canvas object texts
#define CLR_DEF_FRAME_COLOR            (C'0x66,0x6C,0x6F')        // Default color for object frames on canvas
#define CLR_DEF_FRAME_COLOR_OPACITY    (255)                      // Default color non-transparency for canvas object frames
#define CLR_DEF_FRAME_COLOR_DARKNESS   (-2.0)                     // Default color opacity for canvas object frames (when using the background color)
#define CLR_DEF_FRAME_GBOX_COLOR       (C'0xDC,0xDC,0xDC')        // Default color for GroupBox object frames on canvas
#define CLR_DEF_OPACITY                (200)                      // Default color non-transparency for canvas objects
#define CLR_DEF_SHADOW_COLOR           (C'0x6B,0x6B,0x6B')        // Default color for canvas object shadows
#define CLR_DEF_SHADOW_OPACITY         (127)                      // Default color non-transparency for canvas objects
#define DEF_SHADOW_BLUR                (4)                        // Default blur for canvas object shadows
#define DEF_FONT                       ("Calibri")                // Default font
#define DEF_FONT_SIZE                  (8)                        // Default font size
#define DEF_CHECK_SIZE                 (12)                       // Verification flag default size
#define OUTER_AREA_SIZE                (16)                       // Size of one side of the outer area around the form workspace
#define DEF_FRAME_WIDTH_SIZE           (3)                        // Default form/panel/window frame width
//--- Graphical object parameters

复选框标志在 CheckBox 对象中的默认大小为 12x12 像素。

目前,我们在函数库对象类型列表的 WinForms 部分中已有了基类、面板、和标签类型。 它们并不是必需的,因为在另一个枚举中设置了相同的类型。 但是在这里,我们可以用它们作为 WinForms 对象类别的指示。 如此,我们修复枚举中的名称,以便它们仅显示对象类别:

//+------------------------------------------------------------------+
//| List of library object types                                     |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
//--- Graphics
   OBJECT_DE_TYPE_GBASE =  COLLECTION_ID_LIST_END+1,              // "Base object of all library graphical objects" object type
   OBJECT_DE_TYPE_GELEMENT,                                       // "Graphical element" object type
   OBJECT_DE_TYPE_GFORM,                                          // Form object type
   OBJECT_DE_TYPE_GFORM_CONTROL,                                  // "Form for managing pivot points of graphical object" object type
   OBJECT_DE_TYPE_GSHADOW,                                        // Shadow object type
   //--- WinForms
   OBJECT_DE_TYPE_GWF_BASE,                                       // WinForms Base object type (base abstract WinForms object)
   OBJECT_DE_TYPE_GWF_CONTAINER,                                  // WinForms container object type
   OBJECT_DE_TYPE_GWF_COMMON,                                     // WinForms standard control object type
//--- Animation

现在,在 WinForms 对象的情况下,我们可将函数库对象类型用作一种类型,以及 WinForms 对象的类别。 在图形元素类型列表枚举当中,我们将在其类别中指定类型

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms ChackBox
  };
//+------------------------------------------------------------------+


CheckBox 对象验证标志可取三种状态之一。 我们创建一个枚举来指定它们:

//+------------------------------------------------------------------+
//| Control flag status                                              |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_CHEK_STATE
  {
   CANV_ELEMENT_CHEK_STATE_UNCHECKED,                 // Unchecked
   CANV_ELEMENT_CHEK_STATE_CHECKED,                   // Checked
   CANV_ELEMENT_CHEK_STATE_INDETERMINATE,             // Undefined
  };
//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+


在基于画布的图形元素的整数型属性枚举的最末尾添加新属性,以及将整数型属性的数量从 44 增加到 48

//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Element ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type
   
   //--- ...
   //--- ...

   CANV_ELEMENT_PROP_PADDING_LEFT,                    // Left margin inside the control
   CANV_ELEMENT_PROP_PADDING_RIGHT,                   // Right margin inside the control
   CANV_ELEMENT_PROP_TEXT_ALIGN,                      // Text position within text label boundaries
   CANV_ELEMENT_PROP_CHECK_ALIGN,                     // Position of the verification flag within control borders
   CANV_ELEMENT_PROP_CHECKED,                         // Control verification flag status
   CANV_ELEMENT_PROP_CHECK_STATE,                     // Status of a control having a verification flag
   CANV_ELEMENT_PROP_AUTOCHECK,                       // Auto change flag status when it is selected
   
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (48)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+


将新属性添加到画布上图形元素的可能排序条件的枚举当中:

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical elements on the canvas    |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Sort by element ID
   SORT_BY_CANV_ELEMENT_TYPE,                         // Sort by graphical element type
   
   //--- ...
   //--- ...

   SORT_BY_CANV_ELEMENT_PADDING_LEFT,                 // Sort by left margin inside the control
   SORT_BY_CANV_ELEMENT_PADDING_RIGHT,                // Sort by right margin inside the control
   SORT_BY_CANV_ELEMENT_TEXT_ALIGN,                   // Sort by text position within text label boundaries
   SORT_BY_CANV_ELEMENT_CHECK_ALIGN,                  // Sort by position of the verification flag within control borders
   SORT_BY_CANV_ELEMENT_CHECKED,                      // Sort by control verification flag status
   SORT_BY_CANV_ELEMENT_CHECK_STATE,                  // Sort by status of a control having a verification flag
   SORT_BY_CANV_ELEMENT_AUTOCHECK,                    // Sort by auto change flag status when it is selected
//--- Sort by real properties

//--- Sort by string properties
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Sort by the graphical resource name
   SORT_BY_CANV_ELEMENT_TEXT,                         // Sort by graphical element text
  };
//+------------------------------------------------------------------+

现在我们可以依据新属性选择对象,并针对列表进行排序。


对于不同的 WinForms 对象,BorderStyle 属性有不同的用途。 对于面板对象,该属性指定对象边框的样式。 对于 GroupBox 的情况,它给围绕一组对象绘制的边框设置类型(对象本身没有边框)。 相应地,我们可以出于不同目的,针对不同对象中使用相同的方法。
为达成目标,在所有 WinForms 对象的基类文件 \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh 中,我们将其设为虚拟:

//--- (1) Set and (2) return the font width type
   void              SetFontBoldType(ENUM_FW_TYPE type);
   ENUM_FW_TYPE      FontBoldType(void)                        const { return (ENUM_FW_TYPE)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE);               }
//--- (1) Set and (2) return the frame style
   virtual void      SetBorderStyle(const ENUM_FRAME_STYLE style)    { this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,style);                           }
   ENUM_FRAME_STYLE  BorderStyle(void)                         const { return (ENUM_FRAME_STYLE)this.GetProperty(CANV_ELEMENT_PROP_BORDER_STYLE);        }

在继承类中重新定义方法能令我们为每个类创建单独的方法实现。


容器类别中的基准对象

在 Containers 类别中创建第二个 WinForms 对象时,很明显,许多属性和方法是相同的,并且在对象之间重复。 为了避免不同对象中的相同类型方法重复,我们需要将它们全部移到一个公共类中,该类的对象将从中继承。 因此,每个子对象均从其父对象继承所有这些方法。 对于同一类别的不同对象,其方法实现应该有所不同,故应默认虚拟的,并需在继承类中重新定义。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\ 中,创建一个新文件Container.mqh,内含 CContainer 类。 该类应从函数库的所有 WinForms 对象的基类继承在所创建的类中应包含其文件

//+------------------------------------------------------------------+
//|                                                    Container.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\WForms\WinFormBase.mqh"
//+------------------------------------------------------------------+
//| Class of the base container object of WForms controls            |
//+------------------------------------------------------------------+
class CContainer : public CWinFormBase
  {
  }


我们应该从面板容器类中把许多已经存在的方法移到新类,因为这些方法对于同类别的其它类也需要。 由于我在第六篇文章讲述 anel 类时已经研究过这些方法中的大多数,因此这里我们就简单地研究类主体及其方法的实现。

由于类对象没有参考底图对象(与面板对象对比而言),故我们需要以某种方式指定对象工作区域。 您放置绑定到容器的其它对象的所在,可被视为工作区域。 我们引入新的方法来强调工作区域的限制:

//+------------------------------------------------------------------+
//|                                                    Container.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\WForms\WinFormBase.mqh"
#include "..\..\WForms\Common Controls\CheckBox.mqh"
//+------------------------------------------------------------------+
//| Class of the base container object of WForms controls            |
//+------------------------------------------------------------------+
class CContainer : public CWinFormBase
  {
private:
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);

//--- Calculate Dock objects' binding coordinates
   void              CalculateCoords(CArrayObj *list);

protected:
//--- Adjust the element size to fit its content
   bool              AutoSizeProcess(const bool redraw);
   
public:
//--- Return the size and coordinates of the working area
   int               GetWidthWorkspace(void)       const
                       {
                        return this.Width()-::fmax(this.FrameWidthLeft(),this.PaddingLeft())-::fmax(this.FrameWidthRight(),this.PaddingRight());
                       }
   int               GetHeightWorkspace(void)      const
                       {
                        return this.Height()-::fmax(this.FrameWidthTop(),this.PaddingTop())-::fmax(this.FrameWidthBottom(),this.PaddingBottom());
                       }
   int               GetCoordXWorkspace(void)      const
                       {
                        return this.CoordX()+::fmax(this.FrameWidthLeft(),this.PaddingLeft());
                       }
   int               GetCoordYWorkspace(void)      const
                       {
                        return this.CoordY()+::fmax(this.FrameWidthTop(),this.PaddingTop());
                       }
   int               GetRightEdgeWorkspace(void)   const
                       {
                        return this.RightEdge()-::fmax(this.FrameWidthRight(),this.PaddingRight());
                       }
   int               GetBottomEdgeWorkspace(void)  const
                       {
                        return this.BottomEdge()-::fmax(this.FrameWidthBottom(),this.PaddingBottom());
                       }

//--- Return the list of bound WinForms objects with (1) any and (2) specified WinForms object type (from the base one and higher)
   CArrayObj        *GetListWinFormsObj(void);
   CArrayObj        *GetListWinFormsObjByType(const ENUM_GRAPH_ELEMENT_TYPE type);
//--- Return the pointer to the specified WinForms object with the specified type by index
   CWinFormBase     *GetWinFormsObj(const ENUM_GRAPH_ELEMENT_TYPE type,const int index);
   
//--- Set the (1) X, (2) Y coordinates, (3) element width and (4) height
   virtual bool      SetCoordX(const int coord_x)              { return CGCnvElement::SetCoordX(coord_x);   }
   virtual bool      SetCoordY(const int coord_y)              { return CGCnvElement::SetCoordY(coord_y);   }
   virtual bool      SetWidth(const int width)                 { return CGCnvElement::SetWidth(width);      }
   virtual bool      SetHeight(const int height)               { return CGCnvElement::SetHeight(height);    }
   
//--- Create a new attached element
   virtual bool      CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                      CGCnvElement *main,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool activity,
                                      const bool redraw);
//--- Redraw the object
   virtual void      Redraw(bool redraw)                             { CWinFormBase::Redraw(redraw);        }
   
//--- Reset the size of all bound objects to the initial ones
   bool              ResetSizeAllToInit(void);
//--- Place bound objects in the order of their Dock binding
   virtual bool      ArrangeObjects(const bool redraw);
   
//--- Set the (1) field width, (2) height, (3) the height of all fields around the control during auto scrolling
   void              SetAutoScrollMarginWidth(const int value)       { this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,value);  }
   void              SetAutoScrollMarginHeight(const int value)      { this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,value);  }
   void              SetAutoScrollMarginAll(const int value)
                       {
                        this.SetAutoScrollMarginWidth(value); this.SetAutoScrollMarginHeight(value);
                       }
   void              SetAutoScrollMargin(const int width,const int height)
                       {
                        this.SetAutoScrollMarginWidth(width); this.SetAutoScrollMarginHeight(height);
                       }
//--- Return the (1) field width and (2) height around the control during auto scrolling
   int               AutoScrollMarginWidth(void)               const { return (int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W); }
   int               AutoScrollMarginHeight(void)              const { return (int)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H); }
  
//--- Set the flag of the element auto resizing depending on the content
   virtual void      SetAutoSize(const bool flag,const bool redraw)
                       {
                        bool prev=this.AutoSize();
                        if(prev==flag)
                           return;
                        CWinFormBase::SetAutoSize(flag,redraw);
                        if(prev!=this.AutoSize() && this.ElementsTotal()>0)
                           this.AutoSizeProcess(redraw);
                       }
//--- (1) Set and (2) return the mode of the element auto resizing depending on the content
   void              SetAutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode,const bool redraw)
                       {
                        ENUM_CANV_ELEMENT_AUTO_SIZE_MODE prev=this.AutoSizeMode();
                        if(prev==mode)
                           return;
                        this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,mode);
                        if(prev!=this.AutoSizeMode() && this.ElementsTotal()>0)
                           this.AutoSizeProcess(redraw);
                       }
   ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void)   const { return (ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE); }
   
//--- (1) Set and (2) return the mode of binding element borders to the container
   virtual void      SetDockMode(const ENUM_CANV_ELEMENT_DOCK_MODE mode,const bool redraw)
                       {
                        if(this.DockMode()==mode)
                           return;
                        CWinFormBase::SetDockMode(mode,redraw);
                        CContainer *base=this.GetBase();
                        if(base!=NULL)
                           base.ArrangeObjects(redraw);
                       }

//--- Set the width of the form frame (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides of the control
   virtual void      SetFrameWidthLeft(const uint value)
                       {
                        this.m_frame_width_left=(int)value;
                        if(this.PaddingLeft()<this.FrameWidthLeft())
                           this.SetPaddingLeft(this.FrameWidthLeft());
                       }
   virtual void      SetFrameWidthTop(const uint value)
                       {
                        this.m_frame_width_top=(int)value;
                        if(this.PaddingTop()<this.FrameWidthTop())
                           this.SetPaddingTop(this.FrameWidthTop());
                       }
   virtual void      SetFrameWidthRight(const uint value)
                       {
                        this.m_frame_width_right=(int)value;
                        if(this.PaddingRight()<this.FrameWidthRight())
                           this.SetPaddingRight(this.FrameWidthRight());
                       }
   virtual void      SetFrameWidthBottom(const uint value)
                       {
                        this.m_frame_width_bottom=(int)value;
                        if(this.PaddingBottom()<this.FrameWidthBottom())
                           this.SetPaddingBottom(this.FrameWidthBottom());
                       }
   virtual void      SetFrameWidthAll(const uint value)
                       {
                        this.SetFrameWidthLeft(value); this.SetFrameWidthTop(value); this.SetFrameWidthRight(value); this.SetFrameWidthBottom(value);
                       }

//--- Constructors
                     CContainer(const long chart_id,
                                const int subwindow,
                                const string name,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
                     CContainer(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
                        this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; 
                        this.SetForeColor(CLR_DEF_FORE_COLOR);
                        this.SetFontBoldType(FW_TYPE_NORMAL);
                        this.SetMarginAll(3);
                        this.SetPaddingAll(0);
                        this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
                        this.SetBorderStyle(FRAME_STYLE_NONE);
                        this.SetAutoScroll(false,false);
                        this.SetAutoScrollMarginAll(0);
                        this.SetAutoSize(false,false);
                        this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
                        this.Initialize();
                       }
//--- Destructor
                    ~CContainer();
  };
//+------------------------------------------------------------------+

我们从上一篇文章中获知其它面板对象的创建方法。

在类构造函数中,所有变量均以默认值初始化:

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CContainer::CContainer(const long chart_id,
                       const int subwindow,
                       const string name,
                       const int x,
                       const int y,
                       const int w,
                       const int h) : CWinFormBase(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoScroll(false,false);
   this.SetAutoScrollMarginAll(0);
   this.SetAutoSize(false,false);
   this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
   this.Initialize();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+


在类析构函数中,调用位于窗体对象类中的逆初始化方法,该类是函数库 WinForms 对象的基准对象类的父类:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CContainer::~CContainer()
  {
   CForm::Deinitialize();
  }
//+------------------------------------------------------------------+


创建新图形对象的方法提供了创建所有现有(稍后研究) WinForms 对象(以及更多)的代码,并非来自容器对象类别但同一类的对象除外

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CContainer::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                           const int obj_num,
                                           const string obj_name,
                                           const int x,
                                           const int y,
                                           const int w,
                                           const int h,
                                           const color colour,
                                           const uchar opacity,
                                           const bool movable,
                                           const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER :
         element=new CContainer(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+

为什么我们不能从容器类别创建对象? 原因在于它们是这个类的衍生后代,而它对这些仍然一无所知。 不过,可以在这里创建自身类(CContainer)的对象。 由于这是虚拟方法,我们就能够在派生类中创建该类的子类对象。


创建新绑定元素的方法也取自面板对象类,并简单地补充一些设置新创建对象参数的代码

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  CGCnvElement *main,
                                  const int x,
                                  const int y,
                                  const int w,
                                  const int h,
                                  const color colour,
                                  const uchar opacity,
                                  const bool activity,
                                  const bool redraw)
  {
//--- If the object type is less than the base WinForms object
   if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE)
     {
      //--- report the error and return 'false'
      CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE);
      return false;
     }
//--- If failed to create a new graphical element, return 'false'
   CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,main,x,y,w,h,colour,opacity,activity);
   if(obj==NULL)
      return false;
//--- Set the text color of the created object as that of the base panel
   obj.SetForeColor(this.ForeColor());
//--- If the object type is a container
   if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_CONTAINER)
     {
      //--- set the frame color equal to the background color 
      obj.SetColorFrame(obj.ColorBackground());
     }
//--- If the object type is a panel
   if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_PANEL)
     {
      //--- set the frame color equal to the background color 
      obj.SetColorFrame(obj.ColorBackground());
     }
//--- If the object type is GroupBox
   if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_GROUPBOX)
     {
      //--- set the frame color equal to the background color 
      obj.SetColorFrame(obj.ColorBackground());
     }
//--- If the object type is a text label,
   if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_LABEL)
     {
      //--- set the object text color depending on the one passed to the method
      //--- or the panel text color or the one passed to the method and the frame color equal to the text color 
      obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour);
      obj.SetColorFrame(main!=NULL ? main.ColorBackground() : obj.ForeColor());
     }
//--- If the object type is CheckBox
   if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_CHECKBOX)
     {
      //--- set the object text color depending on the one passed to the method
      //--- or the object text color or the one passed to the method and the frame color equal to the text color 
      obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour);
      obj.SetColorFrame(main!=NULL ? main.ColorBackground() : obj.ForeColor());
     }
//--- If the panel has auto resize enabled and features bound objects, call the resize method
   if(this.AutoSize() && this.ElementsTotal()>0)
      this.AutoSizeProcess(redraw);
//--- Redraw the panel and all added objects, and return 'true'
   this.Redraw(redraw);
   return true;
  }
//+------------------------------------------------------------------+

因为已创建了 CWinFormBase 基准对象类型,在这里称为父类,故在该方法中,我们可以设置所有现有和未来(但已在 Defines.mqh 文件中指定)的对象。 代码模块中设置的所有参数,即针对所创建对象的参数设订,都来自 CWinFormBase 类清单,故不会导致冲突或错误。

在该方法里继续补充处理新创建对象的代码模块。 如果初始化需要设置类原型中未知的参数,则该方法应当是虚拟的。 新对象的未知属性的处理应在这些新类的重定义方法中设置。

该方法将所有绑定对象的大小重置为初始值:

//+------------------------------------------------------------------+
//| Reset the size of all bound objects to the initial ones          |
//+------------------------------------------------------------------+
bool CContainer::ResetSizeAllToInit(void)
  {
   bool res=true;
   CArrayObj *list=this.GetListWinFormsObj();
   if(list==NULL)
      return false;
   for(int i=0;i<list.Total();i++)
     {
      CWinFormBase *obj=list.At(i);
      if(obj==NULL)
        {
         res &=false;
         continue;
        }
      res &=obj.Resize(i,obj.GetWidthInit(),obj.GetHeightInit());
     }
   return res;
  }
//+------------------------------------------------------------------+

在此,我们只得到 WinForms 对象的列表。 接下来,循环遍历所获列表,为每个对象设置初始大小

该方法调整元素大小来适合其内容:

//+------------------------------------------------------------------+
//| Adjust the element size to fit its content                       |
//+------------------------------------------------------------------+
bool CContainer::AutoSizeProcess(const bool redraw)
  {
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
//--- Get objects with the maximum and minimum X and Y coordinates from the list by their indices
   CWinFormBase *maxx=list.At(CSelect::FindGraphCanvElementMax(list,CANV_ELEMENT_PROP_COORD_X));
   CWinFormBase *minx=list.At(CSelect::FindGraphCanvElementMin(list,CANV_ELEMENT_PROP_COORD_X));
   CWinFormBase *maxy=list.At(CSelect::FindGraphCanvElementMax(list,CANV_ELEMENT_PROP_COORD_Y));
   CWinFormBase *miny=list.At(CSelect::FindGraphCanvElementMin(list,CANV_ELEMENT_PROP_COORD_Y));
//--- If at least one of the four objects is not received, return 'false'
   if(maxx==NULL || minx==NULL || maxy==NULL || miny==NULL)
      return false;

//--- Get the minimum X and Y coordinate
   int min_x=minx.CoordX();
   int min_y=fmin(miny.CoordY(),maxy.BottomEdge());
//--- Calculate the total width and height of all bound objects
   int w=maxx.RightEdge()-min_x;
   int h=int(::fmax(miny.CoordY(),maxy.BottomEdge())-min_y);
//--- Calculate the number of pixels, by which we need to resize the container in width and height
   int excess_x=w-this.GetWidthWorkspace();
   int excess_y=h-this.GetHeightWorkspace();
//--- Calculate the offset, by which the bound objects are to be moved
   int shift_x=this.GetCoordXWorkspace()-min_x;
   int shift_y=this.GetCoordYWorkspace()-min_y;
//--- If failed to change the container size, return 'true'
   if(excess_x==0 && excess_y==0)
      return true;
//--- If it is necessary to move the attached objects inside the container along the X or Y coordinate
   bool res=true;
   if(shift_x>0 || shift_y>0)
     {
      //--- In the loop by all attached objects,
      for(int i=0;i<list.Total();i++)
        {
         //--- get the next object
         CWinFormBase *obj=list.At(i);
         if(obj==NULL)
            continue;
         //--- If the object needs to be shifted horizontally, write the shift result to 'res'
         if(shift_x>0)
            res &=obj.Move(obj.CoordX()+shift_x,obj.CoordY());
         //--- If the object needs to be shifted vertically, write the shift result to 'res'
         if(shift_y>0)
            res &=obj.Move(obj.CoordX(),obj.CoordY()+shift_y);
         //--- Set new relative object X and Y coordinates
         obj.SetCoordXRelative(obj.CoordX()-this.GetCoordXWorkspace());
         obj.SetCoordYRelative(obj.CoordY()-this.GetCoordYWorkspace());
        }
     }
//--- Return the result of resizing the container
   return
     (
      //--- If we failed to move at least one bound object, return 'false'
      !res ? false :
      //--- Otherwise, if only a size increase
      this.AutoSizeMode()==CANV_ELEMENT_AUTO_SIZE_MODE_GROW ? 
      this.Resize(this.Width()+(excess_x>0  ? excess_x : 0),this.Height()+(excess_y>0  ? excess_y : 0),redraw) :
      //--- if both increase and decrease
      this.Resize(this.Width()+(excess_x!=0 ? excess_x : 0),this.Height()+(excess_y!=0 ? excess_y : 0),redraw)
     );
  }
//+------------------------------------------------------------------+

该方法也已从面板对象中移出。 不过,由于该类没有参考底图,此处我们采用对象工作区域参数的值,来替代其属性值。

目前,该方法工作尚不正确。 我会考虑改进它,及在后续文章中同时改进按索引在容器中排列对象的方法

//+------------------------------------------------------------------+
//| Place bound objects in the order of their Dock binding           |
//+------------------------------------------------------------------+
bool CContainer::ArrangeObjects(const bool redraw)
  {
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
   CWinFormBase *prev=NULL, *obj=NULL, *elm=NULL;
//--- In the loop by all bound objects,
   for(int i=0;i<list.Total();i++)
     {
      //--- Get the current and previous elements from the list
      obj=list.At(i);
      prev=list.At(i-1);
      //--- If the object is not received, move on
      if(obj==NULL)
         continue;
      int x=0, y=0; // Object binding coordinates
      //--- Depending on the current object binding mode...
      //--- Top
      if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_TOP)
        {
         //--- If failed to change the object size (for the entire working area width and by the initial object height), move on to the next one
         if(!obj.Resize(this.GetWidthWorkspace(),obj.GetHeightInit(),false))
            continue;
         //--- Get the object binding coordinates
         x=this.GetCoordXWorkspace();
         y=(prev!=NULL ? prev.BottomEdge()+1 : this.GetCoordYWorkspace());
         //--- If failed to move the object to the obtained coordinates, move on to the next one
         if(!obj.Move(x,y,false))
            continue;
        }
      //--- Bottom
      if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM)
        {
         //--- If failed to change the object size (for the entire working area width and by the initial object height), move on to the next one
         if(!obj.Resize(this.GetWidthWorkspace(),obj.GetHeightInit(),false))
            continue;
         //--- Get the object binding coordinates
         x=this.GetCoordXWorkspace();
         y=(prev!=NULL ? prev.CoordY()-obj.Height()-1 : this.GetBottomEdgeWorkspace()-obj.Height()-1);
         //--- If failed to move the object to the obtained coordinates, move on to the next one
         if(!obj.Move(x,y,false))
            continue;
        }
      //--- Left
      if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT)
        {
         //--- If failed to change the object size (for the initial object width and the entire working area height), move on to the next one
         if(!obj.Resize(obj.GetWidthInit(),this.GetHeightWorkspace(),false))
            continue;
         //--- Get the object binding coordinates
         x=(prev!=NULL ? prev.RightEdge()+1 : this.GetCoordXWorkspace());
         y=this.GetCoordYWorkspace();
         //--- If failed to move the object to the obtained coordinates, move on to the next one
         if(!obj.Move(x,y,false))
            continue;
        }
      //--- Right
      if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT)
        {
         //--- If failed to change the object size (for the initial object width and the entire working area height), move on to the next one
         if(!obj.Resize(obj.GetWidthInit(),this.GetHeightWorkspace(),false))
            continue;
         //--- Get the object binding coordinates
         x=(prev!=NULL ? prev.CoordX()-obj.Width()-1 : this.GetRightEdgeWorkspace()-obj.Width());
         y=this.GetCoordYWorkspace();
         //--- If failed to move the object to the obtained coordinates, move on to the next one
         if(!obj.Move(x,y,false))
            continue;
        }
      //--- Binding with filling
      if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL)
        {
         //--- If failed to change the object size (for the entire working area width and height), move on to the next one
         if(!obj.Resize(this.GetWidthWorkspace(),this.GetHeightWorkspace(),false))
            continue;
         //--- Get the object binding coordinates
         x=this.GetCoordXWorkspace();
         y=this.GetCoordYWorkspace();
         //--- If failed to move the object to the obtained coordinates, move on to the next one
         if(!obj.Move(x,y,false))
            continue;
        }
      //--- No binding
      if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_NONE)
        {
         //--- Reset the object size
         obj.Resize(obj.GetWidthInit(),obj.GetHeightInit(),false);
         //--- Get the initial object location coordinates
         x=this.GetCoordXWorkspace()+obj.CoordXRelativeInit();
         y=this.GetCoordYWorkspace()+obj.CoordYRelativeInit();
         //--- If failed to move the object to the obtained coordinates, move on to the next one
         if(!obj.Move(x,y,false))
            continue;
        }
      //--- Calculate and set the relative object coordinates
      obj.SetCoordXRelative(x-this.GetCoordXWorkspace());
      obj.SetCoordYRelative(y-this.GetCoordYWorkspace());
     }

//--- If auto resizing mode is enabled
   if(this.AutoSize())
      this.AutoSizeProcess(false);

//--- Redraw the object with the redraw flag and return 'true'
   this.Redraw(redraw); 
   return true;
  }
//+------------------------------------------------------------------+

上述两种方法都需要重新返工。 它们出现在这里只是因为它们存在于面板对象类当中。 稍后我会重新修订它们。

下面的方法也从面板对象类中移出。 它们已被移走,并无更改:

//+------------------------------------------------------------------+
//| Return the list of bound objects                                 |
//| of any WinForms base type and higher                             |
//+------------------------------------------------------------------+
CArrayObj *CContainer::GetListWinFormsObj(void)
  {
   return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE,EQUAL_OR_MORE);
  }
//+------------------------------------------------------------------+
//| Return the list of bound objects                                 |
//| with the specified WinForms object type                          |
//+------------------------------------------------------------------+
CArrayObj *CContainer::GetListWinFormsObjByType(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,type,EQUAL);
  }
//+------------------------------------------------------------------+
//| Return the pointer to the specified WinForms object              |
//| with the specified type by index                                 |
//+------------------------------------------------------------------+
CWinFormBase *CContainer::GetWinFormsObj(const ENUM_GRAPH_ELEMENT_TYPE type,const int index)
  {
   CArrayObj *list=this.GetListWinFormsObjByType(type);
   return(list!=NULL ? list.At(index) : NULL);
  }
//+------------------------------------------------------------------+
//| Calculate Dock objects' binding coordinates                      |
//+------------------------------------------------------------------+
void CContainer::CalculateCoords(CArrayObj *list)
  {
   
  }
//+------------------------------------------------------------------+

容器对象基类的主题至此完毕。

现在,我们创建新的 GroupBox 对象类,它也是存储其它 WinForms 对象的容器。


'GroupBox' WinForms 对象

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\ 中,创建内含 CGroupBox 类的新文件 GroupBox.mqh。 该类应继承自新创建的容器对象基类且其文件,以及面板对象类文件均应包含在类文件当中

//+------------------------------------------------------------------+
//|                                                     GroupBox.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "Panel.mqh"
//+------------------------------------------------------------------+
//| GroupBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CGroupBox : public CContainer
  {
  }

在类的私密、受保护和公开部分中,添加已经熟悉的方法。
由于GroupBox 对象的特征是包含一组对象的边框,在私密部分中声明绘制此类边框的方法

//+------------------------------------------------------------------+
//| GroupBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CGroupBox : public CContainer
  {
private:
//--- Draw a frame
   void              DrawFrame(void);
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
                                          
protected:
//--- Initialize the variables
   virtual void      Initialize(void);

public:
//--- Clear the element filling it with color and opacity
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Clear the element completely
   virtual void      Erase(const bool redraw=false);
//--- Set a frame style
   virtual void      SetBorderStyle(const ENUM_FRAME_STYLE style)
                       {
                        if((this.FrameWidthTop()<2 || this.FrameWidthBottom()<2 || this.FrameWidthLeft()<2 || this.FrameWidthRight()<2) && 
                            style>FRAME_STYLE_FLAT)
                           this.SetFrameWidthAll(2);
                        this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,style);
                       }
   
//--- Constructors
                     CGroupBox(const long chart_id,
                               const int subwindow,
                               const string name,
                               const int x,
                               const int y,
                               const int w,
                               const int h);
                     CGroupBox(const string name) : CContainer(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
                        this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
                        this.Initialize();
                       }
//--- Destructor
                    ~CGroupBox(){ CForm::Deinitialize(); }
  };
//+------------------------------------------------------------------+

在设置边框样式的方法中,检查传递来的样式。 如果边框不平坦,其宽度应至少为两个像素如果是这种情况,则为所有边框设置 2 个像素的宽度


在参数化构造函数中设置初始对象大小和坐标,并调用初始化方法

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CGroupBox::CGroupBox(const long chart_id,
                     const int subwindow,
                     const string name,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CContainer(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
   this.Initialize();
  }
//+------------------------------------------------------------------+

如您所见,WinForms 对象类型设置为 GroupBox,而函数库对象类型则被设为容器


在创建新图形对象的方法中,我们可以创建面板对象其类对象CheckBox 类对象稍后会在文中实现。

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int obj_num,
                                          const string obj_name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+


变量初始化方法:

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CGroupBox::Initialize(void)
  {
//--- Clear all object lists and set sorted list flags for them
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
//--- GroupBox has no shadow object
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
//--- The width of the object frame on each side is 1 pixel by default
   this.SetFrameWidth(1,1,1,1);
//--- The object does not have a gradient filling (neither vertical, nor horizontal)
   this.m_gradient_v=false;
   this.m_gradient_c=false;
//--- Reset all "working" flags and variables
   this.m_mouse_state_flags=0;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::SetInteraction(false);
//--- Create an animation object and add it to the list for storing such objects
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Set a transparent background for the object background and the default color for the frame
   this.SetColorBackground(CLR_CANV_NULL);
   this.SetOpacity(0);
   this.SetColorFrame(CLR_DEF_FRAME_GBOX_COLOR);
//--- Set the default color and text opacity, as well as the absence of the object frame
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
//--- Set the default text parameters
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.SetText("GroupBox");
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
//--- Set the default object parameters
   this.SetAutoSize(false,false);
   this.SetMarginAll(3);
   this.SetPaddingAll(3);
   this.SetEnabled(true);
   this.SetVisible(true,false);
  }
//+------------------------------------------------------------------+

在方法中所有类变量都采用默认值初始化。 某些变量的初始值与父类中设置的值不同。

清理对象的方法:

//+------------------------------------------------------------------+
//| Clear the element filling it with color and opacity              |
//+------------------------------------------------------------------+
void CGroupBox::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Fill the element having the specified color and the redrawing flag
   CGCnvElement::Erase(colour,opacity,redraw);
//--- Draw a frame encasing a group of objects
   this.DrawFrame();
//--- Draw a header above the frame
   CGCnvElement::Text(6,0,this.Text(),this.ForeColor(),this.ForeColorOpacity());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Clear the element with a gradient fill                           |
//+------------------------------------------------------------------+
void CGroupBox::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Fill the element having the specified color array and the redrawing flag
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- Draw a frame encasing a group of objects
   this.DrawFrame();
//--- Draw a header above the frame
   CGCnvElement::Text(6,0,this.Text(),this.ForeColor(),this.ForeColorOpacity());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Clear the element completely                                     |
//+------------------------------------------------------------------+
void CGroupBox::Erase(const bool redraw=false)
  {
//--- Fully clear the element with the redrawing flag
   CGCnvElement::Erase(redraw);
  }
//+------------------------------------------------------------------+

这些方法都是虚拟的。 除了用背景色填充整个对象(默认为透明)外,还显示边框,及其上方的对象组标题文本。

绘制边框的方法:

//+------------------------------------------------------------------+
//| Draw the frame                                                   |
//+------------------------------------------------------------------+
void CGroupBox::DrawFrame(void)
  {
//--- Get half of the text height
   int w=0;
   int h=0;
   this.TextSize(Text(),w,h);
   int height=this.Height()-h/2;
//--- Depending on the frame style, draw its necessary type
   switch(this.BorderStyle())
     {
      case FRAME_STYLE_FLAT :
        this.DrawFrameFlat(0,h/2,this.Width(),height,this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.ForeColorOpacity());
        break;
      case FRAME_STYLE_BEVEL :
        this.DrawFrameBevel(0,h/2,this.Width(),height,this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.ForeColorOpacity());
        break;
      case FRAME_STYLE_STAMP :
        this.DrawFrameStamp(0,h/2,this.Width(),height,this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.ForeColorOpacity());
        break;
      //--- FRAME_STYLE_SIMPLE
      default:
        this.DrawFrameSimple(0,h/2,this.Width(),height,this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.ForeColorOpacity());
        break;
     }
//--- If the text set for an object is not an empty string, erase the frame area where a text should be located using the transparent color
   if(this.Text()!="")
      this.DrawRectangleFill(5,h/2-1,w+7,h/2+this.FrameWidthTop()+1,CLR_CANV_NULL,0);
  }
//+------------------------------------------------------------------+

方法逻辑已在代码注释中阐明。 简而言之,我们需要绘制一个边框,将位于容器中的一组对象包围起来。 边框应沿整个对象的边缘绘制,并采用在其属性中指定的样式和颜色。 边框的上边缘不应与对象的顶部边缘重合,而是沿着标题的中心。 为了计算边框的初始 Y 坐标,取字体高度除以 2。 这就是边框的 Y 坐标。 标题文本不应直接绘制在边框上,即文本所在区域位置不应绘制边框线。
为达成这一点,我们简单地在正确的位置擦除线条,并在线的顶部绘制一个透明颜色的矩形。 矩形大小应超出标签每边大小一个像素。

WinForms 对象 “GroupBox” 已就绪。

打开面板类文件 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh,并删除所有方法,将其移至基类。 此外,将容器对象基类的文件和 GroupBox 类的文件包含到类文件当中。 该类现在继承自基准容器对象 :

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "GroupBox.mqh"
//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+
class CPanel : public CContainer

从类的私密部分,删除指向对象的指针变量,即 所绑定的 Dock 对象的坐标,以及为这类对象设置参考底图的方法,因为现在我们将调用 CContainer 基类的方法:

class CPanel : public CWinFormBase
  {
private:
   CGCnvElement     *m_obj_top;                                      // Pointer to the object whose coordinates the current upper object is bound to
   CGCnvElement     *m_obj_bottom;                                   // Pointer to the object whose coordinates the current bottom object is bound to
   CGCnvElement     *m_obj_left;                                     // Pointer to the object whose coordinates the current left object is bound to
   CGCnvElement     *m_obj_right;                                    // Pointer to the object whose coordinates the current right object is bound to
   CGCnvElement     *m_underlay;                                     // Underlay for placing elements

//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Return the initial coordinates of a bound object
   virtual void      GetCoords(int &x,int &y);
//--- Create the underlay object
   bool              CreateUnderlayObj(void);
//--- Set the underlay as a coordinate system zero
   void              SetUnderlayAsBase(void);


从类构造函数中删除不必要的代码,因为它们现在均设置在父类当中:

//--- Constructors
                     CPanel(const long chart_id,
                            const int subwindow,
                            const string name,
                            const int x,
                            const int y,
                            const int w,
                            const int h);
                     CPanel(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL);
                        this.m_type=OBJECT_DE_TYPE_GWF_PANEL; 
                        this.SetForeColor(CLR_DEF_FORE_COLOR);
                        this.SetFontBoldType(FW_TYPE_NORMAL);
                        this.SetMarginAll(3);
                        this.SetPaddingAll(0);
                        this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
                        this.SetBorderStyle(FRAME_STYLE_NONE);
                        this.SetAutoScroll(false,false);
                        this.SetAutoScrollMarginAll(0);
                        this.SetAutoSize(false,false);
                        this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
                        this.Initialize();
                        if(this.CreateUnderlayObj())
                           this.SetUnderlayAsBase();
                       }
//--- Destructor
                    ~CPanel();
  };
//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CPanel::CPanel(const long chart_id,
               const int subwindow,
               const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CWinFormBase(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_PANEL;
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetFontBoldType(FW_TYPE_NORMAL);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetAutoScroll(false,false);
   this.SetAutoScrollMarginAll(0);
   this.SetAutoSize(false,false);
   this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false);
   this.Initialize();
   if(this.CreateUnderlayObj())
      this.SetUnderlayAsBase();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+

此外,在构造函数中,设置一个新的对象类型初始化 CContainer 类,替代初始化清单中以前的 CWinFormBase

//--- Constructors
                     CPanel(const long chart_id,
                            const int subwindow,
                            const string name,
                            const int x,
                            const int y,
                            const int w,
                            const int h);
                     CPanel(const string name) : CContainer(::ChartID(),0,name,0,0,0,0)
                       {
                        CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL);
                        CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL);
                        this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; 
                        this.CreateUnderlayObj();
                       }
//--- Destructor
                    ~CPanel(){ CForm::Deinitialize(); }
  };
//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CPanel::CPanel(const long chart_id,
               const int subwindow,
               const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CContainer(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_PANEL);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_PANEL);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.CreateUnderlayObj();
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetWidthInit(w);
   this.SetHeightInit(h);
  }
//+------------------------------------------------------------------+


在创建新图形对象的方法中,设置新对象的创建

//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                       const int obj_num,
                                       const string obj_name,
                                       const int x,
                                       const int y,
                                       const int w,
                                       const int h,
                                       const color colour,
                                       const uchar opacity,
                                       const bool movable,
                                       const bool activity)
  {
   string name=this.CreateNameDependentObject(obj_name);
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT :
         element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
        break;
      case GRAPH_ELEMENT_TYPE_FORM :
         element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX :
         element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL :
         element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL :
         element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX :
         element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h);
        break;
      default:
        break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+

我将研究创建下面的 CheckBox 对象。

该类中的其余变化无关紧要。 在此研究它们没有意义。 您可在文后附件中找到它们。 最主要的是剔除已移至基类的方法。

现在,我们以同样的方式为 WinForms 标准控件对象创建基准对象。


标准控件类别中的基准对象

这个对象保存了我在上一篇文章中研究的文本标签对象的方法。 该对象的大多数方法将在该类别的其它对象中复制,故我们在此(和其它类别中)也需要一个基准对象。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 函数库文件中,创建内含 CCommonBase 类的新文件 CommonBase.mqh
该类应继承自函数库 WinForms 对象的基类
,且类文件应包含在文件当中

//+------------------------------------------------------------------+
//|                                                   CommonBase.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\WForms\WinFormBase.mqh"
//+------------------------------------------------------------------+
//| Class of the base WForms standard control object                 |
//+------------------------------------------------------------------+
class CCommonBase : public CWinFormBase
  {
  }


该类相当精巧。 我们整体上看一下:

//+------------------------------------------------------------------+
//| Class of the base WForms standard control object                 |
//+------------------------------------------------------------------+
class CCommonBase : public CWinFormBase
  {
private:

protected:
//--- Set the element width and height automatically
   virtual void      AutoSetWH(void)   { return;   }
//--- Initialize the variables
   virtual void      Initialize(void);
   
public:
//--- Clear the element filling it with color and opacity
   virtual void      Erase(const color colour,const uchar opacity,const bool redraw=false);
//--- Clear the element with a gradient fill
   virtual void      Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false);
//--- Clear the element completely
   virtual void      Erase(const bool redraw=false);

//--- Constructor
                     CCommonBase(const long chart_id,
                                 const int subwindow,
                                 const string name,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h);
  };
//+------------------------------------------------------------------+

在此,我们为自动调整对象大小声明了一个空的虚拟方法。 该方法应该在继承类中实现,因为每个类都可能有自己的设置对象大小的准则。

类构造函数指定对象类型,和所有默认属性值
所有值设置完毕后,对象会被重新绘制

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCommonBase::CCommonBase(const long chart_id,
                         const int subwindow,
                         const string name,
                         const int x,
                         const int y,
                         const int w,
                         const int h) : CWinFormBase(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_COMMON_BASE);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_COMMON_BASE);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   if(this.AutoSize())
      this.AutoSetWH();
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


初始化方法:

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CCommonBase::Initialize(void)
  {
//--- Clear all object lists and set sorted list flags for them
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_list_tmp.Clear();
   this.m_list_tmp.Sort();
//--- Standard control has no shadow object
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
//--- The width of the object frame on each side is 1 pixel by default
   this.m_frame_width_right=1;
   this.m_frame_width_left=1;
   this.m_frame_width_top=1;
   this.m_frame_width_bottom=1;
//--- The object does not have a gradient filling (neither vertical, nor horizontal)
   this.m_gradient_v=false;
   this.m_gradient_c=false;
//--- Reset all "working" flags and variables
   this.m_mouse_state_flags=0;
   this.m_offset_x=0;
   this.m_offset_y=0;
   CGCnvElement::SetInteraction(false);
//--- Create an animation object and add it to the list for storing such objects
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
   this.m_list_tmp.Add(this.m_animations);
//--- Set the transparent color for the object background
   this.SetColorBackground(CLR_CANV_NULL);
   this.SetOpacity(0);
//--- Set the default color and text opacity, as well as the absence of the object frame
   this.SetForeColor(CLR_DEF_FORE_COLOR);
   this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY);
   this.SetBorderStyle(FRAME_STYLE_NONE);
//--- Set the default text parameters
   this.SetFont(DEF_FONT,DEF_FONT_SIZE);
   this.SetText("");
   this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
   this.SetTextAlign(ANCHOR_LEFT_UPPER);
//--- Set the default object parameters
   this.SetAutoSize(false,false);
   this.SetMarginAll(3);
   this.SetPaddingAll(0);
   this.SetEnabled(true);
   this.SetVisible(true,false);
  }
//+------------------------------------------------------------------+


清除和填充对象背景的方法:

//+------------------------------------------------------------------+
//| Clear the element filling it with color and opacity              |
//+------------------------------------------------------------------+
void CCommonBase::Erase(const color colour,const uchar opacity,const bool redraw=false)
  {
//--- Fill the element having the specified color and the redrawing flag
   CGCnvElement::Erase(colour,opacity,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),255,this.BorderStyle());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Clear the element with a gradient fill                           |
//+------------------------------------------------------------------+
void CCommonBase::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false)
  {
//--- Fill the element having the specified color array and the redrawing flag
   CGCnvElement::Erase(colors,opacity,vgradient,cycle,redraw);
//--- If the object has a frame, draw it
   if(this.BorderStyle()!=FRAME_STYLE_NONE && redraw)
      this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),255,this.BorderStyle());
//--- Update the element having the specified redrawing flag
   this.Update(redraw);
  }
//+------------------------------------------------------------------+
//| Clear the element completely                                     |
//+------------------------------------------------------------------+
void CCommonBase::Erase(const bool redraw=false)
  {
//--- Fully clear the element with the redrawing flag
   CGCnvElement::Erase(redraw);
  }
//+------------------------------------------------------------------+

我们已在其它函数库对象中遇见过所有这些方法。 它们都在代码中进行了注释,无需任何多余解释。 每个对象的初始化和清理均类似,但变量的默认值、背景绘制、和在其上绘制某些元素的原则和顺序略有不同。 出于此原因,每个类都可以实现自己的方法。

由于我已经将一些方法从 “Text label” 对象类移到了标准控件的基准对象类之中,因此我们需要调整在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Label.mqh 里的 CLabel 类。

取代 WinFormBase.mqh,将基准对象文件类包含到类中,并从其继承类

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "CommonBase.mqh"
//+------------------------------------------------------------------+
//| Label object class of WForms controls                            |
//+------------------------------------------------------------------+
class CLabel : public CCommonBase


在类构造函数中,即初始化清单中,设置新父类的初始化,并设置新的函数库对象类型

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLabel::CLabel(const long chart_id,
               const int subwindow,
               const string name,
               const int x,
               const int y,
               const int w,
               const int h) : CCommonBase(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_LABEL);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_LABEL);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   this.SetMargin(3,0,3,0);
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.Redraw(false);
  }
//+------------------------------------------------------------------+

类中的其余修改无关紧要,但移到父类的方法必须从当前类里删除。
您可以在以下附件中找到它们。


'CheckBox' WinForms 对象

CheckBox 对象是带有标签的复选框。 标签和标志可分别位于相对于对象边界的九个位置:

  • 左上角
  • 中间居左
  • 左下角
  • 底部居中
  • 右下角
  • 中间居右
  • 右上角
  • 顶部居中
  • 中间

配合复选框和文本位置的各种组合,有必要调整文本位置,从而令其尽可能不与复选框重叠。 对象可以显示其边框。

由于对象含有文本,而文本(和标志)大小可自动调整对象大小,因此从 “Text label” 对象继承,并加入以三种组合方式显示复选框的功能是有意义的:

  • 未选中
  • 选中
  • 未定义

如果复选框显示一组相同对象的状态,其中一些被选中,而另一些未选中,则会出现未定义状态。

在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ 中,创建内含 CCheckBox 类的新文件 CheckBox.mqhCLabel 类文件应包含在该文件之中,且该类应自其继承。

//+------------------------------------------------------------------+
//|                                                     CheckBox.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Label.mqh"
//+------------------------------------------------------------------+
//| CheckBox object class of the WForms controls                     |
//+------------------------------------------------------------------+
class CCheckBox : public CLabel
  {
  }


在类的私密部分中,添加存储文本标签、复选框坐标和大小的变量,以及更改对象大小、处理文本和复选框坐标的方法:

class CCheckBox : public CLabel
  {
private:
   int               m_text_x;                                       // Text X coordinate
   int               m_text_y;                                       // Text Y coordinate
   int               m_check_x;                                      // Checkbox X coordinate
   int               m_check_y;                                      // Checkbox Y coordinate
   int               m_check_w;                                      // Checkbox width
   int               m_check_h;                                      // Checkbox height
//--- Set the element width and height automatically
   virtual void      AutoSetWH(void);
//--- Set X and Y coordinates(1) of the checkbox and (2) the text depending on the alignment type
   void              SetCheckFlagCoords(int &x,int &y);
   void              SetTextCoords(int &x,int &y);
//--- Set the corrected text coordinates depending on the text alignment and checkbox
   void              SetCorrectTextCoords(void);

protected:

在受保护和公开部分中,声明类的处理方法:

protected:
//--- Displays the checkbox for the specified state
   virtual void      ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state);
   
//--- (1) Set and (2) return the checkbox size on the element
   void              SetCheckWidth(const int width)                  { this.m_check_w=(width<5  ? 5 : width);  }
   void              SetCheckHeight(const int height)                { this.m_check_h=(height<5 ? 5 : height); }
   int               CheckWidth(void)                          const { return this.m_check_w;                  }
   int               CheckHeight(void)                         const { return this.m_check_h;                  }
   
public:
//--- Set the element (1) width and (2) height,
   virtual bool      SetWidth(const int width)                       { return CGCnvElement::SetWidth(width>this.m_check_w   ? width  : this.m_check_w);     }
   virtual bool      SetHeight(const int height)                     { return CGCnvElement::SetHeight(height>this.m_check_h ? height : this.m_check_h);     }
   
//--- (1) Set and (2) return the element checkbox location angle (alignment type)
   void              SetCheckAlign(const ENUM_ANCHOR_POINT anchor)   { this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,anchor);                              }
   ENUM_ANCHOR_POINT CheckAlign(void)                          const { return (ENUM_ANCHOR_POINT)this.GetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN);           }
   
//--- (1) Set and (2) return the checkbox status
   void              SetChecked(const bool flag)                     { this.SetProperty(CANV_ELEMENT_PROP_CHECKED,flag);                                    }
   bool              Checked(void)                             const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_CHECKED);                            }
   
//--- (1) Set and (2) return the control status
   void              SetCheckState(const ENUM_CANV_ELEMENT_CHEK_STATE state) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,state);                       }
   ENUM_CANV_ELEMENT_CHEK_STATE CheckState(void)               const { return (ENUM_CANV_ELEMENT_CHEK_STATE)this.GetProperty(CANV_ELEMENT_PROP_CHECK_STATE);}

//--- Redraw the object
   virtual void      Redraw(bool redraw);

//--- Constructor
                     CCheckBox(const long chart_id,
                               const int subwindow,
                               const string name,
                               const int x,
                               const int y,
                               const int w,
                               const int h);
  };
//+------------------------------------------------------------------+

所有这些方法都已在方法描述中命名。 我们研究它们的实现。

在类构造函数中,设置属性的默认值:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCheckBox::CCheckBox(const long chart_id,
                     const int subwindow,
                     const string name,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CLabel(chart_id,subwindow,name,x,y,w,h)
  {
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX);
   CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CHECKBOX);
   this.m_type=OBJECT_DE_TYPE_GWF_COMMON;
   this.SetCoordX(x);
   this.SetCoordY(y);
   this.SetCheckWidth(DEF_CHECK_SIZE);
   this.SetCheckHeight(DEF_CHECK_SIZE);
   this.SetWidth(w);
   this.SetHeight(h);
   this.Initialize();
   this.SetWidthInit(this.Width());
   this.SetHeightInit(this.Height());
   this.SetCoordXInit(x);
   this.SetCoordYInit(y);
   this.SetTextAlign(ANCHOR_LEFT);
   this.m_text_x=0;
   this.m_text_y=0;
   this.m_check_x=0;
   this.m_check_y=0;
   this.Redraw(false);
  }
//+------------------------------------------------------------------+


该方法重绘对象:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CCheckBox::Redraw(bool redraw)
  {
//--- Fill the object with the background color having full transparency
   this.Erase(this.ColorBackground(),0,true);
//--- Set corrected text coordinates relative to the checkbox
   this.SetCorrectTextCoords();
//--- Draw the text and checkbox within the set coordinates of the object and the binding point, and update the object 
   this.Text(this.m_text_x,this.m_text_y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor());
   this.ShowControlFlag(this.CheckState());
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

这里的一切都很简单。 首先,用背景色(默认为透明)填充整个对象,以便完全擦除整个对象。 接下来,计算相对于复选框的有效文本坐标,绘制文本和复选框,并更新对象。


根据对齐类型设置 X 和 Y 标志坐标的方法:

//+------------------------------------------------------------------+
//| Set X and Y checkbox coordinates                                 |
//| depending on the alignment type                                  |
//+------------------------------------------------------------------+
void CCheckBox::SetCheckFlagCoords(int &x,int &y)
  {
//--- Depending on the checkbox location
   switch(this.CheckAlign())
     {
      //--- The checkbox is located vertically from the left side of the object in the center
      case ANCHOR_LEFT : 
        x=0;
        y=(this.Height()-this.CheckHeight())/2;
        break;
      //--- The checkbox is located in the lower left corner of the object
      case ANCHOR_LEFT_LOWER : 
        x=0;
        y=this.Height()-this.CheckHeight()-1;
        break;
      //--- The checkbox is located in the center of the bottom edge of the object
      case ANCHOR_LOWER : 
        x=(this.Width()-this.CheckWidth())/2;
        y=this.Height()-this.CheckHeight()-1;
        break;
      //--- The checkbox is located in the lower right corner of the object
      case ANCHOR_RIGHT_LOWER : 
        x=this.Width()-this.CheckWidth()-1;
        y=this.Height()-this.CheckHeight()-1;
        break;
      //--- The checkbox is located vertically from the right side of the object in the center
      case ANCHOR_RIGHT : 
        x=this.Width()-this.CheckWidth()-1;
        y=(this.Height()-this.CheckHeight())/2;
        break;
      //--- The checkbox is located in the upper right corner of the object
      case ANCHOR_RIGHT_UPPER : 
        x=this.Width()-this.CheckWidth()-1;
        y=0;
        break;
      //--- The checkbox is located in the center of the upper edge of the object
      case ANCHOR_UPPER : 
        x=(this.Width()-this.CheckWidth())/2;
        y=0;
        break;
      //--- The checkbox is located in the object center
      case ANCHOR_CENTER : 
        x=(this.Width()-this.CheckWidth())/2;
        y=(this.Height()-this.CheckHeight())/2;
        break;
      //--- The checkbox is located in the upper left corner of the object
      //---ANCHOR_LEFT_UPPER
      default:
        x=0;
        y=0;
        break;
     }
  }
//+------------------------------------------------------------------+

该方法接收的变量,保存计算出的复选框坐标。 取决于对齐方法(对象边界内的复选框位置),计算其坐标,并在传递给该方法的变量中设置它们。


该方法依据对齐类型设置文本的 X 和 Y 坐标:

//+------------------------------------------------------------------+
//| Set X and Y text coordinates                                     |
//| depending on the alignment type                                  |
//+------------------------------------------------------------------+
void CCheckBox::SetTextCoords(int &x,int &y)
  {
//--- Depending on the element text alignment type
   switch(this.TextAlign())
     {
      //--- The text is displayed in the upper left corner of the object
      case ANCHOR_LEFT_UPPER : 
        //--- Set the text binding point at the top left
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP);
        //--- Set the text binding point coordinate
        x=this.FrameWidthLeft();
        y=this.FrameWidthTop();
        break;
      //--- The text is drawn vertically from the left side of the object in the center
      case ANCHOR_LEFT : 
        //--- Set the text binding point at the center left
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_CENTER);
        //--- Set the text binding point coordinate
        x=this.FrameWidthLeft();
        y=this.Height()/2;
        break;
      //--- The text is displayed in the lower left corner of the object
      case ANCHOR_LEFT_LOWER : 
        //--- Set the text binding point at the bottom left
        this.SetTextAnchor(FRAME_ANCHOR_LEFT_BOTTOM);
        //--- Set the text binding point coordinate
        x=this.FrameWidthLeft();
        y=this.Height()-this.FrameWidthBottom();
        break;
      
      //--- The text is drawn at the center of the bottom edge of the object
      case ANCHOR_LOWER : 
        //--- Set the text anchor point at the bottom center
        this.SetTextAnchor(FRAME_ANCHOR_CENTER_BOTTOM);
        //--- Set the text binding point coordinate
        x=this.Width()/2;
        y=this.Height()-this.FrameWidthBottom();
        break;
      //--- The text is displayed in the lower right corner of the object
      case ANCHOR_RIGHT_LOWER : 
        //--- Set the text binding point at the bottom right
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_BOTTOM);
        //--- Set the text binding point coordinate
        x=this.Width()-this.FrameWidthRight();
        y=this.Height()-this.FrameWidthBottom();
        break;
      //--- The text is drawn vertically from the right side of the object in the center
      case ANCHOR_RIGHT : 
        //--- Set the text binding point at the center right
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_CENTER);
        //--- Set the text binding point coordinate
        x=this.Width()-this.FrameWidthRight();
        y=this.Height()/2;
        break;
      //--- The text is displayed in the upper right corner of the object
      case ANCHOR_RIGHT_UPPER : 
        //--- Set the text binding point at the top right
        this.SetTextAnchor(FRAME_ANCHOR_RIGHT_TOP);
        //--- Set the text binding point coordinate
        x=this.Width()-this.FrameWidthRight();
        y=this.FrameWidthTop();
        break;
      //--- The text is drawn at the center of the upper edge of the object
      case ANCHOR_UPPER : 
        //--- Set the text binding point at the center top
        this.SetTextAnchor(FRAME_ANCHOR_CENTER_TOP);
        //--- Set the text binding point coordinate
        x=this.Width()/2;
        y=this.FrameWidthTop();
        break;
      //--- The text is drawn at the object center
      //---ANCHOR_CENTER
      default:
        //--- Set the text binding point at the center
        this.SetTextAnchor(FRAME_ANCHOR_CENTER);
        //--- Set the text binding point coordinate
        x=this.Width()/2;
        y=this.Height()/2;
        break;
     }
  }
//+------------------------------------------------------------------+

此处的所有这些都类似于上面研究的复选框坐标计算方法。 此处还额外更改了文本锚定点,简化了新文本标签坐标的计算。


该方法依据文本对齐和复选框,为文本设置有效坐标:

//+------------------------------------------------------------------+
//| Set valid text coordinates depending on                          |
//| text alignment and checkbox                                      |
//+------------------------------------------------------------------+
void CCheckBox::SetCorrectTextCoords(void)
  {
//--- Set checkbox and text coordinates depending on their alignment method
   this.SetCheckFlagCoords(this.m_check_x,this.m_check_y);
   this.SetTextCoords(this.m_text_x,this.m_text_y);
//--- Get the text size
   int text_w=0, text_h=0;
   this.TextSize(this.Text(),text_w,text_h);
//--- Depending on the checkbox location within the object boundaries
   switch(this.CheckAlign())
     {
      //--- The checkbox is located in the upper left corner of the object
      //--- The checkbox is located vertically from the left side of the object in the center
      //--- The checkbox is located in the lower left corner of the object
      case ANCHOR_LEFT_UPPER  : 
      case ANCHOR_LEFT        : 
      case ANCHOR_LEFT_LOWER  : 
        //--- If the text is left-aligned, set the text X coordinate with the checkbox width + 2 pixels
        if(this.TextAlign()==ANCHOR_LEFT_UPPER || this.TextAlign()==ANCHOR_LEFT || this.TextAlign()==ANCHOR_LEFT_LOWER)
           this.m_text_x=this.CheckWidth()+2;
        break;

      //--- The checkbox is located in the upper right corner of the object
      //--- The checkbox is located vertically from the right side of the object in the center
      //--- The checkbox is located in the lower right corner of the object
      case ANCHOR_RIGHT_UPPER : 
      case ANCHOR_RIGHT       : 
      case ANCHOR_RIGHT_LOWER : 
        //--- If the text is right-aligned, set the text X coordinate with the checkbox width + 2 pixels
        if(this.TextAlign()==ANCHOR_RIGHT_UPPER || this.TextAlign()==ANCHOR_RIGHT || this.TextAlign()==ANCHOR_RIGHT_LOWER)
           this.m_text_x=this.Width()-this.CheckWidth()-2;
        break;

      //--- The checkbox is located in the center of the bottom edge of the object
      case ANCHOR_LOWER : 
        //--- If the text is bottom-aligned, set the text X coordinate with the checkbox height
        if(this.TextAlign()==ANCHOR_LEFT_LOWER || this.TextAlign()==ANCHOR_LOWER || this.TextAlign()==ANCHOR_RIGHT_LOWER)
           this.m_text_y=this.m_check_y;
        break;
      
      //--- The checkbox is located in the center of the upper edge of the object
      case ANCHOR_UPPER : 
        //--- If the text is top-aligned, set the text X coordinate with the checkbox height
        if(this.TextAlign()==ANCHOR_LEFT_UPPER || this.TextAlign()==ANCHOR_UPPER || this.TextAlign()==ANCHOR_RIGHT_UPPER)
           this.m_text_y=this.m_check_h;
        break;
      //--- The checkbox is located in the object center
      //---ANCHOR_CENTER
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

在此,依据复选框和文本标签的相对位置,设置新的文本坐标,以便文本与复选框图标不重叠。 这并非一个普遍的解决方案,但这里参考到了所有的主要相关性。 两个对象组件的其它冲突可以通过更改对象本身的大小,或设置文本和复选框位置之间的正确比率来解决。

该方法按照指定状态显示复选框:

//+------------------------------------------------------------------+
//| Display the checkbox for the specified state                     |
//+------------------------------------------------------------------+
void CCheckBox::ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state)
  {
//--- Draw the rectangle of checkbox boundaries
   this.DrawRectangle(this.m_check_x,this.m_check_y,this.m_check_x+this.CheckWidth(),this.m_check_y+this.CheckHeight(),this.ColorFrame());
//--- Create X and Y coordinate arrays for drawing a polyline
   int array_x[]={m_check_x+2,m_check_x+m_check_w/2-1,m_check_x+m_check_w-2};
   int array_y[]={m_check_y+m_check_h/2,m_check_y+m_check_h-3,m_check_y+3};
//--- Depending on the checkbox status passed to the method
   switch(state)
     {
      //--- Set checkbox
      case CANV_ELEMENT_CHEK_STATE_CHECKED :
        //--- Draw a polyline in the form of a checkmark inside the checkbox boundaries
        this.DrawPolylineAA(array_x,array_y,ColorFrame());
        break;
      //--- Unchecked checkbox
      case CANV_ELEMENT_CHEK_STATE_INDETERMINATE :
        //--- Draw a filled rectangle inside the checkbox boundaries
        this.DrawRectangleFill(m_check_x+3,m_check_y+3,m_check_x+m_check_w-3,m_check_y+m_check_h-3,ColorFrame());
        break;
      //--- Undefined state
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

取决于传递给方法的复选框状态,我们在复选框边界内绘制复选标记或填充矩形。 计算多边形坐标时,无论复选框边框的大小如何,绘制的复选标记的比例始终保持不变。 针对未定义状态绘制矩形。 截至目前为止,这些图标只有一种 — 复选标记和一种颜色的矩形。 在未来,我们将介绍更多类型的显示标志状态。

自动设置元素宽度和高度的方法:

//+------------------------------------------------------------------+
//| Set the element width and height automatically                   |
//+------------------------------------------------------------------+
void CCheckBox::AutoSetWH(void)
  {
//--- Define the variables for receiving the label width and height
   int w=0, h=0;
//--- Get the width and height depending on the object text
   CGCnvElement::TextSize(this.Text()!="" && this.Text()!=NULL ? this.Text() : " ",w,h);
//--- Add the Margin values of the object on the left and right to the resulting width, as well as the checkbox size
   w+=(this.MarginLeft()+this.MarginRight()+this.CheckWidth());
//--- If the width is equal to the size of the checkbox, set it to three pixels + checkbox size
   if(w==this.CheckWidth())
      w=this.CheckWidth()+3;
//--- Add the Margin values of the object on the top and bottom to the resulting height
   h+=(this.MarginTop()+this.MarginBottom());
//--- If failed to get the height, set it as "font size" * ratio
   if(h==0)
      h=(int)ceil(FontSize()*1.625);
//--- If the height is ultimately less than the size of the checkbox, set the height equal to the height of the checkbox
   if(h<this.CheckHeight())
      h=this.CheckHeight();
//--- Set the object width and height from the received values
   this.SetWidth(w);
   this.SetHeight(h);
  }
//+------------------------------------------------------------------+

与父类方法不同,虚拟方法在更改对象大小时会考虑复选框大小。

将类的文件包含到 CContainer 类文件中(打开类容器文件,并将类包含到它当中),以便我们可从容器类创建类对象,并将它们与其绑定:

//+------------------------------------------------------------------+
//|                                                    Container.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\WForms\WinFormBase.mqh"
#include "..\..\WForms\Common Controls\CheckBox.mqh"
//+------------------------------------------------------------------+
//| Class of the base container object of WForms controls            |
//+------------------------------------------------------------------+
class CContainer : public CWinFormBase


为了使所有新类在图形元素的集合类中保持可见,将 CGroupBox 类的文件包含到集合类文件 \MQL5\include\DoEasy\Collections\GraphElementsCollection.mqh 之中:

//+------------------------------------------------------------------+
//|                                      GraphElementsCollection.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Graph\WForms\Containers\GroupBox.mqh"
#include "..\Objects\Graph\WForms\Containers\Panel.mqh"

其余的类包含在 GroupBox.mqh 中,并令其在那里可见。

编写在指定图表和子窗口的画布上创建 “GroupBox” WinForms 图形对象的方法:

//--- Create the 'GroupBox' WinForms graphical object on canvas on the specified chart and subwindow
   int               CreateGroupBox(const long chart_id,
                                    const int subwindow,
                                    const string name,
                                    const int x,
                                    const int y,
                                    const int w,
                                    const int h,
                                    const string text,
                                    const color text_color=clrNONE,
                                    const color frame_color=WRONG_VALUE,
                                    const int  frame_width=WRONG_VALUE,
                                    ENUM_FRAME_STYLE frame_style=FRAME_STYLE_SIMPLE,
                                    const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CGroupBox *obj=new CGroupBox(chart_id,subwindow,name,x,y,w,h);
                        ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id);
                        if(res==ADD_OBJ_RET_CODE_ERROR)
                           return WRONG_VALUE;
                        obj.SetID(id);
                        obj.SetActive(true);
                        obj.SetMovable(false);
                        obj.SetText(text);
                        obj.SetForeColor(text_color==clrNONE ? CLR_DEF_FORE_COLOR : text_color);
                        obj.SetColorBackground(CLR_CANV_NULL);
                        obj.SetColorFrame(frame_color==clrNONE ? CLR_DEF_FRAME_GBOX_COLOR : frame_color);
                        obj.SetBorderStyle(frame_style!=FRAME_STYLE_NONE ? frame_style : FRAME_STYLE_SIMPLE);
                        obj.SetOpacity(0,false);
                        obj.SetFrameWidthAll(frame_width==WRONG_VALUE ? 1 : frame_width);
                        //--- Draw the shadow drawing flag
                        obj.SetShadow(false);
                        if(redraw)
                           obj.Erase(CLR_CANV_NULL,0,redraw);
                        obj.SetActiveAreaShift(obj.FrameWidthLeft(),obj.FrameWidthBottom(),obj.FrameWidthRight(),obj.FrameWidthTop());
                        obj.Done();
                        return obj.ID();
                       }
 
//--- Create graphical object WinForms Panel object on canvas on a specified chart and subwindow

该方法接收创建对象所需的参数。 成功创建对象后,把这些参数赋值给对象属性。 该方法返回在创建过程中获得的对象 ID。

为了能够从自定义程序创建新对象,在函数库主对象类 \MQL5\Include\DoEasy\Engine.mqh 中编写创建和接收新对象的方法。

该方法返回“GroupBox” WForm 对象

//--- Return the WForm Panel object by object ID
   CPanel              *GetWFPanel(const int element_id)
                          {
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_WF_PANEL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
                          
//--- Return the GroupBox WForm object by object name on the current chart
   CGroupBox           *GetWFGroupBox(const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,::ChartID(),EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the GroupBox WForm object by chart ID and object name
   CGroupBox           *GetWFGroupBox(const long chart_id,const string name)
                          {
                           string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name;
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }
//--- Return the WForm GroupBox object by object ID
   CGroupBox           *GetWFGroupBox(const int element_id)
                          {
                           CArrayObj *list=GetListCanvElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX);
                           list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_ID,element_id,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }

//--- Create the WinForm Element object

在每个方法中,获取 GroupBox 类型的对象列表,并根据传递给该方法的参数对获取的列表进行排序。 如果所需的对象在列表中,那么它是列表中唯一的对象 — 从方法返回指向它的指针。 如果在列表中找不到对象,则该方法返回 NULL

添加了三种方法创建 “GroupBox” WinForm 对象:

//--- Create the 'GroupBox' WinForm object
   CGroupBox           *CreateWFGroupBox(const long chart_id,
                                         const int subwindow,
                                         const string name,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         const string text,
                                         const color text_color=clrNONE,
                                         const color frame_color=clrNONE,
                                         const int frame_width=WRONG_VALUE,
                                         const ENUM_FRAME_STYLE frame_style=FRAME_STYLE_SIMPLE,
                                         const bool redraw=false)
                          {
                           int obj_id=this.m_graph_objects.CreateGroupBox(chart_id,subwindow,name,x,y,w,h,text,text_color,frame_color,frame_width,frame_style,redraw);
                           return this.GetWFGroupBox(obj_id);
                          }
//--- Create the Groupbox WinForm object in the specified subwindow on the current chart
   CGroupBox           *CreateWFGroupBox(const int subwindow,
                                         const string name,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         const string text,
                                         const color text_color=clrNONE,
                                         const color frame_color=clrNONE,
                                         const int frame_width=WRONG_VALUE,
                                         const ENUM_FRAME_STYLE frame_style=FRAME_STYLE_SIMPLE,
                                         const bool redraw=false)
                          {
                           return this.CreateWFGroupBox(::ChartID(),subwindow,name,x,y,w,h,text,text_color,frame_color,frame_width,frame_style,redraw);
                          }
//--- Create the GroupBox WinForm object in the main window of the current chart
   CGroupBox           *CreateWFGroupBox(const string name,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         const string text,
                                         const color text_color=clrNONE,
                                         const color frame_color=clrNONE,
                                         const int frame_width=WRONG_VALUE,
                                         const ENUM_FRAME_STYLE frame_style=FRAME_STYLE_SIMPLE,
                                         const bool redraw=false)
                          {
                           return this.CreateWFGroupBox(::ChartID(),0,name,x,y,w,h,text,text_color,frame_color,frame_width,frame_style,redraw);
                          }
   
//--- Fill in the array with IDs of the charts opened in the terminal

最初的第一个方法在指定子窗口中指定 ID 的图表上,按照传递给该方法的参数创建 GroupBox 对象。 调用从上述集合类创建此类对象的方法,并依据新创对象 ID 返回指向新创对象的指针。 所有其它方法调用这个(第一个)方法,在参数行中显式指定当前图表 ID 和主图表窗口。

创建新的函数库对象至此完毕。


测试

为了执行测试,我取用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part108\ 中,命名为 TstDE108.mq5

我们创建一个面板对象,并在其上放置绑定对象:两个面板对象和绑定到它们的文本标签对象。 在这些面板下方创建并附加 GroupBox 对象。 由于我还没有提供从容器对象创建和绑定复选框对象的功能,所以我们从主面板中单独创建这样一个对象 — 简单地在图表上查看我们从中得到了什么。 在 EA 设置中设定对象参数,以便快速、清晰地更改它们,并查看结果。

在全局区域中,创建两个新的枚举,分别是英语俄语编译版本,以及新的输入

//--- enumerations by compilation language
#ifdef COMPILE_EN
enum ENUM_AUTO_SIZE_MODE
  {
   AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                // Grow
   AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK   // Grow and Shrink
  };
enum ENUM_BORDER_STYLE
  {
   BORDER_STYLE_NONE=FRAME_STYLE_NONE,                                  // None
   BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE,                              // Simple
   BORDER_STYLE_FLAT=FRAME_STYLE_FLAT,                                  // Flat
   BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL,                                // Embossed (bevel)
   BORDER_STYLE_STAMP=FRAME_STYLE_STAMP,                                // Embossed (stamp)
  };
enum ENUM_CHEK_STATE
  {
   CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED,              // Unchecked
   CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED,                  // Checked
   CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE,      // Indeterminate
  };
#else 
enum ENUM_AUTO_SIZE_MODE
  {
   AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW,                // Increase only
   AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK   // Increase and decrease
  };
enum ENUM_BORDER_STYLE
  {
   BORDER_STYLE_NONE=FRAME_STYLE_NONE,                                  // No frame
   BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE,                              // Simple frame
   BORDER_STYLE_FLAT=FRAME_STYLE_FLAT,                                  // Flat frame
   BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL,                                // Embossed (convex)
   BORDER_STYLE_STAMP=FRAME_STYLE_STAMP,                                // Embossed (concave)
  };
enum ENUM_CHEK_STATE
  {
   CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED,              // Unchecked
   CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED,                  // Checked
   CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE,      // Undefined
  };
#endif 
//--- input parameters
sinput   bool                          InpMovable        =  true;                   // Movable forms flag
sinput   ENUM_INPUT_YES_NO             InpAutoSize       =  INPUT_YES;              // Autosize
sinput   ENUM_AUTO_SIZE_MODE           InpAutoSizeMode   =  AUTO_SIZE_MODE_GROW;    // Autosize mode
sinput   ENUM_BORDER_STYLE             InpFrameStyle     =  BORDER_STYLE_NONE;      // Label border style
sinput   ENUM_ANCHOR_POINT             InpTextAlign      =  ANCHOR_LEFT_UPPER;      // Label text align
sinput   ENUM_ANCHOR_POINT             InpCheckAlign     =  ANCHOR_LEFT_UPPER;      // Check flag align
sinput   ENUM_ANCHOR_POINT             InpCheckTextAlign =  ANCHOR_LEFT_UPPER;      // Check label text align
sinput   ENUM_CHEK_STATE               InpCheckState     =  CHEK_STATE_UNCHECKED;   // Check flag state
sinput   ENUM_INPUT_YES_NO             InpCheckAutoSize  =  INPUT_YES;              // CheckBox autosize
sinput   ENUM_BORDER_STYLE             InpCheckFrameStyle=  BORDER_STYLE_NONE;      // CheckBox border style
//--- global variables
CEngine        engine;
color          array_clr[];
//+------------------------------------------------------------------+


OnInit() 处理程序中,设置创建 GroupBox 和 CheckBox 对象的代码模块,并略微进行修改,以便修改所创建对象的数量、及其放置坐标:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'26,100,128';      // Original ≈Dark-azure color
   array_clr[1]=C'35,133,169';      // Lightened original color
//--- Create the array with the current symbol and set it to be used in the library
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Create the timeseries object for the current symbol and period, and show its description in the journal
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions

//--- Create WinForms Panel object
   CPanel *pnl=NULL;
   pnl=engine.CreateWFPanel("WFPanel",50,50,230,150,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false);
   if(pnl!=NULL)
     {
      //--- Set Padding to 4
      pnl.SetPaddingAll(4);
      //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs
      pnl.SetMovable(InpMovable);
      pnl.SetAutoSize(InpAutoSize,false);
      pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false);
      //--- In the loop, create 2 bound panel objects
      CPanel *obj=NULL;
      for(int i=0;i<2;i++)
        {
         //--- create the panel object with calculated coordinates, width of 90 and height of 40
         CPanel *prev=pnl.GetElement(i-1);
         int xb=0, yb=0;
         int x=(prev==NULL ? xb : xb+prev.Width()+20);
         int y=0;
         if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,pnl,x,y,90,40,C'0xCD,0xDA,0xD7',200,true,false))
           {
            obj=pnl.GetElement(i);
            if(obj==NULL)
               continue;
            obj.SetFrameWidthAll(3);
            obj.SetBorderStyle(FRAME_STYLE_BEVEL);
            obj.SetColorBackground(obj.ChangeColorLightness(obj.ColorBackground(),4*i));
            obj.SetForeColor(clrRed);
            //--- Calculate the width and height of the future text label object
            int w=obj.Width()-obj.FrameWidthLeft()-obj.FrameWidthRight()-4;
            int h=obj.Height()-obj.FrameWidthTop()-obj.FrameWidthBottom()-4;
            //--- Create a text label object
            obj.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,obj,2,2,w,h,clrNONE,255,false,false);
            //--- Get the pointer to a newly created object
            CLabel *lbl=obj.GetElement(0);
            if(lbl!=NULL)
              {
               //--- If the object has an even or zero index in the list, set the default text color for it
               if(i % 2==0)
                  lbl.SetForeColor(CLR_DEF_FORE_COLOR);
               //--- If the object index in the list is odd, set the object opacity to 127
               else
                  lbl.SetForeColorOpacity(127);
               //--- Set the font Black width type and
               //--- specify the text alignment from the EA settings
               lbl.SetFontBoldType(FW_TYPE_BLACK);
               lbl.SetTextAlign(InpTextAlign);
               //--- For an object with an even or zero index, specify the Bid price for the text, otherwise - the Ask price of the symbol 
               lbl.SetText(GetPrice(i % 2==0 ? SYMBOL_BID : SYMBOL_ASK));
               //--- Set the frame width and type for a text label and update the modified object
               lbl.SetFrameWidthAll(1);
               lbl.SetBorderStyle((ENUM_FRAME_STYLE)InpFrameStyle);
               lbl.Update(true);
              }
           }
        }
      //--- Create the 'GroupBox' WinForms object
      CGroupBox *gbox=NULL;
      //--- GroupBox width is a width of the main panel underlay,
      //--- while the Y coordinate is an indent from attached panels by 6 pixels
      int w=pnl.GetUnderlay().Width();
      int y=obj.BottomEdgeRelative()+6;
      //--- If the attached GroupBox object is created
      if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,pnl,0,y,w,100,C'0x91,0xAA,0xAE',0,true,false))
        {
         //--- get the pointer to the GroupBox object by its index in the list of bound objects
         gbox=pnl.GetElement(2);
         if(gbox!=NULL)
           {
            //--- set the "indented frame" type, the frame color matches the main panel background color,
            //--- while the text color is the background color of the last attached panel darkened by 1
            gbox.SetBorderStyle(FRAME_STYLE_STAMP);
            gbox.SetColorFrame(pnl.ColorBackground());
            gbox.SetForeColor(gbox.ChangeColorLightness(obj.ColorBackground(),-1));
           }
        }
      //--- Create an independent CheckBox object separately from the main panel directly on the chart
      CCheckBox *cbox=new CCheckBox(ChartID(),0,"CBox",pnl.RightEdge()+20,pnl.CoordY()+10,100,60);
      //--- If the object has been created
      if(cbox!=NULL)
        {
         //--- Add the newly created object to the list of library objects temporarily to avoid memory leaks
         ListStorage.Add(cbox);
         //--- Set object parameters from EA inputs
         cbox.SetAutoSize((bool)InpCheckAutoSize,false);
         cbox.SetCheckAlign(InpCheckAlign);
         cbox.SetTextAlign(InpCheckTextAlign);
         //--- Set the displayed text, frame style and checkbox status
         cbox.SetText("CheckBox");
         cbox.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle);
         cbox.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)InpCheckState);
         //--- Redraw the object
         cbox.Redraw(true);
        }
      //--- Redraw all objects according to their hierarchy
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


OnChartEvent() 处理程序中,更改对象类型,从而避免对象类型转换错误:

   //--- If a key is pressed
   if(id==CHARTEVENT_KEYDOWN)
     {
      CPanel *panel=engine.GetWFPanel(0);
      if(panel!=NULL && (lparam==KEY_UP || lparam==KEY_DOWN || lparam==KEY_LEFT || lparam==KEY_RIGHT || lparam==KEY_FILL || lparam==KEY_ORIGIN || lparam==KEY_INDEX))
        {
         for(int i=0;i<panel.ElementsTotal();i++)
           {
            CWinFormBase *obj=panel.GetElement(i);
            if(obj!=NULL)
              {
               if(lparam==KEY_UP)
                  obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_TOP,false);
               else if(lparam==KEY_DOWN)
                  obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_BOTTOM,false);
               else if(lparam==KEY_LEFT)
                  obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_LEFT,false);
               else if(lparam==KEY_RIGHT)
                  obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_RIGHT,false);
               else if(lparam==KEY_FILL)
                  obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_FILL,false);
               else if(lparam==KEY_ORIGIN)
                  obj.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false);
               else if(lparam==KEY_INDEX)
                 {
                  obj.SetDockMode((ENUM_CANV_ELEMENT_DOCK_MODE)i,true);
                  Sleep(i>0 ? 500 : 0);
                 }
              }
           }
         panel.Redraw(true);
        }

之前,我们在此获得了 CPanel 对象的特定类型,而不是函数库 WinForms 对象的基类。 这些不会有错误,因为只有这种类型的对象(面板)参与了测试。

编译 EA,并在图表上启动它:


正如我们所看到的,CheckBox 对象组件的定位工作正常,GroupBox 对象在面板上创建,并与之绑定。

至于目前,所有这些对象都是静态的 — 它们没有与鼠标交互的能力。 稍后我将实现它 — 可一次性应对多个 WinForms 对象。

下一步是什么?

在下一篇文章中,我将继续开发 WinForms 对象。

以下是 MQL5 的当前函数库版本、测试 EA,和图表事件控制指标的所有文件,供您测试和下载。 在评论中留下您的问题、意见和建议。

返回内容目录

*该系列的前几篇文章:

DoEasy. 控件 (第 1 部分): 第一步
DoEasy. 控件 (第 2 部分): 操控 CPanel 类
DoEasy. 控件 (第 3 部分): 创建绑定控件
DoEasy. 控件 (第 4 部分): 面板控件,Padding(填充)和 Dock(驻靠)参数
DoEasy. 控件 (第 5 部分): 基准 WinForms 对象,面板控件,AutoSize 参数
DoEasy. 控件 (第 6 部分): 面板控件,自动调整容器大小来适应内部内容
DoEasy. 控件 (第 7 部分): 文本标签控件



本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/11075

附加的文件 |
MQL5.zip (4364.52 KB)
神经网络实验(第 1 部分):重温几何学 神经网络实验(第 1 部分):重温几何学
在本文中,我将利用实验和非标准方法开发一个可盈利的交易系统,并验证神经网络是否对交易者有任何帮助。
在莫斯科交易所(MOEX)里使用限价订单进行自动网格交易 在莫斯科交易所(MOEX)里使用限价订单进行自动网格交易
本文研究针对 MetaTrader 5 平台开发 MQL5 智能交易系统(EA),旨在能在 MOEX 上操作。 该 EA 采用网格策略,面向 MetaTrader 5 终端,并在 MOEX 上进行交易。 EA 包括了依据止损和止盈平仓,以及在某些市场条件下取消挂单。
学习如何基于交易量设计交易系统 学习如何基于交易量设计交易系统
这是我们系列文集中的新篇章,介绍如何基于最流行的技术指标设计交易系统。 本文将专门讨论交易量指标。 作为一个概念,交易量是金融市场交易中非常重要的因素之一,我们必须予以关注。 贯穿本文,我们将学习如何基于交易量指标设计一款简单的系统。
神经网络变得轻松(第十七部分):降低维度 神经网络变得轻松(第十七部分):降低维度
在本部分中,我们将继续讨论人工智能模型。 即,我们研究无监督学习算法。 我们已经讨论了众多聚类算法之一。 在本文中,我将分享一种解决与降维相关问题的方法。