DoEasy. Controls (Part 6): Panel control, auto resizing the container to fit inner content

11 July 2022, 16:27
Artyom Trishkin
0
3 714

Contents


Concept

In the article, I will implement the panel auto resizing in case the Dock property is set as active for object bound to the panel. If the property is set for any of the bound objects, it should be located into its binding place, while the panel should adjust its size to the combined size of all objects bound to it. In other words, panel size changes after the Dock property is set for any of the objects attached to it. If the property is set for a large number of attached objects one after another, the panel should adjust its size to fit its changed internal content with each next bound object.

Adjustment of the panel size to a new object arrangement causes unpleasant visual effects. To avoid them, I have optimized the batch handling of binding objects to the panel and modifying it. The panel adjusts its size visually only after the last bound object is located where it should be.
In the current article, I will continue optimization of handling a batch placement of objects inside their container.

In addition to working on WinForms objects, I will add new properties to the Symbol library object previously announced for MetaTrader 5 Build 3260:

MQL5: New properties in the ENUM_SYMBOL_INFO_DOUBLE enumeration:

  • SYMBOL_SWAP_SUNDAY
  • SYMBOL_SWAP_MONDAY
  • SYMBOL_SWAP_TUESDAY
  • SYMBOL_SWAP_WEDNESDAY
  • SYMBOL_SWAP_THURSDAY
  • SYMBOL_SWAP_FRIDAY
  • SYMBOL_SWAP_SATURDAY

Use the values to obtain swap calculation rates for specific days of the week. 1 — single swap, 3 — triple swap, 0 — no swap.

The new properties are added as class variables for the newly created WinForms objects. At the same time, all library objects are built according to a certain concept described in the first article dedicated to the library creation: each object has a set of properties located in three enumerations of integer, real and string object properties. In the current article, I will make so that all previously added new WinForms object properties become constants of these enumerations. In this case, the new properties will be located in general property lists of each graphical object. Later, this will allow using all WinForms object properties to display them in graphical elements, for example a GUI of a program for visually building a graphical shell of EAs or indicators like MS Visual Studio in the terminal.


Improving library classes

In \MQL5\Include\DoEasy\Data.mqh, add the new message indices:

   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3245,          // Property not supported in MetaTrader 5 versions lower than 3245
   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260,          // Property not supported in MetaTrader 5 versions lower than 3260
   MSG_LIB_PROP_NOT_SUPPORTED_POSITION,               // Property not supported for position

...

   MSG_SYM_PROP_SUBSCRIPTION_DELAY,                   // Delay for quotes passed by symbol for instruments working on subscription basis
   MSG_SYM_PROP_SWAP_SUNDAY,                          // Swap accrual ratio. Sunday
   MSG_SYM_PROP_SWAP_MONDAY,                          // Swap accrual ratio. Monday
   MSG_SYM_PROP_SWAP_TUESDAY,                         // Swap accrual ratio. Tuesday
   MSG_SYM_PROP_SWAP_WEDNESDAY,                       // Swap accrual ratio. Wednesday
   MSG_SYM_PROP_SWAP_THURSDAY,                        // Swap accrual ratio. Thursday
   MSG_SYM_PROP_SWAP_FRIDAY,                          // Swap accrual ratio. Friday
   MSG_SYM_PROP_SWAP_SATURDAY,                        // Swap accrual ratio. Saturday
   MSG_SYM_PROP_SWAP_1,                               // Single swap accrual
   MSG_SYM_PROP_SWAP_3,                               // Triple swap accrual
   MSG_SYM_PROP_SWAP_0,                               // No swap accrual
   //---
   MSG_SYM_PROP_BIDHIGH,                              // Maximal Bid of the day

and text messages corresponding to the newly added indices:

   {"Свойство не поддерживается в MetaTrader5 версии ниже 3245","The property is not supported in MetaTrader5, build lower than 3245"},
   {"Свойство не поддерживается в MetaTrader5 версии ниже 3260","The property is not supported in MetaTrader5, build lower than 3260"},
   {"Свойство не поддерживается у позиции","Property not supported for position"},

...

   {"Размер задержки у котировок, передаваемых по символу, для инструментов, работающих по подписке","Delay size for quotes transmitted per symbol for instruments working by subscription"},
   {"Коэффициент начисления свопов. Воскресение","Swap rate. Sunday"},
   {"Коэффициент начисления свопов. Понедельник","Swap rate. Monday"},
   {"Коэффициент начисления свопов. Вторник","Swap rate. Tuesday"},
   {"Коэффициент начисления свопов. Среда","Swap rate. Wednesday"},
   {"Коэффициент начисления свопов. Четверг","Swap rate. Thursday"},
   {"Коэффициент начисления свопов. Пятница","Swap rate. Friday"},
   {"Коэффициент начисления свопов. Суббота","Swap rate. Saturday"},
   {"Одиночное начисление свопов","Single swap accrual"},
   {"Тройное начисление свопов","Triple swap accrual"},
   {"Начисление свопов отсутствует","No accrual"},
   
   {"Максимальный Bid за день","Maximal Bid of the day"},


In \MQL5\Include\DoEasy\Defines.mqh, namely in the enumeration of symbol object real properties, add new properties and increase the number of real symbol properties from 68 to 75:

   SYMBOL_PROP_PRICE_SENSITIVITY,                           // Option/warrant sensitivity.  Shows by how many points the price of the option's underlying asset should change so that the price of the option changes by one point
   SYMBOL_PROP_SWAP_SUNDAY,                                 // Swap accrual ratio (Sunday)
   SYMBOL_PROP_SWAP_MONDAY,                                 // Swap accrual ratio (Monday)
   SYMBOL_PROP_SWAP_TUESDAY,                                // Swap accrual ratio (Tuesday)
   SYMBOL_PROP_SWAP_WEDNESDAY,                              // Swap accrual ratio (Wednesday)
   SYMBOL_PROP_SWAP_THURSDAY,                               // Swap accrual ratio (Thursday)
   SYMBOL_PROP_SWAP_FRIDAY,                                 // Swap accrual ratio (Friday)
   SYMBOL_PROP_SWAP_SATURDAY,                               // Swap accrual ratio (Saturday)
  };
#define SYMBOL_PROP_DOUBLE_TOTAL     (75)                   // Total number of real properties
#define SYMBOL_PROP_DOUBLE_SKIP      (0)                    // Number of real symbol properties not used in sorting
//+------------------------------------------------------------------+

To be able to sort symbol objects and perform selections by new properties, add new constants, corresponding to added properties, to the enumeration of possible symbol object sorting criteria:

   SORT_BY_SYMBOL_PRICE_SENSITIVITY,                        // Sort by option/warrant sensitivity
   SORT_BY_SYMBOL_SWAP_SUNDAY,                              // Sort by swap accrual ratio (Sunday)
   SORT_BY_SYMBOL_SWAP_MONDAY,                              // Sort by swap accrual ratio (Monday)
   SORT_BY_SYMBOL_SWAP_TUESDAY,                             // Sort by swap accrual ratio (Tuesday)
   SORT_BY_SYMBOL_SWAP_WEDNESDAY,                           // Sort by swap accrual ratio (Wednesday)
   SORT_BY_SYMBOL_SWAP_THURSDAY,                            // Sort by swap accrual ratio (Thursday)
   SORT_BY_SYMBOL_SWAP_FRIDAY,                              // Sort by swap accrual ratio (Friday)
   SORT_BY_SYMBOL_SWAP_SATURDAY,                            // Sort by swap accrual ratio (Saturday)
//--- Sort by string properties
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Sort by a symbol name


In the enumeration of canvas-based graphical element integer properties, add the properties corresponding to the previously added variables of WinForms objects storing these properties and increase the number of object integer properties from 25 to 38:

   CANV_ELEMENT_PROP_ENABLED,                         // Element availability flag
   CANV_ELEMENT_PROP_FORE_COLOR,                      // Default text color 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_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_MARGIN_BOTTOM,                   // Bottom margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_LEFT,                     // Left margin between the fields of this and another control
   CANV_ELEMENT_PROP_MARGIN_RIGHT,                    // Right margin between the fields of this and another control
   CANV_ELEMENT_PROP_PADDING_TOP,                     // Top margin inside the control
   CANV_ELEMENT_PROP_PADDING_BOTTOM,                  // Bottom margin inside the control
   CANV_ELEMENT_PROP_PADDING_LEFT,                    // Left margin inside the control
   CANV_ELEMENT_PROP_PADDING_RIGHT,                   // Right margin inside the control
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (38)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting

In the enumeration of possible WinForms objects sorting criteria, add sorting by newly added integer properties:

   SORT_BY_CANV_ELEMENT_ENABLED,                      // Sort by the element availability flag
   SORT_BY_CANV_ELEMENT_FORE_COLOR,                   // Sort by default text color 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 element auto resizing depending on the content
   SORT_BY_CANV_ELEMENT_DOCK_MODE,                    // Sort by mode of binding control borders to the container
   SORT_BY_CANV_ELEMENT_MARGIN_TOP,                   // Sort by top margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_MARGIN_BOTTOM,                // Sort by bottom margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_MARGIN_LEFT,                  // Sort by left margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_MARGIN_RIGHT,                 // Sort by right margin between the fields of this and another control
   SORT_BY_CANV_ELEMENT_PADDING_TOP,                  // Sort by top margin inside the control
   SORT_BY_CANV_ELEMENT_PADDING_BOTTOM,               // Sort by bottom margin inside the control
   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 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
  };
//+------------------------------------------------------------------+

Now WinForms objects can be selected and sorted by graphical object properties unique to WinForms objects only.


Since new properties have been added to the symbol object, we need to implement handling them in the symbol object class.

Make the necessary improvements in \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh class file.

In the protected section of the class, namely in the block of methods for receiving and returning real properties of a selected symbol from its parameters, declare the method returning the swap ratio for a specified day of the week:

   double            SymbolMarginHedged(void)            const;
   double            SymbolSwapRatio(ENUM_DAY_OF_WEEK day)const;
   bool              SymbolMarginLong(void);


In the public section of the class, declare the method returning the description of swap accrual ratio for a specified day of the week:

   string            GetSectorDescription(void)                   const;
   string            GetIndustryDescription(void)                 const;
   string            GetSwapRatioDescription(const ENUM_DAY_OF_WEEK day)const;


In the block of methods for a simplified access to real symbol object properties, write the methods returning swap accrual ratios for each day of the week and declare the method returning the swap accrual ratio for the specified day of the week:

   double            PriceSensitivity(void)                       const { return this.GetProperty(SYMBOL_PROP_PRICE_SENSITIVITY);                           }
   double            SwapRatioSunday(void)                        const { return this.GetProperty(SYMBOL_PROP_SWAP_SUNDAY);                                 }
   double            SwapRatioMonday(void)                        const { return this.GetProperty(SYMBOL_PROP_SWAP_MONDAY);                                 }
   double            SwapRatioTuesday(void)                       const { return this.GetProperty(SYMBOL_PROP_SWAP_TUESDAY);                                }
   double            SwapRatioWednesday(void)                     const { return this.GetProperty(SYMBOL_PROP_SWAP_WEDNESDAY);                              }
   double            SwapRatioThursday(void)                      const { return this.GetProperty(SYMBOL_PROP_SWAP_THURSDAY);                               }
   double            SwapRatioFriday(void)                        const { return this.GetProperty(SYMBOL_PROP_SWAP_FRIDAY);                                 }
   double            SwapRatioSaturday(void)                      const { return this.GetProperty(SYMBOL_PROP_SWAP_SATURDAY);                               }
   double            SwapRatioDay(const ENUM_DAY_OF_WEEK day)     const;
   double            NormalizedPrice(const double price)          const;


In the block of methods for receiving and setting the parameters of tracked property changes, add the methods for handling new symbol object properties:

   //--- Option/warrant sensitivity
   //--- setting the controlled maximum Bid price (1) increase, (2) decrease value and (3) option/warrant sensitivity control level in points
   //--- getting (4) option/warrant sensitivity change value in points,
   //--- getting the flag of the option/warrant sensitivity change exceeding the (5) increase, (6) decrease value
   void              SetControlPriceSensitivityInc(const double value)        { this.SetControlledValueINC(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value));             }
   void              SetControlPriceSensitivityDec(const double value)        { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value));             }
   void              SetControlPriceSensitivityLevel(const double value)      { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value));           }
   double            GetValueChangedPriceSensitivity(void)              const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_SENSITIVITY);                }
   bool              IsIncreasedPriceSensitivity(void)                  const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_SENSITIVITY);               }
   bool              IsDecreasedPriceSensitivity(void)                  const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_SENSITIVITY);               }
//--- Swap accrual ratio
   //--- setting the controlled Sunday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Sunday),
   //--- get the swap accrual ratio change for Sunday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapSundayInc(const double value)              { this.SetControlledValueINC(SYMBOL_PROP_SWAP_SUNDAY,::fabs(value));                   }
   void              SetControlSwapSundayDec(const double value)              { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_SUNDAY,::fabs(value));                   }
   void              SetControlSwapSundayLevel(const double value)            { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_SUNDAY,::fabs(value));                 }
   double            GetValueChangedSwapSunday(void)                    const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_SUNDAY);                      }
   bool              IsIncreasedSwapSunday(void)                        const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_SUNDAY);                     }
   bool              IsDecreasedSwapSunday(void)                        const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_SUNDAY);                     }
   //--- setting the controlled Monday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Monday),
   //--- get the swap accrual ratio change for Monday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapMondayInc(const double value)              { this.SetControlledValueINC(SYMBOL_PROP_SWAP_MONDAY,::fabs(value));                   }
   void              SetControlSwapMondayDec(const double value)              { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_MONDAY,::fabs(value));                   }
   void              SetControlSwapMondayLevel(const double value)            { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_MONDAY,::fabs(value));                 }
   double            GetValueChangedSwapMonday(void)                    const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_MONDAY);                      }
   bool              IsIncreasedSwapMonday(void)                        const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_MONDAY);                     }
   bool              IsDecreasedSwapMonday(void)                        const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_MONDAY);                     }
   //--- setting the controlled Tuesday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Tuesday),
   //--- get the swap accrual ratio change for Tuesday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapTuesdayInc(const double value)             { this.SetControlledValueINC(SYMBOL_PROP_SWAP_TUESDAY,::fabs(value));                  }
   void              SetControlSwapTuesdayDec(const double value)             { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_TUESDAY,::fabs(value));                  }
   void              SetControlSwapTuesdayLevel(const double value)           { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_TUESDAY,::fabs(value));                }
   double            GetValueChangedSwapTuesday(void)                   const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_TUESDAY);                     }
   bool              IsIncreasedSwapTuesday(void)                       const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_TUESDAY);                    }
   bool              IsDecreasedSwapTuesday(void)                       const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_TUESDAY);                    }
   //--- setting the controlled Wednesday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Wednesday),
   //--- get the swap accrual ratio change for Wednesday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapWednesdayInc(const double value)           { this.SetControlledValueINC(SYMBOL_PROP_SWAP_WEDNESDAY,::fabs(value));                }
   void              SetControlSwapWednesdayDec(const double value)           { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_WEDNESDAY,::fabs(value));                }
   void              SetControlSwapWednesdayLevel(const double value)         { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_WEDNESDAY,::fabs(value));              }
   double            GetValueChangedSwapWednesday(void)                 const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_WEDNESDAY);                   }
   bool              IsIncreasedSwapWednesday(void)                     const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_WEDNESDAY);                  }
   bool              IsDecreasedSwapWednesday(void)                     const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_WEDNESDAY);                  }
   //--- setting the controlled Thursday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Thursday),
   //--- get the swap accrual ratio change for Thursday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapThursdayInc(const double value)            { this.SetControlledValueINC(SYMBOL_PROP_SWAP_THURSDAY,::fabs(value));                 }
   void              SetControlSwapThursdayDec(const double value)            { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_THURSDAY,::fabs(value));                 }
   void              SetControlSwapThursdayLevel(const double value)          { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_THURSDAY,::fabs(value));               }
   double            GetValueChangedSwapThursday(void)                  const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_THURSDAY);                    }
   bool              IsIncreasedSwapThursday(void)                      const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_THURSDAY);                   }
   bool              IsDecreasedSwapThursday(void)                      const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_THURSDAY);                   }
   //--- setting the controlled Friday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Friday),
   //--- get the swap accrual ratio change for Friday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapFridayInc(const double value)              { this.SetControlledValueINC(SYMBOL_PROP_SWAP_FRIDAY,::fabs(value));                   }
   void              SetControlSwapFridayDec(const double value)              { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_FRIDAY,::fabs(value));                   }
   void              SetControlSwapFridayLevel(const double value)            { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_FRIDAY,::fabs(value));                 }
   double            GetValueChangedSwapFriday(void)                    const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_FRIDAY);                      }
   bool              IsIncreasedSwapFriday(void)                        const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_FRIDAY);                     }
   bool              IsDecreasedSwapFriday(void)                        const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_FRIDAY);                     }
   //--- setting the controlled Saturday swap accrual ratio (1) increase, (2) decrease and (3) control levels
   //--- get (4) the swap accrual ratio change (Saturday),
   //--- get the swap accrual ratio change for Saturday exceeding the (5) increase and (6) decrease values
   void              SetControlSwapSaturdayInc(const double value)            { this.SetControlledValueINC(SYMBOL_PROP_SWAP_SATURDAY,::fabs(value));                 }
   void              SetControlSwapSaturdayDec(const double value)            { this.SetControlledValueDEC(SYMBOL_PROP_SWAP_SATURDAY,::fabs(value));                 }
   void              SetControlSwapSaturdayLevel(const double value)          { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_SATURDAY,::fabs(value));               }
   double            GetValueChangedSwapSaturday(void)                  const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_SATURDAY);                    }
   bool              IsIncreasedSwapSaturday(void)                      const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_SATURDAY);                   }
   bool              IsDecreasedSwapSaturday(void)                      const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_SATURDAY);                   }

//--- Return a trading object
   CTradeObj        *GetTradeObj(void)                                           { return &this.m_trade; }

  };
//+------------------------------------------------------------------+

These methods allow setting the tracked value, by which the controlled parameter should change, directly from their programs. When registering such a change, we should get a signal about the event in the program. I have already considered them when developing the interactivity of library objects.

In the protected parametric class constructor, add saving new properties of a symbol object:

   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SUNDAY)]                      = this.SymbolSwapRatio(SUNDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_MONDAY)]                      = this.SymbolSwapRatio(MONDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_TUESDAY)]                     = this.SymbolSwapRatio(TUESDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_WEDNESDAY)]                   = this.SymbolSwapRatio(WEDNESDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_THURSDAY)]                    = this.SymbolSwapRatio(THURSDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_FRIDAY)]                      = this.SymbolSwapRatio(FRIDAY);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SATURDAY)]                    = this.SymbolSwapRatio(SATURDAY);
//--- Save string properties
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                             = this.m_name;

The values received using the SymbolSwapRatio() method (considered below) are set into the array of object properties here.

Let's write implementations of the declared new methods outside the class body.

The protected method returning the swap accrual ratio for a specified day of the week:

//+------------------------------------------------------------------+
//|Return the swap accrual ratio for a specified day of the week     |
//+------------------------------------------------------------------+
double CSymbol::SymbolSwapRatio(ENUM_DAY_OF_WEEK day) const
  {
#ifdef __MQL4__ 
   return 0;
#else 
   switch(day)
     {
      case MONDAY    :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_MONDAY);
      case TUESDAY   :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_TUESDAY);
      case WEDNESDAY :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_WEDNESDAY);
      case THURSDAY  :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_THURSDAY);
      case FRIDAY    :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_FRIDAY);
      case SATURDAY  :  return ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SATURDAY);
      //---SUNDAY
      default        :  return (int)::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SUNDAY);
     }
#endif 
  }
//+------------------------------------------------------------------+

MQL4 has no such symbol — return zero. In case of MQL5, return the appropriate symbol property depending on a day of the week passed to the method. The method is used to set a symbol property to the enumeration of its real properties in the class constructor.

The public method returning the swap accrual ratio for a specified day of the week:

//+------------------------------------------------------------------+
//|Return the swap accrual ratio for a specified day of the week     |
//+------------------------------------------------------------------+
double CSymbol::SwapRatioDay(const ENUM_DAY_OF_WEEK day) const
  {
   switch(day)
     {
      case MONDAY    :  return this.SwapRatioMonday();
      case TUESDAY   :  return this.SwapRatioTuesday();
      case WEDNESDAY :  return this.SwapRatioWednesday();
      case THURSDAY  :  return this.SwapRatioThursday();
      case FRIDAY    :  return this.SwapRatioFriday();
      case SATURDAY  :  return this.SwapRatioSaturday();
      //---SUNDAY
      default        :  return this.SwapRatioSunday();
     }
  }
//+------------------------------------------------------------------+

Here, depending on the day of the week passed to the method, get the property value using public methods returning a property value for a specific day of the week set in the enumeration of real object properties.

In the method returning the description of a real symbol property, add returning the description of new properties of a symbol object:

      property==SYMBOL_PROP_PRICE_SENSITIVITY   ?  CMessage::Text(MSG_SYM_PROP_PRICE_SENSITIVITY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==SYMBOL_PROP_SWAP_SUNDAY      ?  CMessage::Text(MSG_SYM_PROP_SWAP_SUNDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(SUNDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_MONDAY      ?  CMessage::Text(MSG_SYM_PROP_SWAP_MONDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(MONDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_TUESDAY     ?  CMessage::Text(MSG_SYM_PROP_SWAP_TUESDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(TUESDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_WEDNESDAY   ?  CMessage::Text(MSG_SYM_PROP_SWAP_WEDNESDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(WEDNESDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_THURSDAY    ?  CMessage::Text(MSG_SYM_PROP_SWAP_THURSDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(THURSDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_FRIDAY      ?  CMessage::Text(MSG_SYM_PROP_SWAP_FRIDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(FRIDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      property==SYMBOL_PROP_SWAP_SATURDAY    ?  CMessage::Text(MSG_SYM_PROP_SWAP_SATURDAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         #ifdef __MQL5__
         (::TerminalInfoInteger(TERMINAL_BUILD)<3260 ?  ": ("+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3260)+")" : 
                                                        ": "+this.GetSwapRatioDescription(SATURDAY))
         #else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) #endif 
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Here we check the terminal build and, if it is below 3260, then we are notified that such a property is not supported in this version. Otherwise, we get the property description for MQL5, while in case of MQL4, we see the notification that the property is not supported.

The method returning the description of a swap accrual ratio for a specified day of the week:

//+------------------------------------------------------------------+
//| Return the swap accrual ratio description                        |
//| for a specified day of the week                                  |
//+------------------------------------------------------------------+
string CSymbol::GetSwapRatioDescription(const ENUM_DAY_OF_WEEK day) const
  {
   double ratio=this.SwapRatioDay(day);
   return
     (
      ratio==0 ? CMessage::Text(MSG_SYM_PROP_SWAP_0)  :
      ratio==1 ? CMessage::Text(MSG_SYM_PROP_SWAP_1)  :
      ratio==3 ? CMessage::Text(MSG_SYM_PROP_SWAP_3)  : 
      ::DoubleToString(ratio,3)
     );
  }
//+------------------------------------------------------------------+

Here we first get the property value for a day of the week passed to the method, next we return either a text description of a property value (if the value is 0, 1 or 3), or display a double value converted into a text with three decimal places. (The number of decimal places to be displayed can be defined only after performing a sufficient number of tests).

In order to save objects to graphical object files (and to all library objects in the future), we should use the object property structure. All properties are saved to the structure, while the structure itself is saved to the file. In the same way, it is read from the file to restore the object properties.
Since we have new properties for graphical objects, we need to add them to the structure.

In \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, add new properties to the object structure:

private:
   int               m_shift_coord_x;                          // Offset of the X coordinate relative to the base object
   int               m_shift_coord_y;                          // Offset of the Y coordinate relative to the base object
   struct SData
     {
      //--- Object integer properties
      int            id;                                       // Element ID
      int            type;                                     // Graphical element type
      int            number;                                   // Element index in the list
      long           chart_id;                                 // Chart ID
      int            subwindow;                                // Chart subwindow index
      int            coord_x;                                  // Element X coordinate on the chart
      int            coord_y;                                  // Element Y coordinate on the chart
      int            width;                                    // Element width
      int            height;                                   // Element height
      int            edge_right;                               // Element right border
      int            edge_bottom;                              // Element bottom border
      int            act_shift_left;                           // Active area offset from the left edge of the element
      int            act_shift_top;                            // Active area offset from the top edge of the element
      int            act_shift_right;                          // Active area offset from the right edge of the element
      int            act_shift_bottom;                         // Active area offset from the bottom edge of the element
      bool           movable;                                  // Element moveability flag
      bool           active;                                   // Element activity flag
      bool           interaction;                              // Flag of interaction with the outside environment
      int            coord_act_x;                              // X coordinate of the element active area
      int            coord_act_y;                              // Y coordinate of the element active area
      int            coord_act_right;                          // Right border of the element active area
      int            coord_act_bottom;                         // Bottom border of the element active area
      long           zorder;                                   // Priority of a graphical object for receiving the event of clicking on a chart
      bool           enabled;                                  // Element availability flag
      int            belong;                                   // Graphical element affiliation
      color          fore_color;                               // Default text color for all control objects
      int            bold_type;                                // Font width type
      int            border_style;                             // Control frame style
      bool           autosize;                                 // Flag of the element auto resizing depending on the content
      int            dock_mode;                                // Mode of binding control borders to the container
      int            margin_top;                               // Top margin between the fields of this and another control
      int            margin_bottom;                            // Bottom margin between the fields of this and another control
      int            margin_left;                              // Left margin between the fields of this and another control
      int            margin_right;                             // Right margin between the fields of this and another control
      int            padding_top;                              // Top margin inside the control
      int            padding_bottom;                           // Bottom margin inside the control
      int            padding_left;                             // Left margin inside the control
      int            padding_right;                            // Right margin inside the control
      uchar          opacity;                                  // Element opacity
      color          color_bg;                                 // Element background color
      //--- Object real properties

      //--- Object string properties
      uchar          name_obj[64];                             // Graphical element object name
      uchar          name_res[64];                             // Graphical resource name
     };
   SData             m_struct_obj;                             // Object structure


In the method creating the object structure, add writing object properties to the structure fields:

   this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);      // Bottom border of the element active area
   this.m_struct_obj.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG);                    // Graphical element affiliation
   this.m_struct_obj.zorder=this.GetProperty(CANV_ELEMENT_PROP_ZORDER);                         // Priority of a graphical object for receiving the on-chart mouse click event
   this.m_struct_obj.fore_color=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR);          // Default text color for all control objects
   this.m_struct_obj.bold_type=(int)this.GetProperty(CANV_ELEMENT_PROP_BOLD_TYPE);              // Font width type
   this.m_struct_obj.border_style=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_STYLE);        // Control frame style
   this.m_struct_obj.autosize=this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE);                     // Flag of the element auto resizing depending on the content
   this.m_struct_obj.dock_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_DOCK_MODE);              // Mode of binding control borders to the container
   this.m_struct_obj.margin_top=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_TOP);            // Top margin between the fields of this and another control
   this.m_struct_obj.margin_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM);      // Bottom margin between the fields of this and another control
   this.m_struct_obj.margin_left=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT);          // Left margin between the fields of this and another control
   this.m_struct_obj.margin_right=(int)this.GetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT);        // Right margin between the fields of this and another control
   this.m_struct_obj.padding_top=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_TOP);          // Top margin inside the control
   this.m_struct_obj.padding_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM);    // Bottom margin inside the control
   this.m_struct_obj.padding_left=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_LEFT);        // Left margin inside the control
   this.m_struct_obj.padding_right=(int)this.GetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT);      // Right margin inside the control
   this.m_struct_obj.color_bg=this.m_color_bg;                                                  // Element background color
   this.m_struct_obj.opacity=this.m_opacity;                                                    // Element opacity


In the method creating an object out of the structure, add reading object property values from the structure fields:

   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);           // Bottom border of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_BELONG,this.m_struct_obj.belong);                         // Graphical element affiliation
   this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,this.m_struct_obj.fore_color);                 // Default text color for all control objects
   this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,this.m_struct_obj.bold_type);                   // Font width type
   this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,this.m_struct_obj.border_style);             // Control frame style
   this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,this.m_struct_obj.autosize);                     // Flag of the element auto resizing depending on the content
   this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,this.m_struct_obj.dock_mode);                   // Mode of binding control borders to the container
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,this.m_struct_obj.margin_top);                 // Top margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,this.m_struct_obj.margin_bottom);           // Bottom margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,this.m_struct_obj.margin_left);               // Left margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,this.m_struct_obj.margin_right);             // Right margin between the fields of this and another control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,this.m_struct_obj.padding_top);               // Top margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,this.m_struct_obj.padding_bottom);         // Bottom margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,this.m_struct_obj.padding_left);             // Left margin inside the control
   this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,this.m_struct_obj.padding_right);           // Right margin inside the control
   this.SetZorder(this.m_struct_obj.zorder,false);                                              // Priority of a graphical object for receiving the on-chart mouse click event
   this.m_color_bg=this.m_struct_obj.color_bg;                                                  // Element background color
   this.m_opacity=this.m_struct_obj.opacity;                                                    // Element opacity

Now all graphical objects are correctly saved to the file and restored from it when we start saving the properties of library objects in files.


Let's make a minor improvement in the base class of WinForms objects in \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.

First, we need to be able to select these objects and sort them by their properties. To achieve this, include the CSelect class file:

//+------------------------------------------------------------------+
//|                                                  WinFormBase.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 "..\Form.mqh"
#include "..\..\..\Services\Select.mqh"
//+------------------------------------------------------------------+

Now we can use the CSelect class in all inherited classes to select and sort WinForms objects by their properties.

Besides, add a check for the redraw flag in the loop handling all attached objects in the method redrawing the object, so that the objects are actually redrawn only if the redraw flag is activated. If this is not the case, there is no need to redraw bound objects (for example, in order to get rid of visual artifacts when making changes to the panel object properties). When the redraw flag is reset, the objects bound to the container should not be actually redrawn as their properties change in other methods, while all objects are actually redrawn here.

Thus, add the check for the flag and calling for object redrawing only if the flag is set:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CWinFormBase::Redraw(bool redraw)
  {
//--- If the object type is less than the "Base WinForms object", exit
   if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE)
      return;
//--- Get the "Shadow" object
   CShadowObj *shadow=this.GetShadowObj();
//--- If the object has a shadow and the "Shadow" object exists, redraw it
   if(this.IsShadow() && shadow!=NULL)
     {
      //--- remove the previously drawn shadow,
      shadow.Erase();
      //--- save the relative shadow coordinates,
      int x=shadow.CoordXRelative();
      int y=shadow.CoordYRelative();
      //--- redraw the shadow,
      if(redraw)
         shadow.Draw(0,0,shadow.Blur(),redraw);
      //--- restore relative shadow coordinates
      shadow.SetCoordXRelative(x);
      shadow.SetCoordYRelative(y);
     }
//--- If the redraw flag is set,
   if(redraw)
     {
      //--- completely redraw the object and save its new initial look
      this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,redraw);
      this.Done();
     }
//--- otherwise, remove the object
   else
      this.Erase();
//--- Redraw all bound objects with the redraw flag
   for(int i=0;i<this.ElementsTotal();i++)
     {
      CWinFormBase *element=this.GetElement(i);
      if(element==NULL)
         continue;
      if(redraw)
         element.Redraw(redraw);
     }
//--- If the redraw flag is set and if this is the main object the rest are bound to,
//--- redraw the chart to display changes immediately
   if(redraw && this.GetMain()==NULL)
      ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+


Now let's improve the class of the Panel WinForms object in \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh.

In the private section of the class, declare the method for calculating Dock objects' binding coordinates:

//--- Set the underlay as a coordinate system zero
   void              SetUnderlayAsBase(void);
//--- Calculate Dock objects' binding coordinates
   void              CalculateCoords(CArrayObj *list);

protected:

I will not implement the method in the current article, so let's add an empty method outside the class body:

//+------------------------------------------------------------------+
//| Calculate Dock objects' binding coordinates                      |
//+------------------------------------------------------------------+
void CPanel::CalculateCoords(CArrayObj *list)
  {
   
  }
//+------------------------------------------------------------------+

I will deal with its implementation after creating the "Text Label" (Label) WinForms object class so that we can visually see the sorting of objects when they are bound in accordance with the value of the Dock property and at the same time handle the correct movement of objects bound to their containers, which in turn are attached to the main container — panels.

From the protected section of the class, remove the declaration of the methods returning the maximum value of Dock object borders going beyond the container by width and height:

protected:
//--- Return the maximum value of Dock object borders going beyond the container by width and (2) height
   int               GetExcessMaxX(void);
   int               GetExcessMaxY(void);
//--- Set (1) X, (2) Y coordinate, (3) width, (4) height and (5) all underlay parameters

Also, remove the implementation of the methods set outside the class body.

Now there is no need for these methods, since we can now use the CSelect library class to find the necessary values.

In the public section of the class, declare the method returning the list of bound objects with WinForms type basic and higher:

public:
//--- Return the underlay
   CGCnvElement     *GetUnderlay(void)                               { return this.m_underlay;              }
//--- Return the list of bound objects with WinForms type basic and higher
   CArrayObj        *GetListWinFormsObj(void);


The panel object should still be created without a frame. If the frame is necessary for the panel, it can be added after creating the object. Therefore, set the frame type as absent in the class constructors:

//--- 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);
                        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);
   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);
  }
//+------------------------------------------------------------------+

In the last constructor, remove the frame creation in addition to setting a frame type:

   this.Initialize();
   this.DrawFormFrame(this.FrameWidthTop(),this.FrameWidthBottom(),this.FrameWidthLeft(),this.FrameWidthRight(),this.ColorFrame(),this.Opacity(),this.BorderStyle());
   if(this.CreateUnderlayObj())
      this.SetUnderlayAsBase();


Previously, the method that arranges bound objects in the order of their Dock binding did not contain handling of object location in case the auto resize flag was not set for the container (panel). In the current article, I will simply copy binding mode handlers from the fixed panel handlers. However, I will preliminarily add a check for the underlay, while the list of objects will now be received using a new method returning the list of WinForms objects only. If the panel has the auto resize mode enabled, first change the panel size to the original one, next, call the method adjusting the panel size to its content. After that, in the loop, handle the binding modes of all bound objects and re-adjust the panel size to the objects changed inside it:

//+------------------------------------------------------------------+
//| Place bound objects in the order of their Dock binding           |
//+------------------------------------------------------------------+
bool CPanel::ArrangeObjects(const bool redraw)
  {
//--- If the panel has no underlay, return 'false'
   if(this.m_underlay==NULL)
      return false;
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
   CWinFormBase *prev=NULL, *obj=NULL, *elm=NULL;
   //--- If auto resizing mode is enabled
   if(this.AutoSize())
     {
      //--- Return the original panel size and then adjust it to the objects located inside it
      this.Resize(this.GetWidthInit(),this.GetHeightInit(),false);
      this.AutoSizeProcess(false);
      //--- 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 there is no previous element, set the underlay as a previous element
         if(prev==NULL)
            this.SetUnderlayAsBase();
         //--- If the object has not been received or its type is less than the base WinForms object or the current element has no underlay, 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 underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the top whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetTopObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordY() : coord_base.BottomEdge()+1);
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the top one whose edges will be used to bind the next one
            this.m_obj_top=obj;
           }
         //--- Bottom
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM)
           {
            //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the bottom whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetBottomObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.BottomEdge()-obj.Height() : coord_base.CoordY()-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;
            //--- Set the current object as the bottom one whose edges will be used to bind the next one
            this.m_obj_bottom=obj;
           }
         //--- Left
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the left whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetLeftObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordX() : coord_base.RightEdge()+1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the left one whose edges will be used to bind the next one
            this.m_obj_left=obj;
           }
         //--- Right
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the right whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetRightObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? m_underlay.RightEdge()-obj.Width() : coord_base.CoordX()-obj.Width()-1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the right one whose edges will be used to bind the next one
            this.m_obj_right=obj;
           }
         //--- Binding with filling
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL)
           {
            //--- If failed to change the object size (for the entire underlay width and height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),this.GetHeightUnderlay(),false))
               continue;
            //--- Set the underlay as a binding object
            this.SetUnderlayAsBase();
            //--- Get the object binding coordinates
            x=this.GetLeftObj().CoordX();
            y=this.GetTopObj().CoordY();
            //--- 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.GetCoordXUnderlay()+obj.CoordXRelativeInit();
            y=this.GetCoordYUnderlay()+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.m_underlay.CoordX());
         obj.SetCoordYRelative(y-this.m_underlay.CoordY());
        }
      this.Resize(this.GetWidthInit(),this.GetHeightInit(),false);
      this.AutoSizeProcess(false);
     }
   //--- If auto resizing mode disabled 
   else
     {
      //--- 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 there is no previous element, set the underlay as a previous element
         if(prev==NULL)
            this.SetUnderlayAsBase();
         //--- If the object has not been received or its type is less than the base WinForms object or the current element has no underlay, 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 underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the top whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetTopObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordY() : coord_base.BottomEdge()+1);
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the top one whose edges will be used to bind the next one
            this.m_obj_top=obj;
           }
         //--- Bottom
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_BOTTOM)
           {
            //--- If failed to change the object size (for the entire underlay width and by the initial object height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),obj.GetHeightInit(),false))
               continue;
            //--- Get the pointer to the object at the bottom whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetBottomObj();
            //--- Get the object binding coordinates
            x=this.GetCoordXUnderlay();
            y=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.BottomEdge()-obj.Height()-1 : coord_base.CoordY()-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;
            //--- Set the current object as the bottom one whose edges will be used to bind the next one
            this.m_obj_bottom=obj;
           }
         //--- Left
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_LEFT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the left whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetLeftObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? coord_base.CoordX() : coord_base.RightEdge()+1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the left one whose edges will be used to bind the next one
            this.m_obj_left=obj;
           }
         //--- Right
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_RIGHT)
           {
            //--- If failed to change the object size (for the initial object width and the entire underlay height), move on to the next one
            if(!obj.Resize(obj.GetWidthInit(),this.GetHeightUnderlay(),false))
               continue;
            //--- Get the pointer to the object at the right whose edges are used to bind the current one
            CGCnvElement *coord_base=this.GetRightObj();
            //--- Get the object binding coordinates
            x=(coord_base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? m_underlay.RightEdge()-obj.Width() : coord_base.CoordX()-obj.Width()-1);
            y=this.GetCoordYUnderlay();
            //--- If failed to move the object to the obtained coordinates, move on to the next one
            if(!obj.Move(x,y,false))
               continue;
            //--- Set the current object as the right one whose edges will be used to bind the next one
            this.m_obj_right=obj;
           }
         //--- Binding with filling
         if(obj.DockMode()==CANV_ELEMENT_DOCK_MODE_FILL)
           {
            //--- If failed to change the object size (for the entire underlay width and height), move on to the next one
            if(!obj.Resize(this.GetWidthUnderlay(),this.GetHeightUnderlay(),false))
               continue;
            //--- Set the underlay as a binding object
            this.SetUnderlayAsBase();
            //--- Get the object binding coordinates
            x=this.GetLeftObj().CoordX();
            y=this.GetTopObj().CoordY();
            //--- 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.GetCoordXUnderlay()+obj.CoordXRelativeInit();
            y=this.GetCoordYUnderlay()+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.m_underlay.CoordX());
         obj.SetCoordYRelative(y-this.m_underlay.CoordY());
        }
     }
//--- Redraw the object with the redraw flag and return 'true'
   this.Redraw(redraw); 
   return true;
  }
//+------------------------------------------------------------------+

Here we can do all in a single loop, i.e. without splitting into two blocks with two separate identical loops, as well as additionally check the panel auto resize flag. This will cut the code in half. But I decided to leave it in case I will have to further refine the code block handling the panel auto resize flag. Only when everything is debugged and it is clear that no more changes and improvements are required, I will optimize the code of this method.

The method adjusting the element size to fit its content has also been revised since now we can obtain the necessary data using the CSelect class:

//+------------------------------------------------------------------+
//| Adjust the element size to fit its content                       |
//+------------------------------------------------------------------+
bool CPanel::AutoSizeProcess(const bool redraw)
  {
//--- Get the list of bound objects with WinForms type basic and higher
   CArrayObj *list=this.GetListWinFormsObj();
//--- Get object indices in the list with the maximum and minimum X and Y coordinates
   int imaxx=CSelect::FindGraphCanvElementMax(list,CANV_ELEMENT_PROP_COORD_X);
   int iminx=CSelect::FindGraphCanvElementMin(list,CANV_ELEMENT_PROP_COORD_X);
   int imaxy=CSelect::FindGraphCanvElementMax(list,CANV_ELEMENT_PROP_COORD_Y);
   int iminy=CSelect::FindGraphCanvElementMin(list,CANV_ELEMENT_PROP_COORD_Y);
//--- Get objects with the maximum and minimum X and Y coordinates from the list
   CWinFormBase *maxx=list.At(imaxx);
   CWinFormBase *minx=list.At(iminx);
   CWinFormBase *maxy=list.At(imaxy);
   CWinFormBase *miny=list.At(iminy);
//--- 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 panel in width and height
   int excess_x=w-this.m_underlay.Width();
   int excess_y=h-this.m_underlay.Height();
//--- Calculate the offset, by which the bound objects are to be moved
   int shift_x=m_underlay.CoordX()-min_x;
   int shift_y=m_underlay.CoordY()-min_y;
//--- If failed to change the panel size, return 'true'
   if(excess_x==0 && excess_y==0)
      return true;
//--- If it is necessary to move the attached objects inside the panel 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.m_underlay.CoordX());
         obj.SetCoordYRelative(obj.CoordY()-this.m_underlay.CoordY());
        }
     }
//--- Return the result of resizing the panel
   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)
     );
  }
//+------------------------------------------------------------------+

The method logic is fully described in the code comments. When getting the minimum Y axis coordinate, I occasionally bump into a strange behavior related to the obtained value... When the object is first constructed, the maximum coordinate is returned with the maximum value, while the minimum coordinate is returned with the minimum value. All is correct. However, after re-arranging the objects, the maximum coordinate is returned with the minimum value, while the minimum coordinate is returned with the maximum one. I have not yet been able to find out the reasons for this behavior, and I had to make a choice between two values — when requesting the maximum value, we get the maximum of the two, while when requesting the minimum one, we get the minimum of the two.
Why do we calculate X and Y offsets? When binding the objects to the right or bottom of the panel, they are built either from the right or the bottom edge of the panel. Thus, they are able to go beyond the panel on the left or top. Since the panel (and any other graphical element) coordinate origin starts in the upper left corner, the panel increases to the right or bottom when increasing the panel size to fit the size of all objects located inside. Thus, the panel will have the size corresponding to all objects arranged inside but the panel coordinate origin will not correspond to the visible origin of all objects. Therefore, we will need to shift these objects either to the right or to the bottom by the calculated amount depending on the object binding mode.

The method returning the list of attached objects of WinForms base type or higher:

//+------------------------------------------------------------------+
//| Return the list of bound objects                                 |
//| of WinForms base type and higher                                 |
//+------------------------------------------------------------------+
CArrayObj *CPanel::GetListWinFormsObj(void)
  {
   return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE,EQUAL_OR_MORE);
  }
//+------------------------------------------------------------------+

The method simply returns the list containing only objects of WinForms Base type or higher , i.e. either base WinForms object or its descendants. Objects are selected by sorting by type from the general list of all objects bound to the panel using the CSelect class.

Let's perform optimization and eliminate logic errors in the methods of creating the Panel WinForms objects of the CGraphElementsCollection class in \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

I have made a mistake because the object frame was drawn twice, while the frame color may not have been set for it. Besides, the panel frame size may not have been set as well if they were passed to the methods as a default value equal to -1. We did not have a check for this value, so it was set instead of setting the default frame size in case of the value of -1.

Identical or similar changes have been made in all panel creation methods:

//--- Create a WinForms Panel object graphical object on canvas on a specified chart and subwindow with the vertical gradient filling
   int               CreatePanelVGradient(const long chart_id,
                                          const int subwindow,
                                          const string name,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          color &clr[],
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity,
                                          const int  frame_width=-1,
                                          ENUM_FRAME_STYLE frame_style=FRAME_STYLE_BEVEL,
                                          const bool shadow=false,
                                          const bool redraw=false)
                       {
                        int id=this.m_list_all_canv_elm_obj.Total();
                        CPanel *obj=new CPanel(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(activity);
                        obj.SetMovable(movable);
                        obj.SetColorsBackground(clr);
                        obj.SetColorFrame(obj.ColorBackground());
                        obj.SetBorderStyle(frame_style);
                        obj.SetOpacity(opacity,false);
                        obj.SetFrameWidthAll(frame_width==WRONG_VALUE ? DEF_FRAME_WIDTH_SIZE : frame_width);
                        //--- Draw the shadow drawing flag
                        obj.SetShadow(shadow);
                        if(shadow)
                          {
                           color clrS=obj.ChangeColorLightness(obj.ChangeColorSaturation(obj.ColorBackground(),-100),-20);
                           obj.DrawShadow(3,3,clrS,CLR_DEF_SHADOW_OPACITY,DEF_SHADOW_BLUR);
                          }
                        obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                        if(redraw)
                          {
                           obj.Erase(clr,opacity,true,false,redraw);
                           obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity());
                          } 
                        obj.SetActiveAreaShift(obj.FrameWidthLeft(),obj.FrameWidthBottom(),obj.FrameWidthRight(),obj.FrameWidthTop());
                        obj.Done();
                        return obj.ID();
                       }

Set the frame color equal to the panel background color (to the first of the gradient or the only one). When setting the frame size, check which value has been passed to the method. If -1, set the default value set in the DEF_FRAME_WIDTH_SIZE macro substitution in Defines.mqh. If the redraw flag is set, then paint the panel with a background color or a gradient and draw an outlining rectangle on top. In this case, the frame is drawn in the virtual Erase() method of the CPanel class.

Such improvements have been made in all methods for creating panels. They can be found in the files attached to the article.


Test

To perform the test, I will use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part106\ as TestDoEasyPart106.mq5.

Since panel objects bound to the container are now created without a frame by default, specify the frame width and type for each of them in the OnInit() handler. The color of each subsequent panel will be lighter by the value calculated from the loop index (background color lightened by an amount equal to the loop index * 4). This is necessary in order to clearly see the changes in the arrangement of panels in the container when altering the way they are bound:

//+------------------------------------------------------------------+
//| 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,80,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));
           }
        }
      pnl.Redraw(true);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Compile the EA and launch it on a symbol chart:


So, the panel adapts to the general size of the objects attached to it, different binding methods work correctly and the panel changes its size correctly as well. When placing objects with different binding methods, they are not arranged as I would like — they should not be attached to the edges of the panel, and each subsequent object in the list should be attached to the edges of the previous one if it has the same binding. In particular, the very last object is stretched to the full width and height of the container which is incorrect since it should be limited to the inner edges of objects previously bound to the edges of the panel, i.e. it should be set within the free space between them. I will implement the correct behavior of bound objects in subsequent articles.


What's next?

In the next article, I will continue my work on the Panel object and start developing new controls, including WinForms text label object.

All files of the current library version, test EA and chart event control indicator for MQL5 are attached below for you to test and download. Leave your questions, comments and suggestions in the comments.

Back to contents

*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


Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/10989

Attached files |
MQL5.zip (4347.87 KB)
Developing a trading Expert Advisor from scratch (Part 15): Accessing data on the web (I) Developing a trading Expert Advisor from scratch (Part 15): Accessing data on the web (I)
How to access online data via MetaTrader 5? There are a lot of websites and places on the web, featuring a huge amount information. What you need to know is where to look and how best to use this information.
Developing a trading Expert Advisor from scratch (Part 14): Adding Volume At Price (II) Developing a trading Expert Advisor from scratch (Part 14): Adding Volume At Price (II)
Today we will add some more resources to our EA. This interesting article can provide some new ideas and methods of presenting information. At the same time, it can assist in fixing minor flaws in your projects.
DoEasy. Controls (Part 7): Text label control DoEasy. Controls (Part 7): Text label control
In the current article, I will create the class of the WinForms text label control object. 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.
MQL5 Wizard techniques you should know (Part 02): Kohonen Maps MQL5 Wizard techniques you should know (Part 02): Kohonen Maps
Todays trader is a philomath who is almost always (either consciously or not...) looking up new ideas, trying them out, choosing to modify them or discard them; an exploratory process that should cost a fair amount of diligence. This clearly places a premium on the trader's time and the need to avoid mistakes. These series of articles will proposition that the MQL5 Wizard should be a mainstay for traders. Why? Because not only does the trader save time by assembling his new ideas with the MQL5 Wizard, and greatly reduce mistakes from duplicate coding; he is ultimately set-up to channel his energy on the few critical areas of his trading philosophy.