DoEasy. Controls (Part 7): Text label control
Contents
Concept
Any program involves displaying information on the screen. MS Visual Studio uses a text label control in addition to other elements. MetaTrader 5 also features the "Text label" graphical object. Besides, all graphical elements for creating program GUIs in the terminal also provide the ability to display a text on canvas. But this may sometimes be inconvenient. Therefore, in the current article, I will make an independent "Text label" control.
Such an object will have the ability to position its container anywhere, while its own functionality will repeat the functionality of the MS Visual Studio text label. We will be able to set font parameters for a displayed text. The text is to be positioned within the "text label" object boundaries. In turn, the size of the object can either be set with the specified width and height, or automatically adjust its size to the size of the font used for the label. In addition, we will be able to use the object frame (rectangle that frames the entire "text label" object along its borders). The object frame can be either flat or three-dimensional. This will give us enough opportunities to display the text in the appropriate design inside the program GUI elements.
In order for all the new object parameters to be displayed somewhere or in order for us to be able to select objects by desired parameter, we will add all the properties used by the "text label" object to the enumerations of integer, real and string properties of the graphical elements of the library. I try to adhere to this concept in all library objects since it allows us to use the capabilities it provides for quick search, selection and sorting of library objects with sufficient flexibility.
Improving library classes
First of all, add new text messages to the library.
In \MQL5\Include\DoEasy\Data.mqh, add the library new message indices:
MSG_GRAPH_ELEMENT_TYPE_WF_PANEL, // Panel control MSG_GRAPH_ELEMENT_TYPE_WF_LABEL, // Label control MSG_GRAPH_OBJ_BELONG_PROGRAM, // Graphical object belongs to a program
...
//--- CPanel MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ, // Failed to create the underlay object MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE, // Error. The created object should be of WinForms Base type or be derived from it }; //+------------------------------------------------------------------+
and text messages corresponding to the newly added indices:
{"Элемент управления \"Panel\"","Control element \"Panel\""}, {"Элемент управления \"Label\"","Control element \"Label\""}, {"Графический объект принадлежит программе","The graphic object belongs to the program"},
...
//--- CPanel {"Не удалось создать объект-подложку","Failed to create underlay object"}, {"Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником","Error. The object being created must be of type WinForms Base or be derived from it"}, }; //+---------------------------------------------------------------------+
In addition to the text color in the "Text label" control, we will also use its opacity, which will allow us, for example, to create the effect of smooth appearance/disappearance of text on the program GUI elements. Besides, we will need to set the text displayed by the text label object as well as some other parameters that are necessary for constructing WinForms objects we have not added to the graphical element properties, albeit they were already created earlier.
In the block of canvas parameters in \MQL5\Include\DoEasy\Defines.mqh, add a new macro substitution specifying the default text opacity for controls:
//--- 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_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 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
Add a text label type to the list of library object types:
//+------------------------------------------------------------------+ //| 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_PANEL, // WinForms Panel object type OBJECT_DE_TYPE_GWF_LABEL, // WinForms Label object type //--- Animation
Add a text label element to the list of graphical element types:
//+------------------------------------------------------------------+ //| 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_PANEL, // Windows Forms Panel GRAPH_ELEMENT_TYPE_WF_LABEL, // Windows Forms Label }; //+------------------------------------------------------------------+
Add all new constants (both the ones previously added to WimForms objects but not placed in the enumeration, and the new ones) to the list of integer properties of a graphical element on canvas:
//+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { //--- ... CANV_ELEMENT_PROP_FORE_COLOR, // Default text color for all control objects CANV_ELEMENT_PROP_FORE_COLOR_OPACITY, // Default text color opacity for all control objects CANV_ELEMENT_PROP_BOLD_TYPE, // Font width type CANV_ELEMENT_PROP_BORDER_STYLE, // Control frame style CANV_ELEMENT_PROP_AUTOSIZE, // Flag of the element auto resizing depending on the content CANV_ELEMENT_PROP_AUTOSIZE_MODE, // Mode of the element auto resizing depending on the content CANV_ELEMENT_PROP_AUTOSCROLL, // Auto scrollbar flag CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W, // Width of the field inside the element during auto scrolling CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H, // Height of the field inside the element during auto scrolling CANV_ELEMENT_PROP_DOCK_MODE, // Mode of binding control borders to the container CANV_ELEMENT_PROP_MARGIN_TOP, // Top margin between the fields of this and another control //--- ... CANV_ELEMENT_PROP_PADDING_RIGHT, // Right margin inside the control CANV_ELEMENT_PROP_TEXT_ALIGN, // Text position within text label boundaries }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (44) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
Increase the total number of integer properties from 38 to 44.
In the list of canvas-based graphical element string properties, add a new property — "Graphical element text" and increase the total number of string properties from 2 to 3:
//+------------------------------------------------------------------+ //| String properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_STRING { CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), // Graphical element object name CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name CANV_ELEMENT_PROP_TEXT, // Graphical element text }; #define CANV_ELEMENT_PROP_STRING_TOTAL (3) // Total number of string properties //+------------------------------------------------------------------+
Add all new properties to the enumeration of possible criteria of sorting graphical elements on canvas:
//+------------------------------------------------------------------+ //| 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_FORE_COLOR, // Sort by default text color for all control objects SORT_BY_CANV_ELEMENT_FORE_COLOR_OPACITY, // Sort by default text color opacity for all control objects SORT_BY_CANV_ELEMENT_BOLD_TYPE, // Sort by font width type SORT_BY_CANV_ELEMENT_BORDER_STYLE, // Sort by control frame style SORT_BY_CANV_ELEMENT_AUTOSIZE, // Sort by the flag of the control auto resizing depending on the content SORT_BY_CANV_ELEMENT_AUTOSIZE_MODE, // Sort by the mode of the control auto resizing depending on the content SORT_BY_CANV_ELEMENT_AUTOSCROLL, // Sort by auto scrollbar flag SORT_BY_CANV_ELEMENT_AUTOSCROLL_MARGIN_W, // Sort by width of the field inside the element during auto scrolling SORT_BY_CANV_ELEMENT_AUTOSCROLL_MARGIN_H, // Sort by height of the field inside the element during auto scrolling SORT_BY_CANV_ELEMENT_DOCK_MODE, // Sort by mode of binding control borders to the container //--- ... 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 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 }; //+------------------------------------------------------------------+
Now we are able to select and sort all graphical elements by new properties.
The CWinFormBase class serves as a base class of all WinForms objects of the library. The class, in turn, is derived from the form object featuring the mouse interaction. We will need some of its private variables in the inherited classes. Since private variables and methods are visible only in the class where they are declared, we need to move them from the private section to the protected one, so that they are available in inherited classes.
In \MQL5\Include\DoEasy\Objects\Graph\Form.mqh form object file, move the variables from the private section
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CForm : public CGCnvElement { private: CArrayObj m_list_elements; // List of attached elements CAnimations *m_animations; // Pointer to the animation object CShadowObj *m_shadow_obj; // Pointer to the shadow object CMouseState m_mouse; // "Mouse status" class object ENUM_MOUSE_FORM_STATE m_mouse_form_state; // Mouse status relative to the form ushort m_mouse_state_flags; // Mouse status flags color m_color_frame; // Form frame color int m_offset_x; // Offset of the X coordinate relative to the cursor int m_offset_y; // Offset of the Y coordinate relative to the cursor int m_init_x; // Newly created form X coordinate int m_init_y; // Newly created form Y coordinate int m_init_w; // Newly created form width int m_init_h; // Newly created form height //--- Reset the array size of (1) text, (2) rectangular and (3) geometric animation frames
to the protected section and make some methods virtual so that we are able to redefine them in the inherited classes:
protected: CArrayObj m_list_elements; // List of attached elements CAnimations *m_animations; // Pointer to the animation object CShadowObj *m_shadow_obj; // Pointer to the shadow object CMouseState m_mouse; // "Mouse status" class object ENUM_MOUSE_FORM_STATE m_mouse_form_state; // Mouse status relative to the form ushort m_mouse_state_flags; // Mouse status flags color m_color_frame; // Form frame color int m_offset_x; // Offset of the X coordinate relative to the cursor int m_offset_y; // Offset of the Y coordinate relative to the cursor CArrayObj m_list_tmp; // List for storing the pointers int m_frame_width_left; // Form frame width to the left int m_frame_width_right; // Form frame width to the right int m_frame_width_top; // Form frame width at the top int m_frame_width_bottom; // Form frame width at the bottom int m_init_x; // Newly created form X coordinate int m_init_y; // Newly created form Y coordinate int m_init_w; // Newly created form width int m_init_h; // Newly created form height //--- Initialize the variables virtual void Initialize(void); void Deinitialize(void); //--- Create a shadow object void CreateShadowObj(const color colour,const uchar opacity); //--- Return the name of the dependent object string CreateNameDependentObject(const string base_name) const { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name; } //--- Update coordinates of bound objects virtual bool MoveDependentObj(const int x,const int y,const bool redraw=false); //--- Create a new bound element and add it to the list of bound objects virtual CGCnvElement *CreateAndAddNewElement(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); public:
Since we will now use the general concept of constructing library objects for WinForms objects and are going to add WinForms object parameters to graphical element properties, we need to remove the CWinFormBase base class variables for storing object properties and rewrite all methods for setting and getting these properties.
In \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh, remove the already unnecessary variables from the protected section
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CWinFormBase : public CForm { protected: color m_fore_color; // Default text color for all control objects ENUM_FW_TYPE m_bold_type; // Font width type ENUM_FRAME_STYLE m_border_style; // Control frame style bool m_autosize; // Flag of the element auto resizing depending on the content ENUM_CANV_ELEMENT_DOCK_MODE m_dock_mode; // Mode of binding control borders to the container int m_margin[4]; // Array of gaps of all sides between the fields of the current and adjacent controls int m_padding[4]; // Array of gaps of all sides inside controls private:
In the public section of the class, rewrite the methods for receiving and setting the properties to the object property enumerations, as well as add new methods both for handling the "Text label" object text, and for setting and getting general properties of WinForms objects:
public: //--- ... //--- ... //--- Constructors CWinFormBase(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); CWinFormBase(const string name) : CForm(::ChartID(),0,name,0,0,0,0) { this.m_type=OBJECT_DE_TYPE_GWF_BASE; } //--- (1) Set and (2) return the default text color of all panel objects void SetForeColor(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr); } color ForeColor(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR); } //--- (1) Set and (2) return the default text color opacity of all panel objects void SetForeColorOpacity(const uchar value) { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,value); } uchar ForeColorOpacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); } //--- (1) Set and (2) return the element text virtual void SetText(const string text) { this.SetProperty(CANV_ELEMENT_PROP_TEXT,text); } string Text(void) const { return this.GetProperty(CANV_ELEMENT_PROP_TEXT); } //--- (1) Set and (2) return the element text location angle (alignment type) void SetTextAlign(const ENUM_ANCHOR_POINT anchor) { this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,anchor); } ENUM_ANCHOR_POINT TextAlign(void) const { return (ENUM_ANCHOR_POINT)this.GetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN); } //--- (1) Set and (2) return the Bold font flag void SetBold(const bool flag); bool Bold(void); //--- (1) Set and (2) return the Italic font flag void SetItalic(const bool flag); bool Italic(void); //--- (1) Set and (2) return the Strikeout font flag void SetStrikeout(const bool flag); bool Strikeout(void); //--- (1) Set and (2) return the Underline font flag void SetUnderline(const bool flag); bool Underline(void); //--- (1) Set and (2) return the font style void SetFontDrawStyle(ENUM_FONT_STYLE style); ENUM_FONT_STYLE FontDrawStyle(void); //--- (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 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); } //--- (1) Set and (2) return the flag of the element auto resizing depending on the content virtual void SetAutoSize(const bool flag,const bool redraw) { this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,flag); } bool AutoSize(void) { return (bool)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE); } //--- (1) Set and (2) return the auto scrollbar flag virtual void SetAutoScroll(const bool flag,const bool redraw){ this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,flag); } bool AutoScroll(void) { return (bool)this.GetProperty(CANV_ELEMENT_PROP_AUTOSCROLL); } //--- (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) { this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,mode); } ENUM_CANV_ELEMENT_DOCK_MODE DockMode(void) const { return (ENUM_CANV_ELEMENT_DOCK_MODE)this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE);} //--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides between the fields of this and another control void SetMarginLeft(const int value) { this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,value); } void SetMarginTop(const int value) { this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,value); } void SetMarginRight(const int value) { this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,value); } void SetMarginBottom(const int value) { this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,value); } void SetMarginAll(const int value) { this.SetMarginLeft(value); this.SetMarginTop(value); this.SetMarginRight(value); this.SetMarginBottom(value); } void SetMargin(const int left,const int top,const int right,const int bottom) { this.SetMarginLeft(left); this.SetMarginTop(top); this.SetMarginRight(right); this.SetMarginBottom(bottom); } //--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields of this and another control int MarginLeft(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT); } int MarginTop(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_TOP); } int MarginRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT); } int MarginBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM); } //--- Set the gap (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides inside the control virtual void SetPaddingLeft(const uint value) { int padding=((int)value<this.m_frame_width_left ? this.m_frame_width_left : (int)value); this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,padding); } virtual void SetPaddingTop(const uint value) { int padding=((int)value<this.m_frame_width_top ? this.m_frame_width_top : (int)value); this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,padding); } virtual void SetPaddingRight(const uint value) { int padding=((int)value<this.m_frame_width_right ? this.m_frame_width_right : (int)value); this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,padding); } virtual void SetPaddingBottom(const uint value) { int padding=((int)value<this.m_frame_width_bottom ? this.m_frame_width_bottom : (int)value); this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,padding); } virtual void SetPaddingAll(const uint value) { this.SetPaddingLeft(value); this.SetPaddingTop(value); this.SetPaddingRight(value); this.SetPaddingBottom(value); } virtual void SetPadding(const int left,const int top,const int right,const int bottom) { this.SetPaddingLeft(left); this.SetPaddingTop(top); this.SetPaddingRight(right); this.SetPaddingBottom(bottom); } //--- Set the width of the element frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom virtual void SetFrameWidthLeft(const uint value) { this.m_frame_width_left=(int)value; } virtual void SetFrameWidthTop(const uint value) { this.m_frame_width_top=(int)value; } virtual void SetFrameWidthRight(const uint value) { this.m_frame_width_right=(int)value; } virtual void SetFrameWidthBottom(const uint value) { this.m_frame_width_bottom=(int)value; } virtual void SetFrameWidthAll(const uint value) { this.SetFrameWidthLeft(value); this.SetFrameWidthTop(value); this.SetFrameWidthRight(value); this.SetFrameWidthBottom(value); } virtual void SetFrameWidth(const uint left,const uint top,const uint right,const uint bottom) { this.SetFrameWidthLeft(left); this.SetFrameWidthTop(top); this.SetFrameWidthRight(right); this.SetFrameWidthBottom(bottom); } //--- Return the width of the element frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom int FrameWidthLeft(void) const { return this.m_frame_width_left; } int FrameWidthTop(void) const { return this.m_frame_width_top; } int FrameWidthRight(void) const { return this.m_frame_width_right; } int FrameWidthBottom(void) const { return this.m_frame_width_bottom; } //--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control int PaddingLeft(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT); } int PaddingTop(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP); } int PaddingRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT); } int PaddingBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM); } }; //+------------------------------------------------------------------+
In all reworked methods, we now write values and get them not in variables, but in enumerations of object properties using the SetProperty() and GetProperty() methods, as was originally described in the concept of building library objects in the first article.
In the class constructor, set the text for the created object as an "empty string" and set the default values set in Defines.mqh for the text color and its opacity:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CForm(chart_id,subwindow,name,x,y,w,h) { //--- Set the graphical element and library object types as a base WinForms object CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_frame_width_right=0; this.m_frame_width_left=0; this.m_frame_width_top=0; this.m_frame_width_bottom=0; this.m_gradient_v=true; this.m_gradient_c=false; } //+------------------------------------------------------------------+
In the methods setting the Bold font flag and font width type, set values in the object properties instead of setting values to the variables:
//+------------------------------------------------------------------+ //| Set the Bold font flag | //+------------------------------------------------------------------+ void CWinFormBase::SetBold(const bool flag) { uint flags=this.GetFontFlags(); if(flag) { this.SetFontBoldType(FW_TYPE_BOLD); CGCnvElement::SetFontFlags(flags | FW_BOLD); } else this.SetFontBoldType(FW_TYPE_NORMAL); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Set the font width type | //+------------------------------------------------------------------+ void CWinFormBase::SetFontBoldType(ENUM_FW_TYPE type) { this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,type); uint flags=this.GetFontFlags(); switch(type) { case FW_TYPE_DONTCARE : CGCnvElement::SetFontFlags(flags | FW_DONTCARE); break; case FW_TYPE_THIN : CGCnvElement::SetFontFlags(flags | FW_THIN); break; case FW_TYPE_EXTRALIGHT : CGCnvElement::SetFontFlags(flags | FW_EXTRALIGHT); break; case FW_TYPE_ULTRALIGHT : CGCnvElement::SetFontFlags(flags | FW_ULTRALIGHT); break; case FW_TYPE_LIGHT : CGCnvElement::SetFontFlags(flags | FW_LIGHT); break; case FW_TYPE_REGULAR : CGCnvElement::SetFontFlags(flags | FW_REGULAR); break; case FW_TYPE_MEDIUM : CGCnvElement::SetFontFlags(flags | FW_MEDIUM); break; case FW_TYPE_SEMIBOLD : CGCnvElement::SetFontFlags(flags | FW_SEMIBOLD); break; case FW_TYPE_DEMIBOLD : CGCnvElement::SetFontFlags(flags | FW_DEMIBOLD); break; case FW_TYPE_BOLD : CGCnvElement::SetFontFlags(flags | FW_BOLD); break; case FW_TYPE_EXTRABOLD : CGCnvElement::SetFontFlags(flags | FW_EXTRABOLD); break; case FW_TYPE_ULTRABOLD : CGCnvElement::SetFontFlags(flags | FW_ULTRABOLD); break; case FW_TYPE_HEAVY : CGCnvElement::SetFontFlags(flags | FW_HEAVY); break; case FW_TYPE_BLACK : CGCnvElement::SetFontFlags(flags | FW_BLACK); break; default : CGCnvElement::SetFontFlags(flags | FW_NORMAL); break; } } //+------------------------------------------------------------------+
"Text label" control class
In \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, create a new file Label.mqh of the CLabel class. The CWinFormBase class should be a base class whose file is to be included into the file of the newly created class:
//+------------------------------------------------------------------+ //| Label.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" //+------------------------------------------------------------------+ //| Label object class of WForms controls | //+------------------------------------------------------------------+ class CLabel : public CWinFormBase { }
In the private, protected and public sections of the class, add declarations of the class methods:
//+------------------------------------------------------------------+ //| Label object class of WForms controls | //+------------------------------------------------------------------+ class CLabel : public CWinFormBase { private: //--- Set the element width and height automatically void AutoSetWH(void); 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); //--- Redraw the object virtual void Redraw(bool redraw); //--- Set the element text virtual void SetText(const string text) { CWinFormBase::SetText(text); if(this.AutoSize()) this.AutoSetWH(); } //--- Constructors CLabel(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
As you can see, I have declared the virtual methods of the CWinFormBase base class in the protected and public sections. These methods should have a slightly different logic than that of the base one. Therefore, they are to be redefined in this class.
For example, in the method for setting the element text, the base class method is called first. A new value passed to the method is set to the object property. Then, if the object auto resize flag is set, the private method for setting a new size is called. The size is to correspond to the size of the text displayed on the object canvas considered below:
//--- Set the element text virtual void SetText(const string text) { CWinFormBase::SetText(text); if(this.AutoSize()) this.AutoSetWH(); }
The class has a parametric constructor, while the default constructor and destructor are created automatically.
The chart ID and its subwindow where the object is constructed, as well as the object name, its coordinates and size, are passed to the parametric constructor:
//+------------------------------------------------------------------+ //| 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) : CWinFormBase(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_LABEL; 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); } //+------------------------------------------------------------------+
First, the graphical element type is set into all parent classes and the WinForms Label library object type is set for the object.
Next, set object and coordinates and call the virtual method for setting the main parameters of the library graphical element. The method is redefined in the class since it is slightly different from the same base object method. We will consider it below.
If the object auto resize flag is set to fit the text, call the appropriate method for resizing the object (the flag is always disabled here for now, but this can be changed later).
After resizing (with the flag set), set the initial object size and its initial coordinates.
Redraw the entire object in the end.
The virtual method initializing the variables:
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CLabel::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(); //--- Text label 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.SetMargin(3,0,3,0); this.SetPaddingAll(0); this.SetEnabled(true); this.SetVisible(true,false); } //+------------------------------------------------------------------+
The virtual method redefines the base object method — other default values are set here. Besides, there is initialization of property values that are unique to the Label object.
The virtual Erase methods redefining the base object methods draw the object frame with full opacity:
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CLabel::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 CLabel::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 CLabel::Erase(const bool redraw=false) { //--- Fully clear the element with the redrawing flag CGCnvElement::Erase(redraw); } //+------------------------------------------------------------------+
The virtual method redrawing the object:
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CLabel::Redraw(bool redraw) { //--- Fill the object with the background color having full transparency this.Erase(this.ColorBackground(),0,true); int x=0; int y=0; //--- 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 coordinate x=this.FrameWidthLeft(); y=this.FrameWidthTop(); //--- Set the text binding point at the top left this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP); break; //--- The text is drawn vertically from the left side of the object in the center case ANCHOR_LEFT : //--- Set the text binding point coordinate x=this.FrameWidthLeft(); y=this.Height()/2; //--- Set the text binding point at the center left this.SetTextAnchor(FRAME_ANCHOR_LEFT_CENTER); break; //--- The text is displayed in the lower left corner of the object case ANCHOR_LEFT_LOWER : //--- Set the text binding point coordinate x=this.FrameWidthLeft(); y=this.Height()-this.FrameWidthBottom(); //--- Set the text binding point at the bottom left this.SetTextAnchor(FRAME_ANCHOR_LEFT_BOTTOM); break; //--- The text is drawn at the center of the bottom edge of the object case ANCHOR_LOWER : //--- Set the text binding point coordinate x=this.Width()/2; y=this.Height()-this.FrameWidthBottom(); //--- Set the text anchor point at the bottom center this.SetTextAnchor(FRAME_ANCHOR_CENTER_BOTTOM); break; //--- The text is displayed in the lower right corner of the object case ANCHOR_RIGHT_LOWER : //--- Set the text binding point coordinate x=this.Width()-this.FrameWidthRight(); y=this.Height()-this.FrameWidthBottom(); //--- Set the text binding point at the bottom right this.SetTextAnchor(FRAME_ANCHOR_RIGHT_BOTTOM); break; //--- The text is drawn vertically from the right side of the object in the center case ANCHOR_RIGHT : //--- Set the text binding point coordinate x=this.Width()-this.FrameWidthRight(); y=this.Height()/2; //--- Set the text binding point at the center right this.SetTextAnchor(FRAME_ANCHOR_RIGHT_CENTER); break; //--- The text is displayed in the upper right corner of the object case ANCHOR_RIGHT_UPPER : //--- Set the text binding point coordinate x=this.Width()-this.FrameWidthRight(); y=this.FrameWidthTop(); //--- Set the text binding point at the top right this.SetTextAnchor(FRAME_ANCHOR_RIGHT_TOP); break; //--- The text is drawn at the center of the upper edge of the object case ANCHOR_UPPER : //--- Set the text binding point coordinate x=this.Width()/2; y=this.FrameWidthTop(); //--- Set the text binding point at the center top this.SetTextAnchor(FRAME_ANCHOR_CENTER_TOP); break; //--- The text is drawn at the object center //---ANCHOR_CENTER default: //--- Set the text binding point coordinate x=this.Width()/2; y=this.Height()/2; //--- Set the text binding point at the center this.SetTextAnchor(FRAME_ANCHOR_CENTER); break; } //--- Draw the text within the set coordinates of the object and the binding point of the text, and update the object this.Text(x,y,this.Text(),this.ForeColor(),this.ForeColorOpacity(),this.TextAnchor()); this.Update(redraw); } //+------------------------------------------------------------------+
The method redefines the base class method. The object background is removed here first (filled with a completely transparent background color). The text binding point is defined next depending on the text inside the element. The text binding point coordinates (label coordinate origin) are calculated, the text is displayed within the calculated coordinates and the object is updated.
Text binding points are visually marked and explained in the TextOut() function help:
The method automatically setting the element width and height:
//+------------------------------------------------------------------+ //| Set the element width and height automatically | //+------------------------------------------------------------------+ void CLabel::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 w+=(this.MarginLeft()+this.MarginRight()); //--- If failed to get the width, set it to three pixels if(w==0) w=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); //--- Set the object width and height from the received values this.SetWidth(w); this.SetHeight(h); } //+------------------------------------------------------------------+
The method logic is described in the code comments. First, we get the text size depending on the text and font parameters set for the object. If the label is "empty", we will use a space (" ") for the measurement. Next, add object Margin values on the left and right to the resulting width, as well as Margin values on the top and bottom to the height value. If failed to get the text height, calculate its approximate size by multiplying the font size set for the object by a ratio that I selected empirically. I have compared object size values depending on a font size in MS Visual Studio and took the average value from several measurements of different size getting the ratio of 1.625. I do not know of any other, more accurate method. I may find a better way to calculate an object size depending on a font size later. After all calculations have been done, the obtained width and height values are set for the object.
This concludes the creation of the "Text label" object.
Since the WinForms Panel object is a container for binding other same-type objects to it, all created objects of the same type should be visible to it. To achieve this, the file of each created WinForms object is to be included into the panel object file.
Open the panel object file \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh and include the file of a newly created text label object into it:
//+------------------------------------------------------------------+ //| 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 "..\..\WForms\WinFormBase.mqh" #include "..\..\WForms\Common Controls\Label.mqh" //+------------------------------------------------------------------+
Remove unnecessary variables from the private section of the class since they are now set in the basic WinForms object properties:
//+------------------------------------------------------------------+ //| Panel object class of WForms controls | //+------------------------------------------------------------------+ 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 bool m_autoscroll; // Auto scrollbar flag int m_autoscroll_margin[2]; // Array of fields around the control during an auto scroll ENUM_CANV_ELEMENT_AUTO_SIZE_MODE m_autosize_mode; // Mode of the element auto resizing depending on the content //--- Create a new graphical object
In the public section of the class, declare two new methods for receiving the list of bound WinForms objects with a specified type and for getting the pointer to the specified WinForms object by index in the object list of this type:
public: //--- Return the underlay CGCnvElement *GetUnderlay(void) { return this.m_underlay; } //--- Return the list of bound objects with (1) any and (2) specified basic WinForms type 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); //--- Update the coordinates (shift the canvas)
Remove the SetAutoScroll() and AutoScroll() methods from the class since they are members of the CWinFormBase parent class and are adjusted for handling object properties rather than class variables:
//--- Place bound objects in the order of their Dock binding bool ArrangeObjects(const bool redraw); //--- (1) Set and (2) return the auto scrollbar flag void SetAutoScroll(const bool flag) { this.m_autoscroll=flag; } bool AutoScroll(void) { return this.m_autoscroll; } //--- Set the (1) field width, (2) height, (3) the height of all fields around the control during auto scrolling
Rewrite the class methods for handling object properties in a similar way and add the method for simultaneously setting AutoScrollMargin by width and height:
//--- 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); } //--- (1) 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
In the method creating a new graphical object, add the code block for creating a text label object:
//+------------------------------------------------------------------+ //| 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_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; default: break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name); return element; } //+------------------------------------------------------------------+
Here all is clear, and no comments are required.
Let's rewrite the method creating a newly bound element:
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CPanel::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 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 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 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; } //+------------------------------------------------------------------+
The method logic is described in the code comments. Apart from small logic improvements of the method itself, we have added the code block handling the text label object creation. I hope everything is clear here and does not need explanations. If you have any questions, feel free to ask them in the comments below.
The method returning the list of attached objects with the WinForms object specified type:
//+------------------------------------------------------------------+ //| Return the list of bound objects | //| with the specified WinForms object type | //+------------------------------------------------------------------+ CArrayObj *CPanel::GetListWinFormsObjByType(const ENUM_GRAPH_ELEMENT_TYPE type) { return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,type,EQUAL); } //+------------------------------------------------------------------+
Here we simply return the list received in the CSelect class with the specified WinForms object type. If failed to obtain the list or there are no objects of the specified type, the method returns NULL.
The method returning the pointer to the bound WinForms object with the specified type by index:
//+------------------------------------------------------------------+ //| Return the pointer to the specified WinForms object | //| with the specified type by index | //+------------------------------------------------------------------+ CWinFormBase *CPanel::GetWinFormsObj(const ENUM_GRAPH_ELEMENT_TYPE type,const int index) { CArrayObj *list=this.GetListWinFormsObjByType(type); return(list!=NULL ? list.At(index) : NULL); } //+------------------------------------------------------------------+
Here we get the list of objects with the specified type and return the pointer to the object from the list by specified index.
If failed to obtain the list or a non-existent index is specified, the method returns NULL.
Everything is ready for a test.
Test
To perform the test, let's use the EA from the previous article and save it to \MQL5\Experts\TestDoEasy\Part107\ as TstDE107.mq5.
Since the names of all graphical objects contain the program name, while the chart ID + the number of processor ticks elapsed since the program launch + a pseudo-random number are used to create a graphical object resource in the Create() method of the CCanvas class:
//+------------------------------------------------------------------+ //| Create dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { Destroy(); //--- prepare data array if(width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) { //--- generate resource name m_rcname="::"+name+(string)ChartID()+(string)(GetTickCount()+MathRand()); //--- initialize data with zeros ArrayInitialize(m_pixels,0); //--- create dynamic resource if(ResourceCreate(m_rcname,m_pixels,width,height,0,0,0,clrfmt)) { //--- successfully created //--- complete initialization m_width =width; m_height=height; m_format=clrfmt; //--- succeed return(true); } } //--- error - destroy object Destroy(); return(false); } //+------------------------------------------------------------------+
... the name of a graphical object may exceed 63 characters leading to a resource creation error. Therefore, we have to reduce the program name length for now. Later, I will have to solve the issue of constructing graphical object names since each bound object inherits the name of the object it is bound to with the addition of an ending indicating a new element in the hierarchy. The more nested objects in the hierarchy of objects attached to each other, the longer the object name. This is a wrong concept, which will eventually lead to an error in creating a graphical object due to exceeding the length of its name. But for now, let's just shorten the length of the program name itself.
Six panel objects are created on the main panel. Create a single text label object on each of the panels. The object size will be equal to the panel size minus two points on each side. In the EA settings, display the frame type of the text label object, as well as the text alignment value inside the text label object, which will allow us to clearly see where and how the text is displayed inside the object.
In the global area, create the enumeration describing the object frame type for compilation both in the program English version, and the version in the library user's language, and add new program inputs:
//+------------------------------------------------------------------+ //| TstDE107.mq5 | //| 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" //--- includes #include <DoEasy\Engine.mqh> //--- defines #define FORMS_TOTAL (3) // Number of created forms #define START_X (4) // Initial X coordinate of the shape #define START_Y (4) // Initial Y coordinate of the shape #define KEY_LEFT (65) // (A) Left #define KEY_RIGHT (68) // (D) Right #define KEY_UP (87) // (W) Up #define KEY_DOWN (88) // (X) Down #define KEY_FILL (83) // (S) Filling #define KEY_ORIGIN (90) // (Z) Default #define KEY_INDEX (81) // (Q) By index //--- 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) }; #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) }; #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 //--- global variables CEngine engine; color array_clr[]; //+------------------------------------------------------------------+
In the OnInit() handler, add the code block for creating text label objects:
//+------------------------------------------------------------------+ //| 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 6 bound panel objects for(int i=0;i<6;i++) { //--- create the panel object with coordinates along the X axis in the center and 10 along the Y axis, the width of 80 and the height of 50 CPanel *prev=pnl.GetElement(i-1); int xb=0, yb=0; int x=(i<3 ? (prev==NULL ? xb : prev.CoordXRelative()) : xb+prev.Width()+20); int y=(i<3 ? (prev==NULL ? yb : prev.BottomEdgeRelative()+16) : (i==3 ? yb : prev.BottomEdgeRelative()+16)); if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,pnl,x,y,90,40,C'0xCD,0xDA,0xD7',200,true,false)) { CPanel *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,pnl,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); } } } //--- Redraw all objects according to their hierarchy pnl.Redraw(true); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
The entire logic is commented in detail in the code. Before creating text label objects, set the text background color for the panel the label is bound to. This color should be inherited by the text label object. This is what I am checking here. After that, we will change its color in the text object itself as necessary.
In the OnTick() handler, I will enter Ask and Bid price values to each text label. For objects with an even index in the list, enter the Bid price, while for odd ones, enter the Ask price:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Get the pointer to the panel object by name CPanel *pnl=engine.GetWFPanel("WFPanel"); if(pnl!=NULL) { //--- Get the list of all bound panel objects CArrayObj *list=pnl.GetListWinFormsObjByType(GRAPH_ELEMENT_TYPE_WF_PANEL); //--- In the loop by bound panel objects, for(int i=0;i<list.Total();i++) { //--- get the pointer to the next panel object CPanel *obj=pnl.GetWinFormsObj(GRAPH_ELEMENT_TYPE_WF_PANEL,i); if(obj!=NULL) { //--- take the pointer to the first (and only) text label object from the received object CLabel *lbl=obj.GetWinFormsObj(GRAPH_ELEMENT_TYPE_WF_LABEL,0); if(lbl!=NULL) { //--- set the new text for the object and redraw it lbl.SetText(GetPrice(i % 2==0 ? SYMBOL_BID : SYMBOL_ASK)); lbl.Redraw(false); } } } } } //+------------------------------------------------------------------+
At the very end of the EA listing, add the function returning the string value of the Bid or Ask price:
//+------------------------------------------------------------------+ //| Return Bid/Ask string value | //+------------------------------------------------------------------+ string GetPrice(const ENUM_SYMBOL_INFO_DOUBLE price) { return((price==SYMBOL_ASK ? "Ask: " : "Bid: ")+DoubleToString(SymbolInfoDouble(Symbol(),price),Digits())); } //+------------------------------------------------------------------+
Depending on the value passed to the function, select what to write before the price value ("Ask" or "Bid") and add the string price value with its specified type to the text.
Compile the EA and launch it on the chart:
As we can see, the text is displayed in the correct positions within its object whose size can be seen by specifying the presence of a frame for it. The text with prices inside the object is updated according to the update of the corresponding price on the chart.
When creating the panel and its objects, we can see obvious visual unpleasant effects. I am going to get rid of them when optimizing the visual display while interacting with objects on the chart when they are constructed, moved and re-constructed.
What's next?
In the next article, I will continue the development of WinForms objects.
*Previous articles within the series:
DoEasy. Controls (Part 1): First steps
DoEasy. Controls (Part 2): Working on the CPanel class
DoEasy. Controls (Part 3): Creating bound controls
DoEasy. Controls (Part 4): Panel control, Padding and Dock parameters
DoEasy. Controls (Part 5): Base WinForms object, Panel control, AutoSize parameter
DoEasy. Controls (Part 6): Panel control, auto resizing the container to fit inner content
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/11045
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use