English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
カスタムグラフィックコントロール パート3. フォーム

カスタムグラフィックコントロール パート3. フォーム

MetaTrader 5 | 2 10月 2015, 15:45
961 0
Dmitry Fedoseev
Dmitry Fedoseev

はじめに

最初の記事 "シンプルなコントロールの作成"では、グラフィックコントロールの作成の基礎とシンプルなコントロールの作成をステップごとに解説しました。続いての記事 "コントロールライブラリー" ではすぐに使用可能なコントロールを紹介しました。未だ紹介していない重要なグラフィカルインターフェースがあります。フォームです。

フォームはコントロールが表示されるスクリーン上に長方形の形状で表されます。フォームはそれに含まれるコントロールを同時に、隠したり、表示したり、一緒に動かしたりすることができるコンテナーでもあります。

最後の記事ではフォームの作成や、コントロールとの併用の仕方について紹介します。

フォームに関するクラスが、過去の記事で使用したIncGUI.mqhファイルに加えられました。(新しいファイル名はIncGUI_v3.mqhになります)他にも多くのクラスが追加され、CHMenu (横メニュー)やCVMenu (縦メニュー) のクラスはアップデートや修正がされています。

追加されたクラス:

  • CFrameクラス - フレーム。CWorkPieceクラスのフレームメソッドで作成されたフレームと同一のクラスです。
  • CButtonクラス - ボタン。通常のボタン(OBJ_BUTTON)。
  • CLabelクラス - ラベル。グラフィカルオブジェクト"ラベル"(OBJ_LABEL)として表示されたラベルとは違い、このクラスを使うと"\n"で文章を区切ることで複数の行で表示することが可能です。

新しいクラスを追加した結果、CColorSchemesクラスは次のように変更されました: 新しいコントロールのための色の追加

CHMenuクラスとCVMenuクラスにおける変更:これによりドロップダウンタブのついた2層メニューを作成することができるようになりました。これについては”メインメニューの作成”のセクションで詳しく説明します。

エラー: CWorkPieceクラスのFrame() メソッドの修正 - 長方形のラベルのサブウインドウ番号が設定できない。CListMSClassクラス - Selected()メソッドとSetSelected()メソッド - 空のリストを使用することができるようにするため配列のサイズをチェックするようにしました。


1. フォーム

このフォームはグラフィカルオブジェクト"Rectangle Label"にもと OBJ_RECTANGLE_LABEL with use of several buttons OBJ_BUTTON. フォームは長方形の形をしていて (図 1) 上部のバーにフォーム名とコントロールボタンが表示されます。

フォームを動かすボタンは左側に、(手の画像が入った)、最小化ボタン (長方形の画像が入った)と閉じるボタン (×の画像が入った)は右側に配置されています。

図 1. フォーム

図 1. フォーム

フォームを動かすには、移動ボタンを押し(ボタンは押された状態になります)チャート上の移動先としたい場所をクリックします。ボタンは元の状態に戻り、フォームは指定された場所に移動します。(フォームの左上をクリックされたポイントに合わせます。)

指定された場所がチャートの端だった場合は、フォームがチャートの外にはみ出さないように修正されます。

いくつかの異なる種類のフォームを作成することが可能です。

  • タイプ 0 (図 1);
  • タイプ 1 - ”キャンセル”と”有効化”ボタンの追加 (図 2);
  • タイプ 2 - ”閉じる”ボタンの追加 (図 3);

図 2. タイプ1のフォーム ( ”キャンセル”と”有効化”ボタン)

図 2. タイプ1のフォーム ( ”キャンセル”と”有効化”ボタン)

図 3. タイプ2のフォーム ( ”閉じる”ボタン)

図 3. タイプ2のフォーム ( ”閉じる”ボタン)

フォームを扱うプログラミング手段は2つのクラスで表されます。: CFormBaseとCFormTemplateです。

CFormBaseクラスは基礎的なもので、CFormTemplateはCFormBaseクラスのサブクラスになります。その効果と原則において (アプリケーションの原則を含む)、CFormBaseクラスはこれまでに説明してきたコントロールと完全に同じコントロールです。

CFormBaseクラスには、コントロールの準備のためのInit()メソッド、フォーム(フォームを表すグラフィカルオブジェクトの作成)を表示するためのSetPos()やShow()メソッド、隠すためのHide()メソッド、ディスプレイを更新するためのRefresh() メソッド、イベントを処理するEvent()メソッドや、他の全てのコントロールにあるのと同様のその他のメソッドがあります。

コントロールの使用に加え、フォームのアプリケーションはInit()メソッドの呼び出しから始まります。続いてSetPos() メソッドを使い位置を決め、Show()メソッドで有効化され、あるいはパラメーターをつけて有効化され、Hide()メソッドで隠すことができます。

Event()メソッドが OnChartEvent()関数に加えられフォームのコントロール制御を可能にしました。(移動、最少化、閉じるなど。)。フォームをサブウインドウで動作させる必要がある時は、 the SetSubWindow()メソッドを使用します。]CHARTEVENT_CHART_CHANGE イベントが起こったことをチェックするOnChartEvent() 関数やInit()メソッドの直後に呼び出されます。他のコントロールとすべて同じです。

標準的なコントロールは独立していますが、(他のコントロールとは関連していません。)、フォームは一緒に表示されるコントロールの表示/非表示を同時に行うために関連することが必須です。従って、フォームや関連するコントロールのInit()メソッド、 Show()やHide()メソッドの同調は保証されなければなりません。

(例えば、フォームのShow()メソッドを呼び出す時に)フォームに関連するすべてのコントロールのShow()メソッドを呼び出すことはできますが、複数のフォームがプログラムで使用されていると、そのような方法は、構造化されておらず、理解しにくく、デバッグやアップデートに不向きなコードになってしまいます。

それぞれのフォームのフォームクラスをコピーし、コード付きのコントロールを追加することはできますが、その結果コードは見ずらいコードになるでしょう(コントロールの動作に関するコードとフォームのコードが分離されていない)、フォームのデザインや動作に関するコードだけをコピーするような過度なコピーなどは、フォームの関数に変更を加える際に、すべてのクラスコピーに変更を加えないとならないため作業を複雑にします。

クラスとサブクラスを分離することで、複数のフォームの作成、フォームと関連するコントロールの表示の同期化、異なるフォームのコードの分離などの問題を解決することができます。基本的な問題を解決する以外にも、クラスとサブクラスの使用により親クラスの将来のアップグレードが既に完成した仕事になんら変更をせずに済みます。

コントロールやフォームに対するInit()、 Show()やHide()といったメソッドの呼び出しの同調のために、CFormBaseクラスには通常のコントロールクラスとは違いがあります。: いくつかの保護 セクションにある ヴァーチャルlメソッド です:

virtual void MainProperties() {}
virtual void OnInitEvent() {}
virtual void OnShowEvent() {}
virtual void OnHideEvent() {}
virtual void EventsHandler(const int id, const long& lparam, const double& dparam, const string& sparam){}
virtual void OnWindowChangeEvent(int aSubWindow) {}
virtual bool OnCancelEvent() {return(true);}
virtual bool OnApplyEvent()  {return(true);} 

これらのメソッドはヴァーチャルです。これはCFormTemplateのサブクラスの対応するリアルメソッドがこれらのメソッドを経由して実行されることを意味します。

CFormTemplateのサブクラス自体は使用されません。それらはテンプレートだからです。新しいフォームを作成するためには、CFormTemplateクラスのコピーを独自の名前で作ります(CFormTemplateクラスをコピーし、名前を変更します。これをすべてのフォームで繰り返します)。サブクラスのコピーにより、異なるフォームのコードを分離することができます。

ヴァーチャルメソッドは 保護されたセクションにあります。従って、他のフォームイベントの親クラスから既に呼び出されているメソッドに対する不必要なコールなどはアクセスすることができません。- これらのイベントに対応するコントロールの動作のコードだけを追加すれば良いのです。

下のグラフExpert Advisorの関数と標準的なコントロールクラスメソッドのインタラクションを示しています.(図 4) 。そしてフォームクラスメソッドのインタラクションの比較 (図 5).

図 4. Expert Advisor関数とコントロールメソッドのインタラクション

図 4. Expert Advisor関数とコントロールメソッドのインタラクション

 図 5. Expert Advisor関数とフォームクラスメソッドのインタラクション

図 5. Expert Advisor関数とフォームクラスメソッドのインタラクション

それぞれのメソッドの目的について見てみましょう:

  • MainProperties()とOnInitEvent()メソッドはフォームのInit()メソッドに呼び出されます。MainProperties()メソッドはフォームの外観を設定します。(フォームのタイプ、移動、最少化、閉じるなどのボタン)。OnInitEvent()メソッドはすべてのコントロールのInit()メソッドを呼び出し、コントロールのデフォルト値を設定します。

  • OnShowEvent()メソッドはShow()メソッドに呼び出されます;すべてのコントロールのShow()メソッドの呼び出しコードがそれに追加されます。

  • OnHideEvent()メソッドはHide()メソッドから呼び出されます; すべてのコントロールのHide()メソッドの呼び出しコードがそれに追加されます。

  • EventsHandler()メソッドはExpert Advisorの OnChartEvent() 関数と同じです。フォームのEvent() メソッドはExpert AdvisorのOnChartEvent()関数に呼び出され、すべてのチャートイベントはこのメソッドにリダイレクトされます。すべてのコントロールのEvent()メソッドと対応するイベント処理の呼び出しコードは EventsHandler()に追加されます。

  • OnWindowChangeEvent()メソッドは、サブウインドウを変更する際にSetSubWindow()に呼び出されます。すべてのコントロールのSetSubWindow()メソッドの呼び出しコードはここに追加されます。このメソッドにはすべてのコントロールに設定する新しいサブウインドウナンバーを与えるためのパラメータがあります。

  • OnCancelEvent()メソッドはフォームを閉じる際に実行されます(×の画像の "キャンセルl"または"閉じる"ボタン)。フォームを閉じる際にチェックコードを入れることができます。例えば、本当にフォームを閉じるかの確認ウインドウを開くコードなどです。そのメソッドがtrueを返せば、フォームを閉じるコードは完了してフォームを閉じます。;もしもfalseを返せば、フォームを閉じるコードは中断され、フォームは開いたまま残ります。

  • ]OnApplyEvent()メソッドはフォームの"適用"ボタンがクリックされた時に実行されます。ここでは(OnCancelEvent()メソッドと同様に)、チェックコード入れ、trueかfalseを返すかによりフォームを閉じるかどうかを決めることができます。このメソッドはコントロールの値を保存し、次にExpert Advisorを使った時にその同じ値を使うことができるようにするコードがあります。そのため、保存された値はOnInitEvent()メソッド内でロードされコントロールに設定される必要があります


2. 具体的な新しいのフォームの作り方

具体的な新しいフォームの作り方は以下のようになります:

1. IncGUI_v3.mqhファイルをインクルードします。

#include <IncGUI_v3.mqh>

2. IncGUI_v3.mqhファイルのCFormテンプレートクラスからExpert Advisorのファイルにコードをコピーします。(サブクラスはファイルの最後に) 名前を変更します (ライブラリの例ではCFormというサブクラス名が使われています)。

class CFormTemplate: public CFormBase{
   public:
   protected      void MainProperties()
     {
        m_Name        = "Form";       // Form name. The names of all the controls of this form should start with it.
        m_Width       = 200;           // Form width
        m_Height      = 150;           // Form height
        m_Type        = 1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
        m_Caption     = "FormCaption"; // Form caption
        m_Movable     = true;          // Movable form (the button with a hand image is displayed in the top left corner)
        m_Resizable   =  true;         // Form minimizing/maximizing allowed (the button with a rectangle image 
                                           // is displayed in the top right corner)
        m_CloseButton = true;          // Form closing allowed (the button with a cross image is displayed in the top right corner)
     }
      void OnInitEvent() {} 
      void OnShowEvent(int aLeft, int aTop) {}
      void OnHideEvent() {}
      void OnWindowChangeEvent(int aSubWindow) {}
      void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam){}
      bool OnApplyEvent()  {return(true);}
      bool OnCancelEvent() {return(true);}
};

フォームコードごとに別のインクルードファイルを作成することができます。Expert Advisor内にそれをインクルードし、CFormTemplateにサブクラスからコードをコピーします、それ以外はこのファイルで動作するフォームに関するコードを入力します。

ビジュアルエディターを使いアナロジーを描きます。これはボタンをクリックして、様々なイベントの関数を含む新しいフォーム作成するのに似ています form following which a new form file with functions of various form events would appear in the project.

3. フォームの主なプロパティの設定。このステップはMainProperties()メソッド内で実行され、親クラスで宣言された変数に値を割り当てます。それぞれの変数についての詳しいコメントはテンプレート内で見れます。このフォームのプロパティウインドウの使用のステップはビジュアルエディターに似ていると気づかれるかもしれません。(例えばExcelのVBAl)。

void MainProperties()
{
   m_Name         =  "Form";        // Form name. The names of all the controls of this form should start with this one.
   m_Width        =  200;           // Form width
   m_Height       =  150;           // Form height
   m_Type         =  1;             // Form type: 0 - without buttons, 1 - with "Apply" and "Cancel" buttons, 2 - with the "Close" button
   m_Caption      =  "FormCaption";   // Form caption
   m_Movable      =  true;          // Movable form (the button with a hand image is displayed in the top left corner)
   m_Resizable    =  true;          // Form minimizing/maximizing allowed (the button with a rectangle image 
                                    // is displayed in the top right corner)
   m_CloseButton = true;            // Form closing allowed (the button with a cross image is displayed in the top right corner)
}

フォームの他のコントロールとの違いにお気づきでしょうか-フォーム名は、Init() method のパラメータとしてではなくMainProperties()メソッド内に設定されています。

フォームの主な動作はサブクラス内で行われます; フォームのすべてのサブクラスは一度だけしか宣言されないため、フォームの1つのサブクラスのInit()メソッドを呼び出す時に別の名前を特定する必要がありません。

Init()メソッドに送信される唯一のパラメータはStateパラメータですが、 これも必須ではありません。Stateパラメータはフォームが表示された際の最初の状態を決定します:: 最大化あるいは最小化。もしも値が1であれば、フォームは最大化されています。(図 1, 2, 3)。 値が2であれば、フォームは最小化されています (図 6)

(図 6. 最小化されたフォーム

(図 6. 最小化されたフォーム

4. クラスを宣言する。

CForm frm;

5. コントロールを使う標準的なステップを作成します: フォームのInit()メソッドをExpert Advisorの OnInit() 関数から呼び出し、 Deinit() メソッドをOnDeinit() 関数から、Event() メソッドをOnChartEvent() 関数から呼び出し、必要であれば、SetSubWindow() メソッドを CHARTEVENT_CHART_CHANGE イベントから呼び出します。

フォームはその後、チャート内に表れ、クリックにより動かしたり、最少化したり、閉じたりできるようになります。

次にこのフォームにコントロールを追加することにしましょう。


3. 具体的なフォームへのコントロールの追加の仕方

  1. コントロールの宣言。 フォーム外からメソッドをコントロールするためのアクセスが必要な場合は、そのコントロールクラスは public セクション内で宣言されなければいけません;もしもすべてのことがフォームクラス内で行われるのであれば、コントロールクラスはprotected セクション内で宣言します。

  2. コントロール値を再現するための変数を定義します。オプションのステップ。フォームで”キャンセル”や”適用"ボタンを使用する場合は、ユーザーがコントロール値を変更し”キャンセル”ボタンを押した場合に、フォームを開いた時にコントロールが持っていた値を再現するため、すべてのコントロールに変数を追加しそれらの値を保存しなければいけません。

  3. すべてのコントロールのInt()メソッドを呼び出します。 OnInitEvent()メソッド内で実行されます。

  4. 保存されたデータのロード。 オプションのステップ。OnInitEvent()メソッド内で実行されます。Expert Advisorやターミナル、コンピューターを再起動する際に値を保持しておく時に必要なステップです。データの保存についてはステップ12で解説します。

  5. デフォルトの値を設定します。 このステップでは、 すべてのコントロールは Expert Advisor (ユーザーによって値が変更されるまでは)を開始した後に初めてフォームを開いた時にコントロールに表示されている値に設定されています。データがステップ4でロードされていれば、そのデータが使用されます。このステップでは他にもコントロールの準備が行われます。例えば、メニューアイテムが追加されます。

  6. すべてのコントロールのShow() メソッドの呼び出し(パラメータのあるもの)はOnShowEvent()メソッド内で行われます。OnShowEvent() メソッドには2つのパラメータがあります。int aLeftとint aTopで、フォームのワークスペースの左上の座標を受け取ります。コントロールの位置はこれらの変数によって決められます(値は加算されます)。

  7. すべてのコントロールのフォームのHide()メソッドの呼び出しはOnHideEvent()メソッド内で行われます。

  8. SetSubWindow()メソッドの呼び出し。オプションのステップ。フォームをサブウインドウ内に表示する必要がある時には、すべてのコントロールにおいてSetSubWindow() メソッドを呼び出します。これはOnWindowChangeEvent()メソッド内で実行されます、このメソッドにはパラメータ(int aSubWindow)があり新しいサブウインドウ番号を格納します。

  9. すべてのコントロールのEvent()メソッドの呼び出し。これはEventsHandler()メソッド内で実行されます。

  10. コントロールイベントの処理。 オプションのステップ。コントロールとそのイベントのインタラクションを確保しなければならない場合、このステップでイベントは適切に処理され、必要なアクションが実行されます。これはEventsHandler()メソッド内で実行されます。

  11. 新しい値の適用。 "適用"ボタンを使用し実行されるオプションのステップです。OnApplyEvent()メソッド内で実行されます。このステップでは、コントロール値がその正確性をチェックされます; もしも正しくない値が見つかった場合は、ユーザーに通知され、メソッドはfalseを返し、フォームは開いたままになります。値が正しい場合は、trueが返り、フォームは閉じます。

  12. データの保存。オプションのステップ。"適用"ボタンを使用し、OnApplyEvent()メソッド内で実行されます。グローバル変数や、チャートに隠れているオブジェクトなどのデータ(目標や目的に応じて)はファイルに保存されます。

  13. OnCancelEvent()メソッドでのチェック 。オプションのステップ。OnCancelEvent()メソッドでfalseが返された場合、フォームは開いたままです。例.閉じるボタンが反応しません。フォームを閉じるためには、メソッドがtrueを返さなければいけません。

フォームを使った具体例は添付のeIncGUI_v3_Test_Form.mq5ファイルで見ることができます。異なるコントロールを含んだメインフォームがスタートアップ時に開きます (図 7)。

フォームには閉じるためのボタンがありません。これはメインフォームなのでチャートから消すことができません。フォームをもう一度表示するためにExpert Advisorを再起動しなくてはいけなくなってしまいます。

(図 7. メインメニュータブが開いた状態の、eIncGUI_v3_Test_Form.mq5のサンプルメインフォーム。

(図 7. メインメニュータブが開いた状態の、eIncGUI_v3_Test_Form.mq5のサンプルメインフォーム。

主なメニューコマンドを使い、フォームの2つの変数を開くことができます。

メインメニュー - フォームタイプ - タイプ1はタイプ1のフォーム ("キャンセル"と"適用"ボタンを含む)を開きます。
メインメニュー - フォームタイプ - タイプ2はタイプ2のフォーム ("閉じる"ボタンを含む)を開きます。

どちらのフォームも閉じる際には確認が必要です。(MessageBox()関数を使って行われます)。タイプ1のフォームには入力ボックスがあります; 入力された値は"適用"ボタンを押すことでチェックされます。タイプ2のフォームは新しいコントロール"Label" (CLabelクラス)とそれをテストするためのいくつかのボタン(CButtonクラス)があります。

イントロダクションでも述べましたが、ここではメインメニューの作成を通してCHMenuとCVMenuクラスの変更点を見ていきましょう。


まず最初に、2層のメニューしか作ることはできません:水平のバー(CHMenuクラス)とタブ (CVMenuクラス)です。原則として、それ以上の階層のものを作ることは可能ですが、その工程はとても複雑で作業に時間が掛かります。ですからおすすめしませんし、詳しくは述べません 。

実は、フォームのメインメニューは計画すらされていませんでした。そしてCHMenuとCVMenuのクラスは、さまざまな関数やインストルメントを有効/無効にするために別々に独立して使われる予定でした。しかし、CHMenuとCVMenuクラスのわずかな改善により、多くの場合において十分なものとして使えるメインメニューを作ることができるようになりました。

本格的な多層メニューを作るためには、多少違ったアプローチを行う必要がありますが (ツリー状のデータ構造の構築)、それを説明するには多くの時間がかかりますので、今回は現状限られた手段を使用することにしましょう。

まずはCHMenuとCVMenuクラスの変更点と修正点を見てみましょう。

CVMenuクラスでは、チック記号を表示するために使われたグラフィカルオブジェクト "ラベル"をクリックした際の反応が改善されました。それに応じて、tLastClickedX()、 LastClickedY()やLastClickedQuarter()メソッドに変更はありました。ラベルをクリックするとテキストボックスと同じようにその値が返されます。同様の修正はCHMenuクラス、LastClickedX()、 LastClickedY(),、 LastClickedQuarter()や LastClickedW() メソッドでも行われました。

どちらの場合も、メニューコマンドのグラフィックなオブジェクトの座標を計算するためのものだったSolvePosX()とSollvePosY()メソッドを追加することによりアップデートされました。表示されたオブジェクトの幅がSolvePosX()メソッドに渡され、高さはSolvePosY() メソッドに渡され、オブジェクトのXY座標が返されます。これで、LastClickedX()、 LastClickedY()、 LastClickedQuarter()、LastClickedW()メソッドを使う必要はもうありません。

それぞれクラスはLastClickedName1()とLastClickedName2()メソッドの追加によりアップデートされました。これらのメソッドは最後にクリックされたメニューアイテムのグラフィカルオブジェクトの名前を返します。LastClickedName1()はテキストボックスの名前、 LastClickedName2()はチック記号の名前を返します。

ToggleNameAdd() やToggleNamesClear() メソッドがCVMenuに追加されました。The ToggleNameAdd()メソッドが "トグル"ネームのリストを作るのに使用されます。ToggleNamesClear()メソッドはそのリストをクリアするために使われます。。ToggleNameAdd()メソッド (リストに名前を追加します)を使用する時には、メニューを構成するグラフィカルオブジェクトやToggleNameAdd()メソッドで追加された名前を持つグラフィカルオブジェクトのイベント以外ではどのチャートイベントが起こってもメニューは自動的に閉じられます。。ToggleNamesClear()メソッドでリストがクリアされることでメニューは通常の動作状態に戻ります。

2層のメニューの動作は次のように行われます:横メニューのクリックがLastClickedName1()とLastClickedName2()メソッドにより確認されると、クリックされたオブジェクトの名前がToggleNameAdd()に渡されます。

その後、縦メニューは縦メニューか横メニューの1つのアイテム(それによって縦メニューが開かれたもの)以外のチャートイベントによって隠れます。

縦メニューの1つのアイテムがユーザーによって選択された場合、縦メニューは閉じ、対応するアイテムが実行されます。ユーザーが何度も横メニューのアイテムをクリックした場合 (それによって縦メニューが開かれたもの)、縦メニューは隠れています。

t/pheメニューの作成の例について見てみましょう。

メインメニューには3つのアイテムがありますので、縦メニューにも3つ必要です。横メニュークラスと、3つの縦メニューのクラスを 、フォームのサブクラスのパブリック セクションで宣言します。 (下の例はeIncGUI_v3_Test_Form.mq5にあるものと全く同じです):

class CForm: public CFormBase{
public:
   CHMenu m_hm;
   CVMenu m_vm1;
   CVMenu m_vm2;
   CVMenu m_vm3; 

OnInitEvent()メソッドでメニュークラスを初期化し、メニューアイテムを追加します:

// Horizontal menu
m_hm.Init(m_Name+"_HM",m_Width,2);
// Adding horizontal menu items
m_hm.AddItem("Form types");
m_hm.AddItem("Item-2");
m_hm.AddItem("Item-3");
// Vertical menu 1
m_vm1.Init(m_Name+"_VM1",70,10); 
// Adding items to vertical menu 1
m_vm1.AddItem("Type-1");
m_vm1.AddItem("Type-2");
// Vertical menu 2
m_vm2.Init(m_Name+"_VM2",70,3);
// Adding items to vertical menu 2
m_vm2.AddItem("Item-2-1");
m_vm2.AddItem("Item-2-2");
m_vm2.AddItem("Item-2-3"); 
m_vm2.AddItem("Item-2-4");
m_vm2.AddItem("Item-2-5"); 
// Vertical menu 3
m_vm3.Init(m_Name+"_VM3",70,3);
// Adding items to vertical menu 3
m_vm3.AddItem("Item-3-1");
m_vm3.AddItem("Item-3-2");
m_vm3.AddItem("Item-3-3"); 
m_vm3.AddItem("Item-3-4");
m_vm3.AddItem("Item-3-5"); 

OnHideEvent()メソッドでメニューを隠します。

void OnHideEvent()
{
   m_hm.Hide(); 
   m_vm1.Hide(); 
   m_vm2.Hide(); 
   m_vm3.Hide(); 
}

OnShowEvent()メソッドに横メニューを表示します。

void OnShowEvent(int aLeft,int aTop)
{
    m_hm.Show(aLeft,aTop); 
}

最後に、EventsHandler()メソッドのメインの部分です。

すべてのメニューのEvent()メソッドを呼び出します:

void EventsHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
{
    int m_event0=m_hm.Event(id,lparam,dparam,sparam);
    int m_event1=m_vm1.Event(id,lparam,dparam,sparam);
    int m_event2=m_vm2.Event(id,lparam,dparam,sparam);
    int m_event3=m_vm3.Event(id,lparam,dparam,sparam);

m_event0 value (横メニューのアイテムインデックス)の値により、適切な縦メニューが動作します (例.アイテム0と縦メニュー1):

if(m_event0==0)
{ // Clicking item 0
   if(m_vm1.Visible())
   { 
      m_vm1.Hide(); // If the vertical menu is open, close it
   }
   else{ // If the vertical menu is closed
      m_vm1.ToggleNamesClear(); // Clear the list of "toggle" names
      // Add to the list the names of the graphical objects forming the item 
      // of the horizontal menu which led to the last event 
      // of the horizontal menu 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName1()); 
      m_vm1.ToggleNameAdd(m_hm.LastClickedName2());
      // Display the vertical menu
      m_vm1.Show(m_hm.SolvePosLeft(m_vm1.Width()),m_hm.SolvePosTop(m_vm1.Height())); 
   }
}

このような動作は縦メニューを開いてる全ての横メニューに実行する必要があります。縦メニューのイベントが発生した場合、メニューを閉じます。

クリックされたアイテムのインデックスにより以下のようになります:

if(m_event1>=0)
{ // Vertical menu event 1
   m_vm1.Hide(); // Hide the menu
      if(m_event1==0)
      { // Clicking item 0
        //...
      }
      if(m_event1==1)
      { // Clicking item 1
        //...
      }
}

このような動作は全ての縦メニューとその全てのアイテムに対して実行する必要があります。

これでメインメニューの作成が完了しました。


まとめ

この記事はグラフィカルインターフェースの作成に関する記事のまとめになります。

我々の努力の結果、多機能で使いやすいグラフィカル・インタフェースを迅速に作成するための非常に有用な手段を得ました。これは既存のグラフィカル・インターフェースに対する制約と一致します:

  • 最小化/最大化や移動できるフォームやキャプションのフォームのフレームワークの利用;
  • 明確に指定された領域のフォームに関連するコード;
  • コントロールの表示/非表示とフォームの表示/非表示の同期。

すべてのコントロールとフォームには、素早く変更できる一般的なデザインとカラースキームがあります。

フォームの作成についておさらいしましょう。

フォームの作成の簡単な手順:

  1. IncGUI_v3.mqhファイルをインクルードします。

  2. CFormTemplateクラスをコピーし、独自の名前に変更し、クラスを宣言します。

  3. サブクラスのコピーのMainProperties()メソッドでフォームのプロパティを設定する。

  4. Expert AdvisorのOnInit(), OnDeinit() and OnChartEvent()関数からそれぞれInit()、 Hide()やEvent()メソッドを呼び出す。必要であればExpert Advisorの OnInit()関数からShow()メソッドを呼び出す。

  5. コントロールでの作業。サブクラスのコピーのコントロールクラスを宣言する。Call the Init(), Show(), Hide() and Event() methods of the controls from the サブクラスのコピーのOnInitEvent(),、OnShowEvent()、 OnHideEventやEventHandler()メソッドのコントロールの Call the Init()、 Show()、 Hide()やEvent()メソッドを呼び出す。


付録

別記ファイルの一覧:

  • IncGUI_v3.mqh - グラフィック・インターフェースを作成するためのすべてのクラスを含むインクルードファイル。ファイルはターミナルデータディレクトリのMQL5/Includeディクトリに入れて置く必要があります。
  • eIncGUI_v3_Test_Form.mq5 - フォームを使った例。ファイルはターミナルデータディレクトリのMQL5/Expertsディクトリに入れて置く必要があります。
  • IncGUIv3mqh.chm - IncGUI_v3.mqhファイルの説明・ヘルプファイル。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/322

添付されたファイル |
オブジェクト指向プログラミング オブジェクト指向プログラミング
オブジェクト指向プログラミングに関する多相性やカプセル化などについて理解する必要はありません。これらの機能を使うだけで良いのです。この記事ではオブジェクト指向プログラミングの基本を例を使って具体的に見ていきます。
カスタムグラフィックコントロールパート1:簡単なコントロールを作成する カスタムグラフィックコントロールパート1:簡単なコントロールを作成する
本稿では、グラフィックコントロールの一般的な原則を網羅します。グラフィックオブジェクトの作業を早く便利に行うためのツールを用意し、テキストや数値データの入力のために簡単なコントロールを作成する方法や、その使用方法の例を分析していきます。
マルチタイムフレームとマルチ通貨パネルの作成 マルチタイムフレームとマルチ通貨パネルの作成
この記事では、オブジェクト指向プログラミングを使うMetaTrader 5のマルチタイムフレームとマルチ通貨パネルの作成の仕方を解説します。主な目的は、パネルのコードを変更することなく、価格や価格の変化、インディケーターの値や売買条件のカスタマイズなど 多くの異なった種類のデータを表示することのできる汎用パネルの作成です。
MQL5ウィザード:新バージョン MQL5ウィザード:新バージョン
本稿では、最新のMQL5ウィザードで利用できる新しい特徴について述べます。シグナルのアーキテクチャが変更され、さまざまなマーケットパターンにもどつくトレーディングロボットを作成することができるようになっています。本稿に含まれる例では、エキスパートアドバイザのインタラクティブな作成手順を説明しています。