English Русский 中文 Español Deutsch Português
グラフィカルインタフェースVII: テーブルコントロール(チャプター 1)

グラフィカルインタフェースVII: テーブルコントロール(チャプター 1)

MetaTrader 5 | 5 9月 2016, 14:57
890 0
Anatoli Kazharski
Anatoli Kazharski

コンテンツ

 

 

はじめに

シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的が詳しく考察されました。記事へのリンクの完全なリストは各章の末尾でみられます。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。

本稿では、MQLアプリケーションGUIの一部として、2次元データセットを表示する異なるタイプのテーブルの作成を可能にする3つのクラスを扱います。

  • テキストラベルテーブル
  • エディットボックステーブル
  • レンダーテーブル

テーブルタイプのそれぞれの独自の特徴および利点は、以下に記載されています。

次の記事(第九部第2章)では、次のコントロールが説明されます。

  • タブ
  • 画像付きのタブ

 


テキストラベルテーブルコントロール

テーブルは水平および垂直スクロールバーという他のコントロールを含んでいる複雑なGUIコントロールです。データがコントロールの指定された領域の利用可能なスペースを超える可能性があるので、スクロールバーを使用して表示されたデータ配列を垂直方向と水平方向両方にシフトすることができます。

テキストラベルテーブルは、次のコンポーネントで構成されています。

  1. 背景
  2. テキストラベル
  3. 縦スクロールバー
  4. 横スクロールバー

 図1。テキストラベルコントロールの複合部分

図1。テキストラベルコントロールの複合部分

このコントロールのクラスを詳しく見てみましょう。

 


CLabelsTableクラスの開発

LabelsTable.mqhファイルを作成してライブラリ(WndContainer.mqh)に含みます。

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include "LabelsTable.mqh"

LabelsTable.mqhファイルで、開発中のライブラリのそれぞれのコントロールに必要なメソッドのセットを持ったCLabelsTableクラスを作成します。本稿に記載されたすべてのコントロールに同じ操作を行います。

//+------------------------------------------------------------------+
//| テキストラベルテーブル作成クラス                            |
//+------------------------------------------------------------------+
class CLabelsTable : public CElement
  {
private:
   //--- 要素が取り付けられるフォームへのポインタ
   CWindow          *m_wnd;
   //---
public:
                     CLabelsTable(void);
                    ~CLabelsTable(void);
   //--- (1) フォームポインタを格納し (2) スクロールバーポインタを返す
   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) {}
  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CLabelsTable::CLabelsTable(void)
  {
  }
//+------------------------------------------------------------------+
//| デストラクタ                                                       |
//+------------------------------------------------------------------+
CLabelsTable::~CLabelsTable(void)
  {
  }

テーブルを作成する前に、そのプロパティの一部を指定します。これらを次のように呼びましょう。

  • 行の高さ
  • 最初の列のコントロールの左端からのインデント
  • 列間の距離
  • 背景色
  • テキストの色
  • 最初の行のロックモード
  • 最初の列のロックモード
  • 列の合計数
  • 行の合計数
  • テーブルの可視部分の列数
  • テーブルの可視部分の行数

このテーブルタイプでは、テキストラベルの座標は中央アンカーポイント (ANCHOR_CENTER)から計算されています。したがって、最初の列のテキストレベルのコントロールの左端からのインデントと、各列のテキストラベル間の距離は、それぞれの中心から計算されます。

ロックモードでは、縦横のスクロールバーのスライダーが初めの位置から離れて移動されても、テーブルの最初の行および/または最初の列が常に表示されたままである必要があります(ユーザーは各列及び/またはデータ系列の名前を設定することができます)。ヘッダーロックモードは一緒または別々に使用することができます。 

class CLabelsTable : public CElement
  {
private:
   //--- 行の高さ
   int               m_row_y_size;
   //--- テーブルの背景色
   color             m_area_color;
   //--- テーブルテキストのデフォルト色
   color             m_text_color;
   //--- 最初の列のアンカーポイントとコントロールの左端との間の距離
   int               m_x_offset;
   //--- 列のアンカーポイント間の距離
   int               m_column_x_offset;
   //--- 最初の行のロック(固定)モード
   bool              m_fix_first_row;
   //--- 最初の列のロック(固定)モード
   bool              m_fix_first_column; 
   //--- マウスの左クリックの優先順位
   int               m_zorder;
   int               m_area_zorder;
   //---
public:
   //--- (1) 背景色 (2) テキストの色
   void              AreaColor(const color clr)                   { m_area_color=clr;                }
   void              TextColor(const color clr)                   { m_text_color=clr;                }
   //--- (1) 行の高さ (2) 最初の列のアンカーポイントとテーブルの左端との間の距離を設定
   //    (3) 列のアンカーポイント間の距離を設定
   void              RowYSize(const int y_size)                   { m_row_y_size=y_size;             }
   void              XOffset(const int x_offset)                  { m_x_offset=x_offset;             }
   void              ColumnXOffset(const int x_offset)            { m_column_x_offset=x_offset;      }
   //--- 最初の行のロック(固定)モードを (1) 取得および (2) 設定
   bool              FixFirstRow(void)                      const { return(m_fix_first_row);         }
   void              FixFirstRow(const bool flag)                 { m_fix_first_row=flag;            }
   //--- 最初の列のロック(固定)モードを (1) 取得および (2) 設定
   bool              FixFirstColumn(void)                   const { return(m_fix_first_column);      }
   void              FixFirstColumn(const bool flag)              { m_fix_first_column=flag;         }
  };

CLabelsTable::TableSize()CLabelsTable::VisibleTableSize()メソッドを作成してテーブルの行と列の総数と表示される数を設定します。加えて、構造体の形をとった2次元動的配列が必要です。構造体の1つ(LTLabels)はテーブルの表示された部分のテキストラベルの配列を作成し、もう1つ(LTOptions)はそれぞれのテーブルセルの値とプロパティを格納します。この実装では、表示された値(行)とテキストの色を保存します。

2つの引数(行数と列数)はCLabelsTable::TableSize()CLabelsTable::VisibleTableSize()メソッドに受け渡されるべきです。行の数が2未満であるのに列の数が1未満として渡された場合には、これはメソッドの開始時に補正を受けます。すべての配列のサイズが定義され、プロパティ配列はその後初期化されます。

テーブルサイズメソッドに加えて、列と行の合計および可視サイズを受信するためのメソッドが必要です。 

class CLabelsTable : public CElement
  {
private:
   //--- テーブルの可視部分のためのオブジェクトの配列
   struct LTLabels
     {
      CLabel            m_rows[];
     };
   LTLabels          m_columns[];
   //--- テーブルの値とプロパティの配列
   struct LTOptions
     {
      string            m_vrows[];
      color             m_colors[];
     };
   LTOptions         m_vcolumns[];
   //---
public:
   //---  (1) 行と (2) 列の総数を返す
   int               RowsTotal(void)                        const { return(m_rows_total);            }
   int               ColumnsTotal(void)                     const { return(m_columns_total);         }
   //--- テーブルの可視部分の (1) 行と (2) 列の数を返す
   int               VisibleRowsTotal(void)                 const { return(m_visible_rows_total);    }
   int               VisibleColumnsTotal(void)              const { return(m_visible_columns_total); }

   //---  (1) テーブルサイズと (2) 見える部分のサイズを設定する
   void              TableSize(const int columns_total,const int rows_total);
   void              VisibleTableSize(const int visible_columns_total,const int visible_rows_total);
  };
//+------------------------------------------------------------------+
//| テーブルサイズを設定する                                        |
//+------------------------------------------------------------------+
void CLabelsTable::TableSize(const int columns_total,const int rows_total)
  {
//--- 少なくても1列が必要
   m_columns_total=(columns_total<1) ?1 : columns_total;
//--- 少なくても2行が必要
   m_rows_total=(rows_total<2) ?2 : rows_total;
//--- 列の配列のサイズを設定する
   ::ArrayResize(m_vcolumns,m_columns_total);
//--- 行の配列のサイズを設定する
   for(int i=0; i<m_columns_total; i++)
     {
      ::ArrayResize(m_vcolumns[i].m_vrows,m_rows_total);
      ::ArrayResize(m_vcolumns[i].m_colors,m_rows_total);
      //--- テキストの色の配列をデフォルト値で初期化する
      ::ArrayInitialize(m_vcolumns[i].m_colors,m_text_color);
     }
  }
//+------------------------------------------------------------------+
//| テーブルの可視部分のサイズを設定する                    |
//+------------------------------------------------------------------+
void CLabelsTable::VisibleTableSize(const int visible_columns_total,const int visible_rows_total)
  {
//--- 少なくても1列が必要
   m_visible_columns_total=(visible_columns_total<1) ?1 : visible_columns_total;
//--- 少なくても2行が必要
   m_visible_rows_total=(visible_rows_total<2) ?2 : visible_rows_total;
//--- 列の配列のサイズを設定する
   ::ArrayResize(m_columns,m_visible_columns_total);
//--- 行の配列のサイズを設定する
   for(int i=0; i<m_visible_columns_total; i++)
      ::ArrayResize(m_columns[i].m_rows,m_visible_rows_total);
  }

すべてのプロパティは、コントロールを作成する前に、デフォルト値で初期化する必要があります。これはクラスコンストラクタで行うことをお勧めします。

//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CLabelsTable::CLabelsTable(void) : m_fix_first_row(false),
                                   m_fix_first_column(false),
                                   m_row_y_size(18),
                                   m_x_offset(30),
                                   m_column_x_offset(60),
                                   m_area_color(clrWhiteSmoke),
                                   m_text_color(clrBlack),
                                   m_rows_total(2),
                                   m_columns_total(1),
                                   m_visible_rows_total(2),
                                   m_visible_columns_total(1)
  {
//--- 要素クラスの名前を基本クラスに格納する
   CElement::ClassName(CLASS_NAME);
//--- 左マウスクリックの優先順位を設定する
   m_zorder      =0;
   m_area_zorder =1;
//--- テーブルサイズと見える部分のサイズを設定する
   TableSize(m_columns_total,m_rows_total);
   VisibleTableSize(m_visible_columns_total,m_visible_rows_total);
  }

コントロールの開発にあたって、4つのプライベート及び外部呼出しのための1つのパブリックメソッドを作成しましょう。ユーザーがテーブルのスクロールバーを設定するためには、そのポインタを返すメソッドを追加する必要があります。 

class CLabelsTable : public CElement
  {
private:
   //--- テーブル作成のためのオブジェクト
   CRectLabel        m_area;
   CScrollV          m_scrollv;
   CScrollH          m_scrollh;
   //--- テーブルの可視部分のためのオブジェクトの配列
   struct LTLabels
     {
      CLabel            m_rows[];
     };
   LTLabels          m_columns[];
   //---
public:
   //--- スクロールバーポインタを返す
   CScrollV         *GetScrollVPointer(void)                const { return(::GetPointer(m_scrollv)); }
   CScrollH         *GetScrollHPointer(void)                const { return(::GetPointer(m_scrollh)); }
   //--- テーブル作成メソッド
   bool              CreateLabelsTable(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateLabels(void);
   bool              CreateScrollV(void);
   bool              CreateScrollH(void);
  };

ここでは、オブジェクト作成メソッドのうち、テキストラベル配列の開発に使われるCLabelsTable::CreateLabels() のみが表示されています。オブジェクト名の形成時には行および列のインデックスの追加を忘れてはいけません。他のメソッドはすべて下に添付されたファイルの中にあります。 

//+------------------------------------------------------------------+
//| テキストラベルの配列を作成する                                   |
//+------------------------------------------------------------------+
bool CLabelsTable::CreateLabels(void)
  {
//--- 座標とオフセット
   int x      =CElement::X();
   int y      =0;
   int offset =0;
//--- 列
   for(int c=0; c<m_visible_columns_total; c++)
     {
      //--- テーブルオフセットの計算
      offset=(c>0) ?m_column_x_offset : m_x_offset;
      //--- X座標の計算
      x=x+offset;
      //--- 行
      for(int r=0; r<m_visible_rows_total; r++)
        {
         //--- オブジェクト名の形成
         string name=CElement::ProgramName()+"_labelstable_label_"+(string)c+"_"+(string)r+"__"+(string)CElement::Id();
         //--- Y座標を計算する
         y=(r>0) ?y+m_row_y_size-1 : CElement::Y()+10;
         //--- オブジェクトを作成する
         if(!m_columns[c].m_rows[r].Create(m_chart_id,name,m_subwin,x,y))
            return(false);
         //--- プロパティの設定
         m_columns[c].m_rows[r].Description(m_vcolumns[c].m_vrows[r]);
         m_columns[c].m_rows[r].Font(FONT);
         m_columns[c].m_rows[r].FontSize(FONT_SIZE);
         m_columns[c].m_rows[r].Color(m_text_color);
         m_columns[c].m_rows[r].Corner(m_corner);
         m_columns[c].m_rows[r].Anchor(ANCHOR_CENTER);
         m_columns[c].m_rows[r].Selectable(false);
         m_columns[c].m_rows[r].Z_Order(m_zorder);
         m_columns[c].m_rows[r].Tooltip("\n");
         //--- フォームのエッジポイントからのマージン
         m_columns[c].m_rows[r].XGap(x-m_wnd.X());
         m_columns[c].m_rows[r].YGap(y-m_wnd.Y());
         //--- 座標
         m_columns[c].m_rows[r].X(x);
         m_columns[c].m_rows[r].Y(y);
         //--- オブジェクトポインタを格納する
         CElement::AddToArray(m_columns[c].m_rows[r]);
        }
     }
//---
   return(true);
  }

テーブルが作成された後の任意の時点で任意のテーブルセルの値やプロパティを変更するためのメソッドが必要になります。セル値を変更する/受けとるCLabelsTable::SetValue()CLabelsTable::GetValue()メソッドを書きましょう。どちらのメソッドでも、テーブルの列と行のインデックスは、最初の2つの引数として渡される必要があります。特定のインデックスに配置される値はCLabelsTable::SetValue()メソッドの3番目の引数として渡されるべきです。メソッドの冒頭には配列の範囲を超えられたかどうかの確認が必須です。 

class CLabelsTable : public CElement
  {
public:
   //--- 指定されたテーブルセルの値を設定する
   void              SetValue(const int column_index,const int row_index,const string value);
   //--- 指定されたテーブルセルの値を取得する
   string            GetValue(const int column_index,const int row_index);
  };
//+------------------------------------------------------------------+
//| 指定されたインデックスでの値を設定する  |
//+------------------------------------------------------------------+
void CLabelsTable::SetValue(const int column_index,const int row_index,const string value)
  {
//--- 列範囲の超過を確認
   int csize=::ArraySize(m_vcolumns);
   if(csize<1 || column_index<0 || column_index>=csize)
      return;
//--- 行範囲の超過を確認
   int rsize=::ArraySize(m_vcolumns[column_index].m_vrows);
   if(rsize<1 || row_index<0 || row_index>=rsize)
      return;
//--- 値の設定
   m_vcolumns[column_index].m_vrows[row_index]=value;
  }
//+------------------------------------------------------------------+
//| 指定されたインデックスの値を返す                              |
//+------------------------------------------------------------------+
string CLabelsTable::GetValue(const int column_index,const int row_index)
  {
//--- 列範囲の超過を確認
   int csize=::ArraySize(m_vcolumns);
   if(csize<1 || column_index<0 || column_index>=csize)
      return("");
//--- 行範囲の超過を確認
   int rsize=::ArraySize(m_vcolumns[column_index].m_vrows);
   if(rsize<1 || row_index<0 || row_index>=rsize)
      return("");
//--- 値を返す
   return(m_vcolumns[column_index].m_vrows[row_index]);
  }

ライブラリのユーザーは、テーブル値を変更するのに加えてテキストの色を変更したいかもしれません。例えば、正の値は緑、負の値は赤で表示させることができます。このためにCLabelsTable::TextColor() メソッドを実装しましょう。色が3番目の引数として受け渡されることを除いて、これはCLabelsTable::SetValue() と非常に似ています。 

class CLabelsTable : public CElement
  {
public:
   //--- 指定されたテーブルセルの色を変更する
   void              TextColor(const int column_index,const int row_index,const color clr);
  };
//+------------------------------------------------------------------+
//| 指定されたインデックスでの色を変える                          |
//+------------------------------------------------------------------+
void CLabelsTable::TextColor(const int column_index,const int row_index,const color clr)
  {
//--- 列範囲の超過を確認
   int csize=::ArraySize(m_vcolumns);
   if(csize<1 || column_index<0 || column_index>=csize)
      return;
//--- 行範囲の超過を確認
   int rsize=::ArraySize(m_vcolumns[column_index].m_vrows);
   if(rsize<1 || row_index<0 || row_index>=rsize)
      return;
//--- 色を設定する
   m_vcolumns[column_index].m_colors[row_index]=clr;
  }

実装された変更は、テーブルが更新された後にのみ表示されます。そのためには、スクロールバースライダーの位置と相対的にテーブルデータをシフトするのに使用する一般的なメソッドを作成する必要があります。 

このメソッドをCLabelsTable::UpdateTable()と呼びましょう。ヘッダーがロックされている場合、データのシフトは、最初の行が常に上にありおよび/または極端(左)列が常に左にあることを保証するために、配列の2番目のインデックス(1)から始まります。 t及びl変数はメソッドの初めに宣言され、現在使用中のモードによって1または0の値が割り当てられます。

データシフト/更新が始まるインデックスを定義するためには、スクロールバースライダーの現在位置が取得されるべきです。左の列と上の行のヘッダーは別々のループでシフトされます(モードが有効になっている場合)。

テーブルの基本データとセルの色は、メソッドの最後に二重ループでシフトされます。 

class CLabelsTable : public CElement
  {
public:
   //--- 最近の変更を考慮してテーブルのデータを更新する
   void              UpdateTable(void);
  };
//+------------------------------------------------------------------+
//| 最近の変更を考慮してテーブルのデータを更新する       |
//+------------------------------------------------------------------+
void CLabelsTable::UpdateTable(void)
  {
//--- 固定ヘッダーモードが有効になっている場合、1インデックスでシフトする
   int t=(m_fix_first_row) ?1 : 0;
   int l=(m_fix_first_column) ?1 : 0;
//--- 縦および横スクロールバーのスライダーの現在位置を取得する
   int h=m_scrollh.CurrentPos()+l;
   int v=m_scrollv.CurrentPos()+t;
//--- 左列のヘッダーのシフト
   if(m_fix_first_column)
     {
      m_columns[0].m_rows[0].Description(m_vcolumns[0].m_vrows[0]);
      //--- 行
      for(int r=t; r<m_visible_rows_total; r++)
        {
         if(r>=t && r<m_rows_total)
            m_columns[0].m_rows[r].Description(m_vcolumns[0].m_vrows[v]);
         //---
         v++;
        }
     }
//--- 上行のヘッダーのシフト
   if(m_fix_first_row)
     {
      m_columns[0].m_rows[0].Description(m_vcolumns[0].m_vrows[0]);
      //--- 列
      for(int c=l; c<m_visible_columns_total; c++)
        {
         if(h>=l && h<m_columns_total)
            m_columns[c].m_rows[0].Description(m_vcolumns[h].m_vrows[0]);
         //---
         h++;
        }
     }
//--- 横スクロールバーのスライダーの現在位置を取得する
   h=m_scrollh.CurrentPos()+l;
//--- 列
   for(int c=l; c<m_visible_columns_total; c++)
     {
      //--- 縦スクロールバーのスライダーの現在位置を取得する
      v=m_scrollv.CurrentPos()+t;
      //--- 行
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- テーブルデータのシフト
         if(v>=t && v<m_rows_total && h>=l && h<m_columns_total)
           {
            //--- 色の調整
            m_columns[c].m_rows[r].Color(m_vcolumns[h].m_colors[v]);
            //--- 値の調整
            m_columns[c].m_rows[r].Description(m_vcolumns[h].m_vrows[v]);
            v++;
           }
        }
      //---
      h++;
     }
  }

入力ボックスとリストコントロールに行われていたのと同じように、マウスの左ボタンがスクロールバーボタンの上に押されたときの早戻しを実装してみましょう。CLabelsTable::FastSwitching() メソッドのコードは、前の記事でCListViewCSpinEdit及びCCheckBoxEditクラスで説明された同名のメソッドと非常に似ているため、ここで表示する意味はありません。 

コントロールイベントを処理するためのメソッドのコードは、現在以下のように示すことができます。 

//+------------------------------------------------------------------+
//| イベントハンドラ                                              |
//+------------------------------------------------------------------+
void CLabelsTable::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>X() && x<X2() && y>Y() && y<Y2());
      //--- スライダーの管理が有効になっている場合はリストを移動する
      if(m_scrollv.ScrollBarControl(x,y,m_mouse_state) || m_scrollh.ScrollBarControl(x,y,m_mouse_state))
         UpdateTable();
      //---
      return;
     }
//--- オブジェクトの押下の処理
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- 押されたのがテーブルスクロールバーのボタンだった場合
      if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam) ||
         m_scrollh.OnClickScrollInc(sparam) || m_scrollh.OnClickScrollDec(sparam))
         //--- スクロールバーに相対してテーブルをシフトする
         UpdateTable();
      //---
      return;
     }
  }
//+------------------------------------------------------------------+
//| タイマー                                                           |
//+------------------------------------------------------------------+
void CLabelsTable::OnEventTimer(void)
  {
//--- ドロップダウン要素である場合
   if(CElement::IsDropdown())
      FastSwitching();
//--- これがドロップダウン要素ではない場合、フォームのブロック状態を考慮する
   else
     {
      //--- フォームがブロックされていない場合にのみにテーブルの早送りを追跡する
      if(!m_wnd.IsLocked())
         FastSwitching();
     }
  }

テーブルは複雑なGUIコントロールです。したがって、その他のコントロールへのポインタ(私たちの場合は縦横のスクロールバーによって表される)は、ポインタデータベースに含まれなければなりません。テーブルのためのポインタのプライベートな配列を作成する必要があります。これらの変更はすべて CWndContainerクラスのWndContainer.mqhファイルに見られます。このトピックは既にシリーズの他の記事で取り上げられています。したがって、ここでは省略してテキストラベルテーブルのテストに進みます。 

 


テキストラベルテーブルの検証

検証目的のために、前回の記事のエキスパートアドバイザーを使ってメインメニューとステータスバーをそのままにします。MQLアプリケーションGUIにテキストラベルテーブルを追加するためにはCLabelsTable 型クラスインスタンスだけでなく、メソッドとフォームの端ポイントからのインデントが宣言される必要があります(以下のコードを参照)。

class CProgram : public CWndEvents
  {
private:
   //--- テキストラベルテーブル
   CLabelsTable      m_labels_table;
   //---
private:
   //--- テキストラベルテーブル
#define LABELS_TABLE1_GAP_X   (1)
#define LABELS_TABLE1_GAP_Y   (42)
   bool              CreateLabelsTable(void);
  };

ここでCProgram::CreateLabelsTable() メソッドのコードを詳しく見てみましょう。21列と100行のテーブルを作る必要があります。表示されるのは5列と10行です。上の行と最初の列は移動しないようにロックする必要があります。テーブルは作成後にランダム値( -1000~1000)で満たされ、色が定義されます。正の値は緑、負の値は赤で表示されます。最新の変化を表示するにはテーブルを更新することを忘れないでください。 

//+------------------------------------------------------------------+
//| テキストラベルテーブルを作成する                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateLabelsTable(void)
  {
#define COLUMNS1_TOTAL (21)
#define ROWS1_TOTAL    (100)
//--- フォームへのポインタを保存する
   m_labels_table.WindowPointer(m_window1);
//--- 座標
   int x=m_window1.X()+LABELS_TABLE1_GAP_X;
   int y=m_window1.Y()+LABELS_TABLE1_GAP_Y;
//--- 表示される行と列の数
   int visible_columns_total =5;
   int visible_rows_total    =10;
//--- プロパティを設定する
   m_labels_table.XSize(400);
   m_labels_table.XOffset(40);
   m_labels_table.ColumnXOffset(75);
   m_labels_table.FixFirstRow(true);
   m_labels_table.FixFirstColumn(true);
   m_labels_table.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_labels_table.VisibleTableSize(visible_columns_total,visible_rows_total);
//--- テーブルを作成する
   if(!m_labels_table.CreateLabelsTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- テーブルに書き入れる:
//    1番目のセルは空
   m_labels_table.SetValue(0,0,"-");
//--- 列のヘッダー
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_labels_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- 行のヘッダー。テキストは右にアライン
   for(int c=0; c<1; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        m_labels_table.SetValue(c,r,"PARAMETER "+string(r));
     }
//--- データとテーブルのフォーマット(背景色とセルの色)
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
         m_labels_table.SetValue(c,r,string(::rand()%1000-::rand()%1000));
     }
//--- テーブルセルのテキストの色を設定する
   for(int c=1; c<m_labels_table.ColumnsTotal(); c++)
      for(int r=1; r<m_labels_table.RowsTotal(); r++)
         m_labels_table.TextColor(c,r,((double)m_labels_table.GetValue(c,r)>=0) ?clrGreen : clrRed);
//--- テーブルを更新する
   m_labels_table.UpdateTable();
//--- 要素ポインタをベースに追加する
   CWndContainer::AddToElementsArray(0,m_labels_table);
   return(true);
  }

プログラムが端末チャート上で実行されるときにテーブルの値が変更される方法の検証も必要です。これには下に見られるようにMQLアプリケーションのCProgram::OnTimerEvent() タイマーにコードが追加されます。 

//+------------------------------------------------------------------+
//| タイマー                                                           |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();
//--- ステータスラインの2番目のポイントを500ミリ秒ごとに更新
   static int count=0;
   if(count<500)
     {
      count+=TIMER_STEP_MSC;
      return;
     }
//--- タイマーをリセットする
   count=0;
//--- ステータスラインの2番目の点の値を変更する
   m_status_bar.ValueToItem(1,::TimeToString(::TimeLocal(),TIME_DATE|TIME_SECONDS));
//--- テーブルにデータを書き入れる
   for(int c=1; c<m_labels_table.ColumnsTotal(); c++)
      for(int r=1; r<m_labels_table.RowsTotal(); r++)
         m_labels_table.SetValue(c,r,string(::rand()%1000-::rand()%1000));
//--- テーブルセルのテキストの色を設定する
   for(int c=1; c<m_labels_table.ColumnsTotal(); c++)
      for(int r=1; r<m_labels_table.RowsTotal(); r++)
         m_labels_table.TextColor(c,r,((double)m_labels_table.GetValue(c,r)>=0) ?clrGreen : clrRed);
//--- テーブルを更新する
   m_labels_table.UpdateTable();
  }

テキストラベルテーブルを作成するためのメソッドは、アプリケーションGUIのメインメソッドであるCProgram::CreateExpertPanel()で呼び出されるべきです。このメソッドの簡略版を以下に示します。 

//+------------------------------------------------------------------+
//| エキスパートパネルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- コントロールのフォーム1を作成する
//--- コントロールを作成する:
//    メインメニュー
//--- コンテキストメニュー
//--- ステータスラインを作成する:
//--- テキストラベルテーブル
   if(!CreateLabelsTable())
      return(false);
//--- チャートを再描画する
   m_chart.Redraw();
   return(true);
  }

コードをコンパイルしてアプリケーションをチャートで起動する時です。下のスクリーンショットが結果を示します。

 図2。テキストラベルテーブルコントロールの検証

図2。テキストラベルテーブルコントロールの検証

すべてがうまくいっています。さて、2番目のタイプのテーブルを作成するためのクラスを調べてみましょう。 


 


エディットボックステーブルコントロール

テキストラベルテーブルとは異なり、エディットボックステーブルは、より高い柔軟性を提供し、より多くの機能を持っています。テキストの色の変更だけでなく: 

  • セル内のテキストの整列モード(左/右/中央)を設定する
  • 背景色とエディットボックスフレームを変更する
  •  適切なモードが有効になっている場合に手動でエディットボックスの値を変更する

ことが可能です。このすべてがテーブルをよりユーザーフレンドリーにして、広い範囲のタスクのための使用を容易にします。テキストラベルテーブルは、次のコンポーネントで構成されています。

  1. 背景
  2. エディットボックス
  3. 縦スクロールバー
  4. 横スクロールバー

 図3。エディットボックステーブルコントロールの複合部分

図3。エディットボックステーブルコントロールの複合部分


このテーブルのコードが前のものとどのように異なるかを見てみましょう。

 


CTableクラスの開発

テーブルのプロパティを記述し、テキストラベルテーブルとの相違点を強調表示してみましょう。ここではテーブルの可視部分のためのグラフィックオブジェクトの配列が異なる型(CEdit)です。言い換えれば、エディットボックスの代わりにテキストラベルが有されています(以下のコードを参照)。

class CTable : public CElement
  {
private:
   //--- テーブルの可視部分のためのオブジェクトの配列
   struct TEdits
     {
      CEdit             m_rows[];
     };
   TEdits            m_columns[];
  };

テーブルでは、テキストの色に加えてテキストの配置方法エディットボックス背景色の変更/設定が可能なので、各セルのためのより多くのユニークな特性が存在します。

class CTable : public CElement
  {
private:
   //--- テーブルの値とプロパティの配列
   struct TOptions
     {
      string            m_vrows[];
      ENUM_ALIGN_MODE   m_text_align[];
      color             m_text_color[];
      color             m_cell_color[];
     };
   TOptions          m_vcolumns[];
  };

以下はテキストラベルテーブルに存在しないモードと機能の一覧です。

  • 編集可能テーブルモード
  • カーソルがホバリングされたときの行の強調表示のモード
  • 選択された行モード
  • 行の高さ
  • グリッドの色
  • ヘッダー背景色
  • ヘッダーテキストの色
  • カーソルホバー時のセルの色
  • セルテキストのデフォルトの色
  • デフォルトのセルのテキストの配置方法
  • 強調表示された行の背景色
  • ハイライトされた行のテキストの色

このテーブルタイプは、最初の列と最初の行のヘッダーのロックを可能にします。モードが有効になっていると、それらはスクロールバーのスライダーが移動されても所定の位置に留まります。以下のコードは、テーブルのプロパティを設定するためのメソッドとボックスの完全なリストを提示します。 

class CTable : public CElement
  {
private:
   //--- テーブルの行の高さ
   int               m_row_y_size;
   //---(1)背景と(2)背景フレームの色
   color             m_area_color;
   color             m_area_border_color;
   //--- グリッドの色
   color             m_grid_color;
   //--- ヘッダー背景色
   color             m_headers_color;
   //--- ヘッダーテキストの色
   color             m_headers_text_color;
   //--- 異なる状態にあるセルの色
   color             m_cell_color;
   color             m_cell_color_hover;
   //--- セルテキストのデフォルト色
   color             m_cell_text_color;
   //---(1)背景と(2)選択された行のテキストの色
   color             m_selected_row_color;
   color             m_selected_row_text_color;
   //--- 編集可能テーブルモード
   bool              m_read_only;
   //--- カーソルがホバーした時の強調表示された行のモード
   bool              m_lights_hover;
   //--- 行選択モード
   bool              m_selectable_row;
   //--- 最初の行のロック(固定)モード
   bool              m_fix_first_row;
   //--- 最初の列のロック(固定)モード
   bool              m_fix_first_column;
   //--- エディットボックスのデフォルトのテキストアライメントモード
   ENUM_ALIGN_MODE   m_align_mode;
   //---
public:
   //---(1)背景と(2)テーブルフレームの色
   void              AreaColor(const color clr)                        { m_area_color=clr;                }
   void              BorderColor(const color clr)                      { m_area_border_color=clr;         }
   //--- 最初の行のロック(固定)モードを (1) 取得および (2) 設定
   bool              FixFirstRow(void)                           const { return(m_fix_first_row);         }
   void              FixFirstRow(const bool flag)                      { m_fix_first_row=flag;            }
   //--- 最初の列のロック(固定)モードを (1) 取得および (2) 設定
   bool              FixFirstColumn(void)                        const { return(m_fix_first_column);      }
   void              FixFirstColumn(const bool flag)                   { m_fix_first_column=flag;         }
   //---(1)ヘッダー背景と(2)ヘッダーテキストと(3)テーブルグリッドの色
   void              HeadersColor(const color clr)                     { m_headers_color=clr;             }
   void              HeadersTextColor(const color clr)                 { m_headers_text_color=clr;        }
   void              GridColor(const color clr)                        { m_grid_color=clr;                }
   //--- Y軸(高さ)に沿った行のサイズ
   void              RowYSize(const int y_size)                        { m_row_y_size=y_size;             }
   void              CellColor(const color clr)                        { m_cell_color=clr;                }
   void              CellColorHover(const color clr)                   { m_cell_color_hover=clr;          }
   //--- (1) 「読み込みのみ」 (2) ホバーされたときの行の強調表示モード (3) 行選択モード
   void              ReadOnly(const bool flag)                         { m_read_only=flag;                }
   void              LightsHover(const bool flag)                      { m_lights_hover=flag;             }
   void              SelectableRow(const bool flag)                    { m_selectable_row=flag;           }
   //--- セルのテキストの配置方法
   void              TextAlign(const ENUM_ALIGN_MODE align_mode)       { m_align_mode=align_mode;         }
  };

以下に示されているのは、プロパティを設定し、行と列のインデックスでテーブル値を受信するように設計されたメソッドの特徴です。

  • 合計テーブルサイズ(列と行の合計数)
  • テーブルの可視サイズ(表示されている列と行の数)
  • セル内のテキストの配置方法(左/右/中央)
  • テキストの色
  • 背景色
  • 値の設定/変更
  • 値の受け取り

同様のメソッドが既にテキストラベルテーブルのセクションに記載されているので、これらのメソッドのコードを表示する意味はありません。プロパティの設定後、実装された変更が表示されるように CTable::UpdateTable()メソッドを呼び出してテーブルを更新します。 

class CTable : public CElement
  {
public:
   //---  (1) テーブルサイズと (2) 見える部分のサイズを設定する
   void              TableSize(const int columns_total,const int rows_total);
   void              VisibleTableSize(const int visible_columns_total,const int visible_rows_total);
   //--- (1) テキストの整列モード (2) テキストの色 (3) セルの背景色を設定する
   void              TextAlign(const int column_index,const int row_index,const ENUM_ALIGN_MODE mode);
   void              TextColor(const int column_index,const int row_index,const color clr);
   void              CellColor(const int column_index,const int row_index,const color clr);
   //--- 指定されたテーブルセルの値を設定する
   void              SetValue(const int column_index,const int row_index,const string value);
   //--- 指定されたテーブルセルの値を取得する
   string            GetValue(const int column_index,const int row_index);
   //--- 最近の変更を考慮してテーブルのデータを更新する
   void              UpdateTable(void);
  };

それでは、テーブル管理メソッドを検討してみましょう。これらはすべて内部使用に向けたprプライベートクラスメソッドです。その機能は下記を含みます。

  • テーブルの行の押下の処理
  • テーブルのセルへの値入力の処理
  • オブジェクト名からのID受信
  • オブジェクト名からの列インデックスの取得
  • オブジェクト名からの行インデックスの取得
  • 選択された行の強調表示
  • カーソルがボタン上をホバーするときのテーブルの行の色の変更
  • テーブルデータの高速の巻き戻し

テーブルの行の押下を処理するCTable::OnClickTableRow() メソッドから始めましょう。メソッドの初めでは複数のチェックが必要です。プログラムは次の場合にメソッドを終了します。

  • 編集可能テーブルモードが有効
  • スクロールバーの1つがアクティブ(スライダーが移動している)
  • 押下イベントがテーブルセルと関係ない。これは、プログラム名とオブジェクト名でのテーブルセルメンバーシッププロパティの存在によって決定されます。
  • コントロールIDが一致しない。オブジェクト名からIDを取得するにはCTable::IdFromObjectName() メソッドが使われます。このメソッドは他のコントロールをお話ししたときに既に記載されています。

ここでは、ロックされたヘッダの現在のモード(最初の行)と現在の縦スクロールバーのスライダーの位置を考慮しながら、その名前によって押されたセルをすべてのセルから検索します。押されたセルが見つかった場合、行のインデックスと現在のセルの値が、クラスのフィールドに保存されます。

ヘッダ(最初の行)が押されている場合、プログラムはメソッドを終了します。その他の場合にはカスタムメッセージが生成されます。それには (1) チャートID、(2) イベントID(ON_CLICK_LIST_ITEM)、(3) コントロールID 及び (4) 選択された行のインデックスが含まれます。 

class CTable : public CElement
  {
private:
   //--- テーブルの行の押下の処理
   bool              OnClickTableRow(const string clicked_object);
   //---  オブジェクト名から識別子を取得する                     |
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| テーブルの行の押下の処理                           |
//+------------------------------------------------------------------+
bool CTable::OnClickTableRow(const string clicked_object)
  {
//--- 編集可能テーブルモードが有効の場合は終了する
   if(!m_read_only)
      return(false);
//--- スクロールバーがアクティブな場合は終了する
   if(m_scrollv.ScrollState() || m_scrollh.ScrollState())
      return(false);
//--- 押されたのがテーブルセルでなかった場合は終了する
   if(::StringFind(clicked_object,CElement::ProgramName()+"_table_edit_",0)<0)
      return(false);
//---  オブジェクト名から識別子を取得する                     |
   int id=IdFromObjectName(clicked_object);
//--- 識別子が一致しない場合は終了する
   if(id!=CElement::Id())
      return(false);
//--- 行インデックスを探す
   int row_index=0;
//--- 固定ヘッダーモードが有効になっている場合、1インデックスでシフトする
   int t=(m_fix_first_row) ?1 : 0;
//--- 列
   for(int c=0; c<m_visible_columns_total; c++)
     {
      //--- 縦スクロールバーのスライダーの現在位置を取得する
      int v=m_scrollv.CurrentPos()+t;
      //--- 行
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- 押されたのがこのセルでなかった場合
         if(m_columns[c].m_rows[r].Name()==clicked_object)
           {
            //--- 行インデックスを格納する
            m_selected_item=row_index=v;
            //--- セルラインを格納する
            m_selected_item_text=m_columns[c].m_rows[r].Description();
            break;
           }
         //--- 行のカウンターを増加する
         if(v>=t && v<m_rows_total)
            v++;
        }
     }
//--- ヘッダーが押された場合は終了する
   if(m_fix_first_row && row_index<1)
      return(false);
//--- 関連したメッセージを送信する
   ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),m_selected_item,"");
   return(true);
  }

編集可能テーブルモードでセルに入力された値を処理するCTable::OnEndEditCell() メソッドを書きましょう。プログラムはまた、メソッドの開始時にいくつかのチェックを通過する必要があります。次の場合にはメソッドが終了します。

  • 編集可能テーブルモードが無効
  • プログラム名またはテーブルセルメンバーシッププロパティが一致しない
  • コントロールIDが一致しない

すべてのチェックが通過された場合、テーブルの可視部分のセルの列と行のインデックス(グラフィカルオブジェクトの配列のインデックス)が CTable::ColumnIndexFromObjectName()及びCTable::RowIndexFromObjectName() 補助メソッドを使って受け取られます。ここでデータ配列インデックスを受け取るためにスクロールバーのスライダの現在の位置をオブジェクトインデックスへに追加する必要があります。次に、ヘッダーロックモードが有効になっていてオブジェクト配列のインデックスがゼロに等しい場合には行インデックスを修正しなければなりません。ここで、セルの値が変更されているかどうかを確認する必要があります。変更されている場合、新しい値は適切なデータ配列に保存され (1) チャートID、(2) イベントID(ON_END_EDIT)、(3) コントロールID 及び (4) 列、行、および現在のセルの値から生成された線を持ったメッセージが生成されます。"_" シンボルは線内の区切りとして使われます。 

class CTable : public CElement
  {
private:
   //--- テーブルのセルへの値入力の処理
   bool              OnEndEditCell(const string edited_object);
   //--- オブジェクト名から列インデックスを取得する
   int               ColumnIndexFromObjectName(const string object_name);
   //--- オブジェクト名から行インデックスを取得する
   int               RowIndexFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| セル値の編集完了イベント                          |
//+------------------------------------------------------------------+
bool CTable::OnEndEditCell(const string edited_object)
  {
//--- 編集可能テーブルモードが無効の場合は終了する
   if(m_read_only)
      return(false);
//--- 押されたのがテーブルセルでなかった場合は終了する
   if(::StringFind(edited_object,CElement::ProgramName()+"_table_edit_",0)<0)
      return(false);
//---  オブジェクト名から識別子を取得する                     |
   int id=IdFromObjectName(edited_object);
//--- 識別子が一致しない場合は終了する
   if(id!=CElement::Id())
      return(false);
//--- セルの行と列のインデックスを取得する
   int c =ColumnIndexFromObjectName(edited_object);
   int r =RowIndexFromObjectName(edited_object);
//--- データ配列の行と列のインデックスを取得する
   int vc =c+m_scrollh.CurrentPos();
   int vr =r+m_scrollv.CurrentPos();
//--- ヘッダーが押された場合は行インデックスを調整する
   if(m_fix_first_row && r==0)
      vr=0;
//--- 入力値を取得する
   string cell_text=m_columns[c].m_rows[r].Description();
//--- セルの値が変更された場合
   if(cell_text!=m_vcolumns[vc].m_vrows[vr])
     {
      //--- 値を配列に格納する
      SetValue(vc,vr,cell_text);
      //--- 関連したメッセージを送信する
      ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),0,string(vc)+"_"+string(vr)+"_"+cell_text);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| オブジェクト名から列インデックスを取得する
//+------------------------------------------------------------------+
int CTable::ColumnIndexFromObjectName(const string object_name)
  {
   ushort u_sep=0;
   string result[];
   int    array_size=0;
//--- 区切り文字のコードを取得する
   u_sep=::StringGetCharacter("_",0);
//--- 文字列を分ける
   ::StringSplit(object_name,u_sep,result);
   array_size=::ArraySize(result)-1;
//--- 配列サイズの超過を確認する
   if(array_size-3<0)
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//--- 項目インデックスを返す    
   return((int)result[array_size-3]);
  }
//+------------------------------------------------------------------+
//| オブジェクト名から行インデックスを取得する                   |
//+------------------------------------------------------------------+
int CTable::RowIndexFromObjectName(const string object_name)
  {
   ushort u_sep=0;
   string result[];
   int    array_size=0;
//--- 区切り文字のコードを取得する
   u_sep=::StringGetCharacter("_",0);
//--- 文字列を分ける
   ::StringSplit(object_name,u_sep,result);
   array_size=::ArraySize(result)-1;
//--- 配列サイズの超過を確認する
   if(array_size-2<0)
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//--- 項目インデックスを返す    
   return((int)result[array_size-2]);
  }

適切なモードが有効な場合、行の強調表示にはCTable::HighlightSelectedItem() メソッドが必要です。強調表示された行では背景色と文字色が他と異なります。これらの編集可能なプロパティは、以前にすでに検討されています。

メソッド(コードは以下を参照)の開始時には2つのチェックがあります。以下の場合にはプログラムがメソッドを終了します。

  • テーブルセルの編集可能モードが有効
  • 行の強調表示モードが有効

チェックが通った際は、ヘッダーロックモードが有効になっている場合、列と行が1インデックスによってシフトされます。ここで、選択された行を検索し、スクロールバーのスライダの現在の位置を考慮して、(列と行のための)2つのカウンタを使用し、二重ループでそのセルの背景色とテキストのための適切な色を設定する必要があります。配列からの色は他の行からのオブジェクトに使用されます。  

class CTable : public CElement
  {
private:
   //--- 選択された行を強調表示する
   void              HighlightSelectedItem(void);
  };
//+------------------------------------------------------------------+
//| 選択された行を強調表示する                                       |
//+------------------------------------------------------------------+
void CTable::HighlightSelectedItem(void)
  {
//---  「読み込みのみ」「行選択」のいずれかのモードが無効な場合は終了する
   if(!m_read_only || !m_selectable_row)
      return;
//--- 固定ヘッダーモードが有効になっている場合、1インデックスでシフトする
   int t=(m_fix_first_row) ?1 : 0;
   int l=(m_fix_first_column) ?1 : 0;
//--- 横スクロールバーのスライダーの現在位置を取得する
   int h=m_scrollh.CurrentPos()+l;
//--- 列
   for(int c=l; c<m_visible_columns_total; c++)
     {
      //--- 縦スクロールバーのスライダーの現在位置を取得する
      int v=m_scrollv.CurrentPos()+t;
      //--- 行
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- テーブルデータのシフト
         if(v>=t && v<m_rows_total)
           {
            //--- 選択された行を考慮した調整
            color back_color=(m_selected_item==v) ?m_selected_row_color : m_vcolumns[h].m_cell_color[v];
            color text_color=(m_selected_item==v) ?m_selected_row_text_color : m_vcolumns[h].m_text_color[v];
            //--- セルのテキストの色と背景色を調整する
            m_columns[c].m_rows[r].Color(text_color);
            m_columns[c].m_rows[r].BackColor(back_color);
            v++;
           }
        }
      //---
      h++;
     }
  }

マウスカーソルがホバーした時のあと1つの有用なモードは、テーブルの行の強調表示です。これにはCTable::RowColorByHover() メソッドが使われます。これにもいくつかのチェックが含まれます。チェックに落ちた場合、プログラムはメソッドを終了します。次の場合にはメソッドが終了します。

  • カーソルがホバリングされたときの行の強調表示のモードが無効
  • 編集可能テーブルモードが有効
  • コントロールが取り付けられているフォームがブロックされている
  • スクロールバーの1つがアクティブ(スライダーが移動している)

すべてのチェックが通った場合、すべてのセルを二重ループ内で通過し、マウスカーソルが現在ホバーしている行のセルの色を変更する必要があります。唯一の例外は、強調表示された行です。そのような行が到達されると、行数が増加しますが、行のセルの色は変更されません。 

class CTable : public CElement
  {
private:
   //--- ホバーされたときにテーブル行の色を変える
   void              RowColorByHover(const int x,const int y);
  };
//+-------------------------------------------------------------------+
//| ホバーされたときにテーブル行の色を変える                           |
//+-------------------------------------------------------------------+
void CTable::RowColorByHover(const int x,const int y)
  {
//--- ホバー行の強調表示が無効になっている場合やフォームがブロックされている場合には終了する
   if(!m_lights_hover || !m_read_only || m_wnd.IsLocked())
      return;
//--- スクロールバーが移動中の場合には終了する
   if(m_scrollv.ScrollState() || m_scrollh.ScrollState())
      return;
//--- 固定ヘッダーモードが有効になっている場合、1インデックスでシフトする
   int t=(m_fix_first_row) ?1 : 0;
   int l=(m_fix_first_column) ?1 : 0;
//--- 横スクロールバーのスライダーの現在位置を取得する
   int h=m_scrollh.CurrentPos()+l;
//--- 列
   for(int c=l; c<m_visible_columns_total; c++)
     {
      //--- 縦スクロールバーのスライダーの現在位置を取得する
      int v=m_scrollv.CurrentPos()+t;
      //--- 行
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- 配列サイズの超過を確認する
         if(v>=t && v<m_rows_total)
           {
            //--- 「読み込みのみ」モードで、行選択が有効で選択された項目が到達された場合は抜かす
            if(m_selected_item==v && m_read_only && m_selectable_row)
              {
               v++;
               continue;
              }
            //--- ホバーされた場合に行を強調表示する
            if(x>m_columns[0].m_rows[r].X() && x<m_columns[m_visible_columns_total-1].m_rows[r].X2() &&
               y>m_columns[c].m_rows[r].Y() && y<m_columns[c].m_rows[r].Y2())
              {
               m_columns[c].m_rows[r].BackColor(m_cell_color_hover);
              }
            //--- カーソルがこの行の領域の外にある場合、デフォルト色を復元する
            else
              {
               if(v>=t && v<m_rows_total)
                  m_columns[c].m_rows[r].BackColor(m_vcolumns[h].m_cell_color[v]);
              }
            //---
            v++;
           }
        }
      //---
      h++;
     }
  }

これで、すべてのテーブル管理方法が検討されました。CTable::OnEvent() コントロールイベントハンドラコードは下で詳しく見ることができます。横スクロールバースライダーの動きが処理された後にはCTable::HighlightSelectedItem() メソッドもまた使われていることにご注意ください。

//+------------------------------------------------------------------+
//| イベントハンドラ                                                    |
//+------------------------------------------------------------------+
void CTable::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>X() && x<X2() && y>Y() && y<Y2());
      //--- スクロールバーがアクティブな場合
      if(m_scrollv.ScrollBarControl(x,y,m_mouse_state) || m_scrollh.ScrollBarControl(x,y,m_mouse_state))
         //--- テーブルをシフトする
         UpdateTable();
      //--- 選択された行を強調表示する
      HighlightSelectedItem();
      //--- ホバーされたときにテーブル行の色を変える
      RowColorByHover(x,y);
      return;
     }
//--- オブジェクトの押下の処理
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- テーブルの行が押された場合
      if(OnClickTableRow(sparam))
        {
         //--- 選択された行を強調表示する
         HighlightSelectedItem();
         return;
        }
      //--- スクロールバーボタンが押された場合
      if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam) ||
         m_scrollh.OnClickScrollInc(sparam) || m_scrollh.OnClickScrollDec(sparam))
        {
         //--- 最近の変更を考慮してテーブルのデータを更新する
         UpdateTable();
         //--- 選択された行を強調表示する
         HighlightSelectedItem();
         return;
        }
      return;
     }
//--- 入力フィールドの値の変化イベントの処理
   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      OnEndEditCell(sparam);
      //--- テーブル色をリセットする
      ResetColors();
      return;
     }
  }

 


エディットボックステーブルの検証

これでエディットボックスの検証を行うための準備がすべて整いました。以前にテストしたEAのコピーを作成してテキストラベルテーブルに関連した要素をすべて削除しましょう。CProgramカスタムクラスでCTable型のクラスインスタンスを作成し、テーブルとフォームの端からのインデントを作成するメソッドを宣言します。

class CProgram : public CWndEvents
  {
private:
   //--- エディットボックステーブル
   CTable            m_table;
   //---
private:
   //--- エディットボックステーブル
#define TABLE1_GAP_X          (1)
#define TABLE1_GAP_Y          (42)
   bool              CreateTable(void);
  };

100列と1000行を持つテーブルを作りましょう。表示されるのは6列と15行です。スクロールバーのスライダーが移動しているときに所定の位置に留まるようにテーブルのヘッダー(最初の行と列)をロックしてみましょう。行選択とカーソルがホバーしたときの行の強調表示のモードを有効にする必要があります。 

コントロールの作成後には、テーブル配列にデータを書き入れてフォーマットします。. 例としてALIGN_RIGHT整列法を1番目の列のテキストに適応しましょう。列のテキストが赤と青の色を交互に着色される一方、行のセルの背景は、「ゼブラ」スタイルで着色しましょう。変更を表示するにはテーブルの更新を怠ってはいけません。 

//+------------------------------------------------------------------+
//| テーブルを作成する                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateTable(void)
  {
#define COLUMNS1_TOTAL (100)
#define ROWS1_TOTAL    (1000)
//--- フォームへのポインタを保存する
   m_table.WindowPointer(m_window1);
//--- 座標
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- 表示される行と列の数
   int visible_columns_total =6;
   int visible_rows_total    =15;
//--- 作成の前にプロパティを設定する
   m_table.XSize(600);
   m_table.RowYSize(20);
   m_table.FixFirstRow(true);
   m_table.FixFirstColumn(true);
   m_table.LightsHover(true);
   m_table.SelectableRow(true);
   m_table.TextAlign(ALIGN_CENTER);
   m_table.HeadersColor(C'255,244,213');
   m_table.HeadersTextColor(clrBlack);
   m_table.GridColor(clrLightGray);
   m_table.CellColorHover(clrGold);
   m_table.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_table.VisibleTableSize(visible_columns_total,visible_rows_total);
//--- コントロール要素を作成する
   if(!m_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- テーブルに書き入れる:
//    1番目のセルは空
   m_table.SetValue(0,0,"-");
//--- 列のヘッダー
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- 行のヘッダー。テキストは右にアライン
   for(int c=0; c<1; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        {
         m_table.SetValue(c,r,"PARAMETER "+string(r));
         m_table.TextAlign(c,r,ALIGN_RIGHT);
        }
     }
//--- データとテーブルのフォーマット(背景色とセルの色)
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        {
         m_table.SetValue(c,r,string(c)+":"+string(r));
         m_table.TextColor(c,r,(c%2==0)?clrRed : clrRoyalBlue);
         m_table.CellColor(c,r,(r%2==0)?clrWhiteSmoke : clrWhite);
        }
     }
//--- 変更を表示するためにテーブルを更新
   m_table.UpdateTable();
//--- オブジェクトをオブジェクトグループの総合配列に追加する
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
  }

CProgram::CreateTable() メソッドはGUIメインメソッド(下記の短縮版を参照)で呼び出されるべきです。 

//+------------------------------------------------------------------+
//| エキスパートパネルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- コントロールのフォーム1を作成する
//--- コントロールを作成する:
//    メインメニュー
//--- コンテキストメニュー
//--- ステータスラインを作成する:
//--- エディットボックステーブル
   if(!CreateTable())
      return(false);
//--- チャートを再描画する
   m_chart.Redraw();
   return(true);
  }

コードをコンパイルしてプログラムをチャートに読み込みます。ただしすべてが正しく行われると、下のスクリーンショットのような結果を得ることとなります。

 図4。エディットボックステーブルコントロールの検証

図4。エディットボックステーブルコントロールの検証

すべてがうまくいっています。しかし、63文字以上のテキストを1つのセルに置くと、テキストは完全には表示されません。ターミナルのすべてのグラフィックオブジェクトは最大で63シンボルしか表示できないという制限を持ちます。それを回避するためには描画にCCanvasクラスを使います。このクラスは、制限なしでテキストを表示するメソッドを含んでいます。それは以下の記事でコントロール(区切り線とコンテキストメニュー)を描画するときにすでに適用されました。

63文字はデータの表示には不十分な場合があるので、3番目のテーブルタイプはCCanvasクラスを使って描画されます。 

 


レンダーテーブルコントロール

レンダーテーブルでは、各セルでの文字数に制限はありません。また、各列の幅はテーブルの幅を変更せずに設定することができます。上述された他のテーブルタイプこれを達成することは非常に困難です。したがって、レンダーテーブルのいくつかの利点はすでに明確です。

  • 各セル内のシンボルの数が無制限
  • 各列の幅の個別設定が可能
  • テーブルの作成には1つのオブジェクト(OBJ_BITMAP_LABEL)のみが使われ、テキストラベル(OBJ_LABEL) やエディットボックス(OBJ_EDIT)の場合と異なる

レンダーテーブルコントロールは、次のコンポーネントで構成されています。

  1. 背景
  2. レンダーテーブル
  3. 縦スクロールバー
  4. 横スクロールバー

図5。レンダーテーブルコントロールの複合部分
 

図5。レンダーテーブルコントロールの複合部分

このようなテーブルを作成するためのクラスのコードを調べてみましょう。

 


CCanvasTableクラスの開発

テーブル値とプロパティを格納するためのCTOptions構造体を作成しましょう。

//+------------------------------------------------------------------+
//| レンダーテーブル作成クラス                              |
//+------------------------------------------------------------------+
class CCanvasTable : public CElement
  {
private:
   //--- テーブルの値とプロパティの配列
   struct CTOptions
     {
      string            m_vrows[];
      int               m_width;
      ENUM_ALIGN_MODE   m_text_align;
     };
   CTOptions         m_vcolumns[];
  };

CTOptions構造体のフィールドはテーブルの基本サイズ(行と列の数)が設定されたときにデフォルト値で初期化されます。列の幅はすべて100画素に設定され、列のセルのテキストはすべてALIGN_CENTERで整列されます。 

class CCanvasTable : public CElement
  {
public:
   //--- テーブルサイズを設定する
   void              TableSize(const int columns_total,const int rows_total);
  };
//+------------------------------------------------------------------+
//| テーブルサイズを設定する                                        |
//+------------------------------------------------------------------+
void CCanvasTable::TableSize(const int columns_total,const int rows_total)
  {
//--- 少なくても1列が必要
   m_columns_total=(columns_total<1) ?1 : columns_total;
//--- 少なくても2行が必要
   m_rows_total=(rows_total<2) ?2 : rows_total;
//--- 列の配列のサイズを設定する
   ::ArrayResize(m_vcolumns,m_columns_total);
//--- 行の配列のサイズを設定する
   for(int i=0; i<m_columns_total; i++)
     {
      ::ArrayResize(m_vcolumns[i].m_vrows,m_rows_total);
      //--- 列のプロパティをデフォルト値で初期化する
      m_vcolumns[i].m_width      =100;
      m_vcolumns[i].m_text_align =ALIGN_CENTER;
     }
  }

ここで、サイズが設定された後に幅を変更するか(必要な場合に)列のいくつかのテキスト整列法を変えるメソッドを作成しましょう。これを行うには、配列を初期化して対応するメソッドに受け渡す必要があります。例は以下の通りです。 

class CCanvasTable : public CElement
  {
public:
   //--- (1) テキスト整列モードと (2) 各列の幅を設定する
   void              TextAlign(const ENUM_ALIGN_MODE &array[]);
   void              ColumnsWidth(const int &array[]);
  };
//+------------------------------------------------------------------+
//| テキストラベルの配列に書き入れる                                   |
//+------------------------------------------------------------------+
void CCanvasTable::TextAlign(const ENUM_ALIGN_MODE &array[])
  {
   int total=0;
   int array_size=::ArraySize(array);
//---サイズが0の配列が渡された場合は終了する
   if(array_size<1)
      return;
//--- 配列の範囲を超えないように値を調整する
   total=(array_size<m_columns_total)?array_size : m_columns_total;
//--- 値を構造体に格納する
   for(int c=0; c<total; c++)
      m_vcolumns[c].m_text_align=array[c];
  }
//+------------------------------------------------------------------+
//| 列の幅の配列に書き入れる                                 |
//+------------------------------------------------------------------+
void CCanvasTable::ColumnsWidth(const int &array[])
  {
   int total=0;
   int array_size=::ArraySize(array);
//---サイズが0の配列が渡された場合は終了する
   if(array_size<1)
      return;
//--- 配列の範囲を超えないように値を調整する 
   total=(array_size<m_columns_total)?array_size : m_columns_total;
//--- 値を構造体に格納する
   for(int c=0; c<total; c++)
      m_vcolumns[c].m_width=array[c];
  }

テーブルの作成を開始する前に、指定されたパラメータ(すべての列の幅、すべての行の高さとスクロールバーの存在)と比較して、その全体の大きさだけでなく、その可視部分のサイズを設定する必要があります。これには、下にあるCCanvasTable::CalculateTableSize() メソッドを書きます。 

class CCanvasTable : public CElement
  {
private:
   //--- テーブル全体と可視部分のサイズ
   int               m_table_x_size;
   int               m_table_y_size;
   int               m_table_visible_x_size;
   int               m_table_visible_y_size;
//---
private:
   //--- テーブルサイズを計算する
   void              CalculateTableSize(void);
  };
//+------------------------------------------------------------------+
//| テーブルサイズを計算する                                  |
//+------------------------------------------------------------------+
void CCanvasTable::CalculateTableSize(void)
  {
//--- テーブル全体の幅を計算する
   m_table_x_size=0;
   for(int c=0; c<m_columns_total; c++)
      m_table_x_size=m_table_x_size+m_vcolumns[c].m_width;
//--- 縦スクロールバーとのテーブルの幅
   int x_size=(m_rows_total>m_visible_rows_total) ?m_x_size-m_scrollh.ScrollWidth() : m_x_size-2;
//--- すべての列の幅がテーブルの幅より小さい場合、テーブルの幅を使用する
   if(m_table_x_size<m_x_size)
      m_table_x_size=x_size;
//--- テーブル全体の高さを計算する
   m_table_y_size=m_cell_y_size*m_rows_total-(m_rows_total-1);
//--- 画像の断片(テーブルの可視部分)を表示するためにフレームサイズを設定する
   m_table_visible_x_size=x_size;
   m_table_visible_y_size=m_cell_y_size*m_visible_rows_total-(m_visible_rows_total-1);
//--- 横スクロールバーがある場合は、Y軸に沿ってコントロールのサイズを調整する
   int y_size=m_cell_y_size*m_visible_rows_total+2-(m_visible_rows_total-1);
   m_y_size=(m_table_x_size>m_table_visible_x_size) ?y_size+m_scrollh.ScrollWidth()-1 : y_size;
  }

テーブルのサイズを計算してキャンバスを作成した後は、テーブルグリッド(フレーム)とセルのテキストを描画するためのメソッドを開発する必要があります。グリッドの描画にはCCanvasTable::DrawGrid() メソッドが書かれます。まず、最初のループで水平方向のグリッド線が描かれ、その後2番目のループで垂直線が描かれます。  

class CCanvasTable : public CElement
  {
private:
   //--- グリッドの色
   color             m_grid_color;
   //--- セルのサイズ(高さ)
   int               m_cell_y_size;
//---
public:
   //--- グリッドの色
   void              GridColor(const color clr)           { m_grid_color=clr;                }
//---
private:
   //--- グリッドを描画する
   void              DrawGrid(void);
  };
//+------------------------------------------------------------------+
//| グリッドを描画する                                                       |
//+------------------------------------------------------------------+
void CCanvasTable::DrawGrid(void)
  {
//--- グリッドの色
   uint clr=::ColorToARGB(m_grid_color,255);
//--- 描画のためのキャンバスのサイズ
   int x_size =m_canvas.XSize()-1;
   int y_size =m_canvas.YSize()-1;
//--- 座標
   int x1=0,x2=0,y1=0,y2=0;
//--- 横ライン
   x1=0;
   y1=0;
   x2=x_size;
   y2=0;
   for(int i=0; i<=m_rows_total; i++)
     {
      m_canvas.Line(x1,y1,x2,y2,clr);
      y2=y1+=m_cell_y_size-1;
     }
//--- 縦ライン
   x1=0;
   y1=0;
   x2=0;
   y2=y_size;
   for(int i=0; i<m_columns_total; i++)
     {
      m_canvas.Line(x1,y1,x2,y2,clr);
      x2=x1+=m_vcolumns[i].m_width;
     }
//--- 右
   x1=x_size;
   y1=0;
   x2=x_size;
   y2=y_size;
   m_canvas.Line(x1,y1,x2,y2,clr);
  }

テキスト描画メソッドである CCanvasTable::DrawText() メソッドはグリッド描画メソッドより複雑です。インデントを正しく計算するには、現在の列だけでなく1つ前の列のテキストの整列法も検討されるべきです。左/右整列ではではインデントを10画素に設定します。上のセルの端からのインデントは3画素です。メソッドのコードは、以下に詳しく表示されてます。 

class CCanvasTable : public CElement
  {
private:
   //--- テキストの色
   color             m_cell_text_color;
//---
public:
   //--- テーブルテキストの色
   void              TextColor(const color clr)           { m_cell_text_color=clr;           }
//---
private:
   //--- テキストを描画する
   void              DrawText(void);
  };
//+------------------------------------------------------------------+
//| テキストを描画する                                                       |
//+------------------------------------------------------------------+
void CCanvasTable::DrawText(void)
  {
//--- 座標とオフセットの計算
   int  x             =0;
   int  y             =0;
   uint text_align    =0;
   int  column_offset =0;
   int  cell_x_offset =10;
   int  cell_y_offset =3;
//--- テキストの色
   uint clr=::ColorToARGB(m_cell_text_color,255);
//--- フォントプロパティ
   m_canvas.FontSet(FONT,-80,FW_NORMAL);
//--- 列
   for(int c=0; c<m_columns_total; c++)
     {
      //--- 最初の列のオフセット計算
      if(c==0)
        {
         //--- 各列のモード設定に基づいたセル内のテキストの整列
         switch(m_vcolumns[0].m_text_align)
           {
            //--- 中央
            case ALIGN_CENTER :
               column_offset=column_offset+m_vcolumns[0].m_width/2;
               x=column_offset;
               break;
               //--- 右
            case ALIGN_RIGHT :
               column_offset=column_offset+m_vcolumns[0].m_width;
               x=column_offset-cell_x_offset;
               break;
               //--- 左
            case ALIGN_LEFT :
               x=column_offset+cell_x_offset;
               break;
           }
        }
      //--- 最初以外のすべての列のオフセットの計算
      else
        {
         //--- 各列のモード設定に基づいたセル内のテキストの整列
         switch(m_vcolumns[c].m_text_align)
           {
            //--- 中央
            case ALIGN_CENTER :
               //--- 1つ前の列に相対したオフセットの計算
               switch(m_vcolumns[c-1].m_text_align)
                 {
                  case ALIGN_CENTER :
                     column_offset=column_offset+(m_vcolumns[c-1].m_width/2)+(m_vcolumns[c].m_width/2);
                     break;
                  case ALIGN_RIGHT :
                     column_offset=column_offset+(m_vcolumns[c].m_width/2);
                     break;
                  case ALIGN_LEFT :
                     column_offset=column_offset+m_vcolumns[c-1].m_width+(m_vcolumns[c].m_width/2);
                     break;
                 }
               //---
               x=column_offset;
               break;
               //--- 右
            case ALIGN_RIGHT :
               //--- 1つ前の列に相対したオフセットの計算
               switch(m_vcolumns[c-1].m_text_align)
                 {
                  case ALIGN_CENTER :
                     column_offset=column_offset+(m_vcolumns[c-1].m_width/2)+m_vcolumns[c].m_width;
                     x=column_offset-cell_x_offset;
                     break;
                  case ALIGN_RIGHT :
                     column_offset=column_offset+m_vcolumns[c].m_width;
                     x=column_offset-cell_x_offset;
                     break;
                  case ALIGN_LEFT :
                     column_offset=column_offset+m_vcolumns[c-1].m_width+m_vcolumns[c].m_width;
                     x=column_offset-cell_x_offset;
                     break;
                 }
               //---
               break;
               //--- 左
            case ALIGN_LEFT :
               //--- 1つ前の列に相対したオフセットの計算
               switch(m_vcolumns[c-1].m_text_align)
                 {
                  case ALIGN_CENTER :
                     column_offset=column_offset+(m_vcolumns[c-1].m_width/2);
                     x=column_offset+cell_x_offset;
                     break;
                  case ALIGN_RIGHT :
                     x=column_offset+cell_x_offset;
                     break;
                  case ALIGN_LEFT :
                     column_offset=column_offset+m_vcolumns[c-1].m_width;
                     x=column_offset+cell_x_offset;
                     break;
                 }
               //---
               break;
           }
        }
      //--- 行
      for(int r=0; r<m_rows_total; r++)
        {
         //---
         y+=(r>0) ?m_cell_y_size-1 : cell_y_offset;
         //---
         switch(m_vcolumns[c].m_text_align)
           {
            case ALIGN_CENTER :
               text_align=TA_CENTER|TA_TOP;
               break;
            case ALIGN_RIGHT :
               text_align=TA_RIGHT|TA_TOP;
               break;
            case ALIGN_LEFT :
               text_align=TA_LEFT|TA_TOP;
               break;
           }
         //--- テキストを描画する
         m_canvas.TextOut(x,y,m_vcolumns[c].m_vrows[r],clr,text_align);
        }
      //--- 次のサイクルのためにY座標ゼロをゼロにする
      y=0;
     }
  }

ここで調べられた最初の2つのテーブルタイプでは、スクロールバーを経由したデータシフトは、テーブルの可視部分のオブジェクト値(テキストラベルとエディトボックス)を変更することによって実行されます。ここでは、画像の長方形の可視性スコープをシフトします。つまり、テーブル(画像)のサイズは最初にはすべての列の合計とすべての行の高さに等しいです。これは、可視範囲がその範囲内で移動可能であるオリジナル画像のサイズです。可視性スコープサイズはいつでも変更できますが、ここではCCanvasTable::CreateCells() メソッドがコントロールで作成された直後に設定されます。 

可視性スコープはOBJPROP_XSIZE及びOBJPROP_YSIZEプロパティで設定可能な一方、スコープのシフト(オリジナル画像の範囲内)はOBJPROP_XOFFSET及び OBJPROP_YOFFSETプロパティで設定することができます(下記のコードを参照)。 

//--- 可視部分のサイズを設定する
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_XSIZE,m_table_visible_x_size);
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_YSIZE,m_table_visible_y_size);
//--- XおよびY軸に沿って画像内のフレームオフセットを設定する
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_XOFFSET,0);
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_YOFFSET,0);

スクロールバーのスライダーの現在の位置に相対して可視性スコープをシフトする簡単なCCanvasTable::ShiftTable() メソッドを開発しましょう。縦のシフトステップは行の高さに等しく、横のシフトステップは画素単位です(下記のコードリストを参照)。 

class CCanvasTable : public CElement
  {
public:
   //--- スクロールバーの位置に相対してテーブルをシフトする
   void              ShiftTable(void);
  };
//+------------------------------------------------------------------+
//| スクロールバーに相対してテーブルをシフトする                       |
//+------------------------------------------------------------------+
void CCanvasTable::ShiftTable(void)
  {
//--- 縦および横スクロールバーのスライダーの現在位置を取得する
   int h=m_scrollh.CurrentPos();
   int v=m_scrollv.CurrentPos();
//--- スクロールバースライダーに相対したテーブルの位置の計算
   long c=h;
   long r=v*(m_cell_y_size-1);
//--- テーブルのシフト
   ::ObjectSetInteger(m_chart_id,m_canvas.Name(),OBJPROP_XOFFSET,c);
   ::ObjectSetInteger(m_chart_id,m_canvas.Name(),OBJPROP_YOFFSET,r);
  }

下記はテーブル描画のための一般的なCCanvasTable::DrawTable() メソッドです。 

class CCanvasTable : public CElement
  {
public:
   //--- 最近の変更を考慮してテーブルを描画する
   void              DrawTable(void);
  };
//+------------------------------------------------------------------+
//| テーブルを描画する                                                       |
//+------------------------------------------------------------------+
void CCanvasTable::DrawTable(void)
  {
//--- 背景を透明にする
   m_canvas.Erase(::ColorToARGB(clrNONE,0));
//--- グリッドを描画する
   DrawGrid();
//--- テキストを描画する
   DrawText();
//--- 直近に描画された変化を表示する
   m_canvas.Update();
//--- スクロールバーに相対してテーブルをシフトする
   ShiftTable();
  }

これで、このテーブルタイプをテストする準備が整いました。 

 


レンダーテーブルの検証

以前にテストしたEAのコピーを作成してCTableテーブルタイプに関連した要素をすべて削除しましょう。 CProgramカスタムクラスでCCanvasTable型クラスのインスタンスを作成し、下記のコードに見られるように  (1) テーブル作成のための CProgram::CreateCanvasTable() と (2) フォームの端からのインデントを宣言します。

class CProgram : public CWndEvents
  {
private:
   //--- レンダーテーブル
   CCanvasTable      m_canvas_table;
   //---
private:
   //--- レンダーテーブル
#define TABLE1_GAP_X          (1)
#define TABLE1_GAP_Y          (42)
   bool              CreateCanvasTable(void);
  };

15列と1000行のテーブルを作りましょう。表示される行は16です。横シフトは画素単位で行われるため、可視列の数を設定する必要はありません。この場合、テーブルの可視領域の幅は明示的に指定される必要があります。これを601画素に設定しましょう。 

例として、(初めの2列を除く)すべての列の幅を70画素に設定しましょう。1、2番目の列の幅はそれぞれ10090画素に設定されています。テキストは(初めの3列を除く)すべての列では中央に整列されています。1番目と3番目の列では右、2番目の列では左に整列されています。 CProgram::CreateCanvasTable() メソッドの完全なコードは下に表示されています。

//+------------------------------------------------------------------+
//| レンダーテーブルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateCanvasTable(void)
  {
#define COLUMNS1_TOTAL 15
#define ROWS1_TOTAL    1000
//--- フォームへのポインタを保存する
   m_canvas_table.WindowPointer(m_window1);
//--- 座標
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- 表示される行の数
   int visible_rows_total=16;
//--- 列の幅の配列
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,70);
   width[0]=100;
   width[1]=90;
//--- 列でのテキスト整列の配列
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[0]=ALIGN_RIGHT;
   align[1]=ALIGN_LEFT;
   align[2]=ALIGN_RIGHT;
//--- 作成の前にプロパティを設定する
   m_canvas_table.XSize(601);
   m_canvas_table.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_canvas_table.VisibleTableSize(0,visible_rows_total);
   m_canvas_table.TextAlign(align);
   m_canvas_table.ColumnsWidth(width);
   m_canvas_table.GridColor(clrLightGray);
//--- テーブルにデータを書き入れる
   for(int c=0; c<COLUMNS1_TOTAL; c++)
      for(int r=0; r<ROWS1_TOTAL; r++)
         m_canvas_table.SetValue(c,r,string(c)+":"+string(r));
//--- コントロールを作成する
   if(!m_canvas_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- オブジェクトをオブジェクトグループの総合配列に追加する
   CWndContainer::AddToElementsArray(0,m_canvas_table);
   return(true);
  }

呼び出しは主要なGUI作成メソッドで行われます。  

//+------------------------------------------------------------------+
//| 取引パネルを作成する                                         |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- コントロールのフォーム1を作成する
//--- コントロールを作成する:
//    メインメニュー
//--- コンテキストメニュー
//--- ステータスラインを作成する:
//--- レンダーテーブルを作成する
   if(!CreateCanvasTable())
      return(false);
//--- チャートを再描画する
   m_chart.Redraw();
   return(true);
  }

プログラムをコンパイルしてチャートで実行しましょう。結果は以下のスクリーンショットに表示されています。

 図6。EAでのレンダーテーブルコントロールの検証

図6。EAでのレンダーテーブルコントロールの検証

シリーズ第一部の第5章では、スクリプト内でのフォームの使用が検証されました。スクロールバーなしのテーブルはこのタイプのMQLアプリケーションで使用することができます。例として、スクリプトフォームにレンダーテーブルを追加して、データを250 ミリ秒ごとに更新しましょう。先に示したように、アプリケーションのカスタムクラスにテーブルのコードを追加します。加えて、 下に見られるように、コードはCProgram::OnEvent() イベントハンドラに追加されるべきです。 次に、2番目の列のデータは、指定された時間間隔ごとに変更され続けます。 

//+------------------------------------------------------------------+
//| イベント                                                           |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int milliseconds)
  {
   static int count =0;  // カウンタ
   string     str   =""; // ヘッダー行
//--- 過程を表示するヘッダを形成する
   switch(count)
     {
      case 0 : str="SCRIPT PANEL";     break;
      case 1 : str="SCRIPT PANEL .";   break;
      case 2 : str="SCRIPT PANEL ..";  break;
      case 3 : str="SCRIPT PANEL ..."; break;
     }
//--- ヘッダの行を更新する
   m_window.CaptionText(str);
//--- 1番目の列のデータを変える
   for(int r=0; r<13; r++)
      m_canvas_table.SetValue(1,r,string(::rand()));
//--- 新しいテーブルデータを表示する
   m_canvas_table.DrawTable();
//--- チャートを再描画する
   m_chart.Redraw();
//--- カウンタの増加
   count++;
//--- 3を超えた場合にはゼロに設定する
   if(count>3)
      count=0;
//--- 一時停止
   ::Sleep(milliseconds);
  }

プログラムをコンパイルしてスクリプトをチャートで実行します。下記が見えるはずです。 

 図7。スクリプトでのレンダーテーブルコントロールの検証

図7。スクリプトでのレンダーテーブルコントロールの検証

レンダーテーブルを作成するCCanvasTableクラスの開発が完了です。ここでの結果は暫定的です。 

 


おわりに

本稿では、重要なインターフェースコントロールを作成するための3つのテーブルクラスが議論されてきました。これらのクラスのそれぞれには特定のタスクに適する独自の機能が備わっています。例えば、CTableクラスは、よりユーザーフレンドリーな外観のためにフォーマットを可能にしながら、編集可能なボックスをもつテーブルの開発を可能にします。CCanvasTableクラスではセル内の文字数の制限が回避され、画像の視認範囲に沿ったテーブルのシフトによって各列に特定の幅を設定することができます。これらのテーブルは最終版ではありません。必要であれば、さらなる向上が可能です。

次の記事では、グラフィカルインタフェースで同じように頻繁に使用されるタブコントロールを開発するためのクラスを検討します。

第七部の資料はダウンロードされ、動作の検証が可能です。それらのファイルの資料の使用に関するご質問がある場合は、以下のリストにある記事のいずれかでライブラリ開発の詳細な説明をご参照なさるか、本稿へのコメント欄でご質問ください。

第七部の記事(チャプター)のリストは下記の通りです。


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

添付されたファイル |
機械学習モデルの評価と変数の選択 機械学習モデルの評価と変数の選択
この記事では、機械学習モデルで使用する入力変数(予測変数)の選択、前処理および評価の詳細に焦点を当てています。新しいアプローチと予測分析とモデルの可能性と過学習への影響を考慮します。モデルを使用した全体的な結果は、この段階の結果に依存します。予測変数の選択に、新しい、オリジナルなアプローチを提供します。
エキスパートアドバイザやインディケータ、スクリプトの入力パラメータを保存する為のテキストファイル エキスパートアドバイザやインディケータ、スクリプトの入力パラメータを保存する為のテキストファイル
この記事では、エキスパートアドバイザやインディケータ、スクリプトのテキストファイル内のプロパティとしての動的オブジェクトや配列、その他の変数の保存について説明します。MQL言語で提供される標準的なツールへの機能の追加に役立ちます。
自己組織化特徴マップ(Kohonenマップ) - サブジェクトリビジッティング 自己組織化特徴マップ(Kohonenマップ) - サブジェクトリビジッティング
この記事では、Kohonenマップで動作するのテクニックについて説明します。Kohonenマップで困難に直面し、MQL4とMQL5でのプログラミングの基本的なレベルがわかる研究者や経験豊富なプログラマーを対象としています。自己組織化特徴マップ(Kohonenマップ) - サブジェクトリビジッティング
MQL4による取引におけるファジー論理の適用 MQL4による取引におけるファジー論理の適用
この記事ではMQL4による取引におけるファジー論理の適用例をご紹介します。MQL4の為のFuzzyNetライブラリを使用したエキスパートアドバイザとインディケータの開発を解説していきます。