English Русский Español Deutsch 日本語 Português
preview
DoEasy. 控件(第 6 部分):面板控件,自动调整容器大小来适应内部内容

DoEasy. 控件(第 6 部分):面板控件,自动调整容器大小来适应内部内容

MetaTrader 5示例 | 16 八月 2022, 07:47
901 0
Artyom Trishkin
Artyom Trishkin

内容


概述

在本文中,我将实现面板自动调整大小,以防绑定到面板的对象将 Dock 属性设置为激活。 如果任何绑定对象设置了该特性,则它应位于其绑定位置,同时面板应调整自身大小,可容纳绑定到该面板的所有对象组合后的大小。 换言之,面板大小会在附着其内的任意对象 “Dock” 属性设置发生变化后自动调整。 如果有大量附着对象,它们一个接一个地设置该属性,则面板应调整其大小,以使其更改的内部内容与每个下一个绑定对象相匹配。

若每个新对象排列变化都调整一次面板大小,会导致不愉快的视觉效果。 为避免这样的问题,我做了优化,把绑定到面板的对象修改统合到批处理。 只有在最后一个绑定对象放置到其正确位置后,面板才调整其可视大小。
在本文中,我将继续优化容器中对象安置的批量处理流程。

除了处理 WinForms 对象以外,我还为先前 MetaTrader 5 Build 3260 发布的品种函数库对象添加了新属性:

MQL5: 新属性位于 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

使用这些值可以获得一周中特定日期的掉期计算利率。 1 — 单重掉期利率,3 — 三重掉期利率,0 — 无掉期利率。

新属性将在新创建的 WinForms 对象的类变量里添加。 同时,所有函数库对象都是根据专门创建函数库的第一篇文章中讲述的特定概念构建的:每个对象都有一组属性,分别定义在整数型、实数型和字符串型三个对象属性枚举当中。 在本文中,我也要把以前添加的所有新 WinForms 对象的属性变为这些枚举的常量。 在这种情况下,新属性将位于每个图形对象的常规特性列表当中。 稍后,这将允许在图形元素中显示所有 WinForms 对象属性,例如,在终端中可视化构建 EA 或 MS Visual Studio 等指标图形外壳的 GUI 程序。


改进库类

在 \MQL5\Include\DoEasy\Data.mqh 里,添加新的消息索引:

   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


与新增索引相对应的消息

   {"Свойство не поддерживается в 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"},



在 \MQL5\Include\DoEasy\Defines.mqh,即在品种对象的实数型属性的枚举中,加入新属性,并把实数型属性的个数从 68 增加到 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
//+------------------------------------------------------------------+


为了能够对品种对象进行排序,并根据新属性进行选择,添加对应于新属性的常数到品种对象可能的排序标准枚举当中:

   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



在基于画布的图形元素整数型属性的枚举中,在 WinForms 对象里添加对应的新属性,并把对象整数型属性的数量从 25 增加到 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


在可能的 WinForms 对象排序准则的枚举中,添加依据新加整数型属性排序

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


现在,WinForms 对象只能依据其独有的图形对象属性进行选择和排序。


由于新属性已添加到品种对象,我们需要在品种对象类中实现针对它们的处理。

在 \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh 类文件里进行必要的改进。

在类的受保护部分,即从参数中接收和返回选定品种的实数型属性的方法模块中,声明返回周内指定日的掉期利率的方法

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



在类的公开部分,声明返回周内指定日的掉期利率的方法

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



在简化的访问品种对象实数型属性的方法模块中,编写返回周内每天的掉期利率的方法,并声明返回周内指定日的掉期利率的方法

   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;



在接收和设置跟踪属性参数变化的方法模块中,添加处理新品种对象属性的方法

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

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


这些方法允许直接从程序中设置跟踪值,而其受管控参数则相应变更。 当这样的变更注册时,我们应该在程序中获得有关事件的信号。 在开发函数库对象的交互性时,我曾研究过它们。

在受保护的参数型类构造函数中,添加并保存品种对象的新属性

   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;


调用 SymbolSwapRatio() 方法(如下所述)接收的数值会设置到对象属性数组当中。

我们在类主体之外编写已声明新方法的实现。

受保护方法返回周内指定日的掉期利率:

//+------------------------------------------------------------------+
//|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 没有这样的品种 — 返回零若是 MQL5 的情况,取决于传递给该方法的周内所在日,返回相应的品种属性。 该方法在类构造函数中为品种属性设置其对应的实数型属性的枚举值。

返回周内指定日的掉期利率的公开方法:

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


在此,根据传递给该方法的周内日,调用公开方法获取属性值,该方法返回在实数型对象属性枚举中设置的特定日的属性值。

在返回品种实数型属性描述的方法中,添加返回品种对象的新属性描述

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


在此处,我们检查终端构建序号,如果低于 3260,则通知此版本不支持该属性。 否则,我们将获得 MQL5 的属性描述,而对于 MQL4,我们会看到不支持该属性的通知。

返回周内指定日掉期利率说明的方法:

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


此处我们首先依据传递给方法的周内指定日获取其属性值,然后返回属性值的文本描述(如果值为 0、1 或 3),或显示由双精度值转换的文本,小数点后截取三位。 (只有在执行足够数量的测试后,才能定义所要显示的小数位数)。

为了将对象保存到图形对象文件(以及将来的所有函数库对象),我们应该使用对象属性结构。 所有属性都保存到结构当中,而结构本身又被保存到文件之中。 以同样的方式,从文件中读取数值来恢复对象属性。
鉴于我们有图形对象的新属性,所以亦要把它们添加到结构当中。

在 \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh 中,往对象结构里添加新属性:

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



在创建对象结构的方法中,在结构字段中编写新加的对象属性

   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



在结构外实现的创建对象的方法中,添加从结构字段读取对象属性值

   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


现在,当我们开始在文件中保存函数库对象的属性时,所有图形对象均已正确保存到文件中,并能从中还原。


我们还要对 \MQL5\Include\DoEasy\objects\Graph\WForms\WinFormBase.mqh 中的 WinForms 对象基类稍加改进。

首先,我们需要能够选择这些对象,并根据其属性对其进行排序。 为达此目标,包含 CSelect 类文件

//+------------------------------------------------------------------+
//|                                                  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"
//+------------------------------------------------------------------+


现在,我们可以在所有继承的类中使用 CSelect 类,并依据 WinForms 对象属性对其进行选择和排序。

此外,在对象重绘方法中,在处理所有附着对象的循环中添加检查重绘标志,从而仅当重绘标志被激活时,才真正重绘对象。 如果情况并非如此,则无需重新绘制绑定对象(例如,为了在更改面板对象属性时,消除视觉闪烁)。 重绘标志重置后,绑定到容器的对象不会被真正重绘,因为它们的属性是在其它方法中发生了更改,而所有对象只在此处真正重绘。

因此,添加检查标志,并仅在设置了标志时才进行对象重绘

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


现在,我们改进 \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh 中的面板 WinForms 对象类。

在类的私密部分,声明计算 Dock 对象绑定坐标的方法

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

protected:


我不会在本文中实现该方法,因此这样会令我们在类主体之外添加一个空方法:

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

在创建“文本标签”(Label)WinForms 对象类后,我再来处理其实现,这样我们可以直观地看到根据 Dock 属性值绑定到对象时的排序,同时也能正确处理绑定到其容器的对象排列位移,而该容器又连接到主容器 — 面板。

从类的受保护部分,删除已声明的,若 Dock 对象边界最大值超出容器宽度和高度,则返回该 Dock 对象的方法

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


此外,删除该方法在类主体外的实现。

现在不需要这些方法,因为我们现在可以使用 CSelect 函数库类来查找所需的值。

在类的公开部分,声明返回绑定的 WinForms 基类及更高层次对象列表的方法

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);



面板对象在创建时仍然无需边框。 如果面板需要框架,可以在创建对象后再添加框架。 因此,在类构造函数中把边框类型设置为不存在

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


因此,在类构造函数:v中将框架类型设置为不存在

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



以前,如果容器(面板)尚且设置自动调整大小标志,该方法在排布绑定对象位置时,只按照绑定对象 Dock 的顺序处理,且不包含对象位置的处理。 在本文中,我将简单地从固定面板处理程序复制绑定模式处理程序。 不过,我会初步添加对参考底图的检查,而现在将使用新方法接收对象列表,该方法只返回 WinForms 对象列表。 如果面板启用了自动调整大小模式,首先将面板大小更改为原始大小接下来,调用将面板大小调整为适配其内容的方法。 之后,在循环中,处理所有绑定对象的绑定模式,并根据其内部对象变更重新调整面板大小

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


此处,我们可以在一个循环中完成所有操作,即不必把操作拆分为两个雷同的单独循环模块,还可附带检查面板自动调整大小标志。 如此即可把代码砍掉一半。 但我决定留着它,以防将来我必须完善处理面板自动调整大小标志的代码模块。 只有当所有的东西都调试好了,并且很明显不再需要更多的更改和改进时,我才会优化这个方法的代码。

调整元素大小以适应其内容的方法也已进行了修改。现在,我们就可以利用 CSelect 类获得必要的数据

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


代码注释中完整描述了方法逻辑。 当获得最小 Y 轴坐标时,我偶尔会遇到与获得的值相冲突的奇怪行为... 第一次构造对象时,最大坐标会以最大值返回,而最小坐标则以最小值返回。 一切都是正确的。 然而,在重新排列对象后,将会在最小值返回最大坐标,而在最大值里返回最小坐标。 我还没有找到诱发这种行为的原因,我不得不在两个值之间做出选择 — 当请求最大值时,我们得到两个之中的最大值,而当请求最小值时,则得到两个值之中的最小值
为什么要计算 X 和 Y 的偏移? 当把对象绑定到面板的右侧或底部时,它们是从面板的右侧边缘或底部边缘构建的。 因此,它们也许能够超出面板左侧或顶部。 由于面板(和任何其它图形元素)的坐标原点从左上角开始,因此当增加面板大小来适配内部所有对象的大小时,面板将向右或向下延展。 因此,面板的大小将对应于内部排布的所有对象,但面板坐标原点不会与所有对象的可见原点相对应。 因此,我们需要在对象绑定模式下根据计算出的偏移量将这些对象向右或向下移动。

返回 WinForms 基类型或更高类型的附着对象列表的方法:

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


该方法简单地返回仅包含 WinForms 基类或更高级对象的列表,即基准 WinForms 对象或其衍生子对象。 对象则是利用 CSelect 类,从绑定到面板的所有对象的常规列表中类型排序来选择。

我们找到 CGraphElementsCollection 类当中创建面板 WinForms 对象的方法,执行优化并剔除其中的逻辑错误,该类位于 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh 文件中。

我犯了一个错误,因为对象边框绘制了两次,且可能没有为其设置边框颜色。 此外,如果给方法传递参数时,面板边框大小也许不会设置,而是采用默认值 -1。 我们并未检查该值,因此在设置边框大小时,采用默认值 -1 取而代之。

在所有面板创建方法中都进行了相同或雷同的修改:

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


将边框颜色设置为与面板背景颜色相同(设置为渐变的第一个颜色,或单色模式的唯一颜色)。 当设置边框大小时,检查传递给方法的数值。 如果为 -1,则默认值设置为 DEF_FRAME_WIDTH_SIZE 宏替换,该值在 Defines.mqh 中定义。 如果设置了重绘标志,则用背景色或渐变色绘制面板,并在顶部绘制一个矩形轮廓。 在此情况下,边框是在 CPanel 类的 virtual Erase() 方法中绘制的。

在创建面板的所有方法中都进行了这样的改进。 它们可以在文后所附的文件中找到。


测试

为了执行测试,我将借用前一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part106\ 中,命名为 TestDoEasyPart106.mq5

由于绑定到容器的面板对象现在默认创建时没有框架,故在 OnInit() 处理程序中为每个对象指定边框宽度和类型。 后续每个面板的颜色将根据循环索引计算的值变浅(背景颜色变浅的递减量等于循环索引 * 4)。 这是必要的,以便在改变面板的排布方式时能清晰地看到容器中面板排列的变化:

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



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


如此,面板就能适应附着到其上的对象的正常大小,不同的绑定方法均能正确工作,面板也可以正确变更其大小。 当放置具有不同绑定方法的对象时,它们的排列方式与我的不同 — 它们不应附着到面板的边缘,如果列表中的每个后续对象具有相同的绑定,则应附着到前一个对象的边缘。 特别是,最后一个对象会被拉伸铺满容器的整个宽度和高度,这是不正确的;因为它应该限制在之前绑定到面板边缘的对象的内边缘,即,它应该设置在它们之间的自由空间内。 我将在后续文章中实现绑定对象的正确行为。


下一步是什么?

在下一篇文章中,我将继续研究面板对象,并着手开发新控件,包括 WinForms 文本标签对象。

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

返回内容目录

*该系列的前几篇文章:

DoEasy. 控件 (第 1 部分): 第一步
DoEasy. 控件 (第 2 部分): 操控 CPanel 类
DoEasy. 控件 (第 3 部分): 创建绑定控件
DoEasy. 控件(第 4 部分):面板控件,Padding(填充)和 Dock(驻靠)参数
DoEasy. 控件 (第 5 部分): 基准 WinForms 对象,面板控件,AutoSize 参数


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

附加的文件 |
MQL5.zip (4351.9 KB)
数据科学与机器学习(第 03 部分):矩阵回归 数据科学与机器学习(第 03 部分):矩阵回归
这一次,我们的模型是由矩阵构建的,它更具灵活性,同时它允许我们构建更强大的模型,不仅可以处理五个独立变量,但凡我们保持在计算机的计算极限之内,它还可以处理更多变量,这篇文章肯定会是一篇阅读起来很有趣的文章。
带有图表交互控件的指标 带有图表交互控件的指标
本文提供了一个关于指标界面的新视角。 我将重点关注便利性。 多年以来,我尝试了几十种不同的交易策略,并测试过数百种不同的指标,我得出了一些结论,我想在本文中与大家分享。
神经网络变得轻松(第十六部分):聚类运用实践 神经网络变得轻松(第十六部分):聚类运用实践
在上一篇文章中,我们为数据聚类创建了一个类。 在本文中,我想分享在解决实际交易任务时应用所获结果会遇到的可能变体。
学习如何基于 OBV 设计交易系统 学习如何基于 OBV 设计交易系统
这是一篇新文章,将针对初学者继续我们的系列,介绍如何基于一些流行指标设计交易系统。 我们将学习一个新的指标,即能量潮(OBV),我们将学习如何使用并基于它来设计交易系统。