Grafische Interfaces IV: Informierende Interface-Elemente (Kapitel 1)

Anatoli Kazharski | 5 Juli, 2016

Inhalt

 

Einleitung

Der erste Artikel Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) betrachtet im Detail den Sinn und Zweck der Bibliothek. Eine vollständige Liste der Links zu den Artikeln finden Sie am Ende von jedem Kapitel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungszustand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind. 

Zum aktuellen Stand der Entwicklung, beinhaltet die Bibliothek für die Erzeugung von grafischen Interfaces ein Formular und verschiedene Steuerelemente (Controls), welche dem Formular hinzugefügt werden können. Wie zuvor schon erwähnt, sollte sich einer der zukünftigen Artikel mit dem Thema Multi-Window-Modus beschäftigen. Dafür liegt nun alles vor, und wir werden in dem folgenden Kapitel dieses Thema behandeln. In diesem Kapitel schreiben wir Klassen für die Erzeugung der Statusbar und des Tooltip-Elementes.


Das Statusbar Element

Die Statusbar ist eine der informierenden Elemente des grafischen Interfaces. Dieses Element wird für die schnelle Eingabe von wichtigen Daten, Details und Werten verwendet. Auch die MetaTrader Terminals besitzen eine solche Statusbar. Sie besteht aus mehreren Abschnitten (elementen) Das erste Element zeigt Informationen darüber, über welchen Teil des Terminals sich der Mauszeiger befindet oder die Namen der Programm-Kommandos. Es gibt auch Elemente, die die Zeit und die zugehörigen Kursdaten anzeigen, sobald sich der Mauszeiger über dem Bereich eines Charts befindet. Einige Elemente besitzen ein Kontextmenü, welches durch einen Klick mit der linken Maustaste angezeigt werden kann. Auch der MetaEditor Programmeditor besitzt eine Statusbar. Seine Elemente zeigen ebenfalls die Programm-Kommandos an, die Position des Cursors (Zeile/Spalte) und den Text-Eingabemodus (Einfügen/Überschreiben). Weitere Informationen über die Statusbar im Terminal und in dem Programmeditor finden Sie in der dazugehörigen Hilfe (F1).

In diesem Artikel werden wir eine einfache Statusbar, ohne die Möglichkeit ein Kontextmenü hinzuzufügen, erzeugen. Wie auch bei anderen Interfaceelementen, besteht die Statusbar aus verschiedenen einfachen Objekten:

Abbildung  1. Komponenten der Statusbar.

Abbildung 1. Komponenten der Statusbar.

Erzeugen Sie die StatusBar.mqh Datei und beziehen Sie sie in der WndContainer.mqh Datei mit ein, damit sie in der gesamten Bibliothek verfügbar ist. Der nachfolgende Programmcode zeigt die CStatusBar Klasse mit den Virtuellen Standardmethoden, welche in allen Klassen der Controls verwendet werden. 

//+------------------------------------------------------------------+
//|                                                    StatusBar.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Klasse für das Erzeugen der Statusbar                            |
//+------------------------------------------------------------------+
class CStatusBar : public CElement
  {
private:
   //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist
   CWindow          *m_wnd;
   //---
public:
                     CStatusBar(void);
                    ~CStatusBar(void);
   //--- Speichert den Pointer
   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ät der linken Maustaste
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CStatusBar::CStatusBar(void)
  {
  }
//+------------------------------------------------------------------+
//| Destruktor                                                       |
//+------------------------------------------------------------------+
CStatusBar::~CStatusBar(void)
  {
  }
//+------------------------------------------------------------------+

Die OnEvent() und OnEventTimer() Methoden für das Behandeln von Events werden in dieser Version nicht verwendet. 

Wir betrachten nun die Eigenschaften der Statusbar. Da wir Objekt-Arrays verwenden, wird es gemeinsame und spezielle Eigenschaften geben. Die speziellen Eigenschaften umfassen nur die Breite des Elementes. Die Liste der gemeinsamen Eigenschaften ist um einiges länger und wird nachfolgend gezeigt.

Gemeinsame Eigenschaften:

Initialisierung der Eigenschaften-Werte mit Standardwerten innerhalb des Konstruktors. In dem Moment der Erzeugung des Elementes, kann der User diese unter Verwendung der Public-Methoden neu definieren. 

class CStatusBar : public CElement
  {
private:
   //--- Eigenschaften:
   //    Arrays für spezielle Eigenschaften
   int               m_width[];
   //--- (1) Farbe des Hintergrundes und (2) Rahmen des Hintergrundes
   color             m_area_color;
   color             m_area_border_color;
   //--- Die Textfarbe
   color             m_label_color;
   //--- Priorität eines Klicks mit der linken Maustaste
   int               m_zorder;
   //--- Farben für die Trennlinien
   color             m_sepline_dark_color;
   color             m_sepline_light_color;
   //---
public:
   //--- Farbe des (1) Hintergrundes, (2) Rahmen des Hintergrundes und (3) des Textes
   void              AreaColor(const color clr)                       { m_area_color=clr;             }
   void              AreaBorderColor(const color clr)                 { m_area_border_color=clr;      }
   void              LabelColor(const color clr)                      { m_label_color=clr;            }
   //--- Farbe der Trennlinien
   void              SeparateLineDarkColor(const color clr)           { m_sepline_dark_color=clr;     }
   void              SeparateLineLightColor(const color clr)          { m_sepline_light_color=clr;    }
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CStatusBar::CStatusBar(void) : m_area_color(C'240,240,240'),
                               m_area_border_color(clrSilver),
                               m_label_color(clrBlack),
                               m_sepline_dark_color(C'160,160,160'),
                               m_sepline_light_color(clrWhite)
  {
//--- Abspeichern des namens der Elementklasse in der Basisklasse  
   CElement::ClassName(CLASS_NAME);
//--- Setzen der Priorität eines Klicks mit der linken Maustaste
   m_zorder=2;
  }

Lassen Sie uns nun die Methoden für die Erzeugung der Statusbar betrachten. Für den Hintergrund verwenden wir das einfache Objekt vom Typ CRectLabel. Ein dynamisches Array von einfachen Objekten des Typs CEdit muss für die Erzeugung des Elementes deklariert werden. The CSeparateLine Klasse wurde zuvor schon entwickelt. Diese Klasse kann als ein unabhängiges Interface Element verwendet werden. In unserem Fall verwenden wir es als eine Komponente unserer Statusbar und dafür benötigen wir ein Array mit diesen Elementen.

Elemente werden mit der Methode CStatusBar::AddItem() vor der Erzeugung der Statusbar hinzugefügt, anderenfalls wird die Erzeugung des grafischen interfaces abgebrochen. Die Anzahl der Elemente kann über den Aufruf der Methode CStatusBar::ItemsTotal() abgefragt werden. 

//+------------------------------------------------------------------+
//|                                                    StatusBar.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
#include "SeparateLine.mqh"
//+------------------------------------------------------------------+
//| Klasse für das Erzeugen der Statusbar                            |
//+------------------------------------------------------------------+
class CStatusBar : public CElement
  {
private:
   //--- Objekt für die Erzeugung eines Buttons
   CRectLabel        m_area;
   CEdit             m_items[];
   CSeparateLine     m_sep_line[];
   //---
public:
   //--- Methoden für das Erzeugen der Statusbar
   bool              CreateStatusBar(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateItems(void);
   bool              CreateSeparateLine(const int line_number,const int x,const int y);
   //---
public:
   //--- Anzahl der Elemente
   int               ItemsTotal(void)                           const { return(::ArraySize(m_items)); }

   //--- Fügt ein Element mit spezifizierten Eigenschaften vor der Erzeugung der Statusbar hinzu
   void              AddItem(const int width);
  };

Wir werden nur die CStatusBar::CreateItems() Methode für das Erzeugen der Elemente der Statusbar im Detail besprechen. Alle anderen Methoden haben keine fundamentalen Unterschiede, gegenüber den zuvor schon beschriebenen Methoden.

Die Elemente werden so in dem Bereich des Hintergrundes gesetzt, dass sie nicht ihren Rahmen verdecken. Aus diesem Grunde fügen wir zunächst Ränder mit einem Pixel hinzu. Anschließen wird eine Überprüfung der Anzahl der Elemente durchgeführt. Wenn keine Elemente gesetzt wurden, wird eine entsprechende Nachricht ausgegeben und die Erzeugung des grafischen Interfaces wird abgebrochen. 

Wie legen fest, dass wenn die Breite des ersten Element ist nicht definiert ist, sie automatisch berechnet wird. Die Breite der Statusbar sollte der Breite des Formulars entsprechen, zu welchem die Statusbar hinzugefügt wird. (Zwei Pixel kleiner, damit sie sich innerhalb des Formulars befindet) Um die Breite des ersten Elementes ermitteln zu können, wird die Breite der anderen Elemente aufaddiert und dieser Wert von der Breite des Formulars abgezogen. 

Anschließend folgt die Schleife, in welcher die Elemente der Statusbar erzeugt werden und dann die Schleife für die Erzeugung der Trennlinien. Die Koordinaten für die Trennlinien werden in Relation zu den Koordinaten von jedem Element berechnen. Für das erste Element wird keine Trennlinie erzeugt. Aus diesem Grunde ist die Anzahl der Trennlinien immer um einen kleiner als die Anzahl der Elemente.  

//+------------------------------------------------------------------+
//| Erzeugt eine Liste mit Elementen für die Statusbar               |
//+------------------------------------------------------------------+
bool CStatusBar::CreateItems(void)
  {
   int l_w=0;
   int l_x=m_x+1;
   int l_y=m_y+1;
//--- Abfrage der Anzahl der Elemente
   int items_total=ItemsTotal();
//--- Falls es in dieser Gruppe keine Elemente gibt, benachrichtigen und abbrechen
   if(items_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called, "
              "if a group contains at least one item! Use the CStatusBar::AddItem() method");
      return(false);
     }
//--- Falls die Breite des ersten Element ist nicht definiert ist, dann...
   if(m_width[0]<1)
     {
      //--- ...Berechne sie im Verhältnis zu der gemeinsamen Breite der anderen Elemente
      for(int i=1; i<items_total; i++)
         l_w+=m_width[i];
      //---
      m_width[0]=m_wnd.XSize()-l_w-(items_total+2);
     }
//--- Erzeuge eine spezifizierte Anzahl von Elementen
   for(int i=0; i<items_total; i++)
     {
      //--- Den Objektnamen bilden
      string name=CElement::ProgramName()+"_statusbar_edit_"+string(i)+"__"+(string)CElement::Id();
      //--- X Koordinate
      l_x=(i>0)? l_x+m_width[i-1] : l_x;
      //--- Erzeugen eines Objektes
      if(!m_items[i].Create(m_chart_id,name,m_subwin,l_x,l_y,m_width[i],m_y_size-2))
         return(false);
      //--- Festlegen der Eigenschaften
      m_items[i].Description("");
      m_items[i].TextAlign(ALIGN_LEFT);
      m_items[i].Font(FONT);
      m_items[i].FontSize(FONT_SIZE);
      m_items[i].Color(m_label_color);
      m_items[i].BorderColor(m_area_color);
      m_items[i].BackColor(m_area_color);
      m_items[i].Corner(m_corner);
      m_items[i].Anchor(m_anchor);
      m_items[i].Selectable(false);
      m_items[i].Z_Order(m_zorder);
      m_items[i].ReadOnly(true);
      m_items[i].Tooltip("\n");
      //--- Ränder von den Kanten des Panels
      m_items[i].XGap(l_x-m_wnd.X());
      m_items[i].YGap(l_y-m_wnd.Y());
      //--- Koordinaten
      m_items[i].X(l_x);
      m_items[i].Y(l_y);
      //--- Größe
      m_items[i].XSize(m_width[i]);
      m_items[i].YSize(m_y_size-2);
      //--- Abspeichern des Objekt-Pointers
      CElement::AddToArray(m_items[i]);
     }
//--- Erzeugen der Trennlinien
   for(int i=1; i<items_total; i++)
     {
      //--- X Koordinate
      l_x=m_items[i].X();
      //--- Erzeugen einer Linie
      CreateSeparateLine(i,l_x,l_y+2);
     }
//---
   return(true);
  }

Wir benötigen noch eine Public-Methode für die Veränderung des Textes in jedem Element. Wie erzeugen diese Methode und nennen Sie CStatusBar::ValueToItem(). Als Parameter erhält sie die Nummer des Indexes und die Textzeile, die von dem Element angezeigt werden soll. 

class CStatusBar : public CElement
  {
public:
   //--- Setzen des Wertes über den angegebenen Index
   void              ValueToItem(const int index,const string value);
  };
//+------------------------------------------------------------------+
//| Setzen des Wertes über den angegebenen Index                     |
//+------------------------------------------------------------------+
void CStatusBar::ValueToItem(const int index,const string value)
  {
//--- Überprüfung der Überschreitung des ABCs
   int array_size=::ArraySize(m_items);
   if(array_size<1 || index<0 || index>=array_size)
      return;
//--- Setzen des übergebenen Textes
   m_items[index].Description(value);
  }

 


Test der Statusbar

Es ist nun alles bereit für einen ersten Test der Statusbar. Für den Test verwenden wir den ersten EA aus dem vorherigen Teil dieser Serie (dem dritten Teil). Behalten Sie das Hauptmenü und 5 Buttons vom Typ CIconButton Und entfernen Sie alle anderen Controls. die StatusBar.mqh Datei wurde bereits in die Bibliothek mit einbezogen und somit können die Instanzen und Methoden für das Erzeugen der Statusbar in der CProgram Klasse erzeugt werden.

Wir erzeugen eine Statusbar, die aus zwei Elementen besteht. Die Breite des ersten Elementes geben wir nicht an und somit wird diese automatisch berechnet. Nach der Erzeugung der Statusbar, legen Sie den Text «For Help, press F1» in dem ersten Element als Beispiel fest.

//+------------------------------------------------------------------+
//| Kasse für das erzeugende Anwendung                               |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Status bar
   CStatusBar        m_status_bar;
   //---
private:
   //--- Status bar
#define STATUSBAR1_GAP_X         (1)
#define STATUSBAR1_GAP_Y         (175)
   bool              CreateStatusBar(void);
  };
//+------------------------------------------------------------------+
//| Erzeugung des Trading-Panels                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//---Erzeugung der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Erzeugen der Statusbar
   if(!CreateStatusBar())
      return(false);
//--- Neuzeichnen auf dem Chart
   m_chart.Redraw();
   return(true);
  }
//+------------------------------------------------------------------+
//| Erzeugt die Statusbar                                            |
//+------------------------------------------------------------------+
bool CProgram::CreateStatusBar(void)
  {
#define STATUS_LABELS_TOTAL 2
//--- Übergabe des Panel Objektes
   m_status_bar.WindowPointer(m_window1);
//--- Koordinaten
   int x=m_window1.X()+STATUSBAR1_GAP_X;
   int y=m_window1.Y()+STATUSBAR1_GAP_Y;
//--- Breite
   int width[]={0,110};
//--- Festlegen der Eigenschaften vor der Erzeugung
   m_status_bar.YSize(24);
//--- Angabe der Anzahl der Abschnitte und deren Eigenschaften
   for(int i=0; i<STATUS_LABELS_TOTAL; i++)
      m_status_bar.AddItem(width[i]);
//--- Erzeugen eines Controls
   if(!m_status_bar.CreateStatusBar(m_chart_id,m_subwin,x,y))
      return(false);
//--- Den Text im ersten Element der Statusbar setzen
   m_status_bar.ValueToItem(0,"For Help, press F1");
//--- Das Objekt zu dem gemeinsamen Array von Objektgruppen hinzufügen
   CWndContainer::AddToElementsArray(0,m_status_bar);
   return(true);
  }

Das zweite Element der Statusbar soll die lokale Zeit anzeigen. Dafür fügen wir den nachfolgenden Code dem Timer der Anwendung hinzu. Das bedeutet, dass das Element alle 500 Millisekunden aktualisiert wird.

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();

//--- Aktualisieren des zweiten Elementes der Statusbar (alle 500 Millisekunden)
   static int count=0;
   if(count<500)
     {
      count+=TIMER_STEP_MSC;
      return;
     }
//---
   count=0;
   m_status_bar.ValueToItem(1,TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS));
   m_chart.Redraw();
  }

Wenn wir alles richtig gemacht haben, dann sollte das Ergebnis wie in dem nachfolgenden Screenshot gezeigt aussehen:

Abbildung  2. Test des Statusbar-Elementes

Abbildung 2. Test des Statusbar-Elementes

 

Wir haben die Entwicklung der Klasse für die Erzeugung des Statusbar-Elementes abgeschlossen. Sie finden die vollständigen Versionen in den Dateien die diesem Artikel beigefügt sind.  

 


Das Tooltip Element

Jetzt schreiben wir die Klasse für die Erzeugung von Tooltips. In dem vierten Kapitel des ersten Teils dieser Serie, wo wir die Funktionen für Formular-Buttons besprochen haben, haben wir erklärt, warum dieses Element eine separate Klasse benötigt. Kurz gesagt, Tooltips müssen mit einer nicht limitierte Anzahl von Symbolen umgehen können und die Option besitzen, bestimmten Wörter hervorzuheben. Um dieses implementieren zu können, verwenden wir eine Klasse für das Zeichnen von Elementen. Ein Beispiel dafür, wie Interface-Elemente gezeichnet werden können, haben wir zuvor schon gezeigt. Die CSeparateLine Klasse für das Erzeugen der Trennlinie, wurde in dem zweiten Kapitel des zweiten Teils dieser Serie geschrieben. Nun folgen wir dem gleichen Prinzip und erzeugen eine Klasse für Tooltips.

Abbildung 3. Ein Beispiel von einem Tooltip in Word.

Abbildung 3. Ein Beispiel von einem Tooltip in Word.

 

Erzeugen Sie die Tooltip.mqh Datei und beziehen Sie sie in der WndContainer.mqh Datei mit ein. Erzeugen sie dann in der neuen Datei eine Klasse mit den bereits bekannten Standardmethoden für alle Interface-Elemente. In dieser Klasse benötigen wir neben dem Pointer zu dem Formular einen Pointer zu dem Element zu welchen das Tooltip hinzugefügt wird und eine Methode für das Abspeichern dieses Pointers. 

//+------------------------------------------------------------------+
//|                                                      Tooltip.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Klasse für das Erzeugen des Tooltips                             |
//+------------------------------------------------------------------+
class CTooltip : public CElement
  {
private:
   //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist
   CWindow          *m_wnd;
   //--- Ein Pointer zu dem Element, zu welchem der Tooltip hinzugefügt wird
   CElement         *m_element;
   //---
public:
   //--- (1) Speichert den Formular-Pointer, (2) Speichert den Element-Pointer
   void              WindowPointer(CWindow &object)   { m_wnd=::GetPointer(object);     }
   void              ElementPointer(CElement &object) { m_element=::GetPointer(object); }
   //---
public:
   //--- Chart Eventhandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- 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);
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CTooltip::CTooltip(void)
  {
  }
//+------------------------------------------------------------------+
//| Destruktor                                                       |
//+------------------------------------------------------------------+
CTooltip::~CTooltip(void)
  {
  }

Die Eigenschaften, die von dem User vor der Erzeugung des Tooltips festgelegt werden können:

Andere Eigenschaften besitzen Standardwerte. Diese sind:

Das Array mit Zeilen kann Zeile für Zeile dem Tooltip vor seiner Erzeugung mit der Methode CTooltip::AddString() hinzugefügt werden. 

class CTooltip : public CElement
  {
private:
   //--- Eigenschaften:
   //    Kopfzeile
   string            m_header;
   //--- Array mit den Zeilen des Textes des Tooltips
   string            m_tooltip_lines[];
   //--- Der Wert des Alphakanals (Die Transparenz des Tooltips)
   uchar             m_alpha;
   //--- Farben von (1) Text, (2) der kopfzeile und (3) des Rahmens des Hintergrundes
   color             m_text_color;
   color             m_header_color;
   color             m_border_color;
   //--- Array mit Hintergrund-Farbverläufen
   color             m_array_color[];
   //---
public:
   //--- Tooltip Kopfzeile
   void              Header(const string text)        { m_header=text;                  }
   //--- Fügt dem Tooltip  eine Zeile hinzu
   void              AddString(const string text);
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CTooltip::CTooltip(void) : m_header(""),
                           m_alpha(0),
                           m_text_color(clrDimGray),
                           m_header_color(C'50,50,50'),
                           m_border_color(C'118,118,118'),
                           m_gradient_top_color(clrWhite),
                           m_gradient_bottom_color(C'208,208,235')
  {
//--- Abspeichern des namens der Elementklasse in der Basisklasse  
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Fügt eine Zeile hinzu                                            |
//+------------------------------------------------------------------+
void CTooltip::AddString(const string text)
  {
//--- Vergrößern des Arrays um ein Element
   int array_size=::ArraySize(m_tooltip_lines);
   ::ArrayResize(m_tooltip_lines,array_size+1);
//--- Abspeichern des Wertes des übergebenen Parameters
   m_tooltip_lines[array_size]=text;
  }

Ähnlich wie bei der Klasse für die Trennlinien, verwenden wir für die Erzeugung des Tooltips die CRectCanvas Klasse und zwei Methoden (public und private). 

class CTooltip : public CElement
  {
private:
   //--- Objekt für die Erzeugung des Tooltips
   CRectCanvas       m_canvas;
   //---
public:
   //--- Methode für die Erzeugung des Tooltips
   bool              CreateTooltip (const long chart_id,const int subwin);
   //---
private:
   //--- Erzeugt die Grundlage für das Tooltip
   bool              CreateCanvas(void);
  };

Zu der Überprüfung des Pointers zu dem Formular, zu welchem das Element hinzugefügt wird, überprüft die CTooltip::CreateTooltip() public Methode auch den Pointer zu dem Element, für welches dieses Tooltip bestimmt ist.. Falls es diesen Pointer gibt, dann werden die Koordinaten des Tooltips In Relation zu den Koordinaten des Elementes berechnet. In unserem Fall ist das einen Pixel unterhalb der unteren Grenze des Elementes.

//+------------------------------------------------------------------+
//| Erzeugt das Tooltip-Objekt                                       |
//+------------------------------------------------------------------+
bool CTooltip::CreateTooltip(const long chart_id,const int subwin)
  {
//--- Verlassen, wenn es keinen Pointer zu einer Form gibt
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating the tooltip, the class must be passed "
              "the form pointer: CTooltip::WindowPointer(CWindow &object).");
      return(false);
     }
//--- Abbrechen, falls es keinen Pointer zu einem Element gibt
   if(::CheckPointer(m_element)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating the tooltip, the class must be passed "
              "the element pointer: CTooltip::ElementPointer(CElement &object).");
      return(false);
     }
//--- Initialisierung der Pointer
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =m_element.X();
   m_y        =m_element.Y2()+1;
//--- Ränder von den Kanten
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- Erzeugt das Tooltip
   if(!CreateTooltip())
      return(false);
//---
   return(true);
  }

Anders wie bei den ähnliche Methoden in der CSeparateLine Klasse, werden in der CTooltip::CreateCanvas() private Methode für die Erzeugung der Arbeitsfläche, Die folgenden Aktionen ausgeführt anstelle des Zeichnens nach der Erzeugung eines Objektes und der Festlegung der Eigenschaften.

Am Ende der Methode wird das Objekt dem gemeinsamen Array der Elemente hinzugefügt. Nachfolgend wird der Programmcode dieser Methode gezeigt. 

//+------------------------------------------------------------------+
//| Erzeugt die Arbeitsfläche für das Zeichnen                       |
//+------------------------------------------------------------------+
bool CTooltip::CreateCanvas(void)
  {
//--- Den Objektnamen bilden
   string name=CElement::ProgramName()+"_help_tooltip_"+(string)CElement::Id();
//--- Erzeugt die Arbeitsfläche
   if(!m_canvas.CreateBitmapLabel(m_chart_id,m_subwin,name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE))
      return(false);
//--- Hinzufügen zum Chart
   if(!m_canvas.Attach(m_chart_id,name,m_subwin,1))
      return(false);
//--- Festlegen der Eigenschaften
   m_canvas.Background(false);
//--- Ränder von den Kanten
   m_canvas.XGap(m_x-m_wnd.X());
   m_canvas.YGap(m_y-m_wnd.Y());
//--- Festlegen der Größe des Arrays des Farbverlaufes für den Tooltip Hintergrund
   CElement::GradientColorsTotal(m_y_size);
   ::ArrayResize(m_array_color,m_y_size);
//--- Initialisierung des Arrays
   CElement::InitColorArray(m_gradient_top_color,m_gradient_bottom_color,m_array_color);
//--- Säubern der Arbeitsfläche
   m_canvas.Erase(::ColorToARGB(clrNONE,0));
   m_canvas.Update();
   m_alpha=0;
//--- Abspeichern des Objekt-Pointers
   CElement::AddToArray(m_canvas);
   return(true);
  }

Lassen Sie uns die Methoden für das Zeichnen eines Vertikalen Farbverlaufs und des Rahmens betrachten. Für den Farbverlauf gibt es bereits in dem Moment der Erzeugung der Arbeitsfläche ein initialisiertes Array mit den benötigten werten. Das ist der Grund warum wir lediglich Zeile für Zeile eine Linie mit der Farbe aus dem Array innerhalb einer Schleife zeichnen können, wobei die Anzahl der Durchläufe gleich der Höhe der Arbeitsfläche ist (Pixel). Für das Zeichnen des Rahmens, werden am Anfang der Methode Arrays mit den Koordinaten für jede Seite der Arbeitsfläche deklariert und initialisiert. Nachdem der Rahmen gezeichnet worden ist, runden wir die Ecken mit einem Pixel ab und dann fügen wir vier weitere Pixel in denvVier Ecken innerhalb der Arbeitsfläche hinzu. 

Beide Methoden haben lediglich einen Parameter, den Alphakanal. Das bedeutet, dass wir den Grad der Transparenz des Farbverlaufes und des Rahmens mit dem Aufruf der Methode angeben können. 

//+------------------------------------------------------------------+
//| Vertikaler Farbverlauf                                           |
//+------------------------------------------------------------------+
void CTooltip::VerticalGradient(const uchar alpha)
  {
//--- X Koordinate
   int x1=0;
   int x2=m_x_size;
//--- Zeichnen des Farbverlaufs
   for(int y=0; y<m_y_size; y++)
      m_canvas.Line(x1,y,x2,y,::ColorToARGB(m_array_color[y],alpha));
  }
//+------------------------------------------------------------------+
//| Rahmen                                                           |
//+------------------------------------------------------------------+
void CTooltip::Border(const uchar alpha)
  {
//--- Die Farbe des Rahmens
   color clr=m_border_color;
//--- Umrandung
   int x_size =m_canvas.X_Size()-1;
   int y_size =m_canvas.Y_Size()-1;
//--- Koordinaten: oben/rechts/unten/links
   int x1[4]; x1[0]=0;      x1[1]=x_size; x1[2]=0;      x1[3]=0;
   int y1[4]; y1[0]=0;      y1[1]=0;      y1[2]=y_size; y1[3]=0;
   int x2[4]; x2[0]=x_size; x2[1]=x_size; x2[2]=x_size; x2[3]=0;
   int y2[4]; y2[0]=0;      y2[1]=y_size; y2[2]=y_size; y2[3]=y_size;
//--- Zeichnen des Rahmens mit den spezifizierten Koordinaten
   for(int i=0; i<4; i++)
      m_canvas.Line(x1[i],y1[i],x2[i],y2[i],::ColorToARGB(clr,alpha));
//--- Abrunden der Ecken mit einem Pixel
   clr=clrBlack;
   m_canvas.PixelSet(0,0,::ColorToARGB(clr,0));
   m_canvas.PixelSet(0,m_y_size-1,::ColorToARGB(clr,0));
   m_canvas.PixelSet(m_x_size-1,0,::ColorToARGB(clr,0));
   m_canvas.PixelSet(m_x_size-1,m_y_size-1,::ColorToARGB(clr,0));
//--- Hinzufügen von Pixeln mit angegebenen Koordinaten
   clr=C'180,180,180';
   m_canvas.PixelSet(1,1,::ColorToARGB(clr,alpha));
   m_canvas.PixelSet(1,m_y_size-2,::ColorToARGB(clr,alpha));
   m_canvas.PixelSet(m_x_size-2,1,::ColorToARGB(clr,alpha));
   m_canvas.PixelSet(m_x_size-2,m_y_size-2,::ColorToARGB(clr,alpha));
  }

Lassen Sie uns festlegen, dass das Tooltip sofort auftaucht, wenn sich der Mauszeiger über einem Element befindet und wenn der Mauszeiger den Bereich des Elementes verlässt, das Tooltip ausfadet. 

Lassen Sie uns die CTooltip::ShowTooltip() Methode für das Anzeigen des Tooltips erzeugen. Zu Beginn der Methode wird eine Überprüfung des Wertes des m_alpha -Kanals durchgeführt. Falls der Wert des Alphakanals gleich oder größer als 255 ist, bedeutet das, dass das Programm bereits an dieser Stelle war und das Tooltip zu 100% sichtbar ist. Andernfalls müssen im nächsten Schritt die Koordinaten und die Ränder für die Kopfzeile des Tooltips gesetzt werden und der Farbverlauf und der Rahmen gezeichnet werden. Falls der Anwender keinen Text für die Kopfzeile angegeben hat, dann wird die Kopfzeile auch nicht gezeichnet. Wenn ein Text angegeben wurde, dann werden die Schriftparameter gesetzt und die Kopfzeile wird gezeichnet. 

Anschließend werden die Koordinaten für den Haupttext des Tooltips festgelegt, unter Berücksichtigung der Anwesenheit einer Kopfzeile. Anschließend werden die Parameter für den Haupttext gesetzt. Anders als die fette Schriftart der Kopfzeile (FW_BLACK), erhält die Schrift für den Haupttext keine hervorhebenden Merkmale (FW_THIN). Anschließend wird der Text über eine Schleife auf die Arbeitsfläche gebracht. Die Y Koordinate wird für jede Zeile (Iteration) neu eingestellt. Zum Schluss wird die Arbeitsfläche für die Darstellung der Veränderungen aktualisiert und eine Hinweis für ein komplett sichtbares Tooltip wird gesetzt. Nachfolgend wird der Programmcode dieser Methode gezeigt. 

//+------------------------------------------------------------------+
//| Zeigt den Tooltip                                                |
//+------------------------------------------------------------------+
void CTooltip::ShowTooltip(void)
  {
//--- Abbrechen, falls das Tooltip zu 100% sichtbar ist
   if(m_alpha>=255)
      return;
//--- Koordinaten und Ränder für die Kopfzeile
   int  x        =5;
   int  y        =5;
   int  y_offset =15;
//--- Zeichnen des Farbverlaufs
   VerticalGradient(255);
//--- Zeichnen des Rahmens
   Border(255);
//--- Zeichnen der Kopfzeile (falls angegeben)
   if(m_header!="")
     {
      //--- Setzen der Schrift-Parameter
      m_canvas.FontSet(FONT,-80,FW_BLACK);
      //--- Zeichnen des Textes der Kopfzeile
      m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color),TA_LEFT|TA_TOP);
     }
//--- Die Koordinaten für den Haupttext des Tooltips (Unter Berücksichtigung der Anwesenheit einer Kopfzeile)
   x=(m_header!="")? 15 : 5;
   y=(m_header!="")? 25 : 5;
//--- Setzen der Schrift-Parameter
   m_canvas.FontSet(FONT,-80,FW_THIN);
//--- Zeichnen des Haupttextes des Tooltips
   int lines_total=::ArraySize(m_tooltip_lines);
   for(int i=0; i<lines_total; i++)
     {
      m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color),TA_LEFT|TA_TOP);
      y=y+y_offset;
     }
//--- Aktualisieren der Arbeitsfläche
   m_canvas.Update();
//--- Hinweis auf einen komplett sichtbaren Tooltip
   m_alpha=255;
  }

Lassen Sie uns eine Methode für das Ausfaden des Tooltips schreiben und sie CTooltip::FadeOutTooltip() nennen. Zu Beginn dieses Verfahren wird eine rückwärts Prüfung für den Wert des Alphakanals durchgeführt. Das bedeutet, wenn es eine komplette Transparenz gibt, dann gibt es keinen Grund fortzufahren. Falls dieses nicht der Fall ist, dann wird die Arbeitsfläche in einer Schleife solange neu gezeichnet, bis die komplette Transparenz erreicht ist. Der vollständige Programmcode diese Methode wird nachfolgend gezeigt.

//+------------------------------------------------------------------+
//| Langsames Faden des Tooltips                                     |
//+------------------------------------------------------------------+
void CTooltip::FadeOutTooltip(void)
  {
//--- Abbrechen, wenn das Tooltip zu 100% versteckt ist
   if(m_alpha<1)
      return;
//--- Abstände für die Kopfzeile
   int y_offset=15;
//--- Schrittweite für die Transparenz
   uchar fadeout_step=7;
//--- Langsames Faden des Tooltips
   for(uchar a=m_alpha; a>=0; a-=fadeout_step)
     {
      //--- Wenn der nächste Schritt die Transparenz negativ macht, dann stoppen
      if(a-fadeout_step<0)
        {
         a=0;
         m_canvas.Erase(::ColorToARGB(clrNONE,0));
         m_canvas.Update();
         m_alpha=0;
         break;
        }
      //--- Koordinaten für die Kopfzeile
      int x =5;
      int y =5;
      //--- Zeichnen des Farbverlaufs und des Rahmens
      VerticalGradient(a);
      Border(a);
      //--- Zeichnen der Kopfzeile (falls angegeben)
      if(m_header!="")
        {
         //--- Setzen der Schrift-Parameter
         m_canvas.FontSet(FONT,-80,FW_BLACK);
         //--- Zeichnen des Textes der Kopfzeile
         m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color,a),TA_LEFT|TA_TOP);
        }
      //--- Die Koordinaten für den Haupttext des Tooltips (Unter Berücksichtigung der Anwesenheit einer Kopfzeile)
      x=(m_header!="")? 15 : 5;
      y=(m_header!="")? 25 : 5;
      //--- Setzen der Schrift-Parameter
      m_canvas.FontSet(FONT,-80,FW_THIN);
      //--- Zeichnen des Haupttextes des Tooltips
      int lines_total=::ArraySize(m_tooltip_lines);
      for(int i=0; i<lines_total; i++)
        {
         m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color,a),TA_LEFT|TA_TOP);
         y=y+y_offset;
        }
      //--- Aktualisieren der Arbeitsfläche
      m_canvas.Update();
     }
  }

Nun haben wir alles was dafür notwendig ist, um ein Tooltip zu erzeugen und zu zeichnen. Die Methoden müssen in den Eventhandler des Elementes aufgerufen werden. Um herauszufinden, ob auf dem Formular der Button für die Anzeige von Tooltips gedrückt wurde, erzeugen wir in der CWindow-Klasse die >CWindow::TooltipBmpState() Methode.

class CWindow : public CElement
  {
public:
   //--- Überprüfung des Anzeigemodus für das Tooltip
   bool              TooltipBmpState(void)                             const { return(m_button_tooltip.State());   }
  };

Falls sich jetzt der Fokus auf diesem Element befindet, wird der Tooltip angezeigt (unter der Voraussetzung dass der Tooltip-Display-Modus aktiviert ist.) Falls sich der Mauszeiger außerhalb des Bereiches des Elementes befindet oder das Formular gesperrt ist, dann wird der Tooltip wieder versteckt.

//+-----------------------------------------------------------------+
//| Chart Eventhandler                                              |
//+-----------------------------------------------------------------+
void CTooltip::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;
      //--- Abbrechen, falls Tooltip-Button auf dem Formular deaktiviert ist
      if(!m_wnd.TooltipBmpState())
         return;
      //--- Falls das Formular gesperrt ist
      if(m_wnd.IsLocked())
        {
         //--- Verstecke den Tooltip
         FadeOutTooltip();
         return;
        }
      //--- Falls sich der Fokus auf dem Element befindet
      if(m_element.MouseFocus())
         //--- Anzeigen des Tooltips
         ShowTooltip();
      //--- Falls es keinen Fokus gibt
      else
      //--- Verstecke den Tooltip
         FadeOutTooltip();
      //---
      return;
     }
  }

 


Test des Tooltip Elementes

Jetzt können wir es testen und ansehen wie es funktioniert. Es gibt zurzeit fünf Buttons auf dem Formular unseres Test EA. Lassen Sie uns einen Tooltip für jeden Button erstellen. Deklarieren Sie fünf Instanzen der CTooltip Klasse und fünf Methoden. Als Beispiel zeigen wir hier nur die Implementation für einen dieser Buttons.

class CProgram : public CWndEvents
  {
private:
   CTooltip          m_tooltip1;
   CTooltip          m_tooltip2;
   CTooltip          m_tooltip3;
   CTooltip          m_tooltip4;
   CTooltip          m_tooltip5;
   //---
private:
   bool              CreateTooltip1(void);
   bool              CreateTooltip2(void);
   bool              CreateTooltip3(void);
   bool              CreateTooltip4(void);
   bool              CreateTooltip5(void);
  };
//+------------------------------------------------------------------+
//| Erzeugt Tooltip 5                                                |
//+------------------------------------------------------------------+
bool CProgram::CreateTooltip5(void)
  {
#define TOOLTIP5_LINES_TOTAL 3
//--- Abspeichern des Fenster-Pointers
   m_tooltip5.WindowPointer(m_window1);
//--- Abspeichern des Element-Pointers
   m_tooltip5.ElementPointer(m_icon_button5);
//--- Array mit dem Text des Tooltips
   string text[]=
     {
      "Control \"Icon button\" (5).",
      "This is the second line of the tooltip.",
      "This is the third line of the tooltip."
     };
//--- Festlegen der Eigenschaften vor der Erzeugung
   m_tooltip5.Header("Icon Button 5");
   m_tooltip5.XSize(250);
   m_tooltip5.YSize(80);
//--- Hinzufügen von Text, Zeile für Zeile
   for(int i=0; i<TOOLTIP5_LINES_TOTAL; i++)
      m_tooltip5.AddString(text[i]);
//--- Erzeugen eines Controls
   if(!m_tooltip5.CreateTooltip(m_chart_id,m_subwin))
      return(false);
//--- Das Objekt zu dem gemeinsamen Array von Objektgruppen hinzufügen
   CWndContainer::AddToElementsArray(0,m_tooltip5);
   return(true);
  }

Die Erzeugung von Tooltips muss bei dem Verfahren zum Erstellen der grafischen Benutzeroberfläche zuletzt durchgeführt werden, sodass sie am Ende des Elementarrays in der Basis sind. Auf diese Weise sind die Tooltips immer oberhalb der anderen Objekte des Charts. 

//+------------------------------------------------------------------+
//| Erzeugung des Trading-Panels                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- Erzeugen des Formulars 1 für die Controls
//---Erzeugung der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Erzeugen der Statusbar
//--- Icon Buttons

//--- Tooltips
   if(!CreateTooltip1())
      return(false);
   if(!CreateTooltip2())
      return(false);
   if(!CreateTooltip3())
      return(false);
   if(!CreateTooltip4())
      return(false);
   if(!CreateTooltip5())
      return(false);
//--- Neuzeichnen auf dem Chart
   m_chart.Redraw();
   return(true);
  }

Um den Tooltip-Button in dem Moment seiner Erzeugung auf dem Formular anzeigen zu können, verwenden Sie die CWindow::UseTooltipsButton() Methode.

//+------------------------------------------------------------------+
//| Erzeugt das Formular 1 für die Controls                          |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow1(const string caption_text)
  {
//--- Hinzufügen des Pointers des Fensters zu dem Fenster-Array
   CWndContainer::AddWindow(m_window1);
//--- Koordinaten
   int x=1;
   int y=20;
//--- Eigenschaften
   m_window1.Movable(true);
   m_window1.XSize(251);
   m_window1.YSize(200);
   m_window1.UseTooltipsButton();
   m_window1.CaptionBgColor(clrCornflowerBlue);
   m_window1.CaptionBgColorHover(C'150,190,240');
//--- Erzeugen des Formulars
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   return(true);
  }

Sie sollten das gleiche Ergebnis, wie es in dem nachfolgenden Screenshot gezeigt ist, erhalten.

Abbildung 4. Tooltip-Test.

Abbildung 4. Tooltip-Test.

 

Es sieht gut aus und arbeitet einwandfrei! Jetzt benötigt dieses Elements ein privates Array in der Pointer-Basis. Wenn wir den Multi-Window-Modus entwickeln, wird sichtbar, in welchen Fällen wir ein solches Array benötigen.  

 


Privates Array für Tooltips

Gehen Sie zu der CWndContainer Klasse und erweitern Sie sie mit neuen Elementen. Der WindowElements Struktur muss ein privates Array für Tooltips hinzugefügt werden. Wir erzeugen zudem (1) die CWndContainer::TooltipsTotal() Methode für die Abfrage der Nummer des Tooltips und (2) die CWndContainer::AddTooltipElements() Methode um den Tooltip-Pointer dem privaten Array hinzuzufügen.

class CWndContainer
  {
protected:
   //--- Struktur der Element-Arrays
   struct WindowElements
     {
      //--- Tooltips
      CTooltip         *m_tooltips[];
     };
   //--- Array mit Arrays von Elementen für jedes Fenster
   WindowElements    m_wnd[];
   //---
public:
   //--- Nummern von Tooltips
   int               TooltipsTotal(const int window_index);
   //---
private:
   //--- Speichert die Pointer zu Tooltip-Elementen in der Basis
   bool              AddTooltipElements(const int window_index,CElement &object);
  };
//+--------------------------------------------------------------------------+
//| Gibt die Anzahl der Tooltips über den angegebenen Fenster-Index zurück   |
//+--------------------------------------------------------------------------+
int CWndContainer::TooltipsTotal(const int window_index)
  {
   if(window_index>=::ArraySize(m_wnd))
     {
      ::Print(PREVENTING_OUT_OF_RANGE);
      return(WRONG_VALUE);
     }
//---
   return(::ArraySize(m_wnd[window_index].m_tooltips));
  }
//+-----------------------------------------------------------------+
//| Speichert den Tooltip-Pointer in dem privaten Array             |
//+-----------------------------------------------------------------+
bool CWndContainer::AddTooltipElements(const int window_index,CElement &object)
  {
//--- Abbrechen, falls es kein Tooltip ist
   if(object.ClassName()!="CTooltip")
      return(false);
//--- Abfrage des Tooltip-Pointers
   CTooltip *t=::GetPointer(object);
//--- Hinzufügen des Pointers zu dem privaten Array
   AddToRefArray(t,m_wnd[window_index].m_tooltips);
   return(true);
  }

Die CWndContainer::AddTooltipElements() Methode muss in der CWndContainer::AddToElementsArray() public Methode aufgerufen werden, welche in der benutzerdefinierten Klasse für das Hinzufügen von Elementen zu der Basis verwendet wird. Eine abgekürzte Version wird in dem nachfolgenden Code gezeigt.

//+------------------------------------------------------------------+
//| Fügt einen Pointer zu dem Element Array hinzu                    |
//+------------------------------------------------------------------+
void CWndContainer::AddToElementsArray(const int window_index,CElement &object)
  {
//--- Falls die Basis keine Formulare für Controls enthält
//--- Falls es eine Anfrage für eine nicht existierende Form gibt
//--- Hinzufügen zu dem gemeinsamen Array von Elementen
//--- Hinzufügen von Element-Objekten zu dem gemeinsamen Array von Objekten
//--- Abspeichern der ID von dem letzten Element in allen Forms
//--- Erhöhen des Zählers für die Elemente

//--- Speichert die Pointer zu den Kontextmenü-Objekten in der Basis
//--- Speichert die Pointer zu den Hauptmenü-Objekten in der Basis
//--- Speichert die Pointer zu den Splitbutton-Objekten in der Basis 

//--- Speichert die Pointer zu den Tooltip-Objekten in der Basis
   if(AddTooltipElements(window_index,object))
      return;
  }

Die Entwicklung der Klasse für die Erzeugung von Tooltips ist nun abgeschlossen. Sie können eine vollständige Version am Ende dieses Artikels herunterladen. 

 


Schlussfolgerung

In diesem Artikel haben wir die Entwicklung einer Statusbar und des Tooltip-Elementes betrachtet. In dem nächsten Kapitel werden wir Multi-Fenster-Interfaces entwickeln und das Managementsystem für die Prioritäten der linken Maustaste besprechen.

Sie können das Material des ersten Teils dieser Serie über die angehängten Dateien 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 vierten Teils: