
DoEasy - コントロール(第2部):CPanelクラスでの作業
内容
概念
前回は、Windows Formsスタイルでコントロールを作成するという広範囲なトピックを開始しました。ただし、グラフィカルオブジェクトを扱い始めた頃にさかのぼり、いくつかの間違いや欠点がまだ修正されていません。例えば、チャートの時間枠を切り替えたときにグラフィカルオブジェクトがどのように動作するかは、これまでテストしたことがありません。チャート上に表示されないだけで、操作ログにはそのようなオブジェクトがすでに作成されている旨のメッセージが表示されます。したがって、それらは作成されることもなく、レンダリングされることもありません。また、問題もあります。例えば、マウスで操作できるのはフォームオブジェクトのみだということです。フォームの親であるグラフィック要素オブジェクトにはマウスで操作する機能はありません。これはどのようなグラフィカルな構成にも使用できる最小限のライブラリのグラフィカルオブジェクトであるため、この解決策は合理的です。ただし、インタラクションが必要な場合、フォームオブジェクトは最小限のグラフィカルオブジェクトとして機能する必要があります。しかし、その子孫は、前回の記事で開発に着手したPanelコントロールであって、マウスに反応するはずなのが、反応しません。これは、オブジェクトとその機能をライブラリに追加し続けているコストです。
本稿では、いくつかの欠点やエラーを修正し、引き続きPanelコントロールオブジェクトに機能を追加していく予定です。特に、すべてのパネルテキストオブジェクトにデフォルトで使用されるフォントのパラメータを設定するメソッドを実装します。例えば、CPanelクラスのオブジェクトがあるとします。それには他のコントロールを接続することができるようになります。接続されたコントロールがテキストを持つ場合、デフォルトのフォントパラメータは、それが接続されているパネルから継承されます。また、他のライブラリのグラフィック要素と同様、パネル(独立したグラフィック要素)は、それ自身の内部に任意のテキストを描画する機能を備えています。これらのテキストでは、デフォルトでフォントパラメーターも使用されます。もちろん、表示直前に他のフォントパラメータを設定することで、グラフィック要素に新しいテキストを描画することができます。テキストはこれらの明示的に指定されたパラメータで表示されます。
それに、コントロールとしてのパネルには、フレームのパラメータを制御する機能とともに、パネルフレームを表示する機能があるべきです。デフォルト値や明示的に指定されたパラメータでフレームを描画する機能と、パラメータなしで描画する機能を実装してみましょう。
ライブラリクラスの改善
グラフィック要素の様々な表示スタイルを素早く設定できるようにするために \MQL5\Include\DoEasy\GraphINI.mqhを作成しました。
このファイルには、様々な配色のパラメータや、様々なグラフィック要素の種類や表示スタイルが含まれています。後に、既存のパラメータを例にして、カスタムパラメータを追加することが可能になります。
フォームのスタイルパラメータをより視覚的に表示するために、インデックスと対応するスタイル値の順序を少し変えてみましょう。
リストの最初にあるパラメータを一番下に移動し、その所有者を設定するだけです。
//+------------------------------------------------------------------+ //| List of form style parameter indices | //+------------------------------------------------------------------+ enum ENUM_FORM_STYLE_PARAMS { //--- CForm FORM_STYLE_FRAME_SHADOW_OPACITY, // Shadow opacity FORM_STYLE_FRAME_SHADOW_BLUR, // Shadow blur FORM_STYLE_DARKENING_COLOR_FOR_SHADOW, // Form shadow color darkening FORM_STYLE_FRAME_SHADOW_X_SHIFT, // Shadow X axis shift FORM_STYLE_FRAME_SHADOW_Y_SHIFT, // Shadow Y axis shift //--- CPanel FORM_STYLE_FRAME_WIDTH_LEFT, // Panel frame width to the left FORM_STYLE_FRAME_WIDTH_RIGHT, // Panel frame width to the right FORM_STYLE_FRAME_WIDTH_TOP, // Panel frame width on top FORM_STYLE_FRAME_WIDTH_BOTTOM, // Panel frame width below }; #define TOTAL_FORM_STYLE_PARAMS (9) // Number of form style parameters //+------------------------------------------------------------------+ //| Array containing form style parameters | //+------------------------------------------------------------------+ int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]= { //--- "Flat form" style parameters { //--- CForm 80, // Shadow opacity 4, // Shadow blur 80, // Form shadow color darkening 2, // Shadow X axis shift 2, // Shadow Y axis shift //--- CPanel 3, // Panel frame width to the left 3, // Panel frame width to the right 3, // Panel frame width on top 3, // Panel frame width below }, //--- "Embossed form" style parameters { //--- CForm 80, // Shadow opacity 4, // Shadow blur 80, // Form shadow color darkening 2, // Shadow X axis shift 2, // Shadow Y axis shift //--- CPanel 3, // Panel frame width to the left 3, // Panel frame width to the right 3, // Panel frame width on top 3, // Panel frame width below }, }; //+------------------------------------------------------------------+
これは決して重要な改善点ではありませんが、パラメータを正しく構造化することで、後nに読みやすくなります。
\MQL5\Include\DoEasy\Data.mqhに、新しいメッセージインデックスを追加します。
//--- CGraphElementsCollection MSG_GRAPH_ELM_COLLECTION_ERR_OBJ_ALREADY_EXISTS, // Error. A chart control object already exists with chart id MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Failed to create chart control object with chart ID MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ, // Failed to get chart control object with chart ID MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS,// Such graphical object already exists: MSG_GRAPH_ELM_COLLECTION_ERR_EMPTY_OBJECT, // Error! Empty object MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_ELEMENT, // Failed to get a graphical element from the list
新しく追加したインデックスに対応するメッセージテキストも追加します。
//--- CGraphElementsCollection {"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "}, {"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "}, {"Не удалось получить объект управления чартами с идентификатором чарта ","Failed to get chart control object with chart id "}, {"Такой графический объект уже существует: ","Such a graphic object already exists: "}, {"Ошибка! Пустой объект","Error! Empty object"}, {"Не удалось получить графический элемент из списка","Failed to get graphic element from list"},
フラッグセットにより、フォントスタイルの管理が可能ですが、必要なフォントスタイルと幅の種類をリストから選択できるように列挙しておく必要があります。フォントスタイルや幅の種類の列挙型をメソッドに渡すと、ドロップダウンリストでスタイルや種類を選択できるため、フラグ名を覚えておくよりもずっと便利です。
また、多くの場合、パネルはオブジェクトの周囲にフレームを付けて表示されるため、デフォルトのパネルフレーム幅を指定するマクロ置換が必要です。
\MQL5\Include\DoEasy\Defines.mqhに、パネルフレーム側面のデフォルト幅を指定するマクロ置換を作成します。
//--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define CLR_CANV_NULL (0x00FFFFFF) // Zero for the canvas with the alpha channel #define CLR_FORE_COLOR (C'0x2D,0x43,0x48') // Default color for texts of objects on canvas #define DEF_FONT ("Calibri") // Default font #define DEF_FONT_SIZE (8) // Default font size #define OUTER_AREA_SIZE (16) // Size of one side of the outer area around the form workspace #define DEF_FRAME_WIDTH_SIZE (3) // Default form/panel/window frame width
ファイルの最後にはフォントスタイル列挙と幅の種類を作成します。
//+------------------------------------------------------------------+ //| Font style list | //+------------------------------------------------------------------+ enum ENUM_FONT_STYLE { FONT_STYLE_NORMAL=0, // Normal FONT_STYLE_ITALIC=FONT_ITALIC, // Italic FONT_STYLE_UNDERLINE=FONT_UNDERLINE, // Underline FONT_STYLE_STRIKEOUT=FONT_STRIKEOUT // Strikeout }; //+------------------------------------------------------------------+ //| FOnt width type list | //+------------------------------------------------------------------+ enum ENUM_FW_TYPE { FW_TYPE_DONTCARE=FW_DONTCARE, FW_TYPE_THIN=FW_THIN, FW_TYPE_EXTRALIGHT=FW_EXTRALIGHT, FW_TYPE_ULTRALIGHT=FW_ULTRALIGHT, FW_TYPE_LIGHT=FW_LIGHT, FW_TYPE_NORMAL=FW_NORMAL, FW_TYPE_REGULAR=FW_REGULAR, FW_TYPE_MEDIUM=FW_MEDIUM, FW_TYPE_SEMIBOLD=FW_SEMIBOLD, FW_TYPE_DEMIBOLD=FW_DEMIBOLD, FW_TYPE_BOLD=FW_BOLD, FW_TYPE_EXTRABOLD=FW_EXTRABOLD, FW_TYPE_ULTRABOLD=FW_ULTRABOLD, FW_TYPE_HEAVY=FW_HEAVY, FW_TYPE_BLACK=FW_BLACK }; //+------------------------------------------------------------------+
ご覧のように、各列挙定数に対する適切なフラグ値を設定しただけですが、フォントスタイルについては、斜体、下線、取り消し線のいずれでもない、「普通」のフォントの定数を新たに導入しました。この値は0で、以前に有効化された追加フォントスタイルフラグがリセットされます。
フォームオブジェクトクラスは、キャンバスベースのグラフィック要素アニメーションクラスとフォームシャドウオブジェクトを備えています。どちらのオブジェクトもnew演算子を用いて作成され、。完了すると、クラスのデストラクタで削除されます。したがって、これらのオブジェクトは常に追跡され、タイムリーに削除されます。
ただしh、フォームオブジェクトクラスを継承する際に問題が発生しました。パネルオブジェクトクラスはフォームオブジェクトから派生しています。その結果、上記のオブジェクトが削除されず、メモリリークが発生することがわかりました。前回の記事からのEAを起動することができますので、ご自身の目でご確認ください。チャートから削除すると、4つのオブジェクトが失われ、512バイトのメモリがリークしたというメッセージが操作ログに表示されます。
4 undeleted objects left 1 object of type CAnimations left 3 objects of type CArrayObj left 512 bytes of leaked memory
オブジェクトが削除されない理由を長いこと探しているのですが、わかりません。そこで、これらの作業をすべてターミナルサブシステムに割り当てることにします。
そのためには、CArrayObjクラスオブジェクトを作成し、そこにCFormクラスで作成したオブジェクトを追加すればよいのです。完了すると、ターミナルで、リストに位置するすべてのオブジェクトのメモリがクリアされます。
\MQL5\Include\DoEasy\Objects\Graph\Form.mqhを開いてこのようなリストを宣言します。
また、各フォームフレーム側の幅を格納するための変数をクラスのprotectedセクションに移動します。
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CForm : public CGCnvElement { private: CArrayObj m_list_tmp; CArrayObj m_list_elements; // List of attached elements CAnimations *m_animations; // Pointer to the animation object CShadowObj *m_shadow_obj; // Pointer to the shadow object CMouseState m_mouse; // "Mouse status" class object ENUM_MOUSE_FORM_STATE m_mouse_form_state; // Mouse status relative to the form ushort m_mouse_state_flags; // Mouse status flags color m_color_frame; // Form frame color int m_offset_x; // Offset of the X coordinate relative to the cursor int m_offset_y; // Offset of the Y coordinate relative to the cursor //--- Reset the array size of (1) text, (2) rectangular and (3) geometric animation frames void ResetArrayFrameT(void); void ResetArrayFrameQ(void); void ResetArrayFrameG(void); //--- Return the name of the dependent object string CreateNameDependentObject(const string base_name) const { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name; } //--- Create a new graphical object CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity); //--- Create a shadow object void CreateShadowObj(const color colour,const uchar opacity); protected: int m_frame_width_left; // Form frame width to the left int m_frame_width_right; // Form frame width to the right int m_frame_width_top; // Form frame width at the top int m_frame_width_bottom; // Form frame width at the bottom //--- Initialize the variables void Initialize(void); void Deinitialize(void); public:
変数は派生クラスからアクセスされるので、変数はクラスと派生クラスで同様に見えるようにprotectedセクションに格納されなければなりません。
初期化メソッドで、新しいリストをクリアし、そのリストに並び替え済みリストフラグを設定します。フォームフレームの各辺の幅について、新しいマクロ置換値を設定し、新しいリストにアニメーションオブジェクトを追加します。
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CForm::Initialize(void) { this.m_list_elements.Clear(); this.m_list_elements.Sort(); this.m_list_tmp.Clear(); this.m_list_tmp.Sort(); this.m_shadow_obj=NULL; this.m_shadow=false; this.m_frame_width_right=DEF_FRAME_WIDTH_SIZE; this.m_frame_width_left=DEF_FRAME_WIDTH_SIZE; this.m_frame_width_top=DEF_FRAME_WIDTH_SIZE; this.m_frame_width_bottom=DEF_FRAME_WIDTH_SIZE; this.m_mouse_state_flags=0; this.m_offset_x=0; this.m_offset_y=0; CGCnvElement::SetInteraction(false); this.m_animations=new CAnimations(CGCnvElement::GetObject()); this.m_list_tmp.Add(m_animations); } //+------------------------------------------------------------------+
シャドーオブジェクトを作成するメソッドでは、オブジェクトが正常に作成された後、新しいリストにオブジェクトを追加します。
//+------------------------------------------------------------------+ //| Create the shadow object | //+------------------------------------------------------------------+ void CForm::CreateShadowObj(const color colour,const uchar opacity) { //--- ... //--- ... //--- Create a new shadow object and set the pointer to it in the variable this.m_shadow_obj=new CShadowObj(this.ChartID(),this.SubWindow(),this.CreateNameDependentObject("Shadow"),x,y,w,h); if(this.m_shadow_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ)); return; } this.m_list_tmp.Add(m_shadow_obj); //--- ... //--- Move the form object to the foreground this.BringToTop(); } //+------------------------------------------------------------------+
この改善により、制御不能で発見が困難なメモリリークを回避することができます。
引き続き、WinFormsのCPanelコントロールオブジェクトのクラスについて説明します。
パネルフォントのパラメータとそのフレームの処理を実装をしてみましょう。
\MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqhにあるクラスのprivateセクションで、フォントの幅の種類を格納する変数と、フォントに設定されたフラグを返すメソッドを宣言します。
//+------------------------------------------------------------------+ //| Panel object class of WForms controls | //+------------------------------------------------------------------+ class CPanel : public CForm { private: color m_fore_color; // Default text color for all panel objects ENUM_FW_TYPE m_bold_type; // Font width type ENUM_FRAME_STYLE m_border_style; // Panel frame style bool m_autoscroll; // Auto scrollbar flag int m_autoscroll_margin[2]; // Array of fields around the control during an auto scroll bool m_autosize; // Flag of the element auto resizing depending on the content ENUM_CANV_ELEMENT_AUTO_SIZE_MODE m_autosize_mode; // Mode of the element auto resizing depending on the content ENUM_CANV_ELEMENT_DOCK_MODE m_dock_mode; // Mode of binding element borders to the container int m_margin[4]; // Array of gaps of all sides between the fields of the current and adjacent controls int m_padding[4]; // Array of gaps of all sides inside controls //--- Return the font flags uint GetFontFlags(void); public:
クラスにフォント幅タイプの値を設定する場合、その値はm_bold_type変数で指定します。
フォントのフラグを返すメソッドでは、それに設定されているすべてのパラメータ(名前、サイズ、フラグ、角度)を返します。ここではフラグだけを扱うので、各メソッドでフォント関連の値を含むローカル変数を宣言しないように、CCanvasクラスのフォントプロパティから取得したフラグだけを返すメソッドを呼び出すことにします。
クラスのpublicセクションで、フォントスタイルのフラグとフォント幅の種類を処理するメソッドを宣言します。
public: //--- (1) Set and (2) return the default text color of all panel objects void ForeColor(const color clr) { this.m_fore_color=clr; } color ForeColor(void) const { return this.m_fore_color; } //--- (1) Set and (2) return the Bold font flag void Bold(const bool flag); bool Bold(void); //--- (1) Set and (2) return the Italic font flag void Italic(const bool flag); bool Italic(void); //--- (1) Set and (2) return the Strikeout font flag void Strikeout(const bool flag); bool Strikeout(void); //--- (1) Set and (2) return the Underline font flag void Underline(const bool flag); bool Underline(void); //--- (1) Set and (2) return the font style void FontDrawStyle(ENUM_FONT_STYLE style); ENUM_FONT_STYLE FontDrawStyle(void); //--- (1) Set and (2) return the font width type void FontBoldType(ENUM_FW_TYPE type); ENUM_FW_TYPE FontBoldType(void) const { return this.m_bold_type; } //--- (1) Set and (2) return the frame style
...
パネルフレームのプロパティを設定したり、返したりするメソッドを記述します。
//--- Return the gap (1) to the left, (2) at the top, (3) to the right and (4) at the bottom between the fields inside the control int PaddingLeft(void) const { return this.m_padding[0]; } int PaddingTop(void) const { return this.m_padding[1]; } int PaddingRight(void) const { return this.m_padding[2]; } int PaddingBottom(void) const { return this.m_padding[3]; } //--- Set the width of the form frame (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides of the control void FrameWidthLeft(const int value) { this.m_frame_width_left=value; } void FrameWidthTop(const int value) { this.m_frame_width_top=value; } void FrameWidthRight(const int value) { this.m_frame_width_right=value; } void FrameWidthBottom(const int value) { this.m_frame_width_bottom=value; } void FrameWidthAll(const int value) { this.FrameWidthLeft(value); this.FrameWidthTop(value); this.FrameWidthRight(value); this.FrameWidthBottom(value); } //--- Return the width of the form frame (1) to the left, (2) at the top, (3) to the right and (4) at the bottom int FrameWidthLeft(void) const { return this.m_frame_width_left; } int FrameWidthTop(void) const { return this.m_frame_width_top; } int FrameWidthRight(void) const { return this.m_frame_width_right; } int FrameWidthBottom(void) const { return this.m_frame_width_bottom; } //--- Constructors
各コンストラクタで、デフォルトのフォント幅の種類を設定するようにします。
CPanel(const string name) : CForm(::ChartID(),0,name,0,0,0,0) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL); this.m_type=OBJECT_DE_TYPE_GWF_PANEL; this.m_fore_color=CLR_FORE_COLOR; this.m_bold_type=FW_TYPE_NORMAL; this.MarginAll(3); this.PaddingAll(0); this.Initialize(); } //--- 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) : CForm(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL); this.m_type=OBJECT_DE_TYPE_GWF_PANEL; this.m_fore_color=CLR_FORE_COLOR; this.m_bold_type=FW_TYPE_NORMAL; this.MarginAll(3); this.PaddingAll(0); this.Initialize(); } //+------------------------------------------------------------------+ //| Current chart constructor specifying the subwindow | //+------------------------------------------------------------------+ CPanel::CPanel(const int subwindow, const string name, const int x, const int y, const int w, const int h) : CForm(::ChartID(),subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL); this.m_type=OBJECT_DE_TYPE_GWF_PANEL; this.m_fore_color=CLR_FORE_COLOR; this.m_bold_type=FW_TYPE_NORMAL; this.MarginAll(3); this.PaddingAll(0); this.Initialize(); } //+------------------------------------------------------------------+ //| Constructor on the current chart in the main chart window | //+------------------------------------------------------------------+ CPanel::CPanel(const string name, const int x, const int y, const int w, const int h) : CForm(::ChartID(),0,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_PANEL); this.m_type=OBJECT_DE_TYPE_GWF_PANEL; this.m_fore_color=CLR_FORE_COLOR; this.m_bold_type=FW_TYPE_NORMAL; this.MarginAll(3); this.PaddingAll(0); this.Initialize(); } //+------------------------------------------------------------------+
タイプはデフォルトでパネルフォントに設定されています。
以下は、フォントフラグを返すprivateソッドです。
//+------------------------------------------------------------------+ //| Return the font flags | //+------------------------------------------------------------------+ uint CPanel::GetFontFlags(void) { string name; int size; uint flags; uint angle; CGCnvElement::GetFont(name,size,flags,angle); return flags; } //+------------------------------------------------------------------+
グラフィック要素クラスのGetFont()メソッドはリンクで変数を受け取る必要がありますが、渡される変数にはCCanvasクラスのフォントパラメータから得た値が入るため、ここでは必要な変数をすべて宣言し、GetFont()メソッドを呼び出してその値を取得してて、得られたフラグのみを返します。
以下は、Boldフォントフラグを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the Bold font flag | //+------------------------------------------------------------------+ void CPanel::Bold(const bool flag) { uint flags=this.GetFontFlags(); if(flag) { this.m_bold_type=FW_TYPE_BOLD; CGCnvElement::SetFontFlags(flags | FW_BOLD); } else this.m_bold_type=FW_TYPE_NORMAL; } //+------------------------------------------------------------------+
ここでは、上で述べたGetFontFlags()メソッドを使ってフラグを取得しています。メソッドの引数で渡されたフラグが設定されている場合、フォントの幅の種類を格納するm_bold_type変数にBold 値を書き込み、さらに別のフラグFW_BOLDをフォントフラグに設定します。
メソッドの引数で渡されたフラグが設定されていない場合、変数 m_bold_typeはデフォルト値を受け取ります。
以下は、Boldフォントフラグを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the Bold font flag | //+------------------------------------------------------------------+ bool CPanel::Bold(void) { uint flags=this.GetFontFlags(); return(flags &FW_BOLD)==FW_BOLD; } //+------------------------------------------------------------------+
ここでは、GetFontFlags()メソッドでフラグを取得し、FW_BOLDフラグが変数に存在するかどうかを確認した結果を返しています。
残りのフォントフラグを設定するメソッドと返すメソッドは、フラグ値変数への設定を必要としない点では若干異なりますが、
その他の点では、上記で指定したものと同じです。
//+------------------------------------------------------------------+ //| Set the Italic font flag | //+------------------------------------------------------------------+ void CPanel::Italic(const bool flag) { uint flags=this.GetFontFlags(); if(flag) CGCnvElement::SetFontFlags(flags | FONT_ITALIC); } //+------------------------------------------------------------------+ //| Return the Italic font flag | //+------------------------------------------------------------------+ bool CPanel::Italic(void) { uint flags=this.GetFontFlags(); return(flags &FONT_ITALIC)==FONT_ITALIC; } //+------------------------------------------------------------------+ //| Set the Strikeout font flag | //+------------------------------------------------------------------+ void CPanel::Strikeout(const bool flag) { uint flags=this.GetFontFlags(); if(flag) CGCnvElement::SetFontFlags(flags | FONT_STRIKEOUT); } //+------------------------------------------------------------------+ //| Return the Strikeout font flag | //+------------------------------------------------------------------+ bool CPanel::Strikeout(void) { uint flags=this.GetFontFlags(); return(flags &FONT_STRIKEOUT)==FONT_STRIKEOUT; } //+------------------------------------------------------------------+ //| Set the Underline font flag | //+------------------------------------------------------------------+ void CPanel::Underline(const bool flag) { uint flags=this.GetFontFlags(); if(flag) CGCnvElement::SetFontFlags(flags | FONT_UNDERLINE); } //+------------------------------------------------------------------+ //| Return the Underline font flag | //+------------------------------------------------------------------+ bool CPanel::Underline(void) { uint flags=this.GetFontFlags(); return(flags &FONT_UNDERLINE)==FONT_UNDERLINE; } //+------------------------------------------------------------------+
これらのメソッドは明快で、説明の必要はないと思います。
以下は、フォントのスタイルを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the font style | //+------------------------------------------------------------------+ void CPanel::FontDrawStyle(ENUM_FONT_STYLE style) { switch(style) { case FONT_STYLE_ITALIC : this.Italic(true); break; case FONT_STYLE_UNDERLINE : this.Underline(true); break; case FONT_STYLE_STRIKEOUT : this.Strikeout(true); break; default: break; } } //+------------------------------------------------------------------+
メソッドに渡されたフォントのスタイル(斜体、下線、取り消し線)に応じて、そのスタイルに対応するインストールメソッドを呼び出します。
以下は、フォントスタイルを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the font style | //+------------------------------------------------------------------+ ENUM_FONT_STYLE CPanel::FontDrawStyle(void) { return ( this.Italic() ? FONT_STYLE_ITALIC : this.Underline() ? FONT_STYLE_UNDERLINE : this.Strikeout() ? FONT_STYLE_UNDERLINE : FONT_STYLE_NORMAL ); } //+------------------------------------------------------------------+
適切なメソッドによって返されるフォントスタイルに応じて、同じフォントスタイルが返されます。
3つのスタイルのいずれも設定されていない場合は、Normalが返されます。
以下は、フォント幅の種類を返すメソッドです。
//+------------------------------------------------------------------+ //| Set the font width type | //+------------------------------------------------------------------+ void CPanel::FontBoldType(ENUM_FW_TYPE type) { this.m_bold_type=type; uint flags=this.GetFontFlags(); switch(type) { case FW_TYPE_DONTCARE : CGCnvElement::SetFontFlags(flags | FW_DONTCARE); break; case FW_TYPE_THIN : CGCnvElement::SetFontFlags(flags | FW_THIN); break; case FW_TYPE_EXTRALIGHT : CGCnvElement::SetFontFlags(flags | FW_EXTRALIGHT); break; case FW_TYPE_ULTRALIGHT : CGCnvElement::SetFontFlags(flags | FW_ULTRALIGHT); break; case FW_TYPE_LIGHT : CGCnvElement::SetFontFlags(flags | FW_LIGHT); break; case FW_TYPE_REGULAR : CGCnvElement::SetFontFlags(flags | FW_REGULAR); break; case FW_TYPE_MEDIUM : CGCnvElement::SetFontFlags(flags | FW_MEDIUM); break; case FW_TYPE_SEMIBOLD : CGCnvElement::SetFontFlags(flags | FW_SEMIBOLD); break; case FW_TYPE_DEMIBOLD : CGCnvElement::SetFontFlags(flags | FW_DEMIBOLD); break; case FW_TYPE_BOLD : CGCnvElement::SetFontFlags(flags | FW_BOLD); break; case FW_TYPE_EXTRABOLD : CGCnvElement::SetFontFlags(flags | FW_EXTRABOLD); break; case FW_TYPE_ULTRABOLD : CGCnvElement::SetFontFlags(flags | FW_ULTRABOLD); break; case FW_TYPE_HEAVY : CGCnvElement::SetFontFlags(flags | FW_HEAVY); break; case FW_TYPE_BLACK : CGCnvElement::SetFontFlags(flags | FW_BLACK); break; default : CGCnvElement::SetFontFlags(flags | FW_NORMAL); break; } } //+------------------------------------------------------------------+
ここで、m_bold_type変数に、メソッドに渡された値を設定します。GetFontFlags()メソッドを使用してフォントフラグを取得します。
メソッドに渡されたフォント幅フラグに応じて、指定されたタイプに対応する別のフラグを、フォントフラグで取得した変数に追加します。
その結果、 m_bold_typeにはメソッドに渡された列挙値が、フォントのフラグには列挙値ENUM_FW_TYPE に対応するフォント幅タイプのフラグが指定されます。
グラフィック要素オブジェクトをコレクションリストに追加する場合、まずリスト内での存在を確認します。もし、そのようなオブジェクトが既に存在する場合は、追加せず、操作ログでその旨を通知し、追加エラーを返すか、あるいは、エラーの代わりに既存のオブジェクトへのポインタを返して追加エラーを返すかのどちらかをおこないます。これは、動的にオブジェクトを作成する場合、全く同じオブジェクトを作成しようとしたときに、動的に作成されたが隠されていたオブジェクトをメソッドから返し、チャート上に表示するために有用です。
グラフィック要素をリストに追加するメソッドから様々なリターンコードを返すための列挙体を作ってみましょう。
グラフィック要素コレクションクラスのファイルである \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhには、クラスを宣言する前に次の列挙を記述します。
//+------------------------------------------------------------------+ //| Collection of graphical objects | //+------------------------------------------------------------------+ #resource "\\"+PATH_TO_EVENT_CTRL_IND; // Indicator for controlling graphical object events packed into the program resources enum ENUM_ADD_OBJ_RET_CODE // Enumerate the codes of returning the method for adding an object to the list { ADD_OBJ_RET_CODE_SUCCESS, // Successful ADD_OBJ_RET_CODE_EXIST, // Object exists in the collection list ADD_OBJ_RET_CODE_ERROR, // Failed to add to the collection list }; class CGraphElementsCollection : public CBaseObj
ここには3つのリターンコードがあります。
- オブジェクトが正常にリストに追加された
- オブジェクトがすでにコレクションリストに存在している
- オブジェクトをコレクションリストに追加できなかった
これで、必要な結果を柔軟に返すことができるようになります。唯一可能なエラーは、リストにオブジェクトを追加できないことです。その他の結果では、作業を継続する可能性が示されています。オブジェクトが作成されてコレクションに追加されているか、そのようなオブジェクトがすでに存在し、それを処理できるかのどちらかです。
コレクションクラスから直接グラフィック要素を作成するためには、その名前に接頭辞(プログラム名とアンダースコア)を付ける必要があります。クラスのprivateセクションで、グラフィカルオブジェクトの接頭辞を格納するための変数を宣言します。
class CGraphElementsCollection : public CBaseObj { private: //--- ... bool m_is_graph_obj_event; // Event flag in the list of graphical objects int m_total_objects; // Number of graphical objects int m_delta_graph_obj; // Difference in the number of graphical objects compared to the previous check string m_name_prefix; // Object name prefix //--- Return the flag indicating the graphical element class object presence in the collection list of graphical elements
また、新しいグラフィック要素を作成するメソッド、または既存の要素のIDを返すメソッドのいずれか
およびコレクションリスト中の指定されたグラフィック要素のインデックスを返すメソッドの2つのprivateメソッドを追加します。
//--- Reset all interaction flags for all forms except the specified one void ResetAllInteractionExeptOne(CGCnvElement *form); //--- Add the element to the collection list bool AddCanvElmToCollection(CGCnvElement *element); //--- Add the element to the collectionl ist or return the existing one ENUM_ADD_OBJ_RET_CODE AddOrGetCanvElmToCollection(CGCnvElement *element,int &id); //--- Return the graphical elemnt index in the collection list int GetIndexGraphElement(const long chart_id,const string name);
チャートIDとオブジェクト名でグラフィック要素リストを返すメソッドに修正が必要です。問題は、すべてのライブラリのグラフィカルオブジェクトの名前がプログラム名から始まるということです。名前はグラフィカルオブジェクト名の接頭辞で設定されます。単にオブジェクト名を検索メソッドに渡しただけでは、そのようなオブジェクトは見つかりません。これは、グラフィカルオブジェクトにも接頭辞がある一方、メソッドに渡された名前を検索しているためです。そのため、検索名に名前の接頭辞があるかどうかを確認し、接頭辞がない場合は、検索対象のオブジェクトの名前に追加する必要があるのです。そうすれば、検索は常に正しくおこなわれます。検索された名前にすでに接頭辞がある場合は、名前には何も追加されず、メソッドに渡された値が検索されます。接頭辞がない場合は、名前に追加して検索します。
クラスのpublicセクションで、チャートIDとオブジェクト名でグラフィック要素のリストを返すメソッドを探し、それに上記の改良を加えます。
//--- Return the list of graphical elements by chart ID and object name CArrayObj *GetListCanvElementByName(const long chart_id,const string name) { string nm=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name; CArrayObj *list=CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL); return CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,nm,EQUAL); }
ここでは、検索する名前を宣言し、その名前に接頭辞が含まれているかどうかを調べ、含まれていなければ接頭辞を追加します。それ以外の場合は、何も追加しません。
次に、チャートIDによるグラフィック要素リストを取得し、検索したオブジェクト名で並び替えたリストを返します。オブジェクトが見つからない場合、このメソッドはNULLを返します。
さらに別のメソッドを書いて、チャートと名前のIDでグラフィック要素を返すようにしましょう。
//--- Return the graphical element by chart ID and name CGCnvElement *GetCanvElement(const long chart_id,const string name) { CArrayObj *list=this.GetListCanvElementByName(chart_id,name); return(list!=NULL ? list.At(0) : NULL); } //--- Constructor
ここでは、チャートIDとオブジェクト名でグラフィック要素のリストを取得します。リストを取得した場合、その中に位置する唯一のオブジェクトへのポインタを返します。それ以外の場合はNULLを返します。
時間枠を切り替える際、すべてのGUI要素を再作成するために、グラフィック要素コレクションリストをクリアする必要があります。すべてのリストをクリアする機能を持つクラスデストラクタはプログラムをチャートから削除するときにのみ呼び出されるので、OnDeinit()ハンドラでクリアを実装する必要があります。宣言します。
//--- Update the list of (1) all graphical objects, (2) on the specified chart, fill in the data on the number of new ones and set the event flag void Refresh(void); void Refresh(const long chart_id); //--- Event handlers void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); void OnDeinit(void); private: //--- Move all objects on canvas to the foreground void BringToTopAllCanvElm(void);
グラフィック要素を作成する各メソッドで、このコードブロックを置き換えます。
if(!this.AddCanvElmToCollection(obj)) { delete obj; return WRONG_VALUE; }
新しいコードは以下です。
//--- Create a graphical element object on canvas on a specified chart and subwindow int CreateElement(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h, const color clr, const uchar opacity, const bool movable, const bool activity, const bool redraw=false) { int id=this.m_list_all_canv_elm_obj.Total(); CGCnvElement *obj=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,id,0,chart_id,subwindow,name,x,y,w,h,clr,opacity,movable,activity,redraw); ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id); if(res==ADD_OBJ_RET_CODE_ERROR) return WRONG_VALUE; if(res==ADD_OBJ_RET_CODE_EXIST) obj.SetID(id); obj.Erase(clr,opacity,redraw); return obj.ID(); } //--- Create a graphical element object on canvas on a specified chart and subwindow with the vertical gradient filling
ここではまず、新しいグラフィック要素を作成するメソッド、または既存の要素のIDを返すメソッドからリターンコードを取得します。次に、このメソッドがエラーを返した場合、-1を返します。リターンコードがオブジェクトの存在を示す場合、AddOrGetCanvElmToCollection()メソッドから受け取ったIDを、新たに作成したオブジェクトのプロパティに設定します。ポイントは、新しいオブジェクトのIDには最大値(リスト内のオブジェクトの数)を初期設定するということです。既に存在するオブジェクトのIDは別のものにします。この場合、オブジェクトをリストに追加するメソッドや既存のオブジェクトを返すメソッドにリンクで渡された変数にIDが設定されます。
したがって、リストに新しいオブジェクトを追加するか、既存のオブジェクトへのポインタを取得してそこでそのIDを設定することになります。
フォームオブジェクトとパネルの作成メソッドではコードブロックが若干異なります。
//--- Create a graphical object form object on canvas on a specified chart and subwindow int CreateForm(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h, const color clr, const uchar opacity, const bool movable, const bool activity, const bool shadow=false, const bool redraw=false) { int id=this.m_list_all_canv_elm_obj.Total(); CForm *obj=new CForm(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.SetColorBackground(clr); obj.SetColorFrame(clr); obj.SetOpacity(opacity,false); obj.SetShadow(shadow); obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity()); obj.Done(); obj.Erase(clr,opacity,redraw); return obj.ID(); } //--- Create a graphical object form object on canvas on a specified chart and subwindow with the vertical gradient filling
この場合はもっとシンプルです。エラーを受信した場合は、-1を返します。IDは、新しいオブジェクトの作成メソッドで指定されるのではなく、クラスのコンストラクタで割り当てられるため、復元しません。
パネルオブジェクトを作成するメソッドで、パネルフレームの幅と 種類を追加します。
//--- Create graphical object WinForms Panel object on canvas on a specified chart and subwindow int CreatePanel(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h, const 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.SetColorBackground(clr); obj.SetColorFrame(clr); obj.SetOpacity(opacity,false); obj.SetShadow(shadow); obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.ColorFrame(),obj.Opacity()); obj.Erase(clr,opacity,redraw); if(frame_width>0) obj.FrameWidthAll(frame_width); obj.SetActiveAreaShift(obj.FrameWidthLeft(),obj.FrameWidthBottom(),obj.FrameWidthRight(),obj.FrameWidthTop()); obj.DrawFormFrame(obj.FrameWidthTop(),obj.FrameWidthBottom(),obj.FrameWidthLeft(),obj.FrameWidthRight(),obj.ColorFrame(),obj.Opacity(),frame_style); obj.Done(); return obj.ID(); }
メソッドの本体で、パネルを色で塗りつぶします。フレームの幅が0を超える場合は、フレームの全辺の幅をパネルのプロパティに設定し、アクティブなパネル領域をフレーム内に設定し、フレームを描画してパネルの外観を保存します。
クラスのコンストラクタで、グラフィカルオブジェクト名の接頭辞の値を設定し、すべてのグラフィック要素のリストをクリアし、それに対する並び替えリストフラグを設定します。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGraphElementsCollection::CGraphElementsCollection() { this.m_type=COLLECTION_GRAPH_OBJ_ID; this.m_name_prefix=this.m_name_program+"_"; ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true); ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true); this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID); this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID); this.m_list_all_graph_obj.Clear(); this.m_list_charts_control.Sort(); this.m_list_charts_control.Clear(); this.m_total_objects=0; this.m_is_graph_obj_event=false; this.m_list_deleted_obj.Clear(); this.m_list_deleted_obj.Sort(); this.m_list_all_canv_elm_obj.Clear(); this.m_list_all_canv_elm_obj.Sort(); } //+------------------------------------------------------------------+
以下は、キャンバス上のグラフィック要素をコレクションに追加するメソッドです。
//+------------------------------------------------------------------+ //| Add the graphical element on canvas to the collection | //+------------------------------------------------------------------+ bool CGraphElementsCollection::AddCanvElmToCollection(CGCnvElement *element) { if(!this.m_list_all_canv_elm_obj.Add(element)) { CMessage::ToLog(DFUN+element.Name()+": ",MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); return false; } return true; } //+------------------------------------------------------------------+
リストへの要素の追加に失敗した場合、その旨を操作ログに記載し、falseを返します。それ以外の場合は、trueを返します。
以下は、コレクションリストに要素を追加するメソッド、または既存のものを返すメソッドです。
//+------------------------------------------------------------------+ //| Add the element to the collectionl ist or return the existing one| //+------------------------------------------------------------------+ ENUM_ADD_OBJ_RET_CODE CGraphElementsCollection::AddOrGetCanvElmToCollection(CGCnvElement *element,int &id) { //--- If an invalid pointer is passed, notify of that and return the error code if(element==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_ELM_COLLECTION_ERR_EMPTY_OBJECT); return ADD_OBJ_RET_CODE_ERROR; } //--- If the graphical element with a specified chart ID and name is already present, if(this.IsPresentCanvElmInList(element.ChartID(),element.Name())) { //--- inform of the object existence, CMessage::ToLog(DFUN+element.Name()+": ",MSG_LIB_SYS_OBJ_ALREADY_IN_LIST); //--- get the element from the collection list. element=this.GetCanvElement(element.ChartID(),element.Name()); //--- If failed to get the object, inform of that and return the error code if(element==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_ELEMENT); return ADD_OBJ_RET_CODE_ERROR; } //--- set the ID of the object obtained from the list to the value returned by the link //--- and return the object existence code in the list id=element.ID(); return ADD_OBJ_RET_CODE_EXIST; } //--- If failed to add the object to the list, remove it and return the error code if(!this.AddCanvElmToCollection(element)) { delete element; return ADD_OBJ_RET_CODE_ERROR; } //--- All is successful return ADD_OBJ_RET_CODE_SUCCESS; } //+------------------------------------------------------------------+
各メソッドのコードについては、コメントで詳しく説明していますので、そちらをご覧いただければと思います。簡単に説明すると、このメソッドでは新しく作成されたオブジェクトへのポインタを受け取ります。そのようなオブジェクトがリストに存在する場合、(既存のリストオブジェクトへの)ポインタをメソッドに渡されたポインタに割り当てます。既存のオブジェクトIDは、リンクでメソッドに渡された変数に設定されます。外から見ると、この変数にオブジェクトIDが割り当てられています。リストにそのようなオブジェクトがなければ、追加して操作の成功フラグを返します。
以下は、コレクションリスト内のグラフィック要素のインデックスを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the graphical elemnt index in the collection list | //+------------------------------------------------------------------+ int CGraphElementsCollection::GetIndexGraphElement(const long chart_id,const string name) { for(int i=0;i<this.m_list_all_canv_elm_obj.Total();i++) { CGCnvElement *obj=this.m_list_all_canv_elm_obj.At(i); if(obj==NULL) continue; if(obj.ChartID()==chart_id && obj.Name()==name) return i; } return WRONG_VALUE; } //+------------------------------------------------------------------+
ここでは、すべてのグラフィック要素によってループ内の次のオブジェクトを取得します。チャートIDと名前がメソッドに渡されたものと等しい場合、ループインデックスが返されます。ループが完了すると、-1が返されます。
ここで、CSelectライブラリクラスを使った高速検索を適用しない理由を明らかにしておきます。コレクション全体の完全なリストからオブジェクトのインデックスを探しているのですが、実は、プロパティでリストを並び替えると新しいリストが作成され、コレクションリストからではなく、並び替えられたリストからオブジェクトのインデックスが取得されてしまいます。当然ながら、ほとんどの場合、両者のインデックスは一致しません。
同じ理由で、コレクションに存在するがチャート上に存在しないオブジェクトを見つけるメソッドでのエラーも修正することにします。また、このメソッドでは、オブジェクトへのポインタとリスト内のオブジェクトインデックスを返します。
//+------------------------------------------------------------------+ //|Find an object present in the collection but not on a chart | //| Return the pointer to the object and its index in the list. | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::FindMissingObj(const long chart_id,int &index) { index=WRONG_VALUE; for(int i=0;i<this.m_list_all_graph_obj.Total();i++) { CGStdGraphObj *obj=this.m_list_all_graph_obj.At(i); if(obj==NULL) continue; if(!this.IsPresentGraphObjOnChart(obj.ChartID(),obj.Name())) { index=i; return obj; } } return NULL; } //+------------------------------------------------------------------+
以前は、まずチャートIDでオブジェクトリストを受け取り、できたリストを反復処理していました。これは誤りでした。
コレクションリスト全体を反復処理して、それに応じてリスト内の正しいオブジェクトインデックスを取得するように実装しています。
前回までのEAでは、マウスで操作できるのはフォームオブジェクトのみでした。
グラフィック要素オブジェクトは、マウスとの自動的な相互作用を持つべきではありませんが、フォームオブジェクトから派生するすべてのオブジェクトは、マウスイベントの処理をフォームオブジェクトから継承する必要があります。イベントハンドラでグラフィック要素の種類を厳密にチェックしたことに誤りがあったのです。フォームでない場合、イベントは処理されませんでした。
このチェックを変更します。グラフィック要素タイプがフォームまたは継承階層に沿ったそれ以上の要素である場合、そのようなオブジェクトはイベントハンドラで処理されなければなりません。
これを修正しましょう。
GetFormUnderCursor()メソッドに、以下の変更を追加します。
//--- If managed to obtain the list and it is not empty, if(list!=NULL && list.Total()>0) { //--- Get the only graphical element there elm=list.At(0); //--- If the element is a form object or its descendants if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_FORM) { //--- Assign the pointer to the element for the form object pointer form=elm; //--- Get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is within the form, return the pointer to the form if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) return form; } } //--- If there is no a single form object with a specified interaction flag, //--- in the loop by all graphical element collection class objects int total=this.m_list_all_canv_elm_obj.Total(); for(int i=0;i<total;i++) { //--- get the next element elm=this.m_list_all_canv_elm_obj.At(i); if(elm==NULL) continue; //--- if the obtained element is a form object or its descendants if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_FORM) { //--- Assign the pointer to the element for the form object pointer form=elm; //--- Get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is within the form, return the pointer to the form if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) return form; } } //--- If there is no a single form object from the collection list //--- Get the list of extended standard graphical objects
以前は、等価比較(==)がここにありました。以上(>=)にします。グラフィック要素タイプ列挙定数のすべての値は昇順であるため、後続のすべてのタイプはGRAPH_ELEMENT_TYPE_FORM定数の値より大きい定数値を持つことになります。
また、指定したフォーム以外のすべてのフォームのインタラクションフラグをリセットするメソッドに変更するようにします。
//+--------------------------------------------------------------------+ //| Reset all interaction flags for all forms except the specified one | //+--------------------------------------------------------------------+ void CGraphElementsCollection::ResetAllInteractionExeptOne(CGCnvElement *form_exept) { //--- In the loop by all graphical element collection class objects int total=this.m_list_all_canv_elm_obj.Total(); for(int i=0;i<total;i++) { //--- get the pointer to the object CGCnvElement *obj=this.m_list_all_canv_elm_obj.At(i); //--- if failed to receive the pointer, or it is not a form or its descendants, or it is not a form whose pointer has been passed to the method, move on if(obj==NULL || obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_FORM || (obj.Name()==form_exept.Name() && obj.ChartID()==form_exept.ChartID())) continue; //--- Reset the interaction flag for the current form in the loop obj.SetInteraction(false); } } //+------------------------------------------------------------------+
以前はここで不等号(!=)の比較があり、フォームオブジェクトを除くすべてのオブジェクトがスキップされていました。これで、フォームオブジェクトの下の階層にあるすべてのオブジェクトがスキップされるようになります。
SetZOrderMAX()メソッドで、グラフィカルオブジェクトに表示されるテストテキストを少し変更し(テキストはテストEAでも変更されるため)、オブジェクトがマウスと相互作用するのを妨げるエラーを修正します。
//+------------------------------------------------------------------+ //| Set ZOrde to the specified element | //| and adjust it in other elements | //+------------------------------------------------------------------+ bool CGraphElementsCollection::SetZOrderMAX(CGCnvElement *obj) { //--- ... //--- Temporarily declare a form object for drawing a text for visually displaying its ZOrder CForm *form=obj; //--- and draw a text specifying ZOrder on the form form.TextOnBG(0,form.TypeElementDescription()+": ID "+(string)form.ID()+", ZD "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); //--- Sort the list of graphical elements by an element ID this.m_list_all_canv_elm_obj.Sort(SORT_BY_CANV_ELEMENT_ID); //--- ... //--- ... //--- In the loop by the obtained list of remaining graphical element objects for(int i=0;i<list.Total();i++) { //--- get the next object CGCnvElement *elm=list.At(i); //--- If failed to get the object or if this is a control form for managing pivot points of an extended standard graphical object //--- or, if the object's ZOrder is zero, skip the object since there is no need in changing its ZOrder as it is the bottom one if(elm==NULL || elm.Type()==OBJECT_DE_TYPE_GFORM_CONTROL || elm.Zorder()==0) continue; //--- If failed to set the object's ZOrder to 1 less than it already is (decreasing ZOrder by 1), add 'false' to the 'res' value if(!elm.SetZorder(elm.Zorder()-1,false)) res &=false; //--- Temporarily (for the test purpose), if the element is a form, if(elm.Type()>=OBJECT_DE_TYPE_GFORM) { //--- assign the pointer to the element for the form and draw a text specifying ZOrder on the form form=elm; form.TextOnBG(0,form.TypeElementDescription()+": ID "+(string)form.ID()+", ZD "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); } } //--- Upon the loop completion, return the result set in 'res' return res; } //+------------------------------------------------------------------+
初期化解除イベントハンドラを記述します。
//+------------------------------------------------------------------+ //| Deinitialization event handler | //+------------------------------------------------------------------+ void CGraphElementsCollection::OnDeinit(void) { this.m_list_all_canv_elm_obj.Clear(); } //+------------------------------------------------------------------+
ここではすべてが簡単です。グラフィック要素のコレクションリストをクリアします。
そのため、このメソッドは、\MQL5\Include\DoEasy\Engine.mqhにあるCEngineライブラリのメインオブジェクトから呼び出す必要があります。
クラスファイルを開き、OnDeinit()ハンドラで、グラフィック要素コレクションクラスのメソッドを呼び出すようにします。
//+------------------------------------------------------------------+ //| Deinitialize library | //+------------------------------------------------------------------+ void CEngine::OnDeinit(void) { this.m_indicators.GetList().Clear(); this.m_graph_objects.OnDeinit(); } //+------------------------------------------------------------------+
ライブラリの改善は今のところこれですべてです。結果をテストしてみましょう。
検証
テストを実行するには、前の記事のEAを使用して、\MQL5\Experts\TestDoEasy\Part102\\TestDoEasyPart102.mq5として保存します。
大幅な変更は加えないつもりです。その代わり、グラフィカルオブジェクトの位置座標を変えて、その間の距離を少し短くし、パネルサイズを大きくするだけにします。パネルサイズを作成するには、OnInit()ハンドラの次のコードを使用します。
//--- Create WinForms Panel object CPanel *pnl=NULL; obj_id=engine.GetGraphicObjCollection().CreatePanel(ChartID(),0,"WFPanel",elm.RightEdge()+20,50,230,150,array_clr[0],200,true,true); list=engine.GetListCanvElementByID(ChartID(),obj_id); pnl=list.At(0); if(pnl!=NULL) { pnl.FontDrawStyle(FONT_STYLE_NORMAL); pnl.Bold(true); Print(DFUN,EnumToString(pnl.FontDrawStyle())); Print(DFUN,EnumToString(pnl.FontBoldType())); pnl.SetFontSize(10); pnl.TextOnBG(0,pnl.TypeElementDescription()+": ID "+(string)pnl.ID()+", ZD "+(string)pnl.Zorder(),pnl.Width()/2,pnl.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',pnl.Opacity()); pnl.Update(true); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
パネルの作成に成功したら、テキストのフォントタイプを「通常」に設定し、「太字」フォントを併用し、操作ログにフォントスタイルの説明と幅の種類を表示します。
すべてのグラフィック要素オブジェクトに表示されるテキストは、前回の記事と比較して若干フォーマットが異なりますが、オブジェクトタイプ、ID、ZOrderを自動的に表示するようになりました。
添付ファイルですべての変更を確認できます。
EAをコンパイルし、チャート上で起動します。
このように、必要なオブジェクトはすべてマウスと連動し、パネルには枠が付き、その上のフォントは意図したとおりに太字で表示されます。チャートを切り替えてもオブジェクトが消えないようになりましたが、新しい位置は保存されません。これを解決するためには、オブジェクトデータをファイルに書き出し、必要に応じて読み出す必要があります。すべてのオブジェクトに計画した継承階層を持たせたらすぐに、これをおこなうつもりです。
次の段階
次の記事では、パネルオブジェクトクラスの開発を続けます。
**連載のこれまでの記事:
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10697




- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索