
グラフィカルインタフェース V:コンボボックス要素(チャプター 3)
コンテンツ
- はじめに
- コンボボックスコントロール
- コンボボックスコントロール作成クラスの記述
- コントロールイベント処理メソッド
- ライブラリエンジンとコントロールクラスの接続
- カスタムアプリケーションのグラフィカルインタフェースでのコンボボックスコントロールの検証
- おわりに
はじめに
シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的を詳細に考察します。記事へのリンクの完全なリストは各章の末尾でみられます。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。
シリーズの第五部の最初の2つの記事では、スクロールバーとビューリストを作成するためのクラスが作成されました。そこでは、データが指定された領域に収まらない場合要素にスクロールバーを取り付ける方法を示しました。この章では、コンボボックスコントロールのクラスを作成する方法についてお話します。これはまた、とりわけ第五部の前の章で考慮された要素を含むコンパウンドコントロールです。
コンボボックスコントロール
コンボボックスとは主な部分が (1) ボタンと (2) リストビューであるコンパウンドコントロールです。この場合、リストビューはドロップダウン要素であってボタンを押すことによって呼び出されます。リストビューの項目が選択されると、そのテキストがボタンに表示されリストビューが隠されます。プログラムに多くのマルチオプションパラメータが含まれている場合、コンボボックスを使用するとコンパクトなグラフィカルインターフェースを作成できるようになります。
以下は、コンボボックスコントロールを構成するためのプリミティブオブジェクトです。
- 要素の背景
- ラベル(要素の記述)
- ボタン
- ドロップダウンリストビューの表示
図1。コンボボックスコントロールの複合部分
本稿の次の部分では、このコントロールを作成するためのクラスを記述します。
コンボボックスコントロール作成クラスの記述
本稿では、将来同じようなクラスを作成するための例として使用することができるように、コンボボックスコントロールの開発のすべての段階が検討されます。初めに、mqhファイル(ComboBox.mqh)を作成し、コンボボックス作成に必要なファイルをすべて含みます。この場合、ファイルは3つあります。
- CElement — コントロール作成の基本クラス
- CWindow — このコントロールが取り付けられるフォームのクラス
- CListView — 可視性がコンボボックスで管理されるリストビューのクラス
//+------------------------------------------------------------------+ //| ComboBox.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "ListView.mqh"
CComboBoxクラスとライブラリ要素に標準的なメソッドをComboBox.mqhファイルで作成します。
//+------------------------------------------------------------------+ //| コンボボックス作成クラス | //+------------------------------------------------------------------+ class CComboBox : public CElement { private: //--- 要素が取り付けられるフォームへのポインタ CWindow *m_wnd; //--- public: CComboBox(void); ~CComboBox(void); //--- フォームポインタを格納する void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- チャートイベントハンドラ virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- タイマー virtual void OnEventTimer(void); //--- 要素の移動 virtual void Moving(const int x,const int y); //--- (1)表示 (2)非表示 (3)リセット (4)削除 virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- マウスの左クリックの優先順位の(1)設定と(2)リセット virtual void SetZorders(void); virtual void ResetZorders(void); //--- 色をリセットする virtual void ResetColors(void); }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CComboBox::CComboBox(void) { //--- 要素クラスの名前を基本クラスに格納する CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CComboBox::~CComboBox(void) { }
ユーザは自分自身でグラフィカルインタフェースのカラースキームを選択する機会を持っている必要があります。そのためには、コントロールを構成するためのオブジェクトのプロパティの設定へのアクセスが必要です。以下はコントロールが作成される前に設定するプロパティです。
- コントロールの背景色
- コンボボックスの表示説明(テキストラベル)
- X及びY軸に沿ったテキストラベルの余白
- 異なる状態にあるテキストラベルの色
- ボタンのテキスト(選択されたリストビュー項目のテキスト)
- ボタンサイズ
- 異なる状態にあるボタンの色
- 異なる状態にあるボタンフレームの色
- 異なる状態にあるボタンテキストの色
- 矢印アイコン - ドロップダウンリストビューの表示にはアクティブモードとブロックモードがあります。
- X及びY軸に沿った矢印アイコンの余白
次のコードには、上記のプロパティを実装するためのフィールドとメソッドが含まれています。
class CComboBox : public CElement { private: //--- コンボボックスプロパティ // 一般的な背景色 color m_area_color; //--- テキストとテキストラベルのマージン string m_label_text; int m_label_x_gap; int m_label_y_gap; //--- 異なる状態にあるテキストラベルの色 color m_label_color; color m_label_color_off; color m_label_color_hover; color m_label_color_array[]; //--- (1) ボタンテキストと (2) サイズ string m_button_text; int m_button_x_size; int m_button_y_size; //--- 異なる状態にあるボタンの色 color m_button_color; color m_button_color_off; color m_button_color_hover; color m_button_color_pressed; color m_button_color_array[]; //--- 異なる状態にあるボタンフレームの色 color m_button_border_color; color m_button_border_color_off; //--- 異なる状態にあるボタンテキストの色 color m_button_text_color; color m_button_text_color_off; //--- ラベルの余白 int m_drop_arrow_x_gap; int m_drop_arrow_y_gap; //--- アクティブおよびブロックされた状態のドロップダウンメニューとボタンのラベル string m_drop_arrow_file_on; string m_drop_arrow_file_off; //--- マウスの左クリックの優先順位 int m_area_zorder; int m_button_zorder; int m_zorder; //--- public: //--- (1) 背景色、テキストラベルの値を(2)設定して (3)返す void AreaColor(const color clr) { m_area_color=clr; } void LabelText(const string label_text) { m_label_text=label_text; } string LabelText(void) const { return(m_label_text); } //--- テキストラベルの余白 void LabelXGap(const int x_gap) { m_label_x_gap=x_gap; } void LabelYGap(const int y_gap) { m_label_y_gap=y_gap; } //--- (1) ボタンテキストを戻す (2) ボタンサイズの設定 string ButtonText(void) const { return(m_button_text); } void ButtonXSize(const int x_size) { m_button_x_size=x_size; } void ButtonYSize(const int y_size) { m_button_y_size=y_size; } //--- (1) 背景色 (2) テキストの色 void LabelColor(const color clr) { m_label_color=clr; } void LabelColorOff(const color clr) { m_label_color_off=clr; } void LabelColorHover(const color clr) { m_label_color_hover=clr; } //--- ボタンの色 void ButtonBackColor(const color clr) { m_button_color=clr; } void ButtonBackColorOff(const color clr) { m_button_color_off=clr; } void ButtonBackColorHover(const color clr) { m_button_color_hover=clr; } void ButtonBackColorPressed(const color clr) { m_button_color_pressed=clr; } //--- ボタンフレームの色 void ButtonBorderColor(const color clr) { m_button_border_color=clr; } void ButtonBorderColorOff(const color clr) { m_button_border_color_off=clr; } //--- ボタンテキストの色 void ButtonTextColor(const color clr) { m_button_text_color=clr; } void ButtonTextColorOff(const color clr) { m_button_text_color_off=clr; } //--- ドロップダウンメニューでのボタンのアクティブおよびブロック状態のアイコンの設定 void DropArrowFileOn(const string file_path) { m_drop_arrow_file_on=file_path; } void DropArrowFileOff(const string file_path) { m_drop_arrow_file_off=file_path; } //--- ラベルの余白 void DropArrowXGap(const int x_gap) { m_drop_arrow_x_gap=x_gap; } void DropArrowYGap(const int y_gap) { m_drop_arrow_y_gap=y_gap; } };
上記のすべてのプロパティのデフォルト値による初期化はクラスコンストラクタで行われます。
//+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CComboBox::CComboBox(void) : m_area_color(C'15,15,15'), m_label_text("combobox: "), m_label_x_gap(0), m_label_y_gap(2), m_label_color(clrWhite), m_label_color_off(clrGray), m_label_color_hover(C'85,170,255'), m_button_text(""), m_button_y_size(18), m_button_text_color(clrBlack), m_button_text_color_off(clrDarkGray), m_button_color(clrGainsboro), m_button_color_off(clrLightGray), m_button_color_hover(C'193,218,255'), m_button_color_pressed(C'153,178,215'), m_button_border_color(clrWhite), m_button_border_color_off(clrWhite), m_drop_arrow_x_gap(16), m_drop_arrow_y_gap(1), m_drop_arrow_file_on(""), m_drop_arrow_file_off("") { //--- 左マウスクリックの優先順位を設定する m_zorder =0; m_area_zorder =1; m_button_zorder =2; }
コンボボックスは5つのプライベートメソッドをもって作成され、これらのメソッドはメインパブリックメソッドのCComboBox::CreateComboBox()から呼び出されます。リストビューの設定とスクロールバープロパティにアクセスするには、これらの要素のポインタ取得メソッドを作成します。
class CComboBox : public CElement { private: //--- コンボボックス作成のためのオブジェクト CRectLabel m_area; CLabel m_label; CEdit m_button; CBmpLabel m_drop_arrow; CListView m_listview; //--- public: //--- コンボボックス作成メソッド bool CreateComboBox(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreateLabel(void); bool CreateButton(void); bool CreateDropArrow(void); bool CreateList(void); //--- public: //--- (1) リストビューと (2) スクロールバーへのポインタを返す CListView *GetListViewPointer(void) { return(::GetPointer(m_listview)); } CScrollV *GetScrollVPointer(void) { return(m_listview.GetScrollVPointer()); } };
上のコードに示されたメソッドのうち、ここではリストビュー作成に使われるCComboBox::CreateList() メソッドの詳細のみを考察します。他のメソッドには、このシリーズの前回の記事ですでに取り上げられていないものが含まれていません。その前にリストビューのCListViewクラスに変化を導入しなければなりません。
ドロップダウンリストビューは、常に他の要素のコンパウンドの一部です。これは、イベントハンドラがそれが取り付けられている要素上の焦点を追跡する必要があることを意味します。我々の場合それはコンボボックスです。リストビューが取り付けられるコンボボックスへのポインタを格納するフィールドとメソッドをCListViewクラスに追加します。
class CListView : public CElement { private: //--- リストビューの可視性を管理する要素へのポインタ CElement *m_combobox; //--- public: //--- コンボボックスポインタを格納する void ComboBoxPointer(CElement &object) { m_combobox=::GetPointer(object); } };
リストビューがドロップダウンの場合は、リストビューを作成するためのメイン(パブリック)メソッドにポインタのチェックを追加します。リストの作成中にポインタがないと判明した場合は、グラフィカルインタフェースの作成が終了され、関連するメッセージが操作ログに印刷されます。
下記はCListView::CreateListView()メソッドの短縮版です。
//+------------------------------------------------------------------+ //| リストビューを作成する | //+------------------------------------------------------------------+ bool CListView::CreateListView(const long chart_id,const int window,const int x,const int y) { //--- フォームポインタがなければ終了する //--- リストビューがドロップダウンである場合、取り付け先のコンボボックスのポインタが必要 if(CElement::IsDropdown()) { //--- コンボボックスのポインタがない場合には終了する if(::CheckPointer(m_combobox)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating a drop-down list view, the class must be passed " "a pointer to the combobox: CListView::ComboBoxPointer(CElement &object)"); return(false); } } //--- 変数の初期化 //--- 端からのマージン //--- ボタンの作成 //--- ダイアログウィンドウか最小化されたウィンドウの場合は要素を非表示にする //--- return(true); }
ここでまたコンボボックスクラス(CComboBox)の開発を始めます。リストビューがドロップダウンであることを示すプロパティは非常に早い段階で、つまりクラスコンストラクタで設定される必要があります。
CComboBox::CComboBox(void) { //--- ドロップダウンリストビューモード m_listview.IsDropdown(true); }
リストビューの作成時には、それが取り付けられるフォームとコンボボックスへのポインタはメソッドの初めに格納されなければなりません。1つのコントロールとみなされるリストビューとコンボボックスには共通の識別子が必要なことにご注意下さい。リストビューは作成後に非表示にされる必要があります。
//+------------------------------------------------------------------+ //| リストビューを作成する | //+------------------------------------------------------------------+ bool CComboBox::CreateList(void) { //--- フォームとコンボボックスのポインタを格納する m_listview.WindowPointer(m_wnd); m_listview.ComboBoxPointer(this); //--- 座標 int x=CElement::X2()-m_button_x_size; int y=CElement::Y()+m_button_y_size; //--- 優先順位の設定 m_listview.Id(CElement::Id()); m_listview.XSize(m_button_x_size); //--- コントロールを作成する if(!m_listview.CreateListView(m_chart_id,m_subwin,x,y)) return(false); //--- リストビューを隠す m_listview.Hide(); return(true); }
リストビューの項目数を設定して値を書き入れるにはCComboBox クラスに適切なメソッドを追加します。
class CListView : public CElement { public: //--- (1) リストビューのサイズ(項目数)と (2) 見える部分の設定 void ItemsTotal(const int items_total) { m_listview.ListSize(items_total); } void VisibleItemsTotal(const int visible_items_total) { m_listview.VisibleListSize(visible_items_total); } //--- 特定のインデックスで渡された値をリストビューに格納する void ValueToList(const int item_index,const string item_text); }; //+------------------------------------------------------------------+ //| 特定のインデックスで渡された値をリストビューに格納する | //+------------------------------------------------------------------+ void CComboBox::ValueToList(const int item_index,const string item_text) { m_listview.ValueToList(item_index,item_text); }
リストビュー項目を選ぶ(強調表示する)にはCComboBox::SelectedItemByIndex()メソッドを作成します。強調表示されなければならない項目のインデックスが、このメソッドに渡されなければならない唯一の引数です。その後項目の強調表示は、リストビューの名を冠したメソッド(CListView)で行われ、範囲を超えた場合にはインデックスが調整されます。その後、項目のテキストが格納され、コンボボックスのボタンに設定されます。
class CListView : public CElement { public: //--- 指定されたインデックスの項目を強調表示する void SelectedItemByIndex(const int index); }; //+------------------------------------------------------------------+ //| 指定されたインデックスの項目を強調表示する | //+------------------------------------------------------------------+ void CComboBox::SelectedItemByIndex(const int index) { //--- リストビュー項目を強調表示する m_listview.SelectedItemByIndex(index); //--- ボタンテキストを格納して設定する m_button_text=m_listview.SelectedItemText(); m_button.Description(m_listview.SelectedItemText()); }
また、要素をブロック/ブロック解除するためのメソッドとその現在の状態を取得するためのメソッドが必要になります。要素の新しい状態に応じて、この状態に対応する色がオブジェクトに設定されます。例は、本稿で後に詳しく説明されます。
class CListView : public CElement { public: //--- 要素の状態の(1)取得と(2) 設定 bool ComboBoxState(void) const { return(m_combobox_state); } void ComboBoxState(const bool state); }; //+------------------------------------------------------------------+ //| コンボボックスの状態の変更 | //+------------------------------------------------------------------+ void CComboBox::ComboBoxState(const bool state) { m_combobox_state=state; //---現在の状態に対応した オブジェクトの色を設定する m_label.Color((state)?m_label_color : m_label_color_off); m_button.Color((state)?m_button_text_color : m_button_text_color_off); m_button.BackColor((state)?m_button_color : m_button_color_off); m_button.BorderColor((state)?m_button_border_color : m_button_border_color_off); m_drop_arrow.State(state); }
マウスカーソルが要素オブジェクトの上をホバーすると、CComboBox::ChangeObjectsColor()メソッドでの色の変更は要素が利用可能な場合のみ行われます。
class CListView : public CElement { public: //--- カーソルが上をホバ-したときのオブジェクトの色の変更 void ChangeObjectsColor(void); }; //+------------------------------------------------------------------+ //| カーソルが上をホバ-したときのオブジェクトの色の変更 | //+------------------------------------------------------------------+ void CComboBox::ChangeObjectsColor(void) { //---要素 がブロックされている場合は終了する if(!m_combobox_state) return; //--- オブジェクトの色を変更する CElement::ChangeObjectColor(m_label.Name(),CElement::MouseFocus(),OBJPROP_COLOR,m_label_color,m_label_color_hover,m_label_color_array); CElement::ChangeObjectColor(m_button.Name(),CElement::MouseFocus(),OBJPROP_BGCOLOR,m_button_color,m_button_color_hover,m_button_color_array); }
CComboBox::ChangeObjectsColor()メソッドはコントロールタイマーのCComboBox::OnEventTimer()で呼び出されます。先走りますが、コンボボックスはより複雑なドロップダウン要素の複合部にもなれることには言及すべきです。<今後の記事の一つでは、このような要素の例であるドロップダウンカレンダーについてお話しします。その複合部分はコンボボックスです。以下のコードに示すように、このような要素のタイマーで色を変更するための条件が形成されます。
- これがドロップダウン要素でリストビューが非表示な場合
- 最初の条件が満たされない場合、フォームと要素自体の可用性がチェックされます。
//+------------------------------------------------------------------+ //| タイマー | //+------------------------------------------------------------------+ void CComboBox::OnEventTimer(void) { //--- これがドロップダウン要素でリストビューが非表示な場合 if(CElement::IsDropdown() && !m_listview.IsVisible()) ChangeObjectsColor(); else { //--- これがフォームで要素がブロックされていない場合 if(!m_wnd.IsLocked() && m_combobox_state) ChangeObjectsColor(); } }
コンボボックスのリストビューの可視性を管理するCComboBox::ChangeComboboxListState()メソッドを作成しましょう。このメソッドは、コンボボックスの現在の状態を反転します。メソッドの開始時には要素の可用性のチェックがあります。コンボボックスがブロックされている場合、プログラムはメソッドを終了します。その後、コードは2つのブランチに分かれます。
- リストビューがすでに表示されている場合、それはここで 非表示にされ、 対応する色がコンボボックスのボタンに設定されます。その後、これがドロップダウン要素でない場合、フォームのブロックが解除されてアクティブ要素の識別子はリセットされる必要があります。
- リストが非表示の場合は、それを表示し、コンボボックスのボタンの色が状態に対応して変えられます。ブランチの終わりには、フォームがブロックされ、アクティブ要素の識別子が格納されなければなりません。
class CListView : public CElement { public: //--- コンボボックスの現在の状態を反転する void ChangeComboBoxListState(void); }; //+------------------------------------------------------------------+ //| コンボボックスの現在の状態を反転する | //+------------------------------------------------------------------+ void CComboBox::ChangeComboBoxListState(void) { //---要素 がブロックされている場合は終了する if(!m_combobox_state) return; //--- リストビューが表示されている場合 if(m_listview.IsVisible()) { //--- リストビューを隠す m_listview.Hide(); //--- 色を設定する m_label.Color(m_label_color_hover); m_button.BackColor(m_button_color_hover); //--- ドロップダウン要素でない場合 if(!CElement::IsDropdown()) { //--- フォームのブロックを解除する m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); } } //--- リストビューが表示されていない場合 else { //--- リストビューを表示する m_listview.Show(); //--- 色を設定する m_label.Color(m_label_color_hover); m_button.BackColor(m_button_color_pressed); //--- フォームをブロックする m_wnd.IsLocked(true); m_wnd.IdActivatedElement(CElement::Id()); } }
コントロールイベント処理メソッド
ここでCComboBox::OnEvent()コントロールイベントハンドラを設定します。これには二つの補助的なプライベートメソッドが必要です。
- CComboBox::OnClickButton() – このメソッドでは、コンボボックスボタンの押下イベントが追跡されます。
- CComboBox::CheckPressedOverButton() – コンボボックスボタン上の左マウスボタンの状態が追跡されます。
class CListView : public CElement { private: //--- ボタン押下の処理 bool OnClickButton(const string clicked_object); //--- コンボボックスボタンで押下された左マウスボタンをチェックする void CheckPressedOverButton(void); };
CComboBox::OnClickButton() メソッドのコードはごく単純です。それには押されたオブジェクトの名前の確認と、下記のコードに示すようにコンボボックスリストビューの可視性を管理する、前述されたCComboBox::ChangeComboBoxListState()メソッドの呼び出しのみが含まれます。
//+------------------------------------------------------------------+ //| コンボボックスボタンの押下 | //+------------------------------------------------------------------+ bool CComboBox::OnClickButton(const string clicked_object) { //--- オブジェクト名が異なる場合には終了する if(clicked_object!=m_button.Name()) return(false); //--- リストビューの状態を変える ChangeComboboxListState(); return(true); }
下記はCComboBox::CheckPressedOverButton() メソッドのコードです。フォームの可用性とアクティブ要素の識別子のチェックがメソッドの初めに行われます。フォームがブロックされていて識別子が一致しない場合はプログラムはメソッドを終了します。
その後、要素にフォーカスがない場合、リストビューでのフォーカスとスクロールバーの状態を確認します。リストビューにフォーカスがないまたはりスクロールバーがスライダー移動モードである場合は、プログラムはメソッドを終了します。ご存知のように、移動モードでは、スクロールバーのスライダーはカーソルがスライダーエリアを離れた場合でも移動することができます。いずれの条件も満たされない場合は
(1)リストビューを非表示にします。
(2) 要素オブジェクトの色を元に戻します。
そして識別子が一致せず要素がドロップダウンでない場合には、このコードのブロックの終わりで(3)フォームのブロックが解除されます。フォームはそれをブロックした要素によってのみブロック解除できることを思い出してみましょう。
要素の上にフォーカスがある場合は、最初にリストビューの可視性のチェックが行われます。リストが表示されている場合は続ける理由がないので、プログラムがメソッドを終了します。リストが隠されている場合は、対応する色が、コンボボックスのボタン上のフォーカスに応じて設定されます。
//+------------------------------------------------------------------+ //| ボタンで押下された左マウスボタンのチェック | //+------------------------------------------------------------------+ void CComboBox::CheckPressedOverButton(void) { //--- フォームがブロックされていて識別子が一致しない場合は終了する if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id()) return; //--- フォーカスのない場合 if(!CElement::MouseFocus()) { //--- リストビューにフォーカスがないかスクロールバーが有効な場合は終了する if(m_listview.MouseFocus() || m_listview.ScrollState()) return; //--- リストビューを隠す m_listview.Hide(); //--- 色を元に戻す ResetColors(); //--- 識別子が一致し要素がドロップダウンでない場合 if(m_wnd.IdActivatedElement()==CElement::Id() && !CElement::IsDropdown()) //--- フォームのブロックを解除する m_wnd.IsLocked(false); } //--- フォーカスがある場合 else { //--- リストビューが表示されている場合は終了する if(m_listview.IsVisible()) return; //--- フォーカスを考慮して色を設定する if(m_button.MouseFocus()) m_button.BackColor(m_button_color_pressed); else m_button.BackColor(m_button_color_hover); } }
CComboBox::OnClickButton()メソッドの呼び出しは、CHARTEVENT_OBJECT_CLICK識別子で特定できるグラフィックオブジェクト押下イベント処理に受け渡しする必要があります。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CComboBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- オブジェクトのマウス左クリックイベントの処理 if(id==CHARTEVENT_OBJECT_CLICK) { //--- コンボボックスボタンの押下 if(OnClickButton(sparam)) return; } }
CComboBox::CheckPressedOverButton()メソッドの呼び出しに先立って、下記のチェックがマウスカーソル移動イベントCHARTEVENT_MOUSE_MOVEに受け渡される必要があります。
- 要素の可視性
- 要素の可用性
- 左マウスボタンの状態
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CComboBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- カーソル移動イベントの処理 if(id==CHARTEVENT_MOUSE_MOVE) { //--- 要素が隠れている場合は終了する if(!CElement::IsVisible()) return; //--- 座標 int x=(int)lparam; int y=(int)dparam; //--- 要素上のフォーカスの確認 CElement::MouseFocus(x>CElement::X() && x<CElement::X2() && y>CElement::Y() && y<CElement::Y2()); m_button.MouseFocus(x>m_button.X() && x<m_button.X2() && y>m_button.Y() && y<m_button.Y2()); //---要素 がブロックされている場合は終了する if(!m_combobox_state) return; //--- マウスの左ボタンが押されていない場合は終了する if(sparam=="0") return; //--- スプリットボタンで押下された左マウスボタンをチェックする CheckPressedOverButton(); return; } }
リストビュー項目が押された瞬間にON_CLICK_LIST_ITEMカスタムイベントが下のコードにあるように生成されます。このメッセージはコンボボックスイベントハンドラで受信されなければいけません。要素の識別子が一致した場合、言葉を変えると、メッセージがこのコンボボックスに取り付けられているリストビューから発信された場合、リストビューの強調表示されたテキストを格納してCComboBox::ChangeComboBoxListState()メソッドを使ってリストビューを隠します。
開発中のアプリケーションとの接続を確保するために ON_CLICK_LIST_ITEM識別子を持ったメッセージはCProgramカスタムクラスで受け取られます。メッセージはコンボボックスから一意の (1)イベント識別子(2) 要素識別子(3)コンボボックス記述をもって発信することもできます。制御イベント識別の能力を拡大するために、そのようなオプションも対応されます。Defines.mqhファイルに、コンボボックスコントロールの一意の識別子を加えます。
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #define ON_CLICK_COMBOBOX_ITEM (17) // コンボボックスリストビューの項目の選択
この場合、下のコードで青で強調表示された行をコントロールイベントハンドラに追加します。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CComboBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- リストビュー項目押下イベントの処理 if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { //--- 識別子が一致した場合 if(lparam==CElement::Id()) { //--- ボタンテキストを格納して設定する m_button_text=m_listview.SelectedItemText(); m_button.Description(m_listview.SelectedItemText()); //--- リストビューの状態を変える ChangeComboBoxListState(); //--- 関連したメッセージを送信する ::EventChartCustom(m_chart_id,ON_CLICK_COMBOBOX_ITEM,CElement::Id(),0,m_label_text); } //--- return; } }
また、チャートプロパティが変更された時にリストビューを非表示に設定することができます。これには、下のコードにあるようにCHARTEVENT_CHART_CHANGE識別子を持ったイベントが処理されます。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CComboBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- チャートプロパティ変更イベントの処理 if(id==CHARTEVENT_CHART_CHANGE) { //---要素 がブロックされている場合は終了する if(!m_combobox_state) return; //--- リストビューを隠す m_listview.Hide(); //--- 色を元に戻す ResetColors(); //--- フォームのブロックを解除する m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); return; } }
コンボボックスコントロール作成クラスの検証の準備は出来ましたが、それが正しく作動するにはその前にライブラリエンジンに接続する必要があります。
ライブラリエンジンとコントロールクラスの接続
ライブラリエンジンとコントロールはいくつかの簡単なアクションを実行するだけで接続できます。<
1.コントロールクラスのファイルをライブラリルートファイルのWndContainer.mqhに含みます。
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "ComboBox.mqh"
2.必要ならば要素のプライベート配列と配列のサイズ作成メソッドを作成します。我々の場合、プライベート配列はドロップダウンリストビューのために必要です。
class CWndContainer { protected: //--- 要素配列の構造体 struct WindowElements { //--- 全てのオブジェクトの共通配列 //--- 全要素の共通配列 //--- 要素のプライベート配列 // コンテキストメニューの配列 //--- メインメニューの配列 //--- ツールヒント //--- 異なる種類のドロップダウンリストビューの配列 CElement *m_drop_lists[]; }; //--- 各ウィンドウの要素配列の配列 WindowElements m_wnd[]; //--- public: //--- ドロップダウンリストビューの数 int DropListsTotal(const int window_index); }; //+------------------------------------------------------------------+ //| 指定されたウィンドウインデックスでのドロップダウンリストビューを返す | //+------------------------------------------------------------------+ int CWndContainer::DropListsTotal(const int window_index) { if(window_index>=::ArraySize(m_wnd)) { ::Print(PREVENTING_OUT_OF_RANGE); return(WRONG_VALUE); } //--- return(::ArraySize(m_wnd[window_index].m_drop_lists)); }
3.プライベート配列にポインタを追加するプライベートメソッドを作成します。この場合、コンボボックスコントロールからリストビューのポインタを取得し、プライベート配列に追加します。加えて、リストビューオブジェクトポインタとリストビュースクロールバーはオブジェクトの共通配列に追加されます。CWndContainer::AddComboBoxElements()メソッドのコードは下に示されています。
class CWndContainer { private: //--- ベースにコンテキストメニュー要素のポインタを格納する //--- ベースにメインメニュー要素へのポインタを格納する //--- スプリットボタン要素のポインタをベースに格納する //--- べースにツールヒントへのポインタを格納する //--- ベース内にリストビューオブジェクトへのポインタを格納する //--- ベース内にドロップダウンリストビューオブジェクトへのポインタを格納する bool AddComboBoxElements(const int window_index,CElement &object); }; //+------------------------------------------------------------------+ //| ドロップダウンリストビューオブジェクトへのポインタをプライベート配列に | //| 格納する | //+------------------------------------------------------------------+ bool CWndContainer::AddComboBoxElements(const int window_index,CElement &object) { //--- これがツールヒントでない場合には終了する if(object.ClassName()!="CComboBox") return(false); //--- コンボボックスポインタを取得する CComboBox *cb=::GetPointer(object); //--- for(int i=0; i<2; i++) { //--- 配列要素の増加 int size=::ArraySize(m_wnd[window_index].m_elements); ::ArrayResize(m_wnd[window_index].m_elements,size+1); //--- リストをベースに追加する if(i==0) { CListView *lv=cb.GetListViewPointer(); m_wnd[window_index].m_elements[size]=lv; AddToObjectsArray(window_index,lv); //--- ポインタをプライベート配列に追加する AddToRefArray(lv,m_wnd[window_index].m_drop_lists); } //--- スクロールバーをベースに追加する else if(i==1) { CScrollV *sv=cb.GetScrollVPointer(); m_wnd[window_index].m_elements[size]=sv; AddToObjectsArray(window_index,sv); } } //--- return(true); }
4.3が必要だった場合、CWndContainer::AddComboBoxElements()メソッドを同様の呼び出しが位置するCWndContainer::AddToElementsArray()メインメソッドで呼び出すことを忘れてはなりません。
//+------------------------------------------------------------------+ //| 配列へのポインタを追加する | //+------------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- ベースにコントロールフォームがない場合 //--- 存在しないフォームへのリクエストの場合 //--- 共通要素配列に追加する //--- 要素オブジェクトを共通オブジェクト配列に追加する //--- すべてのフォームの最後の要素のIDを格納する //--- 要素の識別子のカウンタを増加する //--- ベースにコンテキストメニューオブジェクトへのポインタを格納する //--- ベースにメインメニューオブジェクトのポインタを格納する //--- ベース内のスプリットボタンオブジェクトへのポインタを格納する //--- べースにツールヒントオブジェクトへのポインタを格納する //--- ベース内にリストビューオブジェクトへのポインタを格納する //--- ベース内のコンボボックスコントロールオブジェクトへのポインタを格納する if(AddComboBoxElements(window_index,object)) return; }
要素がドロップダウンである場合、フォームでのこの要素の位置に応じて時々フォームの境界が超えられる場合があります。マウスカーソルの位置の制御は常に必要であり、チャートがそのような要素の上にある場合チャートのスクロールを無効にする必要があります。これで、マウスの左ボタンがドロップダウン要素上で押されたときにチャットがスクロールするのを回避することができます。このためにはすでにCWndEvents::SetChartState()メソッドがCWndEventsクラスで書かれました。ここでドロップダウンリストビューのチェックを富化する必要があります。したのコードでこの部分は黄色で強調表示されています。
//+------------------------------------------------------------------+ //| チャートの状態を設定する | //+------------------------------------------------------------------+ void CWndEvents::SetChartState(void) { int awi=m_active_window_index; //--- 管理を無効にするときイベントを特定する bool condition=false; //--- ウィンドウを確認する int windows_total=CWndContainer::WindowsTotal(); for(int i=0; i<windows_total; i++) { //--- フォームが非表示の場合は次のものに移る if(!m_windows[i].IsVisible()) continue; //--- フォームの内部ハンドラの確認条件 m_windows[i].OnEvent(m_id,m_lparam,m_dparam,m_sparam); //--- フォーカスが見つかったら目印をつける if(m_windows[i].MouseFocus()) { condition=true; break; } } //--- ドロップダウンリストビューを確認する if(!condition) { //--- ドロップダウンリストビューの合計を取得する int drop_lists_total=CWndContainer::DropListsTotal(awi); for(int i=0; i<drop_lists_total; i++) { //--- ドロップダウンリストビューのポインタを取得する CListView *lv=m_wnd[awi].m_drop_lists[i]; //--- リストビューがアクティブ(可視)の場合 if(lv.IsVisible()) { //--- リストビューのフォーカスとスクロールバーの状態の確認 if(m_wnd[awi].m_drop_lists[i].MouseFocus() || lv.ScrollState()) { condition=true; break; } } } } //--- コンテキストメニューのフォーカスの確認 if(!condition) { //--- ドロップダウンコンテキストメニューの合計を確認する int context_menus_total=CWndContainer::ContextMenusTotal(awi); for(int i=0; i<context_menus_total; i++) { //--- フォームがコンテキストメニュー上の場合 if(m_wnd[awi].m_context_menus[i].MouseFocus()) { condition=true; break; } } } //--- すべてのフォームでチャートの状態を設定する for(int i=0; i<windows_total; i++) m_windows[i].CustomEventChartState(condition); }
コンボボックスコントロールをテストする準備が整いました。
カスタムアプリケーションのグラフィカルインタフェースでのコンボボックスコントロールの検証
シリーズの第一部におけるカスタムアプリケーションのグラフィカルインターフェースで実装されたすべてのものを検証してみましょう。前の記事での検証は、3つのリストビューで行われました。このリストビューを維持し、アプリケーションのグラフィカルインターフェースに4つのコンボボックスを追加してみましょう。静的リストビューへのドロップダウンリストビューの影響をテストすることができるように2コンボボックスをその下に配置します。加えてCWndEvents::SetChartState()メソッドの動作も検証します。ドロップダウンリストビューが表示されているときにそれらが取り付けられているフォームの境界線を超えるようになコンボボックスを配置します。
コンボボックスクラスは基本クラスを介してテストアプリケーションのCProgramカスタムクラスですでに利用できます。Create four instances of the class of the CComboBox型のクラスのインスタンスを4つ作成し、下のコードにあるようにそれぞれでフォームの左上からのマージンを指定した4つのメソッドを宣言します。
//+------------------------------------------------------------------+ //| アプリケーション作成のクラス | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- コンボボックス CComboBox m_combobox1; CComboBox m_combobox2; CComboBox m_combobox3; CComboBox m_combobox4; //--- private: //--- コンボボックス1 #define COMBOBOX1_GAP_X (7) #define COMBOBOX1_GAP_Y (50) bool CreateComboBox1(const string text); //--- コンボボックス2 #define COMBOBOX2_GAP_X (160) #define COMBOBOX2_GAP_Y (50) bool CreateComboBox2(const string text); //--- コンボボックス3 #define COMBOBOX3_GAP_X (7) #define COMBOBOX3_GAP_Y (202) bool CreateComboBox3(const string text); //--- コンボボックス4 #define COMBOBOX4_GAP_X (160) #define COMBOBOX4_GAP_Y (202) bool CreateComboBox4(const string text); };
メソッドのすべてがユーザーによって設定されたプロパティを除いて同一であるのでそのうち一つだけについて考えてみましょう。例えば、4番目のコンボボックスは作成直後にブロックします。<以下は、コンボボックスコントロールを作成するための一連の動作です。
- フォームポインタをコントロールクラスに格納します。
- 座標を計算します。
- リストビュー項目のテキスト配列を宣言してすぐに初期化します。
- コントロールのプロパティを設定します。それらの大部分は、デフォルト値で初期化されます。値はコントロールを作成する前に必要に応じて再定義することができます。
- コンボボックスのリストビューに項目値を格納します。
- 必要な場合は、リストビューとスクロールバーのプロパティを設定します。
- リストビューで項目を強調表示します。デフォルトでは最初の項目(0)が強調表示されます。
- コントロールを作成します。
- コントロールは必要に応じてブロックできます。ここでは、例として、テストアプリケーションの4番目のコンボボックスをブロックします。
- メソッドの終わりにポインタを格納するための基本クラスにオブジェクトを渡します。
アクションのシーケンスは異なる場合があります。重要なことは、3つの主要なアクションの順序を維持することです。
- コントロールクラスへのフォームポインタの追加。さもないと、グラフィカルインターフェースの作成が異常終了します。<失敗の理由は、操作ログメッセージから見つけることができます。
- コントロールの作成。
- オブジェクトベースでのコントロールポインタの格納。
//+------------------------------------------------------------------+ //| コンボボックス1を作成する | //+------------------------------------------------------------------+ bool CProgram::CreateComboBox1(const string text) { //--- リストビュー項目の総数 #define ITEMS_TOTAL1 8 //--- フォームオブジェクトを受け渡す m_combobox1.WindowPointer(m_window1); //--- 座標 int x=m_window1.X()+COMBOBOX1_GAP_X; int y=m_window1.Y()+COMBOBOX1_GAP_Y; //--- リストビュー項目の値の配列 string items_text[ITEMS_TOTAL1]={"FALSE","item 1","item 2","item 3","item 4","item 5","item 6","item 7"}; //--- 作成前にプロパティを設定する m_combobox1.XSize(140); m_combobox1.YSize(18); m_combobox1.LabelText(text); m_combobox1.ButtonXSize(70); m_combobox1.AreaColor(clrWhiteSmoke); m_combobox1.LabelColor(clrBlack); m_combobox1.LabelColorHover(clrCornflowerBlue); m_combobox1.ButtonBackColor(C'206,206,206'); m_combobox1.ButtonBackColorHover(C'193,218,255'); m_combobox1.ButtonBorderColor(C'150,170,180'); m_combobox1.ButtonBorderColorOff(C'178,195,207'); m_combobox1.ItemsTotal(ITEMS_TOTAL1); m_combobox1.VisibleItemsTotal(5); //--- コンボボックスリストビューの項目値を格納する for(int i=0; i<ITEMS_TOTAL1; i++) m_combobox1.ValueToList(i,items_text[i]); //--- リストビューポインタを取得する CListView *lv=m_combobox1.GetListViewPointer(); //--- リストビュープロパティを設定する lv.LightsHover(true); lv.SelectedItemByIndex(lv.SelectedItemIndex()==WRONG_VALUE ?2 : lv.SelectedItemIndex()); //--- コントロールを作成する if(!m_combobox1.CreateComboBox(m_chart_id,m_subwin,x,y)) return(false); //--- オブジェクトをオブジェクトグループの共通配列に追加する CWndContainer::AddToElementsArray(0,m_combobox1); return(true); }
コントロールを作成するためのメソッドの呼び出しは、グラフィカルインターフェースを作成するための主要メソッドに配置されます。我々の場合これはCProgram::CreateTradePanel()です。下記はメソッドの短縮版です。
//+------------------------------------------------------------------+ //| 取引パネルを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- コントロールのフォーム1 の作成 //--- コントロールの作成 // メインメニュー //--- コンテキストメニュー //--- ステータスバーの作成 //--- コンボボックス if(!CreateComboBox1("Combobox 1:")) return(false); if(!CreateComboBox2("Combobox 2:")) return(false); if(!CreateComboBox3("Combobox 3:")) return(false); if(!CreateComboBox4("Combobox 4:")) return(false); //--- リストビュー //--- チャートの再描画 m_chart.Redraw(); return(true); }
コンボボックスからのON_CLICK_COMBOBOX_ITEM識別子を持ったメッセージの特定をCProgramカスタムクラスのイベントハンドラに追加します。3番目ののコンボボックスからメッセージを受信した場合には、リストビューで選択された項目によって4番目のコンボボックスの状態が変わることを確認しましょう。最初の項目(0)以外のリストビュー項目が選択されると4番目のコンボボックスは使用できるようになります。最初の項目が選ばれると4番目のコンボボックスはブロックされます。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- コンボボックスイベントの項目選択 if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM) { if(sparam==m_combobox1.LabelText()) ::Print(__FUNCTION__," > This message is from combobox 1 > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(sparam==m_combobox2.LabelText()) ::Print(__FUNCTION__," > This message is from combobox 2 > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- 3番目のコンボボックスのメッセージを処理する else if(sparam==m_combobox3.LabelText()) { ::Print(__FUNCTION__," > This message is from combobox 3 > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- 指定された値が選択された場合はコンボボックス4を無効にする if(m_combobox3.ButtonText()=="FALSE") m_combobox4.ComboBoxState(false); //--- 他の値が選択された場合はコンボボックス4を有効にする else m_combobox4.ComboBoxState(true); } else if(sparam==m_combobox4.LabelText()) ::Print(__FUNCTION__," > This message is from combobox 4 > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } }
ファイルをコンパイルしてチャートに読み込むと、下のスクリーンショットのような結果が見えるはずです。
図2。コンボボックスコントロールの検証
コンボボックスを作成するCComboBoxクラスの開発が完了です。
おわりに
本稿では、複合型のコントロールコンボボックスについて考えました。グラフィカルインタフェースを作成するためのライブラリーの開発の現段階での概略は以下に示されます。
図3。開発の現段階でのライブラリの構造
次回の記事は、グラフィカルインタフェース作成ライブラリーの開発シリーズの第六部の始まりとなります。そこでは、チェックボックスや編集コントロールとその混合型を作成するためのクラスを記述します。
パートVの資料はダウンロードされ、動作の検証が可能です。それらのファイルの資料を使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。
第五部の記事のリスト:
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2381





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