
グラフィカルインタフェース V: 縦横のスクロールバー(チャプター 1)
コンテンツ
はじめに
MetaTrader 環境におけるグラフィカルインタフェースに関する以前の一連の記事では、開発中のライブラリの主要部分が議論され、メインメニュー、コンテキストメニュー、ステータスバー、ボタン、ボタングループとツールチップといったインターフェース要素が作成されました。シリーズの第五部は、スクロールバーとリストビューコントロールに専念します。第1章では、縦横のスクロールバーを作成するためのクラスを作成します。第2章では、コンパウンドインタフェース要素であるリストビューを開発します 。スクロールバーはコンパウンドの一部であるので、作成を始めましょう。
スクロールバーコントロール
スクロールバーは、データセットが指定された領域に収まらない場合にリストビューやテーブルで使用されています。スクロールバーの主なオブジェクトは、押してデータを1ステップ動かすためのボタンと、マウスの左ボタンでドラッグしてデータを迅速に動かすためのスライダーです。
ここでは、5つのグラフィカルオブジェクトのスクロールバーを構成します。
- メイン領域の背景
- スライダー領域の背景
- データを1ステップ動かすための2つのボタン
- データの迅速な移動のためのスライダー
図1。スクロールバーコントロールの複合部品
スクロールバーは縦横の二つのタイプであり得るので、タイプごとに個別のクラスを作成するのが有用でしょう。 これらは、スクロールバーの各タイプのユニークな特性を反映するための派生クラスになります。
- CScrollV – 縦スクロールバーのための派生クラス
- CScrollH – 横スクロールバーのための派生クラス
CScrollクラスが基本クラスとなります。それには、両タイプに共通なフィールドとメソッドが含まれます。すべての3クラスはScrolls.mqhファイルに収められます。スクロールバーコントロールの概略は以下のようになります。
図2。スクロールバーコントロールの概略
ここで、このコントロールの基本クラスのCScroll開発を検討しましょう。
コントロールの基本クラス
スクロールバーコントロールは、グラフィカルインタフェースの独立した要素ではありません。それは補助要素であり、作業領域内のデータのスクロールを必要とする他の要素に接続されます。つまりScrolls.mqhファイルは直接WndContainer.mqhファイルに含まれなくてもいいわけです。スクロールバーのクラスは、他の要素のクラスとファイルに含まれることによってライブラリで利用できるようになります。
CScroll クラスをScrolls.mqhファイルで作成し、インターフェース要素に標準的なメソッドを備えます。
//+------------------------------------------------------------------+ //| Scrolls.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| スクロールバーを作成するための基本クラス | //+------------------------------------------------------------------+ class CScroll : public CElement { protected: //--- 要素が取り付けられるフォームへのポインタ CWindow *m_wnd; //--- public: //--- フォームポインタを格納する 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) {} }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CScroll::CScroll(void) { } //+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CScroll::~CScroll(void) { }
先走ると、派生クラスは下記のコードにあるように同じファイルに作成される必要があります。使われるクラスによって、要素はクラスコンストラクタで適応する名前を取得します。
//+------------------------------------------------------------------+ //| 縦スクロールバー管理のためのクラス | //+------------------------------------------------------------------+ class CScrollV : public CScroll { public: CScrollV(void); ~CScrollV(void); }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CScrollV::CScrollV(void) { //--- 要素クラスの名前を基本クラスに格納する CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CScrollV::~CScrollV(void) { } //+------------------------------------------------------------------+ //| 横スクロールバー管理のためのクラス | //+------------------------------------------------------------------+ class CScrollH : public CScroll { public: CScrollH(void); ~CScrollH(void); }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CScrollH::CScrollH(void) { //--- 要素クラスの名前を基本クラスに格納する CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CScrollH::~CScrollH(void) { }
前述したように、スクロールバーの外観のカスタマイズを可能にしましょう。これにはCScroll基本クラスで下記の要素オブジェクトのパラメータ設定に使われるフィールドとメソッドを作成します。
- スクロールバーの幅
- 一般的な背景フレームの色
- 背景とスクロールバーのスライダーが配置されている領域のフレームの色
- 異なる状態での背景とスライダーのフレームの色
- データのスクロールに使用されるボタンの異なる状態でのアイコン
縦スクロールバーとそのオブジェクトの幅は X 軸に沿ったサイズで、横スクロールバーの幅はY軸に沿ったサイズであryことに注意しなければなりません。幅のデフォルト値は15 画素です。ボタンのデフォルトアイコンは、このサイズに一致するように選択されます。背景フレームが覆われないように、それらは1画素のマージンを持つ一般的な背景の内側に配置されています。そのサイズは13x13画素です。よって、スクロールバーの幅を変更するのであれば、ボタンアイコンを変更する必要があります。必要なメソッドを作成しましょう。
スクロールバーの他のオブジェクト(内部領域とスライダー)の幅は、一般的背景の幅との関係で自動的に計算されます。このパラメータはリスト内のアイテムの数とY 軸に沿った大きさに依存するので、長さも自動的に計算されます。実装は本稿でさらに説明します。
class CScroll : public CElement { protected: //--- スクロールバーの領域のプロパティ int m_area_width; int m_area_length; color m_area_color; color m_area_border_color; //--- スライダーの背景のプロパティ int m_bg_length; color m_bg_border_color; //--- ボタンのアイコン string m_inc_file_on; string m_inc_file_off; string m_dec_file_on; string m_dec_file_off; //--- 異なる状態にあるスライダーの色 color m_thumb_color; color m_thumb_color_hover; color m_thumb_color_pressed; color m_thumb_border_color; color m_thumb_border_color_hover; color m_thumb_border_color_pressed; //--- (1)スライダーの幅、(2)長さ、 (3) 最短値 int m_thumb_width; int m_thumb_length; int m_thumb_min_length; //--- (1) スライダーのステップと (2) ステップの数 double m_thumb_step_size; double m_thumb_steps_total; //--- マウスの左クリックの優先順位 int m_area_zorder; int m_bg_zorder; int m_arrow_zorder; int m_thumb_zorder; //--- public: //--- スライダーの幅 void ScrollWidth(const int width) { m_area_width=width; } int ScrollWidth(void) const { return(m_area_width); } //---(1)背景と(2)背景フレームと(3)背景の内部フレームの色 void AreaColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } void BgBorderColor(const color clr) { m_bg_border_color=clr; } //--- ボタンアイコンの設定 void IncFileOn(const string file_path) { m_inc_file_on=file_path; } void IncFileOff(const string file_path) { m_inc_file_off=file_path; } void DecFileOn(const string file_path) { m_dec_file_on=file_path; } void DecFileOff(const string file_path) { m_dec_file_off=file_path; } //--- (1) スライダー背景の色と (2) スライダー背景のフレーム void ThumbColor(const color clr) { m_thumb_border_color=clr; } void ThumbColorHover(const color clr) { m_thumb_border_color_hover=clr; } void ThumbColorPressed(const color clr) { m_thumb_border_color_pressed=clr; } void ThumbBorderColor(const color clr) { m_thumb_border_color=clr; } void ThumbBorderColorHover(const color clr) { m_thumb_border_color_hover=clr; } void ThumbBorderColorPressed(const color clr) { m_thumb_border_color_pressed=clr; } }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CScroll::CScroll(void) : m_area_width(15), m_area_length(0), m_inc_file_on(""), m_inc_file_off(""), m_dec_file_on(""), m_dec_file_off(""), m_thumb_width(0), m_thumb_length(0), m_thumb_min_length(15), m_area_color(C'210,210,210'), m_area_border_color(C'240,240,240'), m_bg_border_color(C'210,210,210'), m_thumb_color(C'190,190,190'), m_thumb_color_hover(C'180,180,180'), m_thumb_color_pressed(C'160,160,160'), m_thumb_border_color(C'170,170,170'), m_thumb_border_color_hover(C'160,160,160'), m_thumb_border_color_pressed(C'140,140,140') { //--- 左マウスクリックの優先順位を設定する m_area_zorder =8; m_bg_zorder =9; m_arrow_zorder =10; m_thumb_zorder =11; }
スクロールバーの要素を作成するためのメソッドを考えてみましょう。各部分は、独立したプライベートメソッドによって作成されます。リストビューのサイズとリストビューの可視部分のサイズは主要メソッドの最後の2つのパラメータとして渡されなければなりません。これらのパラメータは、その後スクロールバーのスライダーのステップ数の計算に使用されます。
class CScroll : public CElement { protected: //--- スクロールバー作成のためのオブジェクト CRectLabel m_area; CRectLabel m_bg; CBmpLabel m_inc; CBmpLabel m_dec; CRectLabel m_thumb; //--- public: //--- スクロールバー作成メソッド bool CreateScroll(const long chart_id,const int subwin,const int x,const int y,const int items_total,const int visible_items_total); //--- private: bool CreateArea(void); bool CreateBg(void); bool CreateInc(void); bool CreateDec(void); bool CreateThumb(void); };
残りの部分では似たような原理が使用されていて従うのは簡単なので、一例として、これらのメソッドの一部のコードのみを表示します。下でCScroll::CreateBg() メソッドのコードを見てみましょう。オブジェクト名の形成はスクロールバーの種類と要素のインデックスが指定されているかどうかに依存することにご注意下さい。インデックスの指定は、複数の同種類のスクロールバーが使用されるコンパウンドコントロールの開発に必要とされます。このような例は、今後の記事で説明されます。
スクロールバーの種類はまた(1)座標(2)スライダ領域の長さ(3)スライダ領域のサイズのようなパラメータの計算を定義します。これが、以下のコードで青で強調表示され他部分です。
//+------------------------------------------------------------------+ //| スクロールバーの背景を作成する | //+------------------------------------------------------------------+ bool CScroll::CreateBg(void) { //--- オブジェクト名の形成 string name =""; string name_part =(CElement::ClassName()=="CScrollV")?"_scrollv_bg_" : "_scrollh_bg_"; //--- インデックスが指定されていない場合 if(CElement::Index()==WRONG_VALUE) name=CElement::ProgramName()+name_part+(string)CElement::Id(); //--- インデックスが指定されている場合 else name=CElement::ProgramName()+name_part+(string)CElement::Index()+"__"+(string)CElement::Id(); //--- 座標 int x=0; int y=0; //--- サイズ int x_size=0; int y_size=0; //--- スクロールバーの種類を考慮したプロパティの設定 if(CElement::ClassName()=="CScrollV") { m_bg_length =CElement::YSize()-(m_thumb_width*2)-2; x =CElement::X()+1; y =CElement::Y()+m_thumb_width+1; x_size =m_thumb_width; y_size =m_bg_length; } else { m_bg_length =CElement::XSize()-(m_thumb_width*2)-2; x =CElement::X()+m_thumb_width+1; y =CElement::Y()+1; x_size =m_bg_length; y_size =m_thumb_width; } //--- オブジェクトの作成 if(!m_bg.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size)) return(false); //--- プロパティの設定 m_bg.BackColor(m_area_color); m_bg.Color(m_bg_border_color); m_bg.BorderType(BORDER_FLAT); m_bg.Corner(m_corner); m_bg.Selectable(false); m_bg.Z_Order(m_bg_zorder); m_bg.Tooltip("\n"); //--- 座標を格納する m_bg.X(x); m_bg.Y(y); //--- マージンを格納する m_bg.XGap(x-m_wnd.X()); m_bg.YGap(y-m_wnd.Y()); //--- サイズを格納する m_bg.XSize(x_size); m_bg.YSize(y_size); //--- オブジェクトポインタを格納する CElement::AddToArray(m_bg); return(true); }
スクロールバーのスライダーとして機能するグラフィカルオブジェクトを作成するときはその長さを計算するためのメソッドが必要になります。長さは、リストビューの項目数とその可視部分の項目数によって異なります。そのようなメソッドを作成してCScroll::CalculateThumbSize()と呼びましょう。
このメソッドの開始時にはスライダー領域のサイズのチェックがあります。この領域の長さがスライダーの最短の長さよりも短い場合、計算は不要です。このような場合には、メソッドはfalseを返し、スライダーは必要とされないので作成されません。条件が満たされた場合、計算にはいくつかの段階があります。
- スライダー計算のステップ。
- 取得された値が1未満の場合1にする。
- スライダーを動かすための作業領域の計算。
- 作業領域の大きさがスライダーを動かす領域全体のサイズよりも小さい場合、スライダーの長さを計算します。その他の場合は、デフォルトで最小の15 画素にします。
- 整数型 (int)への型キャスト伴うスライダーの計算された長さのチェックはメソッドの終わりで行われ、長さが最小値未満の場合調整されます。
CScroll::CalculateThumbSize() メソッドのコードの詳細は下に見られます。
class CScroll : public CElement { protected: public: //--- スクロールバーのスライダーの長さの計算 bool CalculateThumbSize(void); }; //+------------------------------------------------------------------+ //--- スクロールバーのスライダーの長さの計算 //+------------------------------------------------------------------+ bool CScroll::CalculateThumbSize(void) { //---スライダーを動かすための領域の長さがスライダーの最短の長さより短い場合は計算が必要とされない if(m_bg_length<m_thumb_min_length) return(false); //--- スライダーステップのサイズを計算する m_thumb_step_size=(double)(m_bg_length-m_thumb_min_length)/m_thumb_steps_total; //--- ステップサイズは1未満ではいけない m_thumb_step_size=(m_thumb_step_size<1)?1 : m_thumb_step_size; //--- スライダーを動かするための作業領域のサイズを計算する double work_area=m_thumb_step_size*m_thumb_steps_total; //--- 作業領域のサイズが全領域のサイズより小さい場合はスライダーのサイズを取得し、その他の場合は最小サイズを設定する double thumb_size=(work_area<m_bg_length)?m_bg_length-work_area+m_thumb_step_size : m_thumb_min_length; //--- 型キャストを使ってスライダーサイズを確認する m_thumb_length=((int)thumb_size<m_thumb_min_length)?m_thumb_min_length :(int)thumb_size; return(true); }
CScroll::CalculateThumbSize() メソッドはCScroll::CreateThumb() メソッド内でオブジェクト作成に先立って呼び出される必要があります。後に、コントロールを使用するにあたって、スクロールバーの長さを算出されなければならない後1つのケースを説明します。それはコントロールを開発しているときに検討します。
//+------------------------------------------------------------------+ //| スクロールバーのスライダーを作成する | //+------------------------------------------------------------------+ bool CScroll::CreateThumb(void) { //--- オブジェクト名の形成 string name =""; string name_part =(CElement::ClassName()=="CScrollV")?"_scrollv_thumb_" : "_scrollh_thumb_"; //--- インデックスが指定されていない場合 if(CElement::Index()==WRONG_VALUE) name=CElement::ProgramName()+name_part+(string)CElement::Id(); //--- インデックスが指定されている場合 else name=CElement::ProgramName()+name_part+(string)CElement::Index()+"__"+(string)CElement::Id(); //--- 座標 int x=0; int y=0; //--- サイズ int x_size=0; int y_size=0; //--- スクロールバーのサイズを計算する if(!CalculateThumbSize()) return(true); //--- スクロールバーの種類を考慮したプロパティの設定 if(CElement::ClassName()=="CScrollV") { x =(m_thumb.X()>0) ?m_thumb.X() : m_x+1; y =(m_thumb.Y()>0) ?m_thumb.Y() : m_y+m_thumb_width+1; x_size =m_thumb_width; y_size =m_thumb_length; } else { x =(m_thumb.X()>0) ?m_thumb.X() : m_x+m_thumb_width+1; y =(m_thumb.Y()>0) ?m_thumb.Y() : m_y+1; x_size =m_thumb_length; y_size =m_thumb_width; } //--- オブジェクトの作成 if(!m_thumb.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size)) return(false); //--- プロパティの設定 m_thumb.BackColor(m_thumb_color); m_thumb.Color(m_thumb_border_color); m_thumb.BorderType(BORDER_FLAT); m_thumb.Corner(m_corner); m_thumb.Selectable(false); m_thumb.Z_Order(m_thumb_zorder); m_thumb.Tooltip("\n"); //--- 座標を格納する m_thumb.X(x); m_thumb.Y(y); //--- マージンを格納する m_thumb.XGap(x-m_wnd.X()); m_thumb.YGap(y-m_wnd.Y()); //--- サイズを格納する m_thumb.XSize(x_size); m_thumb.YSize(y_size); //--- オブジェクトポインタを格納する CElement::AddToArray(m_thumb); return(true); }
スクロールバーオブジェクトを作成するための他のメソッドについては自習なさってください。本稿添付のファイルに記載されています。
スクロールバー作成のメイン(public)メソッドにクラス型の確認を加えます。クラス名はCScrollの基本クラスには格納されません。これが、基本クラスを使用してスクロールバーを作成しようとすると、グラフィカルインターフェイスの作成が異常終了する理由です。操作ログはCScrollV やCScrollH型の派生クラスの使用が必要だとのメッセージを受信します。要素のパラメータのいくつかは初期化されていることにご注意下さい。このようなアプローチは、計算を大幅に自動化してライブラリのルーチンを省き、スクロールバーをもったコントロールを作成する際に最小数のプロパティを指定することを可能にします。
//+------------------------------------------------------------------+ //| スクロールバーを作成する | //+------------------------------------------------------------------+ bool CScroll::CreateScroll(const long chart_id,const int subwin,const int x,const int y,const int items_total,const int visible_items_total) { //--- フォームポインタがなければ終了する if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating the scrollbar, the class must be passed " "the form pointer: CScroll::WindowPointer(CWindow &object)"); return(false); } //--- スクロールバーの基本クラスを使う試みがあった場合には終了する if(CElement::ClassName()=="") { ::Print(__FUNCTION__," > Use derived classes of the scrollbar (CScrollV or CScrollH)."); return(false); } //--- 変数の初期化 m_chart_id =chart_id; m_subwin =subwin; m_x =x; m_y =y; m_area_width =(CElement::ClassName()=="CScrollV")?CElement::XSize() : CElement::YSize(); m_area_length =(CElement::ClassName()=="CScrollV")?CElement::YSize() : CElement::XSize(); m_thumb_width =m_area_width-2; m_thumb_steps_total =items_total-visible_items_total+1; //--- 端からのマージン CElement::XGap(m_x-m_wnd.X()); CElement::YGap(m_y-m_wnd.Y()); //--- ボタンの作成 if(!CreateArea()) return(false); if(!CreateBg()) return(false); if(!CreateInc()) return(false); if(!CreateDec()) return(false); if(!CreateThumb()) return(false); //--- ダイアログウィンドウか最小化されたウィンドウの場合は要素を非表示にする if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) Hide(); //--- return(true); }
スクロールバーのスライダーに対した左マウスボタンの状態を認識するにはENUM_THUMB_MOUSE_STATE列挙をEnums.mqhファイルに作成します。
//+------------------------------------------------------------------+ //| スクロールバーに対したマウスの左ボタンの状態を示す列挙体 | //+------------------------------------------------------------------+ enum ENUM_THUMB_MOUSE_STATE { THUMB_NOT_PRESSED =0, THUMB_PRESSED_OUTSIDE =1, THUMB_PRESSED_INSIDE =2 };
そのためには適切なフィールドやメソッドを作成する必要があります。CScroll::CheckMouseButtonState()メソッドはどの領域で左マウスボタンが押されたかの識別に必要です。CScroll::ZeroThumbVariables()メソッドはスライダーの動きに伴って変数をゼロにするために必要で、左マウスボタンが離されたときにCScroll::CheckMouseButtonState() メソッドで呼び出されます。
スクロールバーがスライダーを動かすモードである際は、要素がドロップダウン出ない場合のみにフォームがブロックされることを心に留めておいてください。これは、スクロールバーのスライダーが移動モードにあって領域外のカーソルがホバーしたときに他の要素を強調表示しないために必要です。スライダーの移動モードが無効にされた場合はCScroll::ZeroThumbVariables() メソッドでのフォームのブロックの解除が必要です。
class CScroll : public CElement { protected: //--- スライダーの動きに関連した変数 bool m_scroll_state; int m_thumb_size_fixing; int m_thumb_point_fixing; //--- 左マウスボタンが押された領域の識別 ENUM_THUMB_MOUSE_STATE m_clamping_area_mouse; //--- public: //--- 左マウスボタンが押された領域を識別する void CheckMouseButtonState(const bool mouse_state); //--- 変数のゼロ化 void ZeroThumbVariables(void); }; //+------------------------------------------------------------------+ // 左マウスボタンが押された領域を識別する //+------------------------------------------------------------------+ void CScroll::CheckMouseButtonState(const bool mouse_state) { //--- マウスボタンの状態を識別する // 押されていない場合 if(!mouse_state) { //--- 変数をゼロにする ZeroThumbVariables(); return; } //--- 押されている場合 if(mouse_state) { //--- ボタンが他の領域で押された場合は終了する if(m_clamping_area_mouse!=THUMB_NOT_PRESSED) return; //--- スライダー領域外 if(!m_thumb.MouseFocus()) m_clamping_area_mouse=THUMB_PRESSED_OUTSIDE; //--- スライダー領域内 else { m_clamping_area_mouse=THUMB_PRESSED_INSIDE; //--- ドロップダウン要素でない場合 if(!CElement::IsDropdown()) { //--- フォームをブロックしてアクティブ要素の識別子を格納する m_wnd.IsLocked(true); m_wnd.IdActivatedElement(CElement::Id()); } } } } //+------------------------------------------------------------------+ //| スライダーの動きに関連した変数をゼロにする //+------------------------------------------------------------------+ void CScroll::ZeroThumbVariables(void) { //--- ドロップダウン要素でない場合 if(!CElement::IsDropdown()) { //--- フォームのブロックを解除してアクティブ要素の識別子をリセットする m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); } m_thumb_size_fixing =0; m_clamping_area_mouse =THUMB_NOT_PRESSED; }
マウスカーソルの位置と左ボタンの状態に応じてスクロールバーのオブジェクトの色を変更するにはCScroll::ChangeObjectsColor() メソッドを作成します。メソッドの初めに、フォームがブロックされているスクロールバーの識別子のチェックと、フォームメモリに格納された識別子と異なるかのチェックがあります。これらの両方の条件が満たされている場合は、スクロールバーの現在のモードとそのボタンの上の焦点に応じて、それらは、対応する状態を取得します。スクロールバーには(1) 自由と (2) スライダーを動かしている最中の二つの状態があります。その後、カーソル位置とマウスの左ボタンが押された領域に応じて、スライダーに色が付きます。スクロールバーのモードも同様にここで定義されています。
class CScroll : public CElement { public: //--- スクロールバーオブジェクトの色の変化 void ChangeObjectsColor(void); }; //+------------------------------------------------------------------+ //| リストビュースクロールバーオブジェクトの色の変化 | //+------------------------------------------------------------------+ void CScroll::ChangeObjectsColor(void) { //--- フォームがブロックされ、現在アクティブな要素の識別子が異なる場合は終了する if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id()) return; //--- リストビュースクロールバーのボタンの色 if(!m_scroll_state) { m_inc.State(m_inc.MouseFocus()); m_dec.State(m_dec.MouseFocus()); } //--- カーソルがスクロールバーの領域内の場合 if(m_thumb.MouseFocus()) { //--- マウスの左ボタンが押されていない場合 if(m_clamping_area_mouse==THUMB_NOT_PRESSED) { m_scroll_state=false; m_thumb.BackColor(m_thumb_color_hover); m_thumb.Color(m_thumb_border_color_hover); } //--- 左マウスボタンがスライダーで押された else if(m_clamping_area_mouse==THUMB_PRESSED_INSIDE) { m_scroll_state=true; m_thumb.BackColor(m_thumb_color_pressed); m_thumb.Color(m_thumb_border_color_pressed); } } //--- カーソルがスクロールバーの領域外の場合 else { //--- マウスの左ボタンが押されていない if(m_clamping_area_mouse==THUMB_NOT_PRESSED) { m_scroll_state=false; m_thumb.BackColor(m_thumb_color); m_thumb.Color(m_thumb_border_color); } } }
スクロールバーのボタンの名前と状態を得るためのメソッドは、スクロールバーとのインタフェース要素の開発のために必要とされるでしょう。それに加えて、設定とスクロールバーのスライダーの移動状態とそれが接続されているリストビューに対する現在位置を同定するためのメソッドが必要となります。
class CScroll : public CElement { protected: //--- スクロールバーの状態の定義 bool m_scroll_state; //--- スライダーの現在位置 int m_current_pos; //--- public: //--- ボタンオブジェクトの名前 string ScrollIncName(void) const { return(m_inc.Name()); } string ScrollDecName(void) const { return(m_dec.Name()); } //--- ボタンの状態 bool ScrollIncState(void) const { return(m_inc.State()); } bool ScrollDecState(void) const { return(m_dec.State()); } //--- スクロールバーの状態 void ScrollState(const bool scroll_state) { m_scroll_state=scroll_state; } bool ScrollState(void) const { return(m_scroll_state); } //--- スライダーの現在位置 void CurrentPos(const int pos) { m_current_pos=pos; } int CurrentPos(void) const { return(m_current_pos); } };
スクロールバーの基本クラスの開発が完了しました。このコントロールの派生クラスを埋め始めます。
コントロールの派生クラス
スクロールバーの基本クラスがオブジェクトの作成とパラメータの設定/取得のための一般的なメソッドで満たされているのに対して、派生クラスはこのコントロールを管理するためのものです。CScrollV及びCScrollH派生クラスはScrolls.mqhファイルで既に作成されました。ここでは縦スクロールバークラス(CScrollV)の1メソッドのみについて触れます。2番目のクラス(CScrollH)では、スクロールバーの種類が変えられるだけで他はすべて同じです。Y座標を操作する縦スクロールバーのクラスとは異なり、横スクロールバーのクラスでは X座標を操作します。
最初に、スクロールバーのスライダーの動きのために設計されているCScrollV::OnDragThumb() とCScrollV::UpdateThumb() メソッドのコードを検討します。これらはCScrollVクラス内のみで使用されるのでクラスのプライベートセクションで宣言されます。
//+------------------------------------------------------------------+ //| 縦スクロールバー管理のためのクラス | //+------------------------------------------------------------------+ class CScrollV : public CScroll { private: //--- スライダーを動かす void OnDragThumb(const int y); //--- スライダー位置の更新 void UpdateThumb(const int new_y_point); };
スライダーを動かす試みの認識にはCScrollV::OnDragThumb() メソッドが必要です。マウスの左ボタンがスクロールバーのスライダーの上で押され、そのままカーソルがY軸に沿って移動された場合には、移動が開始されたことを意味し、オブジェクトを移動させなければなりません。スライダーの座標の更新はCScrollV::UpdateThumb()で行われます。
//+------------------------------------------------------------------+ //| スライダーを動かす | //+------------------------------------------------------------------+ void CScrollV::OnDragThumb(const int y) { //--- 新しいY座標を認識する int new_y_point=0; //--- スクロールバーが非アクティブの場合 if(!CScroll::ScrollState()) { //--- ...スライダー移動の補助変数をゼロにする CScroll::m_thumb_size_fixing =0; CScroll::m_thumb_point_fixing =0; return; } //--- 固定点がゼロである場合はカーソルの現在の座標を格納する if(CScroll::m_thumb_point_fixing==0) CScroll::m_thumb_point_fixing=y; //--- スライダーのエッジから現在のカーソル座標の距離がゼロの場合はそれを計算する if(CScroll::m_thumb_size_fixing==0) CScroll::m_thumb_size_fixing=m_thumb.Y()-y; //---押下された状態でしきい値は下向きに越された場合 if(y-CScroll::m_thumb_point_fixing>0) { //--- Y座標を計算する new_y_point=y+CScroll::m_thumb_size_fixing; //--- スライダー位置を更新する UpdateThumb(new_y_point); return; } //---押下された状態でしきい値は上向きに越された場合 if(y-CScroll::m_thumb_point_fixing<0) { //--- Y座標を計算する new_y_point=y-::fabs(CScroll::m_thumb_size_fixing); //--- スライダー位置を更新する UpdateThumb(new_y_point); return; } }
下記がCScrollV::UpdateThumb() メソッドのコードの詳細です。CScrollV::OnDragThumb() メソッドで計算されたY座標が受け渡されます。そこで、正しいかどうかチェックされます。スライダーを動かすために指定された作業領域が超えられたことが判明した場合、値が調整されます。その後初めて座標が更新され、基本クラスのフィールドに格納されます。
//+------------------------------------------------------------------+ //| スライダー位置の更新 //+------------------------------------------------------------------+ void CScrollV::UpdateThumb(const int new_y_point) { int y=new_y_point; //--- 固定点をゼロにする CScroll::m_thumb_point_fixing=0; //--- 作業領域を下向きに超えたかをチェックして調整する if(new_y_point>m_bg.Y2()-CScroll::m_thumb_length) { y=m_bg.Y2()-CScroll::m_thumb_length; CScroll::CurrentPos(int(CScroll::m_thumb_steps_total)); } //--- 作業領域を上向きに超えたかをチェックして調整する if(new_y_point<=m_bg.Y()) { y=m_bg.Y(); CScroll::CurrentPos(0); } //--- 座標とマージンの更新 m_thumb.Y(y); m_thumb.Y_Distance(y); m_thumb.YGap(m_thumb.Y()-(CElement::Y()-CElement::YGap())); }
Y座標に相対したでスライダーの現在位置を補正するための後1つのプライベートメソッドが必要になります。それをCScrollV::CalculateThumbPos()と名付けます。コードは下記です。
class CScrollV : public CScroll { private: //--- スライダー位置の値を補正する void CalculateThumbPos(void); }; //+------------------------------------------------------------------+ //| スライダー位置の値を補正する | //+------------------------------------------------------------------+ void CScrollV::CalculateThumbPos(void) { //--- ステップがゼロの場合は終了する if(CScroll::m_thumb_step_size==0) return; //--- スクロールバーの位置の値を補正する CScroll::CurrentPos(int((m_thumb.Y()-m_bg.Y())/CScroll::m_thumb_step_size)); //--- 作業領域を上下に超えたかをチェックする if(m_thumb.Y2()>=m_bg.Y2()-1) CScroll::CurrentPos(int(CScroll::m_thumb_steps_total-1)); if(m_thumb.Y()<m_bg.Y()) CScroll::CurrentPos(0); }
ここでスライダーを管理するCScrollV::ScrollBarControl() パブリックメソッドを作成しましょう。. 上記で考慮されたプライベートメソッドはすべてこのメソッドで呼び出されます。呼び出しはスクロールバーを含む要素のOnEvent() イベントハンドラで行われます。詳細な例はリストビューコントロールを作成するためのクラスを開発する第2章で見られます。
CScrollV::ScrollBarControl() メソッドのコードは下記です。カーソル座標とマウスの左ボタンの状態は引数として渡されます。初めに、スクロールバー上のフォーカスを確認します。その後、マウスの左ボタンが押された領域を確認します。カーソル位置とマウスの左ボタンの状態に応じて、スクロールバーの色が変更されます。管理がスクロールに渡された場合、その後スクロールバーは新しく計算されたYに応じて移動されます。リストビューの項目数はこれに関連して算出されます。
class CScrollV : public CScroll { public: //--- スクロールバーの管理 bool ScrollBarControl(const int x,const int y,const bool mouse_state); }; //+------------------------------------------------------------------+ //| スクロールバーの管理 | //+------------------------------------------------------------------+ bool CScrollV::ScrollBarControl(const int x,const int y,const bool mouse_state) { //--- スクロールバー上のフォーカスの確認 m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() && y>m_thumb.Y() && y<m_thumb.Y2()); //--- マウスボタンの状態を確認して格納する CScroll::CheckMouseButtonState(mouse_state); //--- スクロールバーの色を変える CScroll::ChangeObjectsColor(); //--- 管理がウィンドウに渡された場合はその場所を特定する if(CScroll::ScrollState()) { //--- スライダーを動かす OnDragThumb(y); //--- スクロールバーの位置を表す値を変える CalculateThumbPos(); return(true); } //--- return(false); }
CScrollV::CalculateThumbPos() メソッドはY座標をリストビュー項目の数に変換します。また Y座標を現在のスクロールバーの位置に対して計算する逆変換を行うためのメソッドも必要です。ここで、また、作業領域を超えたかどうかの確認と超えた場合の調整が行われます。メソッドの最後では、座標およびオブジェクトのマージンが更新されます。
class CScrollV : public CScroll { public: //--- スクロールバーのY座標の計算 void CalculateThumbY(void); }; //+------------------------------------------------------------------+ //| スクロールバーのY座標の計算 | //+------------------------------------------------------------------+ void CScrollV::CalculateThumbY(void) { //--- スクロールバーの現在のY座標を特定する int scroll_thumb_y=int(m_bg.Y()+(CScroll::CurrentPos()*CScroll::m_thumb_step_size)); //--- 作業領域が上に超えられた場合 if(scroll_thumb_y<=m_bg.Y()) scroll_thumb_y=m_bg.Y(); //--- 作業領域が下に超えられた場合 if(scroll_thumb_y+CScroll::m_thumb_length>=m_bg.Y2() || CScroll::CurrentPos()>=CScroll::m_thumb_steps_total-1) { scroll_thumb_y=int(m_bg.Y2()-CScroll::m_thumb_length); } //--- Y軸に沿って座標とマージンを更新する m_thumb.Y(scroll_thumb_y); m_thumb.Y_Distance(scroll_thumb_y); m_thumb.YGap(m_thumb.Y()-m_wnd.Y()); }
CScrollV::CalculateThumbY() メソッドは下のコードで見られるようにスクロールバーボタンの押下を処理するメソッドで呼び出されます。 オブジェクト名はCScrollV::OnClickScrollInc() とCScrollV::OnClickScrollDec() メソッドに渡される唯一の引数です。次のパラメータは、メソッドの最初にチェックされます。
- ボタンが押されたかどうか。オブジェクト名の確認。
- スクロールバーの状態。条件を満たすには、フリーモードが必要です。
- ステップ数が定義され、この値は1未満であることはできません。
すべての条件が満たされた場合は、動作範囲が超えられなければ位置が1ステップ変更されます。その後、CScrollV::CalculateThumbY()メソッドでY座標とフォームの端からのマージンが計算されて更新されます。ボタンは押された後でon状態のままである必要があります。
class CScrollV : public CScroll { public: //--- スクロールバーのボタンの押下の処理 bool OnClickScrollInc(const string clicked_object); bool OnClickScrollDec(const string clicked_object); }; //+------------------------------------------------------------------+ //| 左ボタンの上への押下の処理 | //+------------------------------------------------------------------+ bool CScrollV::OnClickScrollInc(const string clicked_object) { //--- 押されたのがこのオブジェクトでなかった場合、スクロールバーが非アクティブである場合、またはステップ数が特定され邸内場合は終了する if(m_inc.Name()!=clicked_object || CScroll::ScrollState() || CScroll::m_thumb_steps_total<1) return(false); //--- スクロールバーの位置を表す値を減らす if(CScroll::CurrentPos()>0) CScroll::m_current_pos--; //--- スクロールバーのY座標の計算 CalculateThumbY(); //--- Onの状態を設定する m_inc.State(true); return(true); } //+------------------------------------------------------------------+ //| 左ボタンの下への押下の処理 | //+------------------------------------------------------------------+ bool CScrollV::OnClickScrollDec(const string clicked_object) { //--- 押されたのがこのオブジェクトでなかった場合、スクロールバーが非アクティブである場合、またはステップ数が特定され邸内場合は終了する if(m_dec.Name()!=clicked_object || CScroll::ScrollState() || CScroll::m_thumb_steps_total<1) return(false); //--- スクロールバーの位置を表す値を増やす if(CScroll::CurrentPos()<CScroll::m_thumb_steps_total-1) CScroll::m_current_pos++; //--- スクロールバーのY座標の計算 CalculateThumbY(); //--- Onの状態を設定する m_dec.State(true); return(true); }
横スクロールバーを管理するために必要なメソッドの開発が終わりました。縦スクロールバーには、計算がX座標に対して行われることを除いて同じメソッドが使われます。
おわりに
この章では、縦横スクロールバーを検討しました。 次の記事では、グラフィカルインタフェースの後1つの要素であるリストビューについてお話します。リストビューの項目は利用可能なスペースに収まらない場合があり、その場合には、本稿で開発したスクロールバーコントロールが必要になります。
パートVの材料をダウンロードし、どのように動作するかのテストができます。それらのファイルの資料を使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。
第五部の記事のリスト:
- グラフィカルインタフェース V:縦横のスクロールバー(チャプター 1)
- グラフィカルインタフェース V:リストビュー要素(チャプター 2)
- グラフィカルインタフェース V:コンボボックス要素(チャプター 3)
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2379





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