English Русский 中文 Español 日本語 Português
Grafische Interfaces VII: Die Tabellen Controls (Kapitel 1)

Grafische Interfaces VII: Die Tabellen Controls (Kapitel 1)

MetaTrader 5Beispiele | 24 August 2016, 13:22
1 066 0
Anatoli Kazharski
Anatoli Kazharski

Inhalt

 

 

Einleitung

Der erste Artikel Grafisches Interface I: Vorbereiten der Bibliotheksstruktur (Kapitel 1) Beschreibt im Detail wofür diese Bibliothek gedacht ist. Am Ende von jedem Kapitel, finden Sie eine vollständige Liste mit Links zu diesem Artikel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungsstand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.

Dieser Artikel handelt über drei Klassen, die es Ihnen erlauben, unterschiedliche Tabellentypen für zweidimensionale Datensätze als Teil einer MQL Anwendung zu programmieren:

  • Text-Label Tabelle;
  • Edit-Box Tabelle;
  • Gerenderte Tabelle.

Jeder Tabellentyp hat seine eigenen besonderen Eigenschaften und Vorteile, die wir nachfolgend beschreiben.

In den nachfolgenden Artikel (Teil VII, Kapitel 2), werden wir die folgenden controls besprechen:

  • Tabs;
  • Tabs mit Bildern.

 


Das Text-Label Tabellen-Control

Eine Tabelle ist ein komplexes GUI control, da es auch andere Controls beinhaltet - horizontale und vertikale Scrollbars. Da es vorkommen kann, dass die Datenmenge den verfügbaren sichtbaren Platz innerhalb eines Controls überschreitet, bieten Ihnen Scrollbars die Möglichkeit, die Daten in horizontaler und vertikaler Richtung hin und her zu schieben.

Text-Label Tabellen bestehend aus den folgenden Komponenten:

  1. Hintergrund
  2. Text-Labels.
  3. Vertikale Scrollbar.
  4. Horizontale Scrollbar.

 Abbildung  1. Komponenten des Text-Label Controls

Abbildung 1. Komponenten des Text-Label Tabellen-Controls

Lassen Sie uns die Klasse dieses Controls näher anschauen.

 


Entwicklung der CLabelsTable Klasse

Erzeugen Sie die LabelsTable.mqh Datei und beziehen Sie diese in der Bibliothek (WndContainer.mqh) mit ein:

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

Erzeugen sie in der LabelsTable.mqh Datei die Klasse CLabelsTable mit dem Standardsatz der Methoden, die in jedem Control dieser Bibliothek vorhanden sein sollten, sowie die Methode für das Abspeichern des Pointers des Formulars, zu welchem das Control hinzugefügt wird. Machen Sie dieses mit allen Controls, die wir in diesem Artikel besprechen.

//+------------------------------------------------------------------+
//| Class for creating a text label table                            |
//+------------------------------------------------------------------+
class CLabelsTable : public CElement
  {
private:
   //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist
   CWindow          *m_wnd;
   //---
public:
                     CLabelsTable(void);
                    ~CLabelsTable(void);
   //--- (1) Speichert den Pointer das Formulars, (2) Gibt den Pointer zu den Scrollbars zurück
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   //---
public:
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Bewegen des Elementes
   virtual void      Moving(const int x,const int y);
   //--- (1) Anzeigen, (2) verstecken, (3) zurücksetzen, (4) löschen
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Setzen, (2) Zurücksetzen der Prioritäten der linken Maustaste
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Zurücksetzen der Farbe
   virtual void      ResetColors(void) {}
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CLabelsTable::CLabelsTable(void)
  {
  }
//+------------------------------------------------------------------+
//| Destruktor                                                       |
//+------------------------------------------------------------------+
CLabelsTable::~CLabelsTable(void)
  {
  }

Bevor wir die Tabelle erzeugen, spezifizieren wir einige ihre Eigenschaften. Lassen Sie uns diese benennen

  • Die Höhe der Zeile
  • Der erste Spalten-Einzug von der linken Kante des Steuerelementes
  • Der Abstand zwischen den Spalten
  • Die Hintergrundfarbe
  • Die Textfarbe
  • Der Sperrmodus der ersten Reihe
  • Der Sperrmodus der ersten Spalte
  • Gesamtanzahl der Spalten
  • Gesamtanzahl der Reihen
  • Die Anzahl der sichtbaren Spalten
  • Die Anzahl der sichtbaren Zeilen

Bei diesem Tabellentyp, werden die Koordinaten des Text-Labels von dem zentralen Ankerpunkt aus berechnet (ANCHOR_CENTER). Daher wird der Einzug für die erste Spalte der Text-Labels von der linken Ecke des Controls aus berechnet und der Abstand zwischen den Text Labels jeder Spalte wird von dem Mittelpunkt jeder Spalte berechnet.

In den Sperrmodi muss es möglich sein, dass die erste Reihe und/oder Zeile (in welchen der User die Namen für jede Spalte und Zeile angibt) sichtbar bleibt, auch wenn der Anwender die Schieberegler der vertikalen und horizontalen Scrollbars verschiebt. Die Sperrmodi können zusammen oder separat genutzt werden. 

class CLabelsTable : public CElement
  {
private:
   //--- Die Höhe der Zeile
   int               m_row_y_size;
   //--- Die Hintergrundfarbe der Tabelle
   color             m_area_color;
   //--- Die Standardfarbe des Textes der Tabelle
   color             m_text_color;
   //--- Der Abstand zwischen dem Ankerpunkt der ersten Spalte und der linken Ecke des Controls
   int               m_x_offset;
   //--- Der Abstand zwischen den Ankerpunkten der Spalten
   int               m_column_x_offset;
   //--- Sperrmodus der ersten Zeile
   bool              m_fix_first_row;
   //--- Sperrmodus der ersten Spalte
   bool              m_fix_first_column; 
   //--- Priorität für die linke Maustaste
   int               m_zorder;
   int               m_area_zorder;
   //---
public:
   //--- (1) Hintergrundfarbe, (2) Textfarbe
   void              AreaColor(const color clr)                   { m_area_color=clr;                }
   void              TextColor(const color clr)                   { m_text_color=clr;                }
   //--- (1) Höhe der Zeile, (2) Festlegen des Abstandes zwischen dem Ankerpunkt der ersten Spalte und der linken Ecke der Tabelle,
   //    (3) festlegen der Abstände zwischen den Ankerpunkten der Spalten
   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) Abfrage und (2) des Sperrmodus für die erste Zeile
   bool              FixFirstRow(void)                      const { return(m_fix_first_row);         }
   void              FixFirstRow(const bool flag)                 { m_fix_first_row=flag;            }
   //--- (1) Abfrage und (2) des Sperrmodus für die erste Spalte
   bool              FixFirstColumn(void)                   const { return(m_fix_first_column);      }
   void              FixFirstColumn(const bool flag)              { m_fix_first_column=flag;         }
  };

Lassen Sie uns die CLabelsTable::TableSize() und CLabelsTable::VisibleTableSize() Methoden, um die totale sichtbare Anzahl von Spalten und Zeilen festlegen zu können, erzeugen. Zudem benötigen wir noch zweidimensionale dynamische Arrays in Form von Strukturen. Eine dieser Strukturen (LTLabels) erzeugt das Array mit Text-Labeln für den sichtbaren Bereich der Tabelle, während das Andere (LTOptions) alle Werte und Eigenschaften jeder Zelle der Tabelle speichert. In unserer Implementation, speichert dieses die dargestellten Werte (Zeilen) und Textfarben

Den CLabelsTable::TableSize() und CLabelsTable::VisibleTableSize() Methoden werden zwei Argumente (Anzahl der Spalten und Zeilen) übergeben. Am Anfang der Methode werden die Werte daraufhin überprüft, ob die Anzahl der Spalten kleiner als eins ist und ob die Anzahl der Zeilen kleiner als zwei ist. Anschließend werden die größen aller Arraysd definiert und die Eigenschaften der Arrays werden initialisiert.

Neben den Methoden für die Größe der Tabelle, benötigen wir auch Methoden für die Abfrage der gesamten sichtbaren Größe mit Spalten und Zeilen. 

class CLabelsTable : public CElement
  {
private:
   //--- Array mit Objekten für den sichtbaren Bereich der Tabelle
   struct LTLabels
     {
      CLabel            m_rows[];
     };
   LTLabels          m_columns[];
   //--- Array mit den Werten und Eigenschaften der Tabelle
   struct LTOptions
     {
      string            m_vrows[];
      color             m_colors[];
     };
   LTOptions         m_vcolumns[];
   //---
public:
   //--- Gib die gesamte Anzahl von (1) Zeilen und (2) Spalten zurück
   int               RowsTotal(void)                        const { return(m_rows_total);            }
   int               ColumnsTotal(void)                     const { return(m_columns_total);         }
   //--- Gibt die Anzahl von (1) Zeilen und (2) Spalten des sichtbaren Bereichs der Tabelle zurück
   int               VisibleRowsTotal(void)                 const { return(m_visible_rows_total);    }
   int               VisibleColumnsTotal(void)              const { return(m_visible_columns_total); }

   //--- Setzen der (1) Größe der Tabelle und (2) Der Größe des sichtbaren Bereiches
   void              TableSize(const int columns_total,const int rows_total);
   void              VisibleTableSize(const int visible_columns_total,const int visible_rows_total);
  };
//+------------------------------------------------------------------+
//| Legt die Größe der Tabelle fest                                  |
//+------------------------------------------------------------------+
void CLabelsTable::TableSize(const int columns_total,const int rows_total)
  {
//--- Es muss mindestens eine Spalte geben
   m_columns_total=(columns_total<1) ? 1 : columns_total;
//--- Es muss mindestens zwei Zeilen geben
   m_rows_total=(rows_total<2) ? 2 : rows_total;
//--- Festlegen der Größe der Spalten-Arrays
   ::ArrayResize(m_vcolumns,m_columns_total);
//--- Festlegen der Größe der Zeilen-Arrays
   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);
      //--- Initialisieren des Arrays der Textfarben mit den Standardwerten
      ::ArrayInitialize(m_vcolumns[i].m_colors,m_text_color);
     }
  }
//+-----------------------------------------------------------------+
//| Festlegen der Größe des sichtbaren Teils der Tabelle            |
//+-----------------------------------------------------------------+
void CLabelsTable::VisibleTableSize(const int visible_columns_total,const int visible_rows_total)
  {
//--- Es muss mindestens eine Spalte geben
   m_visible_columns_total=(visible_columns_total<1) ? 1 : visible_columns_total;
//--- Es muss mindestens zwei Zeilen geben
   m_visible_rows_total=(visible_rows_total<2) ? 2 : visible_rows_total;
//--- Festlegen der Größe der Spalten-Arrays
   ::ArrayResize(m_columns,m_visible_columns_total);
//--- Festlegen der Größe der Zeilen-Arrays
   for(int i=0; i<m_visible_columns_total; i++)
      ::ArrayResize(m_columns[i].m_rows,m_visible_rows_total);
  }

Alle Eigenschaften sollten mit den Standardwerten initialisiert werden, bevor das Control erzeugt wird. Ich empfehle dieses direkt in den Konstruktor der Klasse vorzunehmen:

//+----------------------------------------------------------------+
//| Konstruktor                                                    |
//+----------------------------------------------------------------+
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)
  {
//--- Abspeichern des namens der Elementklasse in der Basisklasse
   CElement::ClassName(CLASS_NAME);
//--- Setzen der Priorität eines Klicks mit der linken Maustaste
   m_zorder      =0;
   m_area_zorder =1;
//--- Festlegen der Größe der Tabelle und seines sichtbaren Bereiches
   TableSize(m_columns_total,m_rows_total);
   VisibleTableSize(m_visible_columns_total,m_visible_rows_total);
  }

Lassen Sie uns vier private und eine public Method für einen externen Aufruf für die Entwicklung des Controls erzeugen. Um es den Anwender ermöglichen zu können, die Scrollbars der Tabelle konfigurieren zu können, müssen wir eine Methode hinzufügen, die deren Pointer zurückgibt. 

class CLabelsTable : public CElement
  {
private:
   //--- Objekte für die Erzeugung einer Tabelle
   CRectLabel        m_area;
   CScrollV          m_scrollv;
   CScrollH          m_scrollh;
   //--- Array mit Objekten für den sichtbaren Bereich der Tabelle
   struct LTLabels
     {
      CLabel            m_rows[];
     };
   LTLabels          m_columns[];
   //---
public:
   //--- Gibt die Pointer der Scrollbars zurück
   CScrollV         *GetScrollVPointer(void)                const { return(::GetPointer(m_scrollv)); }
   CScrollH         *GetScrollHPointer(void)                const { return(::GetPointer(m_scrollh)); }
   //--- Methoden für die Erzeugung der Tabelle
   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);
  };

Von all den Methoden für die Erzeugung, wird hier nur die CLabelsTable::CreateLabels() Für die Entwicklung des Text-Label Arrays gezeigt. Stellen Sie sicher, dass Sie die Spalten und Zeilen Indizes hinzufügen wenn Sie den Objektnamen bilden. Alle anderen Methoden finden Sie in den beigefügten Dateien. 

//+-----------------------------------------------------------------+
//| Erzeugung eines Arrays mit Text-Labeln                          |
//+-----------------------------------------------------------------+
bool CLabelsTable::CreateLabels(void)
  {
//--- Koordinaten und Offset
   int x      =CElement::X();
   int y      =0;
   int offset =0;
//--- Spalten
   for(int c=0; c<m_visible_columns_total; c++)
     {
      //--- Berechnung des Tabellen-Offsets
      offset=(c>0) ? m_column_x_offset : m_x_offset;
      //--- Berechnung der x-Koordinate
      x=x+offset;
      //--- Zeilen
      for(int r=0; r<m_visible_rows_total; r++)
        {
         //--- Bilden des Objektnamens
         string name=CElement::ProgramName()+"_labelstable_label_"+(string)c+"_"+(string)r+"__"+(string)CElement::Id();
         //--- Berechne die Y Koordinate
         y=(r>0) ? y+m_row_y_size-1 : CElement::Y()+10;
         //--- Erzeugen des Objektes
         if(!m_columns[c].m_rows[r].Create(m_chart_id,name,m_subwin,x,y))
            return(false);
         //--- Festlegen der Eigenschaften
         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");
         //--- Abstände von der Ecke des Formulars
         m_columns[c].m_rows[r].XGap(x-m_wnd.X());
         m_columns[c].m_rows[r].YGap(y-m_wnd.Y());
         //--- Koordinaten
         m_columns[c].m_rows[r].X(x);
         m_columns[c].m_rows[r].Y(y);
         //--- Abspeichern des Objekt-Pointers
         CElement::AddToArray(m_columns[c].m_rows[r]);
        }
     }
//---
   return(true);
  }

Wir benötigen noch Methoden, um die Werte und Eigenschaften jeder Zelle der Tabelle zu jeder Zeit ändern zu können. Lassen Sie uns die CLabelsTable::SetValue() und CLabelsTable::GetValue() Methoden für das setzen und Abfragen des Wertes einer Zelle erzeugen. Bei beiden Methoden müssen die Indizes der Spalte und Zeile als erste Argumente übergeben werden. Der Wert, der anschließend in dem Array abgespeichert werden soll, wird als drittes Argument der CLabelsTable::SetValue() Methode übergeben. Zunächst folgt natürlich die obligatorische Überprüfung, ob die Indexwerte die Grenzen des Arrays überschreiten. 

class CLabelsTable : public CElement
  {
public:
   //--- Festlegen eines Wertes in der spezifizierten Zelle einer Tabelle
   void              SetValue(const int column_index,const int row_index,const string value);
   //--- Abfrage des Wertes einer bestimmten Zelle einer Tabelle
   string            GetValue(const int column_index,const int row_index);
  };
//+-----------------------------------------------------------------+
//| Setzen des Wertes bei den angegebenen Indizes                   |
//+-----------------------------------------------------------------+
void CLabelsTable::SetValue(const int column_index,const int row_index,const string value)
  {
//--- Überprüfung auf Überschreitung des Indexbereiches der Spalten
   int csize=::ArraySize(m_vcolumns);
   if(csize<1 || column_index<0 || column_index>=csize)
      return;
//--- Überprüfung der Überschreitung des Indexbereiches der Zeilen
   int rsize=::ArraySize(m_vcolumns[column_index].m_vrows);
   if(rsize<1 || row_index<0 || row_index>=rsize)
      return;
//--- Setzen des Wertes
   m_vcolumns[column_index].m_vrows[row_index]=value;
  }
//+-----------------------------------------------------------------+
//| Rückgabe des Wertes an den angegebenen Indizes                  |
//+-----------------------------------------------------------------+
string CLabelsTable::GetValue(const int column_index,const int row_index)
  {
//--- Überprüfung auf Überschreitung des Indexbereiches der Spalten
   int csize=::ArraySize(m_vcolumns);
   if(csize<1 || column_index<0 || column_index>=csize)
      return("");
//--- Überprüfung der Überschreitung des Indexbereiches der Zeilen
   int rsize=::ArraySize(m_vcolumns[column_index].m_vrows);
   if(rsize<1 || row_index<0 || row_index>=rsize)
      return("");
//--- Rückgabe des Wertes
   return(m_vcolumns[column_index].m_vrows[row_index]);
  }

Neben dem Ändern eines Wertes in der Tabelle, möchten die Anwender der Bibliothek vielleicht auch noch die Farbe des Textes ändern. Zum Beispiel könnten positive Werte in Grün dargestellt werden, und negative Werte in Rot. Lassen Sie uns dafür die CLabelsTable::TextColor() Methode erzeugen. Sie ist der CLabelsTable::SetValue() Methode sehr ähnlich, mit dem einzigen Unterschied, dass als drittes Argument die Farbe angegeben wird. 

class CLabelsTable : public CElement
  {
public:
   //--- Die Textfarbe einer bestimmten Zelle ändern
   void              TextColor(const int column_index,const int row_index,const color clr);
  };
//+-----------------------------------------------------------------+
//| Ändert die Farbe einer bestimmten Zelle                         |
//+-----------------------------------------------------------------+
void CLabelsTable::TextColor(const int column_index,const int row_index,const color clr)
  {
//--- Überprüfung auf Überschreitung des Indexbereiches der Spalten
   int csize=::ArraySize(m_vcolumns);
   if(csize<1 || column_index<0 || column_index>=csize)
      return;
//--- Überprüfung der Überschreitung des Indexbereiches der Zeilen
   int rsize=::ArraySize(m_vcolumns[column_index].m_vrows);
   if(rsize<1 || row_index<0 || row_index>=rsize)
      return;
//--- Festlegen der Farbe
   m_vcolumns[column_index].m_colors[row_index]=clr;
  }

Die Änderungen werden erst nach einem Update der Tabelle übernommen / angezeigt. Dafür erzeugen wir eine universelle Methode, welche auch benutzt wird, wenn die Daten entsprechend der Position der Schieberegler der Scrollbars verschoben werden müssen. 

Lassen Sie uns diese Methode CLabelsTable::UpdateTable() nennen. Bin die Kopfzeilen gesperrt sind, dann beginnt die Verschiebung mit dem zweiten (1) Array-Index , damit sichergestellt ist, dass die erste Zeile, beziehungsweise die erste Spalte immer sichtbar bleibt. Die t und l Variabeln werden am Anfang der Methode deklariert und deren Werte von 0 oder 1 werden entsprechend dem aktuell verwendeten Modus gesetzt.

Um den Index definieren zu können, von welchem aus eine Verschiebung, beziehungsweise ein Update gestartet werden muss, muss die aktuelle Position der Schieberegler der Scrollbars abgefragt werden. Die Kopfzeilen der linken Spalte und der obersten Zeile werden in separaten schleifen bewegt (falls dieser Modus aktiviert ist).

Die Hauptdaten der Tabelle und die Farben der Zellen, werden in der doppelten Schleife am Ende der Methode bewegt. 

class CLabelsTable : public CElement
  {
public:
   //--- Aktualisierung der Daten der Tabelle unter Berücksichtigung der letzten Veränderungen
   void              UpdateTable(void);
  };
//+-----------------------------------------------------------------------------------------+
//| Aktualisierung der Daten der Tabelle unter Berücksichtigung der letzten Veränderungen   |
//+-----------------------------------------------------------------------------------------+
void CLabelsTable::UpdateTable(void)
  {
//--- Verschieben um einen Index, falls der fixierte Kopfzeilen-Modus aktiviert ist
   int t=(m_fix_first_row) ? 1 : 0;
   int l=(m_fix_first_column) ? 1 : 0;
//--- Abfrage der aktuellen Positionen der Schieberegler der vertikalen und horizontalen Scrollbars
   int h=m_scrollh.CurrentPos()+l;
   int v=m_scrollv.CurrentPos()+t;
//--- Verschieben der Kopfzeile in der linken Spalte
   if(m_fix_first_column)
     {
      m_columns[0].m_rows[0].Description(m_vcolumns[0].m_vrows[0]);
      //--- Zeilen
      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++;
        }
     }
//--- Verschieben der Kopfzeile in der obersten Zeile
   if(m_fix_first_row)
     {
      m_columns[0].m_rows[0].Description(m_vcolumns[0].m_vrows[0]);
      //--- Spalten
      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++;
        }
     }
//--- Abfrage der aktuellen Position des Schiebereglers der Horizontalen Scrollbar
   h=m_scrollh.CurrentPos()+l;
//--- Spalten
   for(int c=l; c<m_visible_columns_total; c++)
     {
      //--- Abfrage der aktuellen Position des Schiebereglers der vertikalen Scrollbar
      v=m_scrollv.CurrentPos()+t;
      //--- Zeilen
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- Verschiebung der Tabellendaten
         if(v>=t && v<m_rows_total && h>=l && h<m_columns_total)
           {
            //--- Einstellung der Farbe
            m_columns[c].m_rows[r].Color(m_vcolumns[h].m_colors[v]);
            //--- Einstellen der Werte
            m_columns[c].m_rows[r].Description(m_vcolumns[h].m_vrows[v]);
            v++;
           }
        }
      //---
      h++;
     }
  }

Lassen Sie uns, ähnlich wie wir es auch schon bei der Inputbox und den List-Controls gemacht haben, das schnelle zurückscrollen implementieren, sobald die linke Maustaste über oberhalb der Scrollbar Buttons gedrückt wird. Es gibt keinen Grund, um den Programmcode der CLabelsTable::FastSwitching() Methode hier darzustellen, da er dem Programmcode der Methoden aus den vorherigen Artikeln (CListView, CSpinEdit and CCheckBoxEdit Klassen) entspricht. 

Der Programmcode der Methoden für das Verarbeiten der Control-Events wird in dem nachfolgenden Listing gezeigt: 

//+-----------------------------------------------------------------+
//| Even handler                                                    |
//+-----------------------------------------------------------------+
void CLabelsTable::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten der Events über die Bewegung des Mauszeigers
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Abbrechen, falls das Element versteckt ist
      if(!CElement::IsVisible())
         return;
      //---Koordinaten und der Status der linken Maustaste
      int x=(int)lparam;
      int y=(int)dparam;
      m_mouse_state=(bool)int(sparam);
      //--- Überprüfen des Fokus über der Tabelle
      CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2());
      //--- Bewege die Liste, falls die Verwaltung des Schiebereglers aktiviert ist
      if(m_scrollv.ScrollBarControl(x,y,m_mouse_state) || m_scrollh.ScrollBarControl(x,y,m_mouse_state))
         UpdateTable();
      //---
      return;
     }
//--- Verarbeitung von Klicks auf Objekte
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Falls auf einen Button der Tabellen-Scrollbars gedrückt wurde
      if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam) ||
         m_scrollh.OnClickScrollInc(sparam) || m_scrollh.OnClickScrollDec(sparam))
         //--- Verschiebe die Tabelle relativ zu der Scrollbar
         UpdateTable();
      //---
      return;
     }
  }
//+-----------------------------------------------------------------+
//| Timer                                                           |
//+-----------------------------------------------------------------+
void CLabelsTable::OnEventTimer(void)
  {
//--- Falls es sich um einen DropDown-Element handelt
   if(CElement::IsDropdown())
      FastSwitching();
//--- Falls es sich nicht um ein Dropdown-Element handelt, berücksichtige die aktuelle Verfügbarkeit des Formulars
   else
     {
      //--- Verfolge den schnellen Vorlauf der Tabelle nur, falls das Formular nicht gesperrt ist
      if(!m_wnd.IsLocked())
         FastSwitching();
     }
  }

Eine Tabelle ist ein komplexes GUI-Control. Daher sollten die Pointer der anderen Controls (die horizontale und vertikale Scrollbar) mit in der Pointer-Datenbank mit einbezogen werden. Wir sollten ein eigenständiges Array für Pointer von Tabellen erstellen. Sie finden alle diese Veränderungen in der WndContainer.mqh Datei der CWndContainer Klasse. Dieses Thema wurde bereits in anderen Artikeln dieser Serie abgedeckt. Daher überspringe ich dieses hier und fahre mit dem Test der Text-Label-Tabelle fort. 

 


Test der Text Label Tabelle

Zu Testzwecken werden wir den Expert Advisor aus dem vorherigen Artikel verwenden und nur das Hauptmenü und die Statusleiste behalten. Um die Text-Label-Tabelle einer MQL Anwendung hinzufügen zu können, muss die CLabelsTable Typenklasseninstanz, Sowie die Methoden und Abstände von der äußersten Spitze des Formulars deklariert werden (Diese sehen Sie im nachfolgendem Programmcode).

class CProgram : public CWndEvents
  {
private:
   //--- Text-Label-Tabelle
   CLabelsTable      m_labels_table;
   //---
private:
   //--- Text-Label-Tabelle
#define LABELS_TABLE1_GAP_X   (1)
#define LABELS_TABLE1_GAP_Y   (42)
   bool              CreateLabelsTable(void);
  };

Lassen Sie uns nun den Programmcode der CProgram::CreateLabelsTable() Method genauer betrachten. Wir möchten eine Tabelle, die aus 21 Spalten und 100 Zeilen besteht entwerfen. Die Anzahl der sichtbaren Spalten ist 5, und die Anzahl der sichtbaren Zeilen ist 10. Wir fixieren die oberste Zeile und die linke Spalte, damit sie nicht bewegt werden. Nachdem die Tabelle erstellt worden ist, wird sie mit zufälligen Werten (von -1000 bis 1000) gefüllt und es werden noch die Farben definiert. Positive Werte werden in Grün dargestellt und negative Werte in Rot. Stellen Sie sicher, dass Sie die Tabelle aktualisieren, damit die letzten Änderungen sichtbar werden

//+-----------------------------------------------------------------+
//| Erzeugung der Text Label Tabelle                                |
//+-----------------------------------------------------------------+
bool CProgram::CreateLabelsTable(void)
  {
#define COLUMNS1_TOTAL (21)
#define ROWS1_TOTAL    (100)
//--- Abspeichern des pointers zu dem Formular
   m_labels_table.WindowPointer(m_window1);
//--- Koordinaten
   int x=m_window1.X()+LABELS_TABLE1_GAP_X;
   int y=m_window1.Y()+LABELS_TABLE1_GAP_Y;
//--- Die Anzahl der sichtbaren Spalten und Zeilen
   int visible_columns_total =5;
   int visible_rows_total    =10;
//--- Setzen der Eigenschaften
   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);
//--- Erzeugen der Tabelle
   if(!m_labels_table.CreateLabelsTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Einfügen in die Tabelle:
//    Die erste Zelle ist leer
   m_labels_table.SetValue(0,0,"-");
//--- Die Überschriften der Spalten
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_labels_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- Die Überschriften der Zeilen, die Ausrichtung ist Rechts
   for(int c=0; c<1; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        m_labels_table.SetValue(c,r,"PARAMETER "+string(r));
     }
//--- Daten und Tabellen-Formatierung (Hintergrund und Farben der Zellen)
   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));
     }
//--- Die Textfarbe in den Zellen setzen
   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);
//--- Aktualisieren der Tabelle
   m_labels_table.UpdateTable();
//--- Hinzufügen des Pointers des Elementes zu der Basis
   CWndContainer::AddToElementsArray(0,m_labels_table);
   return(true);
  }

Zudem sollten wir testen, wie die Tabellenwerte geändert werden, wenn das Programm auf einem Terminal-Chart ausgeführt wird. Um dieses zu tun, wird der folgende Programmcode der CProgram::OnTimerEvent() Timer-Methode der Anwendung hinzugefügt. Schauen Sie sich dazu das nachfolgende Listing an: 

//+----------------------------------------------------------------+
//| Timer                                                          |
//+----------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();
//--- Aktualisierung des zweiten Punktes der Statuszeile alle 500 Millisekunden
   static int count=0;
   if(count<500)
     {
      count+=TIMER_STEP_MSC;
      return;
     }
//--- Zurücksetzen des Timers
   count=0;
//--- Veränderung des Wertes in dem zweiten Punkt der Statuszeile.
   m_status_bar.ValueToItem(1,::TimeToString(::TimeLocal(),TIME_DATE|TIME_SECONDS));
//--- Tabelle mit Daten auffüllen
   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));
//--- Die Textfarbe in den Zellen setzen
   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);
//--- Aktualisieren der Tabelle
   m_labels_table.UpdateTable();
  }

Die Methode für das Erzeugen der Text Label Tabelle sollte in der Hauptmethode CProgram::CreateExpertPanel() der Anwendung aufgerufen werden. Die Kurzfassung des Verfahrens ist unten dargestellt: 

//+----------------------------------------------------------------+
//| Erzeugung des Expert-Bedienfeldes                              |
//+----------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//--- Erzeugen der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Erzeugen der Statuszeile
//--- Text-Label-Tabelle
   if(!CreateLabelsTable())
      return(false);
//--- Neuzeichnen des Charts
   m_chart.Redraw();
   return(true);
  }

Kompilieren Sie den Programmcode und laden Sie das Programm auf einen Chart. Der nachfolgende Screenshot zeigt das Resultat:

 Abbildung  2. Test der Text-Label-Tabelle

Abbildung 2. Test der Text-Label-Tabelle

Alles arbeitet einwandfrei. Lassen Sie uns nun mit der Klasse für die Erzeugung des zweiten Typs der Tabellen fortfahren. 

 


Das Edit-Box Tabellen-Control

Anders als die Text-Label-Tabelle, bietet die Editbox-Tabelle eine größere Flexibilität und mehr Eigenschaften. Außer der Möglichkeit, die Textfarbe zu verändern, bietet sie noch: 

  • Die Ausrichtung des Textes innerhalb einer Zelle(links/mitte/rechts)
  • Veränderung der Hintergrundfarbe und des Rahmens der Editbox;
  •  Die Möglichkeit, die Werte in der Edit-Box manuell zu verändern, falls der entsprechende Modus aktiviert ist.

Alle diese Punkte machen diese Tabelle noch benutzerfreundlicher und erleichtern die Verwendung bei einer großen Menge von Aufgaben. Text-Label Tabellen bestehend aus den folgenden Komponenten:

  1. Hintergrund
  2. Edit boxes
  3. Vertikale Scrollbar.
  4. Horizontale Scrollbar.

 Abbildung 3. Die Komponenten des Edit-Box Tabellen-Controls

Abbildung 3. Die Komponenten des Edit-Box Tabellen-Controls

Lassen Sie uns ansehen, wie sich der Programmcode zu der vorherigen Tabelle unterscheidet.

 


Entwicklung der CTable Klasse

Lassen Sie uns die Eigenschaften der Tabelle beschreiben und dabei die Unterschiede zu der vorherigen Tabelle hervorheben. Das Array für die sichtbaren grafischen Objekte, ist bei dieser Tabelle voneinem anderen Typ– (CEdit). Mit anderen Worten, es besteht aus Edit-Boxen anstelle von Text-Labels (sehen Sie sich dazu den nachfolgenden Programmcode an).

class CTable : public CElement
  {
private:
   //--- Array mit Objekten für den sichtbaren Bereich der Tabelle
   struct TEdits
     {
      CEdit             m_rows[];
     };
   TEdits            m_columns[];
  };

Es gibt hier mehr einzigartige Eigenschaften für jede Zelle, da die Tabelle es Ihnen erlaubt, die Ausrichtung des Textes und die Hintergrundfarbe der Editbox zu verändern.

class CTable : public CElement
  {
private:
   //--- Arrays mit den Tabellenwerten und Eigenschaften
   struct TOptions
     {
      string            m_vrows[];
      ENUM_ALIGN_MODE   m_text_align[];
      color             m_text_color[];
      color             m_cell_color[];
     };
   TOptions          m_vcolumns[];
  };

Nachfolgend ist eine Auflistung der Modi und Eigenschaften, welche in der Text-Label-Tabelle nicht vorhanden sind.

  • Der "Editierbar"-Tabellenmodus
  • Der Modus zum Hervorheben einer Zeile, sobald sich der Mauszeiger darüber befindet.
  • Der Modus für eine ausgewählte Zeile
  • Die Höhe der Zeile
  • Farbe des Tabellen-Gitters
  • Hintergrundfarbe der Überschriften
  • Textfarbe der Überschriften
  • Die Farbe der Zelle, sobald sich der Mauszeiger darüber befindet.
  • Die Standardfarbe der Zelle
  • Die Standard-Ausrichtung einer Zelle
  • Die hervorgehobene Hintergrundfarbe einer Zeile
  • Die Textfarbe im Falle des Hervorhebens

Dieser Tabellentyp, erlaubt es Ihnen ebenfalls, die Überschriften der ersten Zeile und der ersten Spalte zu fixieren. Wenn dieser Modus gewählt ist, dann bleiben diese an ihrem Platz wenn die Schieberegler bewegt werden. Der nachfolgende Programmcode zeigt die vollständigen Edit-Boxen und Methoden für das Setzen der Tabelleneigenschaften: 

class CTable : public CElement
  {
private:
   //--- Höhe der Zeilen der Tabelle
   int               m_row_y_size;
   //--- (1) Die Farbe des Hintergrundes und (2) Des Rahmens des Hintergrundes der Tabelle
   color             m_area_color;
   color             m_area_border_color;
   //--- Farbe des Gitters
   color             m_grid_color;
   //--- Hintergrundfarbe der Überschrift
   color             m_headers_color;
   //--- Textfarbe der Überschrift
   color             m_headers_text_color;
   //--- Die Farben der Zellen in den unterschiedlichen Zuständen
   color             m_cell_color;
   color             m_cell_color_hover;
   //--- Die Standardfarbe für den Text einer Zelle
   color             m_cell_text_color;
   //--- Farbe des (1) Hintergrundes und (2) des Textes der selektierten Zeile
   color             m_selected_row_color;
   color             m_selected_row_text_color;
   //--- Modus für die Editierbarkeit der Tabelle
   bool              m_read_only;
   //--- Modus für das Hervorheben von Zeilen, wenn sich der Mauszeiger darüber befindet
   bool              m_lights_hover;
   //--- Modus für die Auswählbarkeit einer Zeile
   bool              m_selectable_row;
   //--- Sperrmodus der ersten Zeile
   bool              m_fix_first_row;
   //--- Sperrmodus der ersten Spalte
   bool              m_fix_first_column;
   //--- Die Standard-Textausrichtung in den Edit-Boxen
   ENUM_ALIGN_MODE   m_align_mode;
   //---
public:
   //--- Farbe des (1) Hintergrundes und (2) des Rahmens der Tabelle
   void              AreaColor(const color clr)                        { m_area_color=clr;                }
   void              BorderColor(const color clr)                      { m_area_border_color=clr;         }
   //--- (1) Abfrage und (2) des Sperrmodus für die erste Zeile
   bool              FixFirstRow(void)                           const { return(m_fix_first_row);         }
   void              FixFirstRow(const bool flag)                      { m_fix_first_row=flag;            }
   //--- (1) Abfrage und (2) des Sperrmodus für die erste Spalte
   bool              FixFirstColumn(void)                        const { return(m_fix_first_column);      }
   void              FixFirstColumn(const bool flag)                   { m_fix_first_column=flag;         }
   //--- Farbe des (1) Hintergrundes der Überschriften, (2) Text der Überschriften und (3) Dis Tabellengitters
   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;                }
   //--- Die Größe der Zeilen entlang der y-Achse
   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) "Nur lesen", (2) Hervorheben der Zeilen, wenn sich die Maus darüber befindet, (3) Modus für die Auswertbarkeit der Zeile
   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;           }
   //--- Ausrichtung des Textes in der Zelle
   void              TextAlign(const ENUM_ALIGN_MODE align_mode)       { m_align_mode=align_mode;         }
  };

Nachfolgend sind die Eigenschaften der Methoden aufgelistet, die dazu dienen, die Werte der Tabelle zu setzen oder abzufragen, in Abhängigkeit der Spalten- und Zeilen-Indizes.

  • Die Gesamtgröße der Tabelle (Die gesamt Anzahl der Spalten und Zeilen)
  • Die Größe des sichtbaren Bereiches der Tabelle (Die Anzahl der sichtbaren Spalten und Zeilen)
  • Die Textausrichtung der Zelle (links/rechts/mitte)
  • Die Textfarbe
  • Die Hintergrundfarbe
  • Festlegen/Verändern des Wertes
  • Abfrage des Wertes

Es gibt hier keinen Grund, den Programmcode dieser Methode nochmal darzustellen, da wir diesen bereits in dem Abschnitt für die Text-Label-Tabelle besprochen haben. Nachdem Sie alle Werte gesetzt haben, müssen Sie sicherstellen, dass Sie die Tabelle über den Aufruf der CTable::UpdateTable() Methode aktualisieren, damit alle Veränderungen sichtbar werden. 

class CTable : public CElement
  {
public:
   //--- Setzen der (1) Größe der Tabelle und (2) Der Größe des sichtbaren Bereiches
   void              TableSize(const int columns_total,const int rows_total);
   void              VisibleTableSize(const int visible_columns_total,const int visible_rows_total);
   //--- Setzen (1) des Ausrichtungs-Modus, (2) Textfarbe, (3) Hintergrundfarbe der Zelle
   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);
   //--- Festlegen eines Wertes in der spezifizierten Zelle einer Tabelle
   void              SetValue(const int column_index,const int row_index,const string value);
   //--- Abfrage des Wertes einer bestimmten Zelle einer Tabelle
   string            GetValue(const int column_index,const int row_index);
   //--- Aktualisierung der Daten der Tabelle unter Berücksichtigung der letzten Veränderungen
   void              UpdateTable(void);
  };

Lassen Sie uns nun die Methoden für die Verwaltung der Tabelle besprechen. Es handelt sich hierbei ausschließlich um private Klassen-Methoden für die interne Verwendung. Deren Funktionsumfang umfasst:

  • Verarbeitung eines Klicks auf eine Zeile der Tabelle.
  • Verarbeiten der Eingabe eines Wertes in die Zelle einer Tabelle.
  • Abfrage der ID aus einem Objektnamen
  • Abfrage des Index einer Spalte aus einem Objektnamen
  • Abfrage des Index einer Zeile aus einem Objektnamen
  • Hervorheben der ausgewählten Zeile
  • Verändern der Zeilenfarbe, wenn sich der Mauszeiger über dem Button befindet.
  • Schnelles zurückscrollen der Tabelle.

Lassen Sie uns mit der CTable::OnClickTableRow() Methode für das Anklicken einer Zeile einer Tabelle beginnen. Am Anfang der Methode müssen einige Überprüfungen durchgeführt werden. In den folgenden Fällen verlässt das Programm die Methode:

  • Es ist der Modus für die Editierbarkeit der Tabelle ausgewählt;
  • Wenn einer der Scrollbars aktiv ist;
  • Ein Event für einen Klick gehört nicht zu der Zelle dieser Tabelle. Dieses kann durch den Programmnamen und der Zugehörigkeit der Zelle zu der Tabelle aus dem Objektnamen bestimmt werden;
  • Die Control ID nicht zutrifft. Um die ID aus dem Objektnamen zu erhalten, wird die CTable::IdFromObjectName() Methode verwendet. Dieses Verfahren haben wir bereits bei der Untersuchung von anderen Controls beschrieben.

Nun ist es an der Zeit alle Zellen zu durchsuchen und nach der geklickten Zelle zu schauen, unter Berücksichtigung der gesperrten Zeile für die Überschrift und der aktuellen Position des Schiebereglers der vertikalen Scrollbar. Falls die gedrückte Zelle gefunden worden ist, dann wird der Zeilen-Index und der Wert der Zelle in den variablen der Klasse abgespeichert.

Falls auf die Überschrift (die erste Zeile) geklickt wurde, dann verlässt das Programm die Methode. Andernfalls wird eine benutzerdefinierte Nachricht erzeugt. Sie enthält die (1) Chart ID, (2) Event ID (ON_CLICK_LIST_ITEM), (3) Control ID und (4) Den Index der angeklickten Zeile. 

class CTable : public CElement
  {
private:
   //--- Verarbeiten eines Klicks auf eine Tabellenzeile
   bool              OnClickTableRow(const string clicked_object);
   //--- Den Bezeichner aus dem Objektnamen entnehmen
   int               IdFromObjectName(const string object_name);
  };
//+-----------------------------------------------------------------+
//| Verarbeiten eines Klicks auf eine Tabellenzeile                 |
//+-----------------------------------------------------------------+
bool CTable::OnClickTableRow(const string clicked_object)
  {
//--- Abbrechen, falls der Modus für das Editieren einer Tabelle aktiv ist
   if(!m_read_only)
      return(false);
//--- Abbrechen, falls die Scrollbar aktiv ist
   if(m_scrollv.ScrollState() || m_scrollh.ScrollState())
      return(false);
//--- Abbrechen, falls es sich nicht um einen Klick auf eine Zelle dieser Tabelle handelt
   if(::StringFind(clicked_object,CElement::ProgramName()+"_table_edit_",0)<0)
      return(false);
//--- Den Bezeichner aus dem Objektnamen entnehmen
   int id=IdFromObjectName(clicked_object);
//--- Abbrechen, falls der Bezeichner nicht übereinstimmt
   if(id!=CElement::Id())
      return(false);
//--- Suche nach den Zeilenindex
   int row_index=0;
//--- Verschieben um einen Index, falls der fixierte Kopfzeilen-Modus aktiviert ist
   int t=(m_fix_first_row) ? 1 : 0;
//--- Spalten
   for(int c=0; c<m_visible_columns_total; c++)
     {
      //--- Abfrage der aktuellen Position des Schiebereglers der vertikalen Scrollbar
      int v=m_scrollv.CurrentPos()+t;
      //--- Zeilen
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- Falls nicht auf diese Zelle geklickt wurde
         if(m_columns[c].m_rows[r].Name()==clicked_object)
           {
            //--- Speichere den Index der Zeile
            m_selected_item=row_index=v;
            //--- Speichere die Linie der Zelle
            m_selected_item_text=m_columns[c].m_rows[r].Description();
            break;
           }
         //--- Erhöhe den Zähler für die Zeilen
         if(v>=t && v<m_rows_total)
            v++;
        }
     }
//--- Abbrechen, falls auf die Überschrift geklickt wurde
   if(m_fix_first_row && row_index<1)
      return(false);
//--- Eine Nachricht darüber senden
   ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),m_selected_item,"");
   return(true);
  }

Lassen Sie uns die CTable::OnEndEditCell() Methode für die Verarbeitung einer Eingabe eines Wertes in eine Zelle schreiben. Die Methode muss zunächst auch ein paar Überprüfungen vornehmen. In den folgenden Fällen wird die Methode verlassen:

  • Der Modus für die Editierbarkeit ist nicht aktiviert;
  • Der Programmname oder die Zugehörigkeit der Zelle mit der Tabelle stimmt nicht überein.
  • Die Control ID stimmt nicht überein.

Wenn alle Überprüfungen erfolgreich waren, dann erhalten wir unter Verwendung der Hilfsmethoden CTable::ColumnIndexFromObjectName() und CTable::RowIndexFromObjectName() die Zellenspalten und Zeilen des sichtbaren Bereiches (Indices aus dem Array der grafischen Objekte). Nun müssen wir noch die aktuelle Position der Schieberegler der Scrollbars den Indizes der Objekte hinzufügen damit wir den richtigen Index des Data-arrays erhalten. Anschließend müssen wir noch den Zeilenindex korrigieren, falls der Modus für die fixierte Überschrift aktiviert ist und der Index des Arrays = 0 ist. Anschließen müssen wir überprüfen, ob sich der Wert der Zelle geändert hat. Falls ja, dann wird der neue Wert in dem entsprechenden Daten-Array gespeichert und eine Nachricht, die (1) die Chart ID, (2) die Event ID (ON_END_EDIT), (3) die Control ID und die (4) Zeile, die aus dem Index der Spalte und der Zeile und dem Wert der aktuellen Zelle generiert wird, enthält. Das "_" Symbol wird als Trennzeichen innerhalb der Zeile verwendet. 

class CTable : public CElement
  {
private:
   //--- Verarbeiten der manuellen Eingabe eines Wertes in eine Zelle
   bool              OnEndEditCell(const string edited_object);
   //--- Abfrage des Spaltenindex aus dem Objektnamen
   int               ColumnIndexFromObjectName(const string object_name);
   //--- Abfrage des Index einer Zeile aus einem Objektnamen
   int               RowIndexFromObjectName(const string object_name);
  };
//+----------------------------------------------------------------------+
//| Event für das Abschließen einer Eingabe eines Wertes in eine Zelle   |
//+----------------------------------------------------------------------+
bool CTable::OnEndEditCell(const string edited_object)
  {
//--- Abbrechen, falls der Modus für die Editierbarkeit deaktiviert ist
   if(m_read_only)
      return(false);
//--- Abbrechen, falls es sich nicht um einen Klick auf eine Zelle dieser Tabelle handelt
   if(::StringFind(edited_object,CElement::ProgramName()+"_table_edit_",0)<0)
      return(false);
//--- Den Bezeichner aus dem Objektnamen entnehmen
   int id=IdFromObjectName(edited_object);
//--- Abbrechen, falls der Bezeichner nicht übereinstimmt
   if(id!=CElement::Id())
      return(false);
//--- Abfrage des Spalten- und Zeilen- Index der Zelle
   int c =ColumnIndexFromObjectName(edited_object);
   int r =RowIndexFromObjectName(edited_object);
//--- Abfrage des Spalten- und Zeilen-Index in dem Data-Array
   int vc =c+m_scrollh.CurrentPos();
   int vr =r+m_scrollv.CurrentPos();
//--- Korrektur des Zeilenindex, falls auf eine Kopfzeile geklickt wird
   if(m_fix_first_row && r==0)
      vr=0;
//--- Abfrage des eingegebenen Wertes
   string cell_text=m_columns[c].m_rows[r].Description();
//--- Falls sich der Wert der Zelle geändert hat
   if(cell_text!=m_vcolumns[vc].m_vrows[vr])
     {
      //--- Abspeichern des Wertes in dem Array
      SetValue(vc,vr,cell_text);
      //--- Eine Nachricht darüber senden
      ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),0,string(vc)+"_"+string(vr)+"_"+cell_text);
     }
//---
   return(true);
  }
//+-----------------------------------------------------------------+
//| Abfrage des Spaltenindex aus dem Objektnamen                    |
//+-----------------------------------------------------------------+
int CTable::ColumnIndexFromObjectName(const string object_name)
  {
   ushort u_sep=0;
   string result[];
   int    array_size=0;
//--- Den Code des Trennzeichens erhalten
   u_sep=::StringGetCharacter("_",0);
//--- Den String aufteilen
   ::StringSplit(object_name,u_sep,result);
   array_size=::ArraySize(result)-1;
//--- Überprüfung der Überschreitung des ABCs
   if(array_size-3<0)
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//--- Rückgabe des Index des Elementes
   return((int)result[array_size-3]);
  }
//+-----------------------------------------------------------------+
//| Abfrage des Zeilenindex aus dem Objektnamen                     |
//+-----------------------------------------------------------------+
int CTable::RowIndexFromObjectName(const string object_name)
  {
   ushort u_sep=0;
   string result[];
   int    array_size=0;
//--- Den Code des Trennzeichens erhalten
   u_sep=::StringGetCharacter("_",0);
//--- Den String aufteilen
   ::StringSplit(object_name,u_sep,result);
   array_size=::ArraySize(result)-1;
//--- Überprüfung der Überschreitung des ABCs
   if(array_size-2<0)
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//--- Rückgabe des Index des Elementes
   return((int)result[array_size-2]);
  }

Die CTable::HighlightSelectedItem() Methode wird gebraucht, falls das Hervorheben einer Zeile über den entsprechenden Modus aktiviert ist. Die hervorgehobene Zeile zeichnet sich durch andere Hintergrund- und Textfarben aus. Diese editierbaren Eigenschaften wurden bereits zuvor untersucht.

Zu Beginn der Methode werden zwei Überprüfungen vorgenommen (Schauen Sie sich dazu den nachfolgenden Programmcode an). Das Programm bricht hier ab, falls:

  • Wenn der Modus für die Editierbarkeit einer Zelle aktiviert ist;
  • Der Modus für das Hervorheben einer Zeile deaktiviert ist.

Wenn diese Überprüfungen erfolgreich waren, wird noch eine Verschiebung des Index durchgeführt, falls die Kopfzeilen der Spalten oder Zeilen fixiert sind. Jetzt sollten wir die ausgewählte Zeile finden und die passende Farbe für den Hintergrund der Zellen in der Doppel-Schleife mit, zwei Zählern (für Spalten und Zeilen), unter Berücksichtigung der aktuellen Position der Bild des Schiebereglers der Scrollbar, setzen. Für die Objekte der anderen Zeilen, wird die Farbe aus dem Array verwendet.  

class CTable : public CElement
  {
private:
   //--- Hervorheben der ausgewählten Zeile
   void              HighlightSelectedItem(void);
  };
//+-----------------------------------------------------------------+
//| Hervorheben der ausgewählten Zeile                              |
//+-----------------------------------------------------------------+
void CTable::HighlightSelectedItem(void)
  {
//--- Abbrechen, falls einer der Modi ("Nur lesen", "Auswählbare Zeile") deaktiviert ist.
   if(!m_read_only || !m_selectable_row)
      return;
//--- Verschieben um einen Index, falls der fixierte Kopfzeilen-Modus aktiviert ist
   int t=(m_fix_first_row) ? 1 : 0;
   int l=(m_fix_first_column) ? 1 : 0;
//--- Abfrage der aktuellen Position des Schiebereglers der Horizontalen Scrollbar
   int h=m_scrollh.CurrentPos()+l;
//--- Spalten
   for(int c=l; c<m_visible_columns_total; c++)
     {
      //--- Abfrage der aktuellen Position des Schiebereglers der vertikalen Scrollbar
      int v=m_scrollv.CurrentPos()+t;
      //--- Zeilen
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- Verschiebung der Tabellendaten
         if(v>=t && v<m_rows_total)
           {
            //--- Einstellungen unter Berücksichtigung der ausgewählten Zeile
            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];
            //--- Einstellung der Text- und Hintergrundfarbe der Zelle
            m_columns[c].m_rows[r].Color(text_color);
            m_columns[c].m_rows[r].BackColor(back_color);
            v++;
           }
        }
      //---
      h++;
     }
  }

Ein weiterer nützlicher Modus, ist der Modus für das Hervorheben der Zeile, wenn sich der Mauszeiger darüber befindet. Hierfür wird die CTable::RowColorByHover() Methode verwendet. Sie beinhaltet auch einige Überprüfungen. Wenn diese nicht erfolgreich abgeschlossen werden, bricht das Programm an dieser Stelle der Methode ab. Ein Abbruch wird in den folgenden Fällen durchgeführt:

  • Wenn der Modus für das Hervorheben einer Zeile, sobald sich der Mauszeiger darüber befindet, deaktiviert ist.
  • Der Modus für die Editierbarkeit der Tabelle aktiviert ist.
  • Wenn das Formular, zu welchem das Control hinzugefügt worden ist, blockiert ist
  • Wenn einer der Scrollbar aktiv ist (Der Schieberegler bewegt sich gerade).

Wenn alle Überprüfungen erfolgreich durchlaufen worden sind, sollten wir in einer Doppelschleife durch alle Zellen laufen und deren Farbe in Abhängigkeit, über welcher Zelle sich der Mauszeiger gerade befindet, verändern. Die einzige Ausnahme ist eine hervorgehobene Zeile. Wenn wir an eine solche Zeile gelangen, wird nur der Zähler für die Zeile um einen erhöht aber die Zellen werden nicht geändert. 

class CTable : public CElement
  {
private:
   //--- Veränderung der Farbe der Zeile, wenn sich der Mauszeiger darüber befindet
   void              RowColorByHover(const int x,const int y);
  };
//+-------------------------------------------------------------------------+
//| Veränderung der Zeilenfarbe, wenn sich der Mauszeiger darüber befindet  |
//+-------------------------------------------------------------------------+
void CTable::RowColorByHover(const int x,const int y)
  {
//--- Abbrechen, wenn der Modus für das Hervorheben der Zeile deaktiviert ist oder wenn das Formular gesperrt ist
   if(!m_lights_hover || !m_read_only || m_wnd.IsLocked())
      return;
//--- Abbrechen, falls sich der Schieberegler einer Scrollbar gerade bewegt
   if(m_scrollv.ScrollState() || m_scrollh.ScrollState())
      return;
//--- Verschieben um einen Index, falls der fixierte Kopfzeilen-Modus aktiviert ist
   int t=(m_fix_first_row) ? 1 : 0;
   int l=(m_fix_first_column) ? 1 : 0;
//--- Abfrage der aktuellen Position des Schiebereglers der Horizontalen Scrollbar
   int h=m_scrollh.CurrentPos()+l;
//--- Spalten
   for(int c=l; c<m_visible_columns_total; c++)
     {
      //--- Abfrage der aktuellen Position des Schiebereglers der vertikalen Scrollbar
      int v=m_scrollv.CurrentPos()+t;
      //--- Zeilen
      for(int r=t; r<m_visible_rows_total; r++)
        {
         //--- Überprüfen, ob die Bandbreite des Arrays überschritten wird
         if(v>=t && v<m_rows_total)
           {
            //--- Überspringen, falls wir uns in dem "nur lesen"-Modus befinden, das Selektieren einer Zeile aktiviert ist und wir das selektierte Element erreicht haben
            if(m_selected_item==v && m_read_only && m_selectable_row)
              {
               v++;
               continue;
              }
            //--- Hervorheben der Zeile, falls sich der Mauszeiger darüber befindet
            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);
              }
            //--- Wiederherstellen der Standard Farbe, falls sich der Mauszeiger außerhalb des Bereiches dieser Zeile befindet
            else
              {
               if(v>=t && v<m_rows_total)
                  m_columns[c].m_rows[r].BackColor(m_vcolumns[h].m_cell_color[v]);
              }
            //---
            v++;
           }
        }
      //---
      h++;
     }
  }

Wir haben nun alle Verwaltungs Methoden für die Tabelle untersucht. Sie können die Details des CTable::OnEvent() Eventhandler in dem nachfolgenden Programmcode ansehen. Bitte beachten Sie, das die Methode CTable::HighlightSelectedItem() auch nach dem Verarbeiten der Bewegung des vertikalen Scrollbar-Schiebereglers aufgerufen wird.

//+-----------------------------------------------------------------+
//| Event handler                                                   |
//+-----------------------------------------------------------------+
void CTable::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten des Mauszeiger Bewegungs Events
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Abbrechen, falls das Element versteckt ist
      if(!CElement::IsVisible())
         return;
      //---Koordinaten und der Status der linken Maustaste
      int x=(int)lparam;
      int y=(int)dparam;
      m_mouse_state=(bool)int(sparam);
      CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2());
      //--- Falls die Scrollbar aktiv ist
      if(m_scrollv.ScrollBarControl(x,y,m_mouse_state) || m_scrollh.ScrollBarControl(x,y,m_mouse_state))
         //--- Verschieben der Tabelle
         UpdateTable();
      //--- Hervorheben der ausgewählten Zeile
      HighlightSelectedItem();
      //--- Veränderung der Farbe der Zeile, wenn sich der Mauszeiger darüber befindet
      RowColorByHover(x,y);
      return;
     }
//--- Verarbeitung von Klicks auf Objekte
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Falls eine Zeile der Tabelle geklickt wurde
      if(OnClickTableRow(sparam))
        {
         //--- Hervorheben der ausgewählten Zeile
         HighlightSelectedItem();
         return;
        }
      //--- Falls ein Scrollbar-Button geklickt wurde
      if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam) ||
         m_scrollh.OnClickScrollInc(sparam) || m_scrollh.OnClickScrollDec(sparam))
        {
         //--- Aktualisierung der Daten der Tabelle unter Berücksichtigung der letzten Veränderungen
         UpdateTable();
         //--- Hervorheben der ausgewählten Zeile
         HighlightSelectedItem();
         return;
        }
      return;
     }
//--- Verarbeiten des Events über die Veränderung des Wertes in dem Eingabefeld
   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      OnEndEditCell(sparam);
      //--- Zurücksetzen der Farben der Tabelle
      ResetColors();
      return;
     }
  }

 


Test der Edit-Box Tabelle

Nun ist alles bereit für einen Test der Editbox-Tabelle Kopieren Sie den Expert Advisor aus dem vorangegangenen Beispiel und entfernen Sie alle Elemente, die mit der Text-Label-Tabelle in Zusammenhang stehen. Erzeugen Sie nun in der benutzerdefinierten CProgram Klasse die Instanz der CTable Klasse und deklarieren Sie die Methoden für das Erzeugen der Tabelle:

class CProgram : public CWndEvents
  {
private:
   //--- Die Editbox-Tabelle
   CTable            m_table;
   //---
private:
   //--- Die Editbox-Tabelle
#define TABLE1_GAP_X          (1)
#define TABLE1_GAP_Y          (42)
   bool              CreateTable(void);
  };

Lassen Sie uns eine Tabelle erzeugen, die aus 100 Spalten und 1000 Zeilen besteht. Die Anzahl der sichtbaren Spalten ist 6, Und die Anzahl der sichtbaren Zeilen ist 15. Lassen Sie uns die Überschriften fixieren (Die erste Zeile und die erste Spalte), damit sich diese nicht bewegen, falls der Schieberegler einer Scrollbar bewegt wird. Wir aktivieren die Modi für das Selektieren einer Zeile und das Hervorheben einer Zeile, sobald sich der Mauszeiger darüber befindet. 

Nach der Erzeugung des Controls, werden die Tabellen-Arrays mit Daten gefüllt und formatiert. Wir legen beispielhaft die ALIGN_RIGHT Ausrichtungsmethode für die erste Spalte fest. Lassen Sie uns die Zellen der Zeilen im "Zebra" Stil zeichnen, und die Farbe des Textes der Spalten wird in rot und blau vorgenommen. Stellen Sie sicher, dass Sie dieTabelle aktualisieren, damit die Veränderungen sichtbar werden. 

//+-----------------------------------------------------------------+
//| Erzeugung der Tabelle                                           |
//+-----------------------------------------------------------------+
bool CProgram::CreateTable(void)
  {
#define COLUMNS1_TOTAL (100)
#define ROWS1_TOTAL    (1000)
//--- Abspeichern des Pointers des Formulars
   m_table.WindowPointer(m_window1);
//--- Koordinaten
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- Die Anzahl der sichtbaren Spalten und Zeilen
   int visible_columns_total =6;
   int visible_rows_total    =15;
//--- Vor der Erzeugung die Eigenschaften festlegen
   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);
//--- Erzeugung des Control-Elementes
   if(!m_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Einfügen in die Tabelle:
//    Die erste Zelle ist leer
   m_table.SetValue(0,0,"-");
//--- Die Überschriften der Spalten
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- Die Überschriften der Zeilen, die Ausrichtung ist Rechts
   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);
        }
     }
//---  Daten- und Tabellen-Formatierung (Hintergrund und Zellenfarben)
   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);
        }
     }
//--- Aktualisierung der Tabelle um die Änderungen sichtbar zu machen
   m_table.UpdateTable();
//--- Hinzufügen des Objektes zu dem Array aller Objektgruppen
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
  }

Die CProgram::CreateTable() Methode sollte in der Hauptmethode der Anwendung aufgerufen werden. (Sehen Sie sich dazu die abgekürzte Version des nachfolgenden Programmcodes an): 

//+----------------------------------------------------------------+
//| Erzeugung des Expert-Bedienfeldes                              |
//+----------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//--- Erzeugen der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Erzeugen der Statuszeile
//--- Editbox-Tabelle
   if(!CreateTable())
      return(false);
//--- Neuzeichnen des Charts
   m_chart.Redraw();
   return(true);
  }

Kompilieren Sie das Programm und lassen Sie das auf einem Chart laufen. Wenn alles korrekt ausgefüllt worden ist, dann erhalten wir ein Ergebnis, so wie es in den nachfolgenden Screenshot gezeigt wird:

 Abbildung 4. Test der Editbox-Tabelle

Abbildung 4. Test der Editbox-Tabelle

Alles arbeitet einwandfrei. Aber wenn Sie einen Text, der mehr als 63 Zeichen lang ist, in einer einzigen Zelle eingeben, dann wird der Text nicht vollständig angezeigt. Alle grafischen Objekte in dem Terminal, die die Möglichkeit haben einen text anzuzeigen, haben eine Begrenzung von 63 Symbolen. Wir verwenden die CCanvas Klasse für das Zeichnen, um dieses Problem zu umgehen. Diese Klasse beinhaltet Methoden für die Darstellung von Text ohne irgendwelche Einschränkungen. Wir haben diese Klasse schon bei einigen Controls angewendet (Trendy mir und Kontextmenü). Artikel:

Da 63 Zeichen sehr oft nicht ausreichend sind, wird die dritte Tabelle unter Verwendung der CCanvas Klasse gezeichnet. 

 


Das Rendered Tabellen-Control

Eine gerenderte Tabelle besitzt keine Begrenzungen, hinsichtlich der Anzahl der Zeichen für jede Zelle. Zudem kann die Breite von jeder Spalte konfiguriert werden, ohne den sichtbaren Bereich der Tabelle ändern zu müssen. Dieses ist bei den anderen Tabellentypen, die wir zuvor besprochen haben, sehr schwierig. Somit können wir schon einige Vorteile der gerenderten Tabelle aufzeigen:

  • Keine Begrenzung der Anzahl der Symbole pro Zelle;
  • Jede Spaltenbreite kann separat festgelegt werden;
  • Es wird nur ein Objekt (OBJ_BITMAP_LABEL) für die Erzeugung der Tabelle verwendet, anstelle von verschiedenen Objekten, wie es bei Text-Labels (OBJ_LABEL) oder Editboxen (OBJ_EDIT) der Fall ist.

Text-Label Tabellen bestehend aus den folgenden Komponenten:

  1. Hintergrund
  2. Gerenderte Tabelle
  3. Vertikale Scrollbar.
  4. Horizontale Scrollbar.

Abbildung 5. Komponenten einer gerenderten Tabelle
 

Abbildung 5. Komponenten einer gerenderten Tabelle

Lassen Sie uns den Programmcode der Klasse für die Erzeugung einer solchen Tabelle genauer untersuchen.

 


Entwicklung der CCanvasTable Klasse

Lassen Sie uns die CTOptions Struktur erzeugen, um darin die Werte der Tabelle und die Eigenschaften abzuspeichern:

//+-----------------------------------------------------------------+
//| Klasse für die Erzeugung einer gerenderten Tabelle              |
//+-----------------------------------------------------------------+
class CCanvasTable : public CElement
  {
private:
   //--- Array für die Werte und Eigenschaften der Tabelle
   struct CTOptions
     {
      string            m_vrows[];
      int               m_width;
      ENUM_ALIGN_MODE   m_text_align;
     };
   CTOptions         m_vcolumns[];
  };

Die CTOptions Variablen der Struktur werden mit den Standardwerten initialisiert, wenn die grundlegende Größe der Tabelle (Gesamt Anzahl der Spalten und Zeilen) gesetzt wird. Wir legen die breite aller Spalten auf 100 Pixels fest, und die Ausrichtung in den Zellen ist ALIGN_CENTER

class CCanvasTable : public CElement
  {
public:
   //--- Festlegen der Größe der Tabelle
   void              TableSize(const int columns_total,const int rows_total);
  };
//+-----------------------------------------------------------------+
//| Legt die Größe der Tabelle fest                                 |
//+-----------------------------------------------------------------+
void CCanvasTable::TableSize(const int columns_total,const int rows_total)
  {
//--- Es muss mindestens eine Spalte geben
   m_columns_total=(columns_total<1) ? 1 : columns_total;
//--- Es muss mindestens zwei Zeilen geben
   m_rows_total=(rows_total<2) ? 2 : rows_total;
//--- Festlegen der Größe der Spalten-Arrays
   ::ArrayResize(m_vcolumns,m_columns_total);
//--- Festlegen der Größe der Zeilen-Arrays
   for(int i=0; i<m_columns_total; i++)
     {
      ::ArrayResize(m_vcolumns[i].m_vrows,m_rows_total);
      //--- Initialisierung der Spalteneigenschaften mit den Standardwerten
      m_vcolumns[i].m_width      =100;
      m_vcolumns[i].m_text_align =ALIGN_CENTER;
     }
  }

Lassen Sie uns nur die Methoden, die es uns erlauben, die Breite oder die Ausrichtung des Textes von einigen Spalten (falls nötig) festzulegen, nachdem die Größe der Tabelle gesetzt wurde. Um dieses zu tun, müssen Sie lediglich ihr Array initialisieren und es der entsprechenden Methode übergeben. Nachfolgend wird ein Beispiel gezeigt: 

class CCanvasTable : public CElement
  {
public:
   //--- Festlegen der (1) Ausrichtung des Textes und (2) der Breite für jede Spalte
   void              TextAlign(const ENUM_ALIGN_MODE &array[]);
   void              ColumnsWidth(const int &array[]);
  };
//+----------------------------------------------------------------+
//| Füllt das array mit den Textausrichtungs-Modi                  |
//+----------------------------------------------------------------+
void CCanvasTable::TextAlign(const ENUM_ALIGN_MODE &array[])
  {
   int total=0;
   int array_size=::ArraySize(array);
//--- Abbrechen, falls ein Array der Größe 0 übergeben wurde
   if(array_size<1)
      return;
//--- Einstellen des Wertes, um ein Überschreiten der Bandbreite des Arrays zu verhindern
   total=(array_size<m_columns_total)? array_size : m_columns_total;
//--- Abspeichern der Werte in der Struktur
   for(int c=0; c<total; c++)
      m_vcolumns[c].m_text_align=array[c];
  }
//+----------------------------------------------------------------+
//| Sylt das Array mit den Spaltenbreiten                          |
//+----------------------------------------------------------------+
void CCanvasTable::ColumnsWidth(const int &array[])
  {
   int total=0;
   int array_size=::ArraySize(array);
//--- Abbrechen, falls ein Array der Größe 0 übergeben wurde
   if(array_size<1)
      return;
//--- Einstellen des Wertes, um ein Überschreiten der Bandbreite des Arrays zu verhindern 
   total=(array_size<m_columns_total)? array_size : m_columns_total;
//--- Abspeichern der Werte in der Struktur
   for(int c=0; c<total; c++)
      m_vcolumns[c].m_width=array[c];
  }

Bevor wir mit der Erzeugung der Tabelle fortfahren, müssen wir die Gesamtgröße, sowie die Größe des sichtbaren Bereiches, relativ zu den angegebenen Parametern (Breite aller Spalten, Höhe aller Zeilen und die Anwesenheit von Scrollbars) angeben. Dafür schreiben wir die nachfolgend aufgezeigte Methode CCanvasTable::CalculateTableSize(): 

class CCanvasTable : public CElement
  {
private:
   //--- Gesamtgröße und die Größe des sichtbaren Bereiches der Tabelle
   int               m_table_x_size;
   int               m_table_y_size;
   int               m_table_visible_x_size;
   int               m_table_visible_y_size;
//---
private:
   //--- Berechnung der Größe der Tabelle
   void              CalculateTableSize(void);
  };
//+-----------------------------------------------------------------+
//| Berechnung der Größe der Tabelle                                |
//+-----------------------------------------------------------------+
void CCanvasTable::CalculateTableSize(void)
  {
//--- Berechnung der Gesamtbreite der Tabelle
   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;
//--- Breite der Tabelle mit einer vertikalen Scrollbar
   int x_size=(m_rows_total>m_visible_rows_total) ? m_x_size-m_scrollh.ScrollWidth() : m_x_size-2;
//--- Falls die Breite aller Spalten kleiner ist als die Breite der Tabelle, dann wird die Breite der Tabelle verwendet
   if(m_table_x_size<m_x_size)
      m_table_x_size=x_size;
//--- Berechnung der Gesamthöhe der Tabelle
   m_table_y_size=m_cell_y_size*m_rows_total-(m_rows_total-1);
//--- Festlegen der Rahmengröße, um einen Teil des Bildes anzuzeigen (sichtbarer Bereich der Tabelle)
   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);
//--- Falls es eine horizontale Scrollbar gibt, dann wird die Größe des Controls entlang der y-Achse angepasst
   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;
  }

Nach der Berechnung der Größe der Tabelle und der Erzeugung des Canvas, müssen wir eine Methode für das Zeichnen des Tabellengitters und der Zellen-Texte entwickeln. Für das Zeichnen des Gitters, schreiben wir die CCanvasTable::DrawGrid() Methode. Zunächst werden in einer ersten Schleife die horizontalen Gitterlinien gezeichnet und anschließend in einer zweiten Schleife die vertikalen Gitterlinien.  

class CCanvasTable : public CElement
  {
private:
   //--- Farbe des Gitters
   color             m_grid_color;
   //--- Die Größe (Höhe) der Zellen
   int               m_cell_y_size;
//---
public:
   //--- Farbe des Gitters
   void              GridColor(const color clr)           { m_grid_color=clr;                }
//---
private:
   //--- Zeichnen des Gitters
   void              DrawGrid(void);
  };
//+-----------------------------------------------------------------+
//| Zeichnen des Gitters                                            |
//+-----------------------------------------------------------------+
void CCanvasTable::DrawGrid(void)
  {
//--- Farbe des Gitters
   uint clr=::ColorToARGB(m_grid_color,255);
//--- Die Größe des Canvas für das Zeichnen
   int x_size =m_canvas.XSize()-1;
   int y_size =m_canvas.YSize()-1;
//--- Koordinaten
   int x1=0,x2=0,y1=0,y2=0;
//--- Horizontale Linien
   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;
     }
//--- Vertikale Linien
   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;
     }
//--- Rechts
   x1=x_size;
   y1=0;
   x2=x_size;
   y2=y_size;
   m_canvas.Line(x1,y1,x2,y2,clr);
  }

Die CCanvasTable::DrawText() Methode für das Zeichen von Text ist ein wenig komplizierter als die Methode für das Zeichnen des Gitters. Wir sollten nicht nur die Ausrichtung des Textes der aktuellen Spalte berücksichtigen sondern auch die der vorherigen Spalte, um die Einzüge korrekt berechnen zu können. Wir legen einen Einzug von 10 Pixeln von der Ecke einer Zelle für die rechte und linke Ausrichtung fest. Der Einzug von der Oberkante einer Zelle ist 3 Pixel. Nachfolgend sehen Sie den detaillierten Programmcode dieser Methode: 

class CCanvasTable : public CElement
  {
private:
   //--- Die Textfarbe
   color             m_cell_text_color;
//---
public:
   //--- Textfarbe der Tabelle
   void              TextColor(const color clr)           { m_cell_text_color=clr;           }
//---
private:
   //--- Zeichnen des Textes
   void              DrawText(void);
  };
//+-----------------------------------------------------------------+
//| Zeichnen des Textes                                             |
//+-----------------------------------------------------------------+
void CCanvasTable::DrawText(void)
  {
//--- Für die Berechnung der Koordinaten und des Offsets
   int  x             =0;
   int  y             =0;
   uint text_align    =0;
   int  column_offset =0;
   int  cell_x_offset =10;
   int  cell_y_offset =3;
//--- Die Textfarbe
   uint clr=::ColorToARGB(m_cell_text_color,255);
//--- Eigenschaften der Schrift
   m_canvas.FontSet(FONT,-80,FW_NORMAL);
//--- Spalten
   for(int c=0; c<m_columns_total; c++)
     {
      //--- Berechnung des Offsets für die erste Spalte
      if(c==0)
        {
         //--- Die Ausrichtung Inhalt einer Zelle basiert auf dem Modus der für die Spalte gesetzt ist
         switch(m_vcolumns[0].m_text_align)
           {
            //--- Mittig
            case ALIGN_CENTER :
               column_offset=column_offset+m_vcolumns[0].m_width/2;
               x=column_offset;
               break;
               //--- Rechts
            case ALIGN_RIGHT :
               column_offset=column_offset+m_vcolumns[0].m_width;
               x=column_offset-cell_x_offset;
               break;
               //--- Links
            case ALIGN_LEFT :
               x=column_offset+cell_x_offset;
               break;
           }
        }
      //--- Die Berechnung der Offset für alle Spalten mit Ausnahme der ersten
      else
        {
         //--- Die Ausrichtung Inhalt einer Zelle basiert auf dem Modus der für die Spalte gesetzt ist
         switch(m_vcolumns[c].m_text_align)
           {
            //--- Mittig
            case ALIGN_CENTER :
               //--- Die Berechnung des Offset relativ zu der Ausrichtung in der vorherigen Spalte
               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;
               //--- Rechts
            case ALIGN_RIGHT :
               //--- Die Berechnung des Offset relativ zu der Ausrichtung in der vorherigen Spalte
               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;
               //--- Links
            case ALIGN_LEFT :
               //--- Die Berechnung des Offset relativ zu der Ausrichtung in der vorherigen Spalte
               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;
           }
        }
      //--- Zeilen
      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;
           }
         //--- Zeichnen des Textes
         m_canvas.TextOut(x,y,m_vcolumns[c].m_vrows[r],clr,text_align);
        }
      //--- Zurücksetzen der y-Koordinate für den nächsten Durchlauf
      y=0;
     }
  }

In den ersten beiden Tabellentypen, haben wir untersucht, dass das verschieben über die Scrollbar, durch das Verändern der Werte der Objekte, die sich in dem sichtbaren Bereich der Tabelle befinden, geschieht (Text-Labes und Editboxen). Hier verschieben wir den rechteckigen Rahmen für die Sichtbarkeit des Bildes. Mit anderen Worten, die Größe der Tabelle (Bild) ist anfänglich gleich der Summe aller Spalten und die Höhe aller Zeilen. Dieses ist die Größe des original Bildes, in welchem der Rahmen für die Sichtbarkeit bewegt werden kann. Die Größe des Rahmens für die Sichtbarkeit kann zu jederzeit geändert werden, aber hier wird Sie direkt nach der Erzeugung des Controls in der CCanvasTable::CreateCells() Methode festgelegt. 

Die Größe des Rahmens kann über die Eigenschaften OBJPROP_XSIZE und OBJPROP_YSIZE festgelegt werden und die Verschiebung des Rahmens(innerhalb des Bildes) kann über die Eigenschaften OBJPROP_XOFFSET und OBJPROP_YOFFSET gesteuert werden. (Sehen Sie sich dazu das nachfolgende Beispiel an): 

//--- Festlegen der Größe des sichtbaren Bereiches
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_XSIZE,m_table_visible_x_size);
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_YSIZE,m_table_visible_y_size);
//--- Festlegen des Offsets für den Rahmen innerhalb des Bildes entlang der X und Y-Achse
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_XOFFSET,0);
   ::ObjectSetInteger(m_chart_id,name,OBJPROP_YOFFSET,0);

Lassen Sie uns die einfache Methode CCanvasTable::ShiftTable() für die Verschiebung des Rahmens, relativ zu der aktuellen Position des Schiebereglers der Scrollbar, entwickeln. Die vertikale Verschiebung ist gleich der Höhe der Zeile, aber die horizontale Verschiebung wird in Pixeln ausgeführt (Sehen Sie sich dazu das nachfolgende Programmbeispiel an): 

class CCanvasTable : public CElement
  {
public:
   //--- Verschiebung der Tabelle, relativ zu den Positionen der Scrollbars
   void              ShiftTable(void);
  };
//+-----------------------------------------------------------------+
//| Verschiebung der Tabelle relativ zu den Scrollbars              |
//+-----------------------------------------------------------------+
void CCanvasTable::ShiftTable(void)
  {
//--- Abfrage der aktuellen Positionen der Schieberegler der vertikalen und horizontalen Scrollbars
   int h=m_scrollh.CurrentPos();
   int v=m_scrollv.CurrentPos();
//--- Berechnung der Position der Tabelle in Relation zu den Schiebereglern der Scrollbars
   long c=h;
   long r=v*(m_cell_y_size-1);
//--- Verschieben der Tabelle
   ::ObjectSetInteger(m_chart_id,m_canvas.Name(),OBJPROP_XOFFSET,c);
   ::ObjectSetInteger(m_chart_id,m_canvas.Name(),OBJPROP_YOFFSET,r);
  }

Die generelle CCanvasTable::DrawTable() Methode für das Zeichnen der Tabelle, sieht wie folgt aus: 

class CCanvasTable : public CElement
  {
public:
   //--- Zeichne die Tabelle unter Berücksichtigung der letzten Änderungen
   void              DrawTable(void);
  };
//+-----------------------------------------------------------------+
//| Zeichnen der Tabelle                                            |
//+-----------------------------------------------------------------+
void CCanvasTable::DrawTable(void)
  {
//--- Den Hintergrund transparent machen
   m_canvas.Erase(::ColorToARGB(clrNONE,0));
//--- Zeichnen des Gitters
   DrawGrid();
//--- Zeichnen des Textes
   DrawText();
//--- Darstellen der zuletzt gezeichneten Veränderungen
   m_canvas.Update();
//--- Verschiebung der Tabelle in Relation zu den Scrollbars
   ShiftTable();
  }

Nun ist alles dafür bereit, um diese Tabelle zu testen. 

 


Test der gerenderten-Tabelle

Machen Sie eine Kopie des vorangegangenen EAs und entfernen Sie alle Elemente, die in Zusammenhang mit der CTable Tabelle stehen. Erzeugen Sie nun in der benutzerdefinierten CProgram Klassse eine Instanz der CCanvasTable Klasse und deklarieren Sie die (1) CProgram::CreateCanvasTable() Methode für die Erzeugung der Tabelle, sowie die (2) Einzüge, so wie es in dem nachfolgenden Beispiel gezeigt wird:

class CProgram : public CWndEvents
  {
private:
   //--- Gerenderte Tabelle
   CCanvasTable      m_canvas_table;
   //---
private:
   //--- Gerenderte Tabelle
#define TABLE1_GAP_X          (1)
#define TABLE1_GAP_Y          (42)
   bool              CreateCanvasTable(void);
  };

Lassen Sie uns eine Tabelle mit 15 Spalten und 1000 Zeilen erzeugen. Die Anzahl der sichtbaren Zeilen ist 16. Wir müssen die Anzahl der sichtbaren Spalten nicht festlegen, da die horizontale Verschiebung in Pixeln durchgeführt wird. In diesem Fall sollte die Breite des sichtbaren Bereichs der Tabelle explizit angegeben werden. Lassen Sie sie uns auf 601 Pixel festlegen. 

Als Beispiel, lassen Sie uns die Breite aller Spalten (mit Ausnahme der ersten zwei) auf 70 Pixel festlegen. Die Breiten der ersten und zweiten Spalte werden auf 100 und 90 Pixel festgelegt. In allen Spalten (mit Ausnahme der ersten drei), wird der Text mittig dargestellt. In der ersten und dritten Spalte ist die Ausrichtung rechts, während in der Zweiten die Ausrichtung links ist. Der vollständige Programmcode der CProgram::CreateCanvasTable() Methode wird in dem nachfolgenden Programmcode dargestellt:

//+-----------------------------------------------------------------+
//| Erzeugung der gerenderten Tabelle                               |
//+-----------------------------------------------------------------+
bool CProgram::CreateCanvasTable(void)
  {
#define COLUMNS1_TOTAL 15
#define ROWS1_TOTAL    1000
//--- Abspeichern des Pointers des Formulars
   m_canvas_table.WindowPointer(m_window1);
//--- Koordinaten
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- Anzahl der sichtbaren Zeilen
   int visible_rows_total=16;
//--- Array mit Spaltenbreiten
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,70);
   width[0]=100;
   width[1]=90;
//--- Array für die Textausrichtung in den Spalten
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[0]=ALIGN_RIGHT;
   align[1]=ALIGN_LEFT;
   align[2]=ALIGN_RIGHT;
//--- Vor der Erzeugung die Eigenschaften festlegen
   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);
//--- Tabelle mit Daten auffüllen
   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));
//--- Erzeugen des Controls
   if(!m_canvas_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Hinzufügen des Objektes zu dem Array aller Objektgruppen
   CWndContainer::AddToElementsArray(0,m_canvas_table);
   return(true);
  }

Der Aufruf der Methode muss in der Hauptmethode für die Erzeugung des grafischen Interfaces stattfinden. 

//+-----------------------------------------------------------------+
//| Erzeugung des Trading-Panels                                    |
//+-----------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//--- Erzeugen der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Erzeugen der Statuszeile
//--- Erzeugung der gerenderten Tabelle
   if(!CreateCanvasTable())
      return(false);
//--- Neuzeichnen des Charts
   m_chart.Redraw();
   return(true);
  }

Kompilieren Sie das Programm und lassen Sie das auf einem Chart laufen. Der nachfolgende Screenshot zeigt das Ergebnis:

 Abbildung 6. Test der gerenderten Tabelle in einem EA

Abbildung 6. Test der gerenderten Tabelle in einem EA

In den fünften Kapitel des ersten Teil dieser Serie, haben wir die Verwendung von Formularen in Skripten getestet. Tabellen ohne Scrollbars können bei dieser Art von MQL Anwendungen verwendet werden. Als Beispiel können Sie eine gerenderte Tabelle dem Skript des Formulars hinzufügen und die Daten alle 250 Millisekunden aktualisieren. Fügen Sie die zuvor gezeigte Tabelle der benutzerdefinierten Klasse hinzu. Zudem muss noch der Programmcode dem CProgram::OnEvent() Skript Eventhandler, wie nachfolgend gezeigt, hinzugefügt werden. Nun ändern sich mit dem angegebenen Zeitintervall, die Daten innerhalb der zweiten Spalte. 

//+-----------------------------------------------------------------+
//| Events                                                          |
//+-----------------------------------------------------------------+
void CProgram::OnEvent(const int milliseconds)
  {
   static int count =0;  // Counter
   string     str   =""; // Header row
//--- Die Kopfzeile zeigt den Prozess
   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;
     }
//--- Aktualisierung der Kopfzeile
   m_window.CaptionText(str);
//--- Änderung der Daten der ersten Spalte
   for(int r=0; r<13; r++)
      m_canvas_table.SetValue(1,r,string(::rand()));
//--- Anzeigen der neuen Tabellendaten
   m_canvas_table.DrawTable();
//--- Neuzeichnen des Charts
   m_chart.Redraw();
//--- Erhöhung des Zählers
   count++;
//--- Zurücksetzen auf Null, falls 3 überschritten wird
   if(count>3)
      count=0;
//--- Pause
   ::Sleep(milliseconds);
  }

Kompilieren Sie das Programm und starten Sie dieses Skript auf einem Chart Sie sollten nun das folgende sehen: 

 Abbildung 7. Test der gerenderten Tabelle innerhalb eines Skriptes

Abbildung 7. Test der gerenderten Tabelle innerhalb eines Skriptes

Wir haben die Entwicklung der CCanvasTable Klasse für die Erzeugung einer gerenderten Tabelle abgeschlossen. Nun ist es Zeit für einige vorläufige Ergebnisse. 

 


Schlussfolgerung

In diesen Artikel haben wir drei Klassen für die Erzeugung eines wichtigen Interface-Elementes, die Tabelle, besprochen. Jede dieser Klassen hat ihre besonderen Eigenschaften, die für bestimmte Fälle am besten geeignet sind. Zum Beispiel bietet die CTable Klasse die Möglichkeit der Entwicklung einer Tabelle mit editierbaren Boxen, wobei diese formatierbar sind, was sie sehr benutzerfreundlich aussehen lässt. Die CCanvasTable Klasse erlaubt es dagegen, die Begrenzung der Anzahl von Zeichen innerhalb einer Zelle zu umgehen und bietet zudem die Möglichkeit, für jede Spalte eine eigene Breite angeben zu können. Dieses sind nicht die endgültigen Versionen der Tabellen. Falls nötig, können Sie später noch erweitert werden.

In dem nächsten Artikel werden wir Klassen für die Entwicklung von Tab-Controls besprechen, die ebenso häufig in graphischen Interfaces verwendet werden.

Sie können das gesamte Material des siebten Teils herunterladen und testen. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in dem Artikel zu dieser Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.

Liste der Artikel (Kapitel) des siebten Teils:

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2500

Beigefügte Dateien |
Fuzzy-Logik für eine manuelle Handelsstrategie Fuzzy-Logik für eine manuelle Handelsstrategie
Dieser Artikel zeigt die Möglichkeiten der Verbesserung einer manuelle Handelsstrategie durch die Verwendung der Theorie der unscharfen Mengen (fuzzy sets). Als Beispiel bieten wir eine Schritt-für-Schritt-Beschreibung der Suche nach einer Strategie und die Auswahl der Parameter, gefolgt von der Anwendung der Fuzzy-Logik, um allzu formale Kriterien für den Markteintritt zu verwischen. So erhalten wir nach einer Strategie-Modifikation flexible Bedingungen für die Eröffnung einer Position als eine angemessene Reaktion auf eine Marktsituation.
Welche Überprüfungen der Handelsroboter vor der Veröffentlichung in Market bestehen soll Welche Überprüfungen der Handelsroboter vor der Veröffentlichung in Market bestehen soll
Alle Markets Produkte vor der Veröffentlichung bestehen eine obligatorische vorläufige Überprüfung, um eine Standarte Qualität zu haben. In diesem Artikel werden wir von den häufigsten Fehlern erzählen, die die Hersteller in den Handelsrobotern und den technischen Indikatoren machen. Auch werden wir zeigen, wie man sein Produkt vor der Sendung in Market selbständig überprüfen soll.
Universal Expert Advisor: Das Event-Modell und der Trading-Strategie Prototyp (Part 2) Universal Expert Advisor: Das Event-Modell und der Trading-Strategie Prototyp (Part 2)
Dieser Artikel führt die Serie der Publikationen eines universellen Expert-Advisors fort. Dieser Teil beschreibt ausführlich das originale Event-Modell, welches auf einer zentralisierten Datenverarbeitung basiert und bestimmt die Struktur der Basisklasse CStrategy dieser Trading-Engine.
LifeHack für Trader: ein back-Test ist gut, und vier – ist besser LifeHack für Trader: ein back-Test ist gut, und vier – ist besser
Vor jedem Trader bei dem ersten einzelnen Test steht eine und derselbe Frage — "Welchen von vier Modus ich verwenden soll?" Jeder des angebotenen Modus hat eigene Vorteile und Besonderheiten, deshalb machen wir es einfacher — wir werden direkt alle Modus durch eine Taste starten! Im Artikel ist es vorgeführt, wie man mit Hilfe Win API und der kleinen Magie gleichzeitig alle vier Graphik des Tests sehen kann.