Net Framework および C# に基づくグラフィカル インターフェイスの開発 (パート 2): その他のグラフィカル要素

3 7月 2019, 14:57
Vasiliy Sokolov
0
215

目次


イントロダクション

2018 年 10 月以降、MetaTrader5 はNet Famework ライブラリとの統合をサポートしています。 この一連のライブラリは、実際には、グラフィカル ウィンドウの描画やネットワーク操作の実装など、特定の範囲のタスクを実行するためのフレームワークや特殊なシステム以上のものです。 ネットフレームワークは本当になんでもあります。 Web サイト (Net Core、MVC) の開発、統合プロフェッショナル インターフェイス (Windows フォーム) を持つシステム アプリケーションの作成、ノード間のデータ交換を伴う複雑な分散システムの構築、およびデータベース (Entity Framework) の操作を可能にします。 また、Net Frameworkは、何千ものオープンソースプロジェクトを持つプログラマや企業の巨大なコミュニティです。 インタラクションが適切に編成されている場合は、現在 MQL ですべてを使用できます。

この記事では、最初のパート で作成された GuiController の関数を引き続き開発します。 この関数は、Windows フォーム テクノロジに基づく Net Framework のグラフィカルな関数とデータのやり取りすることを目的とします。 現在、MQLグラフィカル関数には多くのインフォが用意されています。 MQL によって多かれ少なかれ同じことを行うさまざまなライブラリがたくさんあります。 したがって、この記事を"フォームを操作するための別のライブラリ"として読者に認識してほしくないと思います。 実際、この記事は、Net Framework とのデータのやり取りを説明し、このソフトウェア プラットフォームの無限の関数を徐々に明らかにする一連の記事の一部にすぎません。 Windows フォームは、このプラットフォーム内のビルディング ブロックの 1 つにすぎませんが、Net テクノロジの一部と同様に、便利で包括的なフォームです。 Windows フォームグラフィカル サブシステムは、このフレームワークを探索するための優れた出発点です。 適切な学習の後、Net Framework との他のデータのやり取りに適用できます。 また、最も重要なのは、トレーディングパネル、EA構成ウィンドウ、高度なグラフィカルインジケータ、ロボット制御システム、およびユーザーとトレードの間の相互作用に関連するその他多くのものを作成することもできます。

ただし、すべての関数を実装するには、MQL プログラムと C# ライブラリ間の相互作用のモジュールを大幅に改善する必要があります。 最初のセクションで、GuiController モジュールは、ボタン (ボタン)、テキスト ラベル (ラベル)、テキストをインプットするためのテキスト フィールド (TextBox)、垂直スクロール 足など、 WinForms 要素としかやり取りできませんでした。 このわずかなサポートにも関わらず、完全でかなり機能的なグラフィカルパネルを作成することができました。

図1. この記事の最初のパートで作成されたトレードパネル

かなり印象的な結果ではありますが、もう少しそこにとどまり、コントローラを改善し続けます。 この記事では、ユーザーがほとんどの種類のフォームを作成できるように、追加のグラフィカル要素を提供します。


新しい要素のテストの配置

新しい要素のサポートを導入するには、"テストベンチ"のようなものを整理する必要があります。 これより、新しい要素の操作を微調整し、新しい関数を導入するときに発生する潜在的なエラーを排除できます。 "テストベンチ" は、コントローラ、必要なグラフィカル要素のセットを持つフォーム、および要素を処理するEAで構成されます。 すべてのフォームは、単一の DemoForm.exe 内に配置されます。 EA内では、DemoForm.exe からダウンロードするグラフィカルフォームを指定するカスタムパラメータを開発します。


 

図2. 必要な要素を含むダウンロード済みのカスタム フォームの選択

テストEA自体は簡単です。 実際には、ダウンロード関数 (標準 OnInit 初期化関数) とグラフィカル イベント ハンドラ関数 (OnTimer 関数のイベント ループ) の 2 つの部分で構成されます。 ご存知かもしれませんが、GuiController でのタスクは静的メソッドの呼び出しによって実行されます。 主なメソッドは 4 つだけです。

  1. Show Form- 特定のビルドからフォームを起動。
  2. HideForm- フォームを非表示に。
  3. GetEvent- フォームからイベントを取得;
  4. SendEvent- フォームにイベントを送信

OnInit 関数では、選択した要素に応じて必要なウィンドウをダウンロードします。 関数プロトタイプを次に示します。

int OnInit()
{
   switch(ElementType)
   {
      case WINFORM_TAB:
         GuiController::ShowForm("DemoForm.exe", "tab_form");
         break;
      case WINFORM_BUTTON:
         GuiController::ShowForm("DemoForm.exe", "button_form");
         break;
      ...
   }
   ...
}

OnTimer 関数では、フォームから発生するイベントを処理します。

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{   
   //-- タイマーで新しいイベントを取得する
   for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      ...
      if(id == TabIndexChange)
         printf("Selecet new tab. Index: " + (string)lparam + " Name: " + sparam);
      else if(id == ComboBoxChange)
         printf("ComboBox '" + el_name + "' was changed on " + sparam);
      ...
   }
}

したがって、各グラフィカル要素について、その要素との相互作用を示す短いタスク例を作成します。 また、サポートするイベントについても詳しく説明します。


MessageBox

2 番目のバージョン以降、コントローラーはメッセージ ボックスをサポートしています。 これは、標準的なユーザー情報要素です。 また、ユーザーにオプションを提供し、選択したオプションの形式で応答を受け取ります。

メッセージ ウィンドウのデモンストレーションを開始するには、EAを起動するときに Windows フォーム要素タイプ パラメータの [ボタンとメッセージ ボックス] オプションを選択します。 EAを起動すると、次のいずれかのオプションを選択するように求めるフォームが表示されます。

図3. メッセージ ボックスを呼び出すサンプル フォーム 

このフォームは、一連のものと同様に、デモンストレーション形式であるため、トレードロジックは装備されていません。 ただし、いずれかのボタンを押すと、EAは選択したアクションの確認を求めるアラートメッセージを送信します。 たとえば、[(SELL)売り] をクリックすると、次のメッセージ ウィンドウが表示されます。


図4. トレーディングEAは、新しいショートポジションを開くために確認

ユーザーがボタンの 1 つをクリックすると、クリック イベントが記憶され、GuiController イベント バッファに記録されます。 このEAは、指定された頻度でイベント バッファを調査し、新しいイベントがバッファに入ったことがわかります。 したがって、このEAは "ボタンをクリックする" イベントを受け取り、 対向するイベントを送信して反応する必要があります。

for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      //-- 新しいイベントを取得する
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      //-- そのタイプを定義する - ボタンのクリック
      if(id == ClickOnElement)
      {
         //-- ターミナルコンソールにクリックしたボタンの名前を表示する
         printf("You press '" + sparam + "' button");
         string msg;
         //-- クリックしたボタンの種類に応じて、メッセージボックスメッセージを形成します。
         if(el_name != "btnCancelAll")
            msg = "Are you sure you want to open a new " + sparam + " position?";
         else
            msg = "Are you sure you want to close all positions?";
         //-- メッセージ ボックス表示コマンドを使用して進行中のイベントを送信する
         GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OKCancel, msg);
      }
      ...
   }

さて、イベントを送信するシグネチャを分析してみましょう。

GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OKCancel, msg);

これは、EAがOKボタンとキャンセルボタン(OKCancel)を示す'msg'変数のメインテキストを含むメッセージボックスを表示することを意味します。 この記事の最初のパートでは、SendEvent メソッドの最初のパラメータに、送信されたイベント受信者のグラフィカル要素の名前が含まれていることについて説明しました。 ただし、このフィールドはメッセージ ボックスの動作が異なります。 メッセージ ボックスは、特定のグラフィカル ウィンドウまたは要素にバインドされません (ただし、Windows Frorms ではそのようなバインディングが許可されます)。 したがって、GuiController は新しいメッセージ ウィンドウを作成し、宛先アドレスは必要ありません。 一般に、メッセージを表示した後、そのメッセージが関連するウィンドウはブロックする必要があります。 実際、表示されるメッセージボックスを無視しながらBuyもしくはSellボタンを繰り返しクリックできるというのは、変です。 したがって、GuiController では、このイベントの最初のパラメータの名前は、ユーザーがメッセージ ボックス ボタンの 1 つをクリックするまでブロックする必要がある要素を表します。 任意のグラフィカル要素のブロッキング関数はオプションです。 lparam 変数を使用して指定されます: 0 -ウィンドウブロッキングなし、1 - ブロッキングが存在します。 ただし、ゼロや定数ではなく定数で操作する方がはるかに便利です。 これを実現するために、BlockingControl 列挙体を使用して GuiController で 2 つの定数が定義されています。

  • LockControl; 
  • NotLockControl

最初の1つは、ユーザーがボタンを押す前にウィンドウをブロックし、2 つ目のウィンドウはグラフィカル ウィンドウにユーザーがアクセスできないようにします。

テキストとは別に、メッセージ ウィンドウにはさまざまなボタンの組み合わせが含まれている場合もあります。 ボタンをクリックすると、ユーザーは特定の選択肢に同意することになります。 ボタン セットは、System.Windows.Forms.MessageBoxButtons システム列挙体を使用して定義されます。 列挙要素は外部ビルドで定義されているので、MQL ユーザーは使用できません。 GuiController を扱う MQL プログラマのタスクを簡略化するために、同じパラメータを持つ System.Windows.Forms.MessageBoxButtons のクローンという新しい列挙体を実装しました。 この列挙体は、IController.csで定義されます。

//
//概要:
//System.Windows.Forms.MessageBox に表示するボタンを定義する定数を指定します。
public enum MessageBoxButtons
{
    //
    //概要:
    //メッセージ ボックスには[OK]ボタンが表示されます。
    OK = 0,
    //
    //概要:
    //メッセージ ボックスには、[OK] ボタンと [キャンセル] ボタンが表示されます。
    OKCancel = 1,
    //
    //概要:
    //このメッセージ ボックスには、中止ボタン、再試行ボタン、および [無視] ボタンがあります。
    AbortRetryIgnore = 2,
    //
    //概要:
    //このメッセージ ボックスには、[はい]、[いいえ]、[キャンセル] ボタンがあります。
    YesNoCancel = 3,
    //
    //概要:
    //このメッセージ ボックスには[はい]ボタンと [いいえ] ボタンが表示されます。
    YesNo = 4,
    //
    //概要:
    // このメッセージ ボックスには、再試行ボタンとキャンセルボタンがあります。
    RetryCancel = 5
}

列挙の定数は、例えばIntelliSensを介してMQLエディタで直接利用可能であり、メッセージボックスの設定が便利です。 たとえば、SendEvent で OKCancel を YesNoCancel に置き換えた場合、ダイアログ ウィンドウには別のボタンセットが表示されます。

GuiController::SendEvent("ButtonForm", MessageBox, LockControl, YesNoCancel, msg);


図5. 3つのボタンの標準的な組み合わせ - はい/いいえ/キャンセル

ボタン セットとは別に、GuiController はメッセージ アイコンとウィンドウ ヘッダ テキストの構成をサポートします。 SendEvent メソッドには一定の数のパラメータがあるため、すべての設定を渡すのは問題があるため、別の解決策が見つかりました。 メッセージテキストラインは、" | " シンボルを使用してセクションに分割できます。 この場合、各セクションは特定の追加パラメータを担当します。 セクションの数は、1 つ (区切りシンボルなし) から 3 (2 つの区切りシンボル) までさまざまです。 さて、サンプルを考えてみましょう。 アイコンや追加のキャプションなしでシンプルなメッセージを表示するとします。 この場合、メッセージ送信形式は次のようになります。

GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OK, "This is a simple message");


図6. ウィンドウ名にアイコンと追加テキストのないシンプルなメッセージ

アイコンは、追加セクションで特別な定数を使用してメッセージに追加できます。 アラートアイコンを付けたメッセージを表示するとします。 これを行うには、メッセージ テキスト形式を次の形式に置き換えます。

GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OK, "Warning|Your action can be dangerous");

図7. アラートアイコン付きのメッセージ

アイコンは、キーワードを使用するだけでなく、仮名アイコンを使用して設定することもできます。 アラートの代わりに "?" とインプットすると、効果は次のようになります。 アラートとは別に、情報、質問、エラーアイコンを設定できます。 アイコンのキーワードと仮名の表を次に示します。

アイコン キーワード 仮名

ワーニング !

エラー !!!

インフォ i

クエスチョン  ?


アイコンとは別に、メッセージ ウィンドウの名前を設定することもできます。 これを行うには、テキストを "|"で区切り、ウィンドウ名をインプットします。 エラー メッセージを含むウィンドウの完全な定義の例を次に示します。

GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OK, "!!!|The operation was cancelled|Critical Error");

図8. エラー アイコンを持つ名前付きメッセージ ウィンドウ

このコントローラは、スマートライン解析を備えています。 "!!!|The operation was cancelled(!!!|操作が取り消されました)"を入力すると、適切なメッセージを含む重大なエラーアイコンが表示されます。 2 つのセクションを示す場合 "The operation was cancelled|Critical Error(操作はキャンセルされました| [重大なエラー)" 、アイコンは表示されませんが、ウィンドウ名は重大なエラーに変更されます。


TabControl

タブは、要素をグループに配置するための便利なツールです。

図9. 2 つのタブを持つパネル

タブコントロール要素は、単一の TabIndexChange イベントをサポートしています。 これはユーザーが別のタブに移動したことを通知します。 このテストEAでは、フォーム上のコードトラッキング集計の変更が機能します。 コードフラグメントを見てみましょう:

for(static int i = 0; i < GuiController::EventsTotal(); i++)
{
  int id;
  string el_name;
  long lparam;
  double dparam;
  string sparam;
  GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
  if(id == TabIndexChange)
     printf("Selecet new tab. Index: " + (string)lparam + " Name: " + sparam);
}

TabIndexChange イベントは、lparam と sparam という 2 つのパラメータを渡します。 最初のタブには、ユーザーが選択したタブのインデックスが含まれます。 2 つ目のタブには、選択したタブの名前があります。 たとえば、ユーザーが最初のタブを選択した場合、EAは次のメッセージをタイプします。

Selecet new tab. Index: 0 Name: tabPage1

タブは便利なグラフィカル要素です。 集計を追跡する必要はありません。 WindowsForm では、1 つのフォームのすべての要素に一意の名前を付ける必要があります。 したがって、異なるタブにある同じタイプの 2 つの要素は一意であり、WindForms に従って、異なる名前を付ける必要があります。 一方、通常は、直接制御の押した状態を追跡する必要があります。 このような要素が配置されているタブは、必ずしも重要ではありません。 ただし、このようなイベントを追跡する必要がある場合があるため、GuiController はこの要素に十分なデータのやり取りインターフェイスを提供します。

CheckBox

チェックボックスは、グラフィカルインターフェイスの重要な要素の 1 つです。 そのシンプルさにも関わらず、古いバージョンの Windows から始まり、Web アプリケーションとモバイル アプリケーションで終わるさまざまなインターフェイスで使用します。 任意のオプションの直感的な表示を可能にします。 また、ユーザーが互いに矛盾しないオプションを直感的に選択できるように、何らかの理由で使用できないオプションを表示することもできます。

 

図10. チェックボックスの組み合わせを使用したオプションの選択

チェックボックスには、チェック(Checked)、オフ(Unchecked)、および部分的にチェック(Indeterminate)の3つの状態があります。 Windows フォームには、次の状態を説明する System.Windows.Forms.CheckState 構造体が備わっています。

namespace System.Windows.Forms
{
    //
    //概要:
    //チェック ボックスなど、チェック ボックスをオンにしてオフにできるコントロールの状態を指定します。
    //または不確定状態に設定します。
    public enum CheckState
    {
        //
        //概要:
        //コントロールのチェックボックスがオフになっています。
        Unchecked = 0,
        //
        //概要:
        //コントロールがチェック状態
        Checked = 1,
        //
        //概要:
        //コントロールが不確定です。 不確定なコントロールは、一般にシェードがあります
        //外観。
        Indeterminate = 2
    }
}

ユーザーがこのチェック ボックスをクリックするたびに、GuiController は lparam 変数を使用して CheckBoxChange イベントを使用して、そのステータスを MQLEAに渡します。 その値は、列挙オプションの 1 つに対応します: 0 — 未チェック、1 — チェック状態、 2 — 不確定。

デモンストレーションの例では、EAは「EURUSDでトレードを有効にする」と「GBPUSDでのトレードを有効にする」チェックボックスの選択を追跡します。 オプションの 1 つが使用可能になるとすぐに、その 'Allow take profit(テイクプロフィットの許可)' 及び 'Allow stop loss(ストップロスの許可)'サブオプションのロックを解除します。 逆に、ユーザーがメイン オプションのいずれかからフラグを削除すると、サブオプションは一度にロックされます。 これは、ElementEnable と CheckBoxChange の 2 つのイベントのおかげで実現されます。 以下のコードは、EAによるチェックボックスの処理アルゴリズムを示します。

void OnTimer()
{   
   //-- タイマーで新しいイベントを取得する
   for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      if(id == CheckBoxChange)
         ChangeEnableStopAndProfit(el_name, id, lparam, dparam, sparam);
   }
}

//+------------------------------------------------------------------+
//| Change enable stops and profit                                   |
//+------------------------------------------------------------------+
void ChangeEnableStopAndProfit(string el_name, int id, long lparam, double dparam, string sparam)
{
   int id_enable = ElementEnable;
   if(el_name == "EURUSDEnable")
   {
      GuiController::SendEvent("EURUSDProfit", id_enable, lparam, dparam, sparam);
      GuiController::SendEvent("EURUSDStop", id_enable, lparam, dparam, sparam);
   }
   else if(el_name == "GBPUSDEnable")
   {
      GuiController::SendEvent("GBPUSDProfit", id_enable, lparam, dparam, sparam);
      GuiController::SendEvent("GBPUSDStop", id_enable, lparam, dparam, sparam);
   }
}

ユーザーがチェックボックスの 1 つをチェックしたことをEAが通知されるとすぐに、進行中の ElementEnable イベントが 「true」と等しいイベントを GuiController に送信します。 逆に、ユーザーがチェックボックスをオフにすると、ElementEnable が 'false' に等しく送信されます。 EAと異なるイベントの助けを借りて、フォームが相互に作用するため、データのやり取りが作成されます。


ラジオボタン

ラジオボタンは、ユーザーが定義済みのものから必要なポイントを選択できるようにするシンプルなグラフィカル要素です。

図11. ラジオボタン

ユーザーが選択を変更すると、EAは変更イベントを 2 回受け取ります。これは、オフのボタンとチェックされたボタンからです。 どちらのイベントも、同じ RadioButtonChange 識別子を使用して追跡されます。 その使用例を次に示します。

for(static int i = 0; i < GuiController::EventsTotal(); i++)
{
  int id;
  string el_name;
  long lparam;
  double dparam;
  string sparam;
  GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
  else if(id == RadioButtonChange)
  {
      if(lparam == true)
         printf("Your have selected " + sparam);
      else
         printf("Your have deselected " + sparam);
  }
}

lparam パラメータには、ボタンに何が起こったかを通知するフラグが含まれています。具体的には、チェックされたかどうか (plaram = true) またはオフ (lparam = false) です。 ボタンをクリックすると、EAはターミナルに同様のメッセージを表示します。

Your have deselected Expert
Your have selected Indicator
Your have deselected Indicator
Your have selected Script
...


コンボボックス

コンボ ボックスは、最も一般的な要素の 1 つです。 CheckBox と共に、Web 開発と最新のモバイル アプリケーションの両方に適用されます。 また、Windows で最も一般的に使用する要素の 1 つでもあります。


図12. コンボボックスと利用可能なメニュー項目

コンボ ボックスは、2 つのメイン モードで使用します。 最初の値は、ユーザーが既存の値とは別に新しい値をインプットできるようにします。

図13. 新しいシンボルをインプットする関数を持つシンボルの選択 

2 番目のモードでは、カスタムメニューを選択できない定義済みのメニュー項目のみがユーザーに提供されます (図11 を参照)。 ここには、メニュー項目を非表示にする 3 番目のモードもありますが、ほとんど使用されないため、今回はこだわりません。 

すべてのコンボボックス表示モードは、DropDownStyleプロパティを使用して設定されます。 このプロパティは通常、グラフィカル インターフェイスの開発時に 1 回設定されるため、GuiController には ComboBox の種類を変更できるイベントはありません。 ただし、このコントローラは、コンボ ボックスから要素の選択を追跡し、新しい値をインプットすることができます。 したがって、コンボボックスは、独自の ComboBoxChangeTextChangeの2つのイベントをサポートします。 デモ フォームは、2 つの ComboBox 要素で構成されています。 最初の1つはプラットフォーム(MetaTrader4/MetaTrader5)を選択し、2つ目はシンボルを選択します。 2 番目の要素はデフォルトでブロックされます。 ただし、ユーザーがプラットフォームを選択するとすぐに、トレードシンボルを選択できます。 関数を実装するコードを次に示します。 

for(static int i = 0; i < GuiController::EventsTotal(); i++)
{
  int id;
  string el_name;
  long lparam;
  double dparam;
  string sparam;
  GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
  if(id == ComboBoxChange)
  {
     if(el_name == "comboBox1")
        //-- ユーザーがプラットフォームを選択するとすぐにシンボルの一覧のブロックを解除します。
        GuiController::SendEvent("comboBox2", ElementEnable, 1, 0.0, "");
     printf("ComboBox '" + el_name + "' was changed on " + sparam);
  }
}

コンボ ボックス要素の選択を開始すると、デモEAは、ユーザーが選択したパラメータをターミナルに表示します。

ComboBox 'comboBox1' was changed on MetaTrader 5(コンボボックス「コンボボックス1」がMetaTrader5で変更されました)
ComboBox 'comboBox2' was changed on GBPUSD(コンボボックス'コンボボックス2'はGBPUSDで変更されました)
ComboBox 'comboBox2' was changed on USDJPY(コンボボックス「コンボボックス2」をUSDJPYで変更)
...


NumericUpDown (数値リスト ウィンドウ)

数値リストウィンドウは、トレーディングパネルを含む分析システムでよく使用します。 この要素が GuiController に最初にあるのはこのためです。 数値リスト ウィンドウを使用すると、インプットタイプを制御する特定の値を設定できます。 インプットできるのは数値のみです。 値変更ステップは、特別なミニスクロールを使用して変更できます。 番号の桁容量も設定可能です。

図14. 数値リスト ウィンドウ

GuiControllerは、この要素型の4つのイベントをサポートします。

  • NumericChangeウィンドウの新しい数値を含むイベントを受信または送信します。
  • NumericFormatChange数値の桁容量 (lparam 変数内) とその変更ステップ (dparam 変数) を指定するイベントを送信します。
  • NumericMaxChange可能な最大値を指定するイベントを送信します。
  • NumericMinChangeは、可能な限り少ない値を指定するイベントを送信します。

NumericUpDown は、単一の NumericChange イベントを介してユーザーとデータのやり取りします。 ユーザーがこのウィンドウの数値を変更すると、EAはイベントを介して適切な通知を受け取ります。 これは、唯一可能なユーザー操作です。 ただし、このEAは、最も重要なパラメータ(ディジット容量、変更ステップ、および許容値の最大値と最小値)を設定するウィンドウを構成できます。 これらのパラメータはすべて、EAロジックと動作するデータの種類に依存するため、フォーム設定で直接定義することは不可能です。 プログラムの起動時に定義する必要があります。

テストEAには、NumericUpDown の操作を示す小さな例があります。 図13からフォームをアップロードするコードを以下に示します。 

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(ElementType != WINFORM_HIDE)
      EventSetMillisecondTimer(100);
   else
      EventSetMillisecondTimer(1000);
   switch(ElementType)
   {
      ...
      case WINFORM_NUMERIC:
      {
         GuiController::ShowForm(assembly, "NumericForm");
         double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
         double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
         double price_step = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE), Digits());
         long digits = (long)Digits();
         GuiController::SendEvent("NumericForm", TextChange, 0, 0.0, "NumericForm (" + Symbol() + ")");
         NumericSet("StopLoss", Digits(), ask, (double)LONG_MAX, 0.0, price_step);
         NumericSet("TakeProfit", Digits(), bid, (double)LONG_MAX, 0.0, price_step);
         break;
      }
      ...
   }
   return(INIT_SUCCEEDED);
}

コードから分かるように、アップロード中に、EAは現在のシンボルに関するデータを受け取ります。そのアスクとビッドの価格、桁容量と価格ステップです。 その後、パラメータは、特別な補助 NumericSet 関数を使用して NumericUpDown フォーム要素に設定されます。 そのコードを見てみましょう:

//+------------------------------------------------------------------+
//| Set NumericUpDownParameter                                       |
//| name - name of NumericUpDown element                             |
//| digits - digits of symbol                                        |
//| init - init double value                                         |
//| max - max value                                                  |
//| min - min value                                                  |
//| step - step of change                                            |
//+------------------------------------------------------------------+
void NumericSet(string name, long digits, double init, double max, double min, double step)
{
   int id_foramt_change = NumericFormatChange;
   int id_change = NumericChange;
   int id_max = NumericMaxChange;
   int id_min = NumericMinChange;
   long lparam = 0;
   double dparam = 0.0;
   string sparam = "";
   GuiController::SendEvent(name, id_max, lparam, max, sparam);
   GuiController::SendEvent(name, id_min, lparam, min, sparam);
   GuiController::SendEvent(name, id_change, lparam, init, sparam);
   GuiController::SendEvent(name, id_foramt_change, digits, step, sparam);
}

コードは適応的に動作します。 起動されるシンボルに応じて、異なる価格形式が表示されます。


図15. シンボルごとに個別の価格形式

DataTimePicker (date selection window)

この要素は NumericUpDown の概念に似ていますが、数値ではなく日付を安全に選択できるのは唯一の違いです。

図16. DataTimePicker 要素で正確な時刻を選択する

DataTimePicker とのデータのやり取りは、数値アップダウン要素と比較して簡単です。 これは、EAの現在の取引環境に応じて番号形式とは異なり、日付形式が多かれ少なかれ普遍的であるという事実によるものです。 フォームを開発する際に設定でき、その後そのままにしておくことができます。 したがって、DataTimePicker は、lparam パラメータを介して正確な日付を渡して受け取る単一の DateTimePickerChangeイベントをサポートします。 要素の使用例を次に示します。

for(static int i = 0; i < GuiController::EventsTotal(); i++)
{
  int id;
  string el_name;
  long lparam;
  double dparam;
  string sparam;
  GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
  if(id == DateTimePickerChange)
     printf("User set new datetime value: " + TimeToString((datetime)lparam));
}

デモEAを起動し、デモ要素として DateTimePicker を選択すると、図15 に示すウィンドウと同様のウィンドウが表示されます。 さまざまな方法で日付と時刻の変更を開始すると、EAはログに新しい日付と時刻の値を表示するイベントに応答します。

User set a new datetime value: 2019.05.16 14:21
User set a new datetime value: 2019.05.16 10:21
User set a new datetime value: 2021.05.16 10:21

ただし、要素とのデータのやり取りは、見かけよりもやや複雑です。 MQL と C# の時刻形式は異なります。 MQL は、1秒刻みで 1970.01.01からシンプル化された POSIX タイム形式を備えていますが、C# は 100 ナノ秒刻みで、より高度な時間形式を備えています。 したがって、異なるシステムとデータのやり取りするには、ある時間形式を別の時間形式に変換するタイムコンバータを開発する必要があります。 このようなコンバータは GuiController で使用します。 MtConverterパブリック静的クラスとして設計されています。

/// <summary>
/// System Converter MetaTrader - C# 
///
public static class MtConverter
{
    /// <summary>
    /// Convert C# DateTime format to MQL (POSIX) DateTime format.
    ///
    ///<param name="""date_time""">
    ///<returns></returns>
    public static long ToMqlDateTime(DateTime date_time)
    {
        DateTime tiks_1970 = new DateTime(1970, 01, 01);
        if (date_time < tiks_1970)
            return 0;
        TimeSpan time_delta = date_time - tiks_1970;
        return (long)Math.Floor(time_delta.TotalSeconds);
    }
    /// <summary>
    /// Convert MQL (Posix) time format to sharp DateTime value.
    ///
    /// <param name="mql_time">MQL datetime as tiks</param>
    ///<returns></returns>
    public static DateTime ToSharpDateTime(long mql_time)
    {
        DateTime tiks_1970 = new DateTime(1970, 01, 01);
        if (mql_time <= 0 || mql_time > int.MaxValue)
            return tiks_1970;
        TimeSpan time_delta = new TimeSpan(0, 0, (int)mql_time);
        DateTime sharp_time = tiks_1970 + time_delta;
        return sharp_time;
    }
}

現在、ToMqlDateTime は DateTime を MQL に変換するという 2 つのメソッドのみで構成されています。 2番目のメソッドでは、MQL 時間値を C# DateTime 構造体に変換するのとまったく逆のメソッドです。 datetime (mql) 型と DateTime(C#) 型は互いに互換性がないため、変換はすべてのシステムで同じ一般的な 'long' 型を介して実行されます。 したがって、DateTimePickerChange イベントを受け取ったときに、正しい時刻値を受け取るように、lparam を日付時刻に明示的に変換する必要があります。

//-- long値を日付時刻に明示的に変換します。 これで完全に安全です
printf("User set new datetime value: " + TimeToString((datetime)lparam));

新しい値を得るだけでなく、同様の方法で自分たちで設定することができます。 たとえば、次のコマンドを使用して、現在のターミナル時刻を設定できます。

GuiController::SendEvent("DateTimePicker", DateTimePickerChange, ((long)TimeCurrent()), 0.0, "");


要素非表示と要素を有効にする - 任意の要素を非表示にして無効にする

WinForms 要素を制御できるユニバーサル イベントがあります。 ElementHide イベントと ElementEnable イベントは、その中に含まれます。 要素を非表示にするには、ElementHide を次の方法で使用します。

GuiController::SendEvent("HideGroup", ElementHide, true, 0.0, "");

ここで、HideGroup は非表示にする要素の名前です。 要素を表示するには、それぞれ次を呼び出します。

GuiController::SendEvent("HideGroup", ElementHide, false, 0.0, "");

使用する要素の名前に注意してください。 WindowsForm の 1 つの要素に内部要素をインクルードする場合があります。 これは、ネストにする要素と呼ばれます。 このような配置により、グループ レベルですべての要素を管理できます。 デモンストレーションの例では、テキスト ボックスに 'label' キャプションがネストになっているテキスト ボックスが使用されています。 特定の頻度で、ボックス内のすべての要素でボックスが消え、再び表示されます。

 

図17. EAから任意のグラフィカル要素を非表示にする

また、各要素を使用不可にすることもできます。 これは消えませんが、扱うことは不可能になります。 これが、より複雑で直感的なインターフェイスを設計できる便利なオプションです。 フラグの説明でこのイベントの操作について既に説明しました。要素は、次のイベントを送信することによって使用不可にすることができます。

GuiController::SendEvent("element", ElementEnable, false, 0.0, "");

この要素は、'false'フラグを'true'に置き換えるだけで、再びアクティブにすることができます。

GuiController::SendEvent("element", ElementEnable, true, 0.0, "");


AddItem — サブ要素の追加

一部の要素には、他の要素が含まれている場合があります。 サブ要素の内容は、プログラムが開始される前に不明なことがよくあります。 ユーザーが必要なものを選択できるように、トレード銘柄のリストを表示する必要があるとします。 このため、コンボボックスを使用するのが最も合理的です。


図18. プリセットシンボルの一覧

ただし、使用可能なシンボルのリストはブローカーによって異なる場合があるため、グラフィカルフォームのコンパイルの段階でシンボルを事前にインプットすることはできません。 したがって、この型の内容は動的に形成する必要があります。 AddItemコマンドを使用します。 最も簡単な方法は、MarketWatch で利用可能なすべてのシンボルを一覧表示し、メニュー項目として追加することです。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   EventSetMillisecondTimer(100);
   GuiController::ShowForm(assembly, "SendOrderForm");
   for(int i = 0; i < SymbolsTotal(true); i++)
   {
      GuiController::SendEvent("SymbolComboBox", AddItem, 0, 0, SymbolName(i, true));
   }
   return(INIT_SUCCEEDED);
}

ご覧のとおり、このコマンドは使いやすいです。 コマンドはユニバーサルですが、すべての要素でサブ要素を追加できるわけではありません。 現在、GuiController はこのイベントをコンボボックスでのみサポートします。 ただし、効率的なグラフィカル インターフェイスを構築するのに十分です。 このリストは、将来的に展開される可能性があります。


例外 — 例外を受け取るイベント

エラーなしでプログラムを記述することは常に可能であるとは限りません。 また、何かが間違っていて、予期せぬシナリオに従ってプログラムが実行された場合もあります。 このような場合は、何が起こったかに関するデータを取得する可能性のあるフィードバックが必要です。 例外イベントは、この種のデータのやり取りに提供されます。 GuiController 自体によって生成され、MQL EAに送信されます。 GuiControllerは、次の 2 つのケースでエラー メッセージを作成します。

  1. システム例外インターセプトの場合。
  2. EAから送信されたイベントが、EAに対して選択された要素をサポートしていない場合、またはイベント自体に無効な値がある場合です。

オプションを 1 つずつ分析してみましょう。 最初のオプションを説明するために、デモンストレーションコード、すなわちNumericUpDown要素を表示するオプションに戻りましょう。 この起動オプションでは、特別な NumericSet 関数が呼び出されます。 NumericUpDown 要素のタスク範囲を設定します。

void NumericSet(string name, long digits, double init, double max, double min, double step)
{
   int id_foramt_change = NumericFormatChange;
   int id_change = NumericChange;
   int id_max = NumericMaxChange;
   int id_min = NumericMinChange;
   long lparam = 0;
   double dparam = 0.0;
   string sparam = "";
   // GuiController::SendEvent(name, id_max, lparam, max, sparam);
   GuiController::SendEvent(name, id_min, lparam, min, sparam);
   GuiController::SendEvent(name, id_change, lparam, init, sparam);
   GuiController::SendEvent(name, id_foramt_change, digits, step, sparam);
}

この関数はわずかに変更されたため、要素の最大値は設定されません (その中の対応する文字列はコメントアウトされます)。 ゴールド チャートでコンパイルした後にこのフォームを実行すると、現在の価格の表示が突然ストップします。

図19. 価格設定フォームのゼロ値

これはどういうことでしょうか。 例外システムは、この質問に答えるために使用します。 各例外は、他のメッセージと同様に、GuiController::GetEvent を介して取得できます。

//-- タイマーで新しいイベントを取得する
for(static int i = 0; i < GuiController::EventsTotal(); i++)
{
  int id;
  string el_name;
  long lparam;
  double dparam;
  string sparam;
  GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
  ...
  if(id == Exception)
     printf("Unexpected exception: " + sparam);
  
}

このメッセージ出力は多くのことを出力します。

Unexpected exception: '1291,32' value is unacceptable for 'Value'. 'Value' should lie in the range from 'Minimum' to 'Maximum'.
Parameter name: Value
Unexpected exception: '1291,06' value is unacceptable for 'Value'. 'Value' should lie in the range from 'Minimum' to 'Maximum'.
Parameter name: Value

実際、NumericUpDown のタスク範囲は 0 ~ 100 です。 したがって、金の現在の価格(トロイオンス当たり1292ドル)を設定すると、エラーが発生します。 これを回避するには、NumericMaxChange イベントを使用してプログラムによって許容値の範囲を拡張します。 NumericSet 関数のコメントアウト文字列が行うものです。

間違ったイベントを送信する場合は、例外を呼び出す 2 番目のオプションが可能です。 たとえば、存在しない宛先にイベントを送信できます。

GuiController::SendEvent("empty", ElementEnable, 0, 0.0, "");

応答は次のようになります。

Unexpected exception: SendEvent: element with name 'empty' not find

また、アイテムをサポートしていないイベントを送信することもできます。 たとえば、SLレベルインプットフィールド (NumericUpDown 要素タイプ) に次のテキストを追加してみましょう。

GuiController::SendEvent("StopLoss", AddItem, 0, 0.0, "New Text");

答えは簡潔になります:

Unexpected exception: Element 'StopLos' doesn't support 'Add Item' event

例外のシステムは、複雑なグラフィックアプリケーションを作成する上で重要な補助を提供します。 このようなアプリケーションのエラーは避けられません。 開発の速度と利便性は、プログラマが識別できる速度によって異なります。


使用可能なグラフィカル要素とイベントの概要テーブル

サポートされているグラフィック要素をシステム化して、GuiController で動作させるのが妥当です。 これを行うには、要素とその使用方法に関する概要情報を含むテーブルを作成します。 "サンプル使用例" 列には、この要素を MQL プログラムから使用する方法を示す簡単なサンプル コードがあります。 問題のコードは、要素を操作する全体的なパターンの一部として考慮する必要があります。 たとえば、最初の例 (MessageBox) のコードを次に示します。

string msg = "!!!|The operation was cancelled|Critical Error";
GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OK, msg);

次のコンテキストで考慮する必要があります。

void OnTimer
{
   //...
   //-- タイマーで新しいイベントを取得する
   for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
     int id;
     string el_name;
     long lparam;
     double dparam;
     string sparam;
     GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
     if(id == MessageBox)
     {
        string msg = "!!!|The operation was cancelled|Critical Error";
        GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OK, msg);
     }
  }
}

このパターンは、同様の方法で他のサンプルの使用に適用されます。

グラフィカル要素 要素またはイベント名 キー イベントの一部 サンプル使用状況

MessageBox MessageBox
string msg = "!!!|The operation was cancelled|Critical Error";
GuiController::SendEvent("ButtonForm", MessageBox, LockControl, OK, msg);



Tabs TabIndexChange
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Selecet new tab. Index: " + (string)lparam + " Name: " + sparam);



CheckBox CheckBoxChange
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Checked " + sparam + " " + lparam);


RadioButton RadioButtonChange
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            

GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam); if(lparam == true)    printf("Your have selected " + sparam); else    printf("Your have deselected " + sparam);




ComboBox ComboBoxChange
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("ComboBox '" + el_name + "' was changed on " + sparam);


NumericUpDown NumericChange
NumericFormatChange
NumericMaxChange
NumericMinChange
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Numeric '" + el_name + "' was changed, new value: " + DoubleToString(dparam, 4));



DateTimePicker DateTimePickerChange
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            

GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam); printf("User set new datetime value: " + TimeToString((datetime)lparam));



 

Vertical Scroll  ScrollChange  
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Vertical Scroll has new value: " + (string)lparam);

 

 TextBox TextChange   
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("new value entered: " + sparam);

 

 Button  ClickOnElement  
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Button " + sparam + " is pressed");

  Label TextChange   
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Label has new text: " + sparam);

グラフィカル要素とは別に、GuiController はユニバーサル イベントをサポートします。 イベントをテーブルの形で並べ替えましょう。

イベント 詳細 サンプル使用状況
ElementHide  要素の非表示/復元
GuiController::SendEvent("HideGroup", ElementHide, true, 0.0, "");
ElementEnable  要素の有効化/無効化
GuiController::SendEvent("HideGroup", ElementEnable, true, 0.0, "");
AddItem  選択した要素に新しいサブ要素を追加する  
GuiController::ShowForm(assembly, "SendOrderForm");
for(int i = 0; i < SymbolsTotal(true); i++)
   GuiController::SendEvent("SymbolComboBox", AddItem, 0, 0, SymbolName(i, true));
Exception   CLR 内で呼び出される例外を取得する  
GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
printf("Unexpected exception: " + sparam);



結論

Windows フォームの主なグラフィカル要素と、Windows フォームとのデータのやり取りの例を分析しました。 これらの要素は少ないですが、グラフィック アプリケーションのバックボーンを表します。 テーブル (トレードにおけるもう 1 つの重要な要素) は含まれていませんが、機能的なグラフィック アプリケーションを作成するために使用できます。

直近の GuiController バージョンは以下に添付されています。 また、このバージョンは GitHub リポジトリ システムからコピーできます。 このライブラリバージョンは、ここにあります: https://github.com/PublicMqlProjects/MtGuiController.git. デモンストレーション フォーム プロジェクトをコピーすることもできます。 ファイルの場所はここにあります。https://github.com/PublicMqlProjects/GuiControllerElementsDemo.git. この記事の最初のパートを読んで、バージョン管理システムを介して最新のライブラリバージョンを入手する方法を見つけてください。 添付ファイルには、すべてのプロジェクトの完全なソースコードがあります。 要素を含むフォームを呼び出すEA、DemoForm.exe ビルドのフォームのセット、コンパイルされた GuiController (Source\MQL5\Libraries\GuiController.dll) とソース コードの形式のフォーム (Source\Sharp\GuiController) の 3 つがあります。 また、デモEAには起動されたフォームへの絶対パスが必要です。 お使いのPCでは、「アセンブル」EAパラメータで指定されたものとは異なるため、絶対パスに置き換えてください。

MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/6549

添付されたファイル |
Source.zip (50.14 KB)
トレードにおけるOLAPの適用(パート1):多次元データのオンライン分析 トレードにおけるOLAPの適用(パート1):多次元データのオンライン分析

この記事では、多次元データ(OLAP)のオンライン分析のフレームワークを作成する方法、およびMQLで実装する方法、およびトレード口座ヒストリー処理の例を使用してMetaTrader環境でそのような分析を適用する方法について説明します。

MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第3部)成行注文と取引のコレクション、検索と並び替え MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第3部)成行注文と取引のコレクション、検索と並び替え

最初の部分では、MetaTrader 5とMetaTrader 4プラットフォーム用のプログラムの開発を単純化するための大規模なクロスプラットフォームライブラリの作成を始めました。さらに、履歴の注文と取引の収集を実装しました。次のステップは、コレクションリスト内の注文、取引、ポジションの便利な選択と並び替えのためのクラスを作成することです。Engineという基本ライブラリオブジェクトを実装し、成行注文とポジションのコレクションをライブラリに追加します。

トレードにおけるOLAPの適用(パート2):インタラクティブな多次元データ分析結果の可視化 トレードにおけるOLAPの適用(パート2):インタラクティブな多次元データ分析結果の可視化

この記事では、OLAP技術を使用して口座ヒストリーとトレードレポートの処理に設計されたMQLプログラム用のインタラクティブなグラフィカルインタフェースの作成について考察します。 視覚的な結果を得るために、最大化可能でスケーラブルなウィンドウ、ラバーコントロールの適応レイアウト、および図を表示するための新しいコントロールを使用します。 ビジュアライゼーション関数を提供するために、座標軸に沿った変数の選択と、集計関数、ダイアグラムタイプ、並べ替えオプションの選択を含むGUIを実装します。

フラクタル指数とハースト指数の財務時系列を予測する能力の評価 フラクタル指数とハースト指数の財務時系列を予測する能力の評価

金融データのフラクタル行動の探索に関する研究は、経済時系列の一見混沌とした行動の背後に、参加者の集団行動の隠されたメカニズムがあることを前提にしています。 これらのメカニズムは、価格シリーズの特性を定義することができ、取引所の価格ダイナミクスの出現につながることができます。 これをトレーディングに適用すると、実際に関連するスケールと時間枠のフラクタルパラメータを効率的かつ確実に推定できるインジケータの恩恵を受けることができます。