
グラフィカルインタフェース V:リストビュー要素(チャプター 2)
コンテンツ
はじめに
シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的を詳細に考察します。記事へのリンクの完全なリストは各章の末尾でみられます。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。
前の章では、縦横スクロールバーを作成するためのクラスを書きました。この章では、それを実装します。縦スクロールバーはそのコンパウンドの一部となるリストビュー要素を作成するためのクラスが記述されます。スクロールバーのボタンが長押しされたときのリストビューの自動スクロールメカニズムの実装のデモンストレーションも行います。終わりには、MQLアプリケーションの実際の例を使用してすべてをテストします。
リストビュー要素
グラフィカルインターフェースのリストビュー要素は、ユーザーにいくつかのオプションを与えます。リストビュー項目の総数が大きすぎてインターフェースの強調表示された作業部に入らない場合、リストビュー項目の総数と可視部分の項目数が異なることがあります。このような場合には、スクロールバーが使用されます。
複数のプリミティブオブジェクトとインクルード要素を含むリストビューを構成します。それらは:
- リストビュー背景
- リストビュー項目の配列
- 縦スクロールバーコントロール
図1。リストビュー要素のコンパウンド部
以下では、リストビュー要素を作成するためのクラスの開発を検討します。
要素作成クラスの開発
要素を作成して開発中のライブラリにこれを埋め込むには、要素のCListViewクラスを持つファイルを作成する必要があります。我々の場合これはListView.mqhです。それをWndContainer.mqhファイルに含みます。
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "ListView.mqh"
このシリーズの前回の記事で検討されているコントロールのクラスと同様にCListViewクラスには標準的なメソッドのセットがあります。この要素でスクロールバーを使うにはScrolls.mqhファイルがListView.mqhファイルに含まれる必要があります。
//+------------------------------------------------------------------+ //| ListView.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "Scrolls.mqh" //+------------------------------------------------------------------+ //| リストビュー作成クラス | //+------------------------------------------------------------------+ class CListView : public CElement { private: //--- 要素が取り付けられるフォームへのポインタ CWindow *m_wnd; //--- public: CListView(void); ~CListView(void); //--- (1) フォームポインタを格納する 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); }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CListView::CListView(void) { //--- 要素クラスの名前を基本クラスに格納する CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CListView::~CListView(void) { }
プリミティブオブジェクト(リストビューの複合部分)のプロパティを設定するためのメソッドは作成前に必要とされるでしょう。
- リストビュー項目の高さ
- リストビューの背景のフレームの色
- 異なる状態での項目の背景色
- 異なる状態での項目テキストの色
上記のプロパティの値は、クラスのコンストラクタで設定されています。
class CListView : public CElement { private: //--- リストビューの背景のプロパティ int m_area_zorder; color m_area_border_color; //--- リストビュー項目のプロパティ int m_item_zorder; int m_item_y_size; color m_item_color; color m_item_color_hover; color m_item_color_selected; color m_item_text_color; color m_item_text_color_hover; color m_item_text_color_selected; //--- public: //--- 項目の縦幅 void ItemYSize(const int y_size) { m_item_y_size=y_size; } //--- 背景フレームの色 void AreaBorderColor(const color clr) { m_area_border_color=clr; } //--- 異なる状態でのリストビュー項目の色 void ItemColor(const color clr) { m_item_color=clr; } void ItemColorHover(const color clr) { m_item_color_hover=clr; } void ItemColorSelected(const color clr) { m_item_color_selected=clr; } void ItemTextColor(const color clr) { m_item_text_color=clr; } void ItemTextColorHover(const color clr) { m_item_text_color_hover=clr; } void ItemTextColorSelected(const color clr) { m_item_text_color_selected=clr; } }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CListView::CListView(void) : m_item_y_size(18), m_area_border_color(C'235,235,235'), m_item_color(clrWhite), m_item_color_hover(C'240,240,240'), m_item_color_selected(C'51,153,255'), m_item_text_color(clrBlack), m_item_text_color_hover(clrBlack), m_item_text_color_selected(clrWhite) { //--- 左マウスクリックの優先順位を設定する m_area_zorder =1; m_item_zorder =2; }
リストビューのオブジェクトを作成するには3つのプライベートと1つのパブリックメソッドがあります。リストビュー項目の作成のためにはCEdit型クラスインスタンスの配列が宣言されます。この配列はOBJ_EDIT型(入力フィールド)のグラフィックオブジェクトの作成に使われます。
class CListView : public CElement { private: //--- リストビュー作成のオブジェクト CRectLabel m_area; CEdit m_items[]; CScrollV m_scrollv; //--- public: //--- リストビュー作成メソッド bool CreateListView(const long chart_id,const int window,const int x,const int y); //--- private: bool CreateArea(void); bool CreateList(void); bool CreateScrollV(void); };
項目を1つだけ含むリストビューを作成する意味はないので、デフォルトのリストビューのサイズとその目に見える部分は2要素に等しいです。リストビューのサイズとその目に見える部分を設定するのにCListView::ListSize()とCListView::VisibleListSize()メソッドを作成して、項目数が少なくても2であると確認しましょう。
class CListView : public CElement { private: //--- リストビュー値の配列 string m_value_items[]; //--- リストビューと見える部分のサイズ int m_items_total; int m_visible_items_total; //--- public: //--- (1) リストビューと(2) 見える部分のサイズを返す int ItemsTotal(void) const { return(m_items_total); } int VisibleItemsTotal(void) const { return(m_visible_items_total); } //--- (1) リストビューと(2) 見える部分のサイズを設定する void ListSize(const int items_total); void VisibleListSize(const int visible_items_total); }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CListView::CListView(void) : m_items_total(2), m_visible_items_total(2) { //--- リストビューと見える部分のサイズを設定する ListSize(m_items_total); VisibleListSize(m_visible_items_total); } //+------------------------------------------------------------------+ //| リストビューのサイズを設定する | //+------------------------------------------------------------------+ void CListView::ListSize(const int items_total) { //--- 1項目のリストビューを作成する意味はない m_items_total=(items_total<2) ?2 : items_total; ::ArrayResize(m_value_items,m_items_total); } //+------------------------------------------------------------------+ //| リストビューの見える部分のサイズを設定する | //+------------------------------------------------------------------+ void CListView::VisibleListSize(const int visible_items_total) { //--- 1項目のリストビューを作成する意味はない m_visible_items_total=(visible_items_total<2) ?2 : visible_items_total; ::ArrayResize(m_items,m_visible_items_total); }
リストビューの強調表示された項目のインデックスとテキストを格納/取得するために関連するメソッドが必要です。デフォルトではリストビューの一番目の項目が強調表示されます。リストビューが作成された後に他の項目を強調表示する必要がある場合はCListView::SelectedItemIndex()メソッドを使用します。リストビューの作成前と、項目数が定義されたあとの要素インデックスを指定する必要があります。
class CListView : public CElement { private: //--- 強調表示された項目の(1) インデックスと (2) テキスト int m_selected_item_index; string m_selected_item_text; //--- public: //--- リストビューの 強調表示された項目の(1) インデックスと (2) テキストを返す void SelectedItemIndex(const int index); int SelectedItemIndex(void) const { return(m_selected_item_index); } void SelectedItemText(const string text) { m_selected_item_text=text; } string SelectedItemText(void) const { return(m_selected_item_text); } }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CListView::CListView(void) : m_selected_item_index(0), m_selected_item_text("") { //--- ... } //+------------------------------------------------------------------+ //| インデックスの格納 | //+------------------------------------------------------------------+ void CListView::SelectedItemIndex(const int index) { //--- サイズが超過した場合の調整 m_selected_item_index=(index>=m_items_total)?m_items_total-1 : (index<0)?0 : index; }
リストビューが作成された後で項目が選択された瞬間には、その項目は異なる色で強調表示されなければなりません。この為にCListView::HighlightSelectedItem()メソッドを書きましょう。このメソッドの開始時にはスクロールバーの現在の状態の確認があります。それがアクティブであってスライダーを動かせる場合は、プログラムはメソッドを終了します。条件が満たされたらリストビューのスライダーの現在位置を取得します。得られた値は、ループ内のカウンタの開始値になります。このループの助けを借りて強調表示する要素を特定します。
class CListView : public CElement { public: //--- 選択された項目の強調表示 void HighlightSelectedItem(void); }; //+------------------------------------------------------------------+ //| 選択された項目を強調表示する | //+------------------------------------------------------------------+ void CListView::HighlightSelectedItem(void) { //--- スクロールバーがアクティブな場合は終了する if(m_scrollv.ScrollState()) return; //--- スクロールバースライダーの現在位置を取得する int v=m_scrollv.CurrentPos(); //--- リストビューの見える部分で反復する for(int r=0; r<m_visible_items_total; r++) { //--- リストビューの範囲内の場合 if(v>=0 && v<m_items_total) { //--- 背景とテキストの色を変える m_items[r].BackColor((m_selected_item_index==v) ?m_item_color_selected : m_item_color); m_items[r].Color((m_selected_item_index==v) ?m_item_text_color_selected : m_item_text_color); //--- カウンタの増加 v++; } } }
リスト項目がCListView::CreateList()メソッドで作成されると、リストビューの背景のフレームが遮られないように座標と幅が算出されます。項目の幅が計算されてリストビューがスクロールバーを持つかどうかが考慮されます。リストビュー2番目以降のすべての項目は前の項目と1画素の重複を持ちます。これは、強調表示された項目にマウスカーソルがホバーしたときに表われる2画素のギャップを除外するために必要です。これは、リストビューの背景とスクロールバーの高さを計算する際に考慮する必要があります。要素作成後には、メソッドの最後に選択された項目が強調表示されてテキストが格納されます。
//+------------------------------------------------------------------+ //| リストビュー項目を作成する | //+------------------------------------------------------------------+ bool CListView::CreateList(void) { //--- 座標 int x =CElement::X()+1; int y =0; //--- リストビュー項目の幅の算出 int w=(m_items_total>m_visible_items_total) ?CElement::XSize()-m_scrollv.ScrollWidth() : CElement::XSize()-2; //--- for(int i=0; i<m_visible_items_total; i++) { //--- オブジェクト名の形成 string name=CElement::ProgramName()+"_listview_edit_"+(string)i+"__"+(string)CElement::Id(); //--- Y座標の計算 y=(i>0) ?y+m_item_y_size-1 : CElement::Y()+1; //--- オブジェクトの作成 if(!m_items[i].Create(m_chart_id,name,m_subwin,x,y,w,m_item_y_size)) return(false); //--- プロパティの設定 m_items[i].Description(m_value_items[i]); m_items[i].TextAlign(m_align_mode); m_items[i].Font(FONT); m_items[i].FontSize(FONT_SIZE); m_items[i].Color(m_item_text_color); m_items[i].BackColor(m_item_color); m_items[i].BorderColor(m_item_color); m_items[i].Corner(m_corner); m_items[i].Anchor(m_anchor); m_items[i].Selectable(false); m_items[i].Z_Order(m_item_zorder); m_items[i].ReadOnly(true); m_items[i].Tooltip("\n"); //--- 座標 m_items[i].X(x); m_items[i].Y(y); //--- サイズ m_items[i].XSize(w); m_items[i].YSize(m_item_y_size); //--- パネルの端からのマージン m_items[i].XGap(x-m_wnd.X()); m_items[i].YGap(y-m_wnd.Y()); //--- オブジェクトポインタを格納する CElement::AddToArray(m_items[i]); } //--- 選択された項目の強調表示 HighlightSelectedItem(); //--- 選択された項目のテキストを格納する m_selected_item_text=m_value_items[m_selected_item_index]; return(true); }
CListView::CreateScrollV()メソッドでスクロールバーを作成する場合、一番初めに全リストビューの項目の総数とその可視部分の比が確認されます。項目の総数が可視部分内の項目数以下の場合はメソッドを継続する意味がないのでプログラムはメソッドを終了します。その後(1)フォームポインタが収納され(2)座標が計算され (3) プロパティが設定されます。スクロールバーの要素識別子は親要素のものと同じでなければなりません。ドロップダウン要素のモードも同じでなければなりません。
//+------------------------------------------------------------------+ //| 縦スクロールバーを作成する | //+------------------------------------------------------------------+ bool CListView::CreateScrollV(void) { //--- 項目数がリストサイズより大きい場合 // 縦スクロールバーを設定する if(m_items_total<=m_visible_items_total) return(true); //--- フォームポインタを格納する m_scrollv.WindowPointer(m_wnd); //--- 座標 int x=CElement::X()+m_area.X_Size()-m_scrollv.ScrollWidth(); int y=CElement::Y(); //--- 優先順位の設定 m_scrollv.Id(CElement::Id()); m_scrollv.XSize(m_scrollv.ScrollWidth()); m_scrollv.YSize(CElement::YSize()); m_scrollv.AreaBorderColor(m_area_border_color); m_scrollv.IsDropdown(CElement::IsDropdown()); //--- スクロールバーの作成 if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_items_total,m_visible_items_total)) return(false); //--- return(true); }
Y軸に沿ったサイズは、リストビューを作成するメインメソッドのCListView::CreateListView() 算出されます。上記のように、サイズが計算される際にはリストビューアイテムの1画素の重なりを考慮しなければなりません。また、項目の配列はそのフレームの邪魔にならないようにリストビュー背景の内側に厳密に配置しなければならないということを心に留めておいてください。
//+------------------------------------------------------------------+ //| リストビューを作成する | //+------------------------------------------------------------------+ bool CListView::CreateListView(const long chart_id,const int window,const int x,const int y) { //--- フォームポインタがない場合は終了する if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating the list view, the class must be passed " "the form pointer: CListView::WindowPointer(CWindow &object)"); return(false); } //--- 変数の初期化 m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =window; m_x =x; m_y =y; m_y_size =m_item_y_size*m_visible_items_total-(m_visible_items_total-1)+2; //--- 端からのマージン CElement::XGap(m_x-m_wnd.X()); CElement::YGap(m_y-m_wnd.Y()); //--- ボタンの作成 if(!CreateArea()) return(false); if(!CreateList()) return(false); if(!CreateScrollV()) return(false); //--- ダイアログウィンドウか最小化されたウィンドウの場合は要素を非表示にする if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) Hide(); //--- return(true); }
マウスカーソルがホーバーしたときに項目を強調表示する必要があるかどうかはライブラリユーザーによって決定されます。強調表示はデフォルトでは無効です。追加の調節可能なプロパティとして、テキストの配置を (1) 左マージン、(2)右マージンまたは(3)中心に設定するためのメソッドを作成しましょう。デフォルトでは、テキストは左マージンに揃えされます。
class CListView : public CElement { private: //--- カーソルがホーバーした時の強調表示のモード bool m_lights_hover; //--- リストビューでのテキスト配置のモード ENUM_ALIGN_MODE m_align_mode; //--- public: //--- (1) カーソルがホーバーした時の強調表示のモード (2) テキストの配置 void LightsHover(const bool state) { m_lights_hover=state; } void TextAlign(const ENUM_ALIGN_MODE align_mode) { m_align_mode=align_mode; } }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CListView::CListView(void) : m_lights_hover(false), m_align_mode(ALIGN_LEFT) { //--- ... }
リストビューを作成する前にはデータ配列の初期化が必要です。それには、配列サイズの確認とサイズ超過の場合のインデックス調整を行うCListView::ValueToList() メソッドを作成します。
class CListView : public CElement { public: //--- 行の指定したインデックスによって、リストビューで値を設定する void ValueToList(const int item_index,const string value); }; //+------------------------------------------------------------------+ //| 指定されたインデックスによって、リストビューで渡された値を格納する | //+------------------------------------------------------------------+ void CListView::ValueToList(const int item_index,const string value) { int array_size=::ArraySize(m_value_items); //--- コンテキストメニューに項目がない場合は報告する if(array_size<1) { ::Print(__FUNCTION__," > This method is to be called, " "when the list view contains at least one item!"); } //--- サイズが超過した場合の調整 int i=(item_index>=array_size)?array_size-1 : (item_index <0)?0 : item_index; //--- リストビューに値を格納する m_value_items[i]=value; }
ここで縦スクロールバーを持ったリストビューの設定をテストし、その管理のために必要なすべてのメソッドを徐々に追加していきます。
リストビュー設定の検証
ListView.mqhファイルが既にライブラリに含まれているので、リストビュー要素のクラス(CListView)はカスタムクラスで既に利用可能です。リストビューの埋め込みをテストする前にCWndContainerクラスにいくつかの追加を導入しましょう。リストビューは複合(コンパウンド)要素なので、要素のベースへのスクロールバーのポインタの追加が確保されなければなりません。
このためにCWndContainer::AddListViewElements()メソッドを書きましょう。クラス名のチェックは、メソッドの開始時に行われます。要素がリストビューでないことが判明した場合はプログラムはメソッドを終了します。次に、ポインタの共通配列のサイズを増加し正しい要素型のポインタが取得できたので、それを保存します。
class CWndContainer { private: //--- ベース内にリストビューオブジェクトへのポインタを格納する bool AddListViewElements(const int window_index,CElement &object); }; //+------------------------------------------------------------------+ //| ベース内にリストビューオブジェクトへのポインタを格納する | //+------------------------------------------------------------------+ bool CWndContainer::AddListViewElements(const int window_index,CElement &object) { //--- これがリストビューでなければ終了する if(object.ClassName()!="CListView") return(false); //--- リストビューポインタを取得する CListView *lv=::GetPointer(object); //--- 配列要素の増加 int size=::ArraySize(m_wnd[window_index].m_elements); ::ArrayResize(m_wnd[window_index].m_elements,size+1); //--- スクロールバーポインタを取得する CScrollV *sv=lv.GetScrollVPointer(); //--- ベースに要素を格納する m_wnd[window_index].m_elements[size]=sv; return(true); }
CWndContainer::AddListViewElements()メソッドはベースに要素を追加するメインメソッドで呼ばれます。
//+------------------------------------------------------------------+ //| 要素配列へのポインタを追加する | //+------------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- ベースにコントロールフォームがない場合 //--- 存在しないフォームへのリクエストの場合 //--- 共通要素配列に追加する //--- 要素オブジェクトを共通オブジェクト配列に追加する //--- すべてのフォームの最後の要素のIDを格納する //--- 要素識別子のカウンタを増加する //--- ベースにコンテキストメニューオブジェクトのポインタを格納する //--- ベースにメインメニューオブジェクトのポインタを格納する //--- ベース内のスプリットボタンオブジェクトへのポインタを格納する //--- ベース内のツールヒントオブジェクトへのポインタを格納する //--- ベース内にリストビューオブジェクトへのポインタを格納する if(AddListViewElements(window_index,object)) return; }
シリーズ前部のEAをテストに使用します。メインメニュー、コンテキストメニューとステータスバーのみを残し、他の要素は削除します。カスタムクラスのインスタンスを作成し、要素作成メソッドを宣言し、フォームの端からのマージンを定義します。
class CProgram : public CWndEvents { private: //--- リストビュー CListView m_listview1; //--- private: //--- リストビュー #define LISTVIEW1_GAP_X (2) #define LISTVIEW1_GAP_Y (43) bool CreateListView1(void); };
以下がリストビュー作成メソッドのコードです。リストビューには20項目が含まれます。見えるのは10のみです。マウスがホバーした時の項目の強調表示を有効にします。リストビューの6つ目の項目 (5) を選びます。これは例に過ぎないのでテキストは項目番号と «SYMBOL» にします。
//+------------------------------------------------------------------+ //| リストビュー1を作成する | //+------------------------------------------------------------------+ bool CProgram::CreateListView1(void) { //--- リストビューのサイズ #define ITEMS_TOTAL1 20 //--- ウィンドウポインタを格納する m_listview1.WindowPointer(m_window1); //--- 座標 int x=m_window1.X()+LISTVIEW1_GAP_X; int y=m_window1.Y()+LISTVIEW1_GAP_Y; //--- 作成前にプロパティを設定する m_listview1.XSize(100); m_listview1.LightsHover(true); m_listview1.ListSize(ITEMS_TOTAL1); m_listview1.VisibleListSize(10); m_listview1.AreaBorderColor(clrDarkGray); m_listview1.SelectedItemIndex(5); //--- スクロールバーポインタを取得する CScrollV *sv=m_listview1.GetScrollVPointer(); //--- スクロールバーのプロパティ sv.ThumbBorderColor(C'190,190,190'); sv.ThumbBorderColorHover(C'180,180,180'); sv.ThumbBorderColorPressed(C'160,160,160'); //--- リストビューにデータを入れる for(int r=0; r<ITEMS_TOTAL1; r++) m_listview1.ValueToList(r,"SYMBOL "+string(r)); //--- リストビューを作成する if(!m_listview1.CreateListView(m_chart_id,m_subwin,x,y)) return(false); //--- 要素ポインタをベースに追加する CWndContainer::AddToElementsArray(0,m_listview1); return(true); }
リストビュー作成メソッドは、グラフィカルインターフェースを作成するための主要なメソッドで呼び出される必要があります。
//+------------------------------------------------------------------+ //| 取引パネルを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- コントロールのフォーム1 の作成 //--- コントロールの作成 // メインメニュー //--- コンテキストメニュー //--- ステータスバーの作成 //--- リストビュー if(!CreateListView1()) return(false); //--- チャートの再描画 m_chart.Redraw(); return(true); }
これで、コードをコンパイルしてプログラムをチャート上に読み込むすることができます。すべてが正しく行われた場合は、以下のスクリーンショットに示されたような結果が得られます。
図2。リストビュー要素設定の検証。
外観は良いですが、現在このリストビューの管理は不可能でそのオブジェクトはマウスカーソルに反応しません。次の部分では、リストビューの管理を可能にするメソッドを記述します。
要素管理メソッド
最初にマウスカーソルがホバーしたときに項目の色を変更できるようにするメソッドを作成してみましょう。次に色をデフォルト値に設定するメソッドがいります。
class CListView : public CElement { public: //--- (1) リストビュー項目の色のリセット(2) カーソルが上をホバリングした時のリストビュー項目の色を変更 void ResetItemsColor(void); void ChangeItemsColor(const int x,const int y); };
選択された項目以外の全項目の色をリセットするにはCListView::ResetItemsColor() メソッドが使われます。メソッドの初めにはスライダーの位置が特定されます。この値は、選択された項目を識別するためのカウンタとしてループにおいてさらに使用される変数で受信されます。
//+------------------------------------------------------------------+ //| リストビュー項目の色のリセット | //+------------------------------------------------------------------+ void CListView::ResetItemsColor(void) { //--- スクロールバースライダーの現在位置を取得する int v=m_scrollv.CurrentPos(); //--- リストビューの見える部分で反復する for(int i=0; i<m_visible_items_total; i++) { //--- Iリストビューの範囲を超えられた場合はカウンタを増やす if(v>=0 && v<m_items_total) v++; //--- 選択された項目をスキップする if(m_selected_item_index==v-1) continue; //--- 背景とテキストの色の設定 m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } }
CListView::ChangeItemsColor()メソッドの初めでは複数のチェックが必要です。プログラムは、次の場合にメソッドを終了します。
- カーソルがホバーした時の項目の強調表示が有効になっていない
- スクロールバーが管理されている
- これがドロップダウン要素ではなくフォームがブロックされている
そして、このクラスの多くのメソッドと同様、スクロールバーの現在位置を変数に取得します。この変数は、色を変化する必要がないので抜かされるようにループ内で選択した項目を識別するために使用されます。メソッドにはカーソル座標が渡されます。これらの座標は、マウスがホバーしている項目をループで識別することを可能にします。CListView::ChangeItemsColor()メソッドのもっと詳しいコードは下記にあります。
//+------------------------------------------------------------------+ //| カーソルが上をホバリングした時のリストビュー項目の色の変更 | //+------------------------------------------------------------------+ void CListView::ChangeItemsColor(const int x,const int y) { //--- カーソルがホバーした時の項目の強調表示が有効になっていないかスクロールバーがアクティブな場合には終了する if(!m_lights_hover || m_scrollv.ScrollState()) return; //--- これがドロップダウン要素ではなくフォームがブロックされている場合には終了する if(!CElement::IsDropdown() && m_wnd.IsLocked()) return; //--- スクロールバースライダーの現在位置を取得する int v=m_scrollv.CurrentPos(); //--- カーソルがホバーしている項目を特定して強調表示する for(int i=0; i<m_visible_items_total; i++) { //--- Iリストビューの範囲を超えられた場合はカウンタを増やす if(v>=0 && v<m_items_total) v++; //--- 選択された項目をスキップする if(m_selected_item_index==v-1) continue; //--- カーソルがこの項目でホバーしている場合は強調表示する if(x>m_items[i].X() && x<m_items[i].X2() && y>m_items[i].Y() && y<m_items[i].Y2()) { m_items[i].BackColor(m_item_color_hover); m_items[i].Color(m_item_text_color_hover); } //--- カーソルがこの項目の上にない場合には、その状態に適切な色を割り当てる else { m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } } }
ここでCListViewクラスのイベントハンドラのCListView::ChangeItemsColor()メソッドを使う必要があります。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CListView::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; //--- カーソルが上をホバリングしたらリストビュー項目の色を変更する ChangeItemsColor(x,y); return; } }
テスト用のプログラムをコンパイルしてリストビュー項目の上にマウスカーソルを移動すると、以下のスクリーンショットに示されるように、それらの色が変更されます。
図3。マウスカーソルがホバーされた際にリストビュー項目の色を変更する検証
リストビューがスクロールバーのスライダーと共に移動できるように CListView::ShiftList() メソッドを書いてみましょう。このメソッドもまた、変数にスライダーの現在位置を保存することから始まります。以前のメソッドと同様に、それはリストビューで選択した項目を保存したりデータを移動させるためのループでのカウンタとしても使用されます。下のコードをご覧ください。
class CListView : public CElement { public: //--- リストビューのスクロール void ShiftList(void); }; //+------------------------------------------------------------------+ //| スクロールバーと一緒にリストビューを動かす | //+------------------------------------------------------------------+ void CListView::ShiftList(void) { //--- スクロールバースライダーの現在位置を取得する int v=m_scrollv.CurrentPos(); //--- リストビューの見える部分で反復する for(int i=0; i<m_visible_items_total; i++) { //--- リストビューの範囲内の場合 if(v>=0 && v<m_items_total) { //--- テキストの移動、背景とテキストの色 m_items[i].Description(m_value_items[v]); m_items[i].BackColor((m_selected_item_index==v) ?m_item_color_selected : m_item_color); m_items[i].Color((m_selected_item_index==v) ?m_item_text_color_selected : m_item_text_color); //--- カウンタの増加 v++; } } }
CListView::ShiftList()メソッドはスクロールバーのCScrollV::ScrollBarControl()メソッドがtrueを返した場合にはCListView::OnEvent()ハンドラで呼ばれる必要があります。 それは、スライダの管理が有効になっていることを意味します。
class CListView : public CElement { private: //--- 左マウスボタンの状態(押されている/いない) bool m_mouse_state; }; //+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CListView::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; m_mouse_state=(bool)int(sparam); //--- リストビューのフォーカスの確認 CElement::MouseFocus(x>CElement::X() && x<CElement::X2() && y>CElement::Y() && y<CElement::Y2()); //--- スライダーの管理が有効になっている場合はリストを移動する if(m_scrollv.ScrollBarControl(x,y,m_mouse_state)) ShiftList(); //--- カーソルが上をホバリングしたらリストビュー項目の色を変更する ChangeItemsColor(x,y); return; } }
プログラムのコンパイル後、リストビューは下のスクリーンショットに示すようにスクロールバーのスライダーによって管理することができます。メソッドは、マウスの左ボタンがその上で押された後にカーソルが領域外に移っても、管理はスクロールバーに渡されスライダが移動されるように実装されています。
図4。スクロールバーのスライダーを使用したリストビューの管理
ここで、リストビュー項目のいずれかが押されたことを識別するためのメソッドが必要です。プライベートメソッドを作成してCListView::OnClickListItem()と呼びましょう。また、私たちのライブラリの他要素のクラスで示されたオブジェクト名から要素識別子を抽出するためにCListView::IdFromObjectName()プライベートメソッドが必要です。
加えて、リストビュー項目のクリックを示す一意の識別子(ON_CLICK_LIST_ITEM)も必要です。それを Defines.mqhファイルに追加します。
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #define ON_CLICK_LIST_ITEM (16) // リストビュー項目の選択
CListView::OnClickListItem()メソッドの初めには、クリックされたオブジェクトの名前と識別子に対するチェックが存在します。その後、スライダーの現在位置がローカル変数に割り当てられます。この変数は、項目のインデックスとテキストを識別するためのループカウンタとして使用されます。メソッドの最後では(1) ON_CLICK_LIST_ITEMイベントの識別子、(2) 要素識別子と (3) 現在選択された小目のテキストを含むメッセージが送られます 。
class CListView : public CElement { private: //--- リストビュー項目押下の処理 bool OnClickListItem(const string clicked_object); //--- リストビュー項目名からの識別子の取得 int IdFromObjectName(const string object_name); }; //+------------------------------------------------------------------+ //| リストビュー項目押下の処理 | //+------------------------------------------------------------------+ bool CListView::OnClickListItem(const string clicked_object) { //--- 押されたのがメニュー項目でなかった場合は終了する if(::StringFind(clicked_object,CElement::ProgramName()+"_listview_edit_",0)<0) return(false); //--- オブジェクト名から識別子とインデックスを取得する int id=IdFromObjectName(clicked_object); //--- 識別子が一致しない場合は終了する if(id!=CElement::Id()) return(false); //--- スクロールバースライダーの現在位置を取得する int v=m_scrollv.CurrentPos(); //--- リストビューの見える部分で反復する for(int i=0; i<m_visible_items_total; i++) { //--- このリストビュー項目が選択された場合 if(m_items[i].Name()==clicked_object) { m_selected_item_index =v; m_selected_item_text =m_value_items[v]; } //--- リストビューの範囲内の場合 if(v>=0 && v<m_items_total) //--- カウンタの増加 v++; } //--- 関連したメッセージを送信する ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),0,m_selected_item_text); return(true); }
リストビュー管理に必要なメソッドはこれですべてそろったので、下にあるようにCListView::OnEvent()リストビューイベントハンドラにコードを入れるだけです。リストビュー項目の押下はCListView::HighlightSelectedItem()メソッドを呼び出します。このメソッドではスクロールバーのボタンの押下が追跡されます。ボタンのいずれかが押されていた場合には、リストビューは、スライダーの現在位置に関連して移動されます。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- オブジェクトの押下の処理 if(id==CHARTEVENT_OBJECT_CLICK) { //--- リストビュー要素が押された場合 if(OnClickListItem(sparam)) { //--- 項目の強調表示 HighlightSelectedItem(); return; } //--- 押されたのがスクロールバーのボタンだった場合 if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam)) { //--- リストをスクロールバーとともに動かす ShiftList(); return; } } }
リストビューの早送り
リストビューを管理するために必要な最低限の開発を完了しました。その能力は拡張することができます。スクロールバーのボタンのいずれかの長押ししてリストビューを早送りするためのプライベートメソッドを後1つ作成してみましょう。
マウスの左ボタンがスクロールバーのボタン上で押された時に、リストビューの早送りが始まる前に若干の遅延があることを確認する必要があります。<これを行わないと、早送りはすぐに行われ、リストビューが1項目だけ移動するようにボタンが1回だけクリックされた時には適していません。SPIN_DELAY_MSC識別子はDefines.mqhファイルに-450の値で追加されます。これで遅れは450 ミリ秒になります。
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //--- カウンタの早送りに先立つ遅れ(ミリ秒) #define SPIN_DELAY_MSC (-450)
CListView::FastSwitching()メソッドのコードは下記です。タイマーカウンタのm_timer_counterフィールドの宣言も必要です。CListView::FastSwitching() メソッドの初めでは、リストビューのフォーカスが確認されます。フォーカスがない場合、プログラムはメソッドを終了します。マウスボタンが離された場合、次に、カウンタに遅延の値(我々の場合には -450ミリ秒)が代入されます。マウスが長押しされた場合、カウンタの値はライブラリで設定されたタイマのステップ(この場合16ミリ秒)によって増加されます。そして、カウンタの値がゼロ以上でなくなるとプログラムの更なる動作を防止する条件があります。この条件が満たされるとすぐに、スクロールボタンの状態がチェックされます。どのボタンが押下されるかに応じて、ボタンの押下を模倣するためのメソッドが呼び出されます。次に、リストビューは、スクロールバーのスライダの現在位置に応じて移動されます。
class CListView : public CElement { private: //--- リストビューの早送りのタイマカウンタ int m_timer_counter; private: //--- リストビューの早送り void FastSwitching(void); }; //+------------------------------------------------------------------+ //| スクロールバーの早送り | //+------------------------------------------------------------------+ void CListView::FastSwitching(void) { //--- リストビューにフォーカスがなければ終了する if(!CElement::MouseFocus()) return; //--- マウスボタンが離された場合は初期値にカウンターを返す if(!m_mouse_state) m_timer_counter=SPIN_DELAY_MSC; //--- マウスボタンが長押しされた場合 else { //--- カウンタを設定されたステップで増加する m_timer_counter+=TIMER_STEP_MSC; //--- 0未満なら終了する if(m_timer_counter<0) return; //--- 上へのスクロールの場合 if(m_scrollv.ScrollIncState()) m_scrollv.OnClickScrollInc(m_scrollv.ScrollIncName()); //--- 下へのスクロールの場合 else if(m_scrollv.ScrollDecState()) m_scrollv.OnClickScrollDec(m_scrollv.ScrollDecName()); //--- リストを動かす ShiftList(); } }
CListView::FastSwitching()メソッドは下にあるようにCListView::OnEventTimer() タイマで実行されます。リストビューがドロップダウン要素である場合、追加のチェックは必要ありません。そうでなければ、フォームが現在ブロックされているかどうかを確認する必要があります。
//+------------------------------------------------------------------+ //| タイマー | //+------------------------------------------------------------------+ void CListView::OnEventTimer(void) { //--- ドロップダウン要素である場合 if(CElement::IsDropdown()) //--- リストビューの早送り FastSwitching(); //--- これがドロップダウン要素ではない場合、フォームのブロック状態を考慮する else { //--- フォームがブロックされていない場合にのみにリストビューの早送りを追跡する if(!m_wnd.IsLocked()) FastSwitching(); } }
リストビューを管理するためのメソッドの準備はこれですべて整いました。検証しましょう。以前に作成したものに更に2つのリストビューを追加してみましょう。
図5。グラフィカルインターフェイスでの3つのリストビューの検証
カスタムクラス(CProgram)のイベントハンドラでリストビューからメッセージを受信します。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- リストビュー項目押下イベント if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { if(lparam==m_listview1.Id()) ::Print(__FUNCTION__," > This message is from the first list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(lparam==m_listview2.Id()) ::Print(__FUNCTION__," > This message is from the second list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(lparam==m_listview3.Id()) ::Print(__FUNCTION__," > This message is from the third list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } }
リストビュー項目が押されると、下図のように、メッセージが操作ログに印刷されます。
2016.01.16 13:02:00.085 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the first list view > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 11 2016.01.16 13:01:59.056 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the third list view > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 12 2016.01.16 13:01:58.479 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the second list view > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 9 2016.01.16 13:01:57.868 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the third list view > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 19 2016.01.16 13:01:56.854 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the second list view > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 4 2016.01.16 13:01:56.136 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the first list view> id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 9 2016.01.16 13:01:55.433 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the first list view > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 14
おわりに
本稿では、コンパウンドコントロールのリストビューについてお話ししました。また、縦スクロールバーを使用する方法が示されました。次の記事では、後一つのコンパウンドコントロールであるコンボボックスについてお話します。
パートVの材料をダウンロードし、どのように動作するかのテストができます。それらのファイルの資料を使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。
第五部の記事のリスト:
- グラフィカルインタフェース V:縦横のスクロールバー(チャプター 1)
- グラフィカルインタフェース V:リストビュー要素(チャプター 2)
- グラフィカルインタフェース V:コンボボックス要素(チャプター 3)
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2380





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