MetaTrader 5 herunterladen

Grafische Interfaces VIII: Das Datei-Navigator Control (Kapitel 3)

14 September 2016, 17:29
Anatoli Kazharski
0
317

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.

In den vorherigen Kapiteln des 8 Teils dieser Serie, haben wir unsere Bibliothek um mehrere Klassen für die Entwicklung von Mauszeigern (CPointer), Kalendern (CCalendar und CDropCalendar Klassen) und Baumansichten (CTreeItem und CTreeView Klassen). In diesem Artikel entwickeln wir das Thema des vorangegangenen Kapitels weiter, sowie auch den Dateien-Navigator. 

 


Das Datei-Navigator Control

Ein Datei Navigator ist eine Art Leitsystem, welches es einem erlaubt, die Elemente des Dateisystems auf eine hierarchische Art und Weise innerhalb eines grafischen Interfaces darzustellen. Zudem bietet es Zugriff auf jedes Element um verschiedene Aktionen, wie zum Beispiel das Öffnen einer Datei oder das Abspeichern von Daten in ein einer Datei, oder auch das Verschieben von Dateien zu ermöglichen.

Dieser Artikel befasst sich mit der ersten Version des Datei-Navigators. Er bietet den Benutzern die folgenden Möglichkeiten:

  • Navigieren der Terminal-Datei "sandbox" innerhalb des grafischen Interfaces einer MQL Anwendung.
  • Das Auffinden von Ordnern und Dateien in dem gemeinsamen Ordner und in dem Ordner des Terminals;
  • Abspeichern eines Pfades für den nachfolgenden Zugriff über auf einen Ordner oder eine Datei, die zuvor in dem Dateien Navigator ausgewählt wurde. 

Hinweis:

Das Arbeiten mit Dateien wird in mql5 aus Sicherheitsgründen sehr stark kontrolliert. Dateien, die mit der mql5 Sprache bearbeitet werden, befinden sich immer in der Datei "sandbox". Eine Datei wird immer in dem Verzeichnis des Terminals MQL5\Files (oder in testing_agent_directory\MQL5\Files für Tests) geöffnet. Falls das FILE_COMMON Flag angegeben wurde, wird die Datei in dem gemeinsamen Verzeichnis für alle Terminals \Terminal\Common\Files geöffnet.

 


Entwicklung der CFileNavigator Klasse

Zum augenblicklichen Stand der Entwicklung, haben wir innerhalb unserer Bibliothek bereits alle notwendigen Werkzeuge, die für die Entwicklung des Datei-Navigators notwendig sind. Das zuvor beschriebenen Baumansicht-Control ist die Basis für die Erzeugung unseres Datei-Navigators. Abgesehen von der Baumansicht mit dem Inhaltsbereich, benötigen wir noch eine Adressleiste, die den vollständigen Pfad, relativ zu einem aktuell ausgewählten Element in der Baumansicht, enthält.

Lassen Sie uns noch eine Auswahlmöglichkeit für die Ordner in dem Hauptverzeichnis anbieten. Zum Beispiel können wir nur den Zugriff auf ein Terminal "sandbox" Verzeichnis oder auch auf zwei erlauben. Dafür fügen wir die ENUM_FILE_NAVIGATOR_CONTENT Enumeration der Datei Enums.mqh hinzu:

  • FN_BOTH – Zeige beide Verzeichnisse.
  • FN_ONLY_MQL – Zeige nur das lokale Verzeichnis des Terminals.
  • FN_ONLY_COMMON – Zeige nur das gemeinsame Verzeichnis aller Terminals.
//+----------------------------------------------------------------
//| Enumeration des Inhalts des Datei-Navigators                        |
//+----------------------------------------------------------------
enum ENUM_FILE_NAVIGATOR_CONTENT
  {
   FN_BOTH        =0,
   FN_ONLY_MQL    =1,
   FN_ONLY_COMMON =2
  };

Erzeugen Sie die FileNavigator.mqh Datei mit der CFileNavigator Klasse und verbinden Sie sie mit der Bibliotheks-Engine(WndContainer.mqh file):

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

Die Standardelemente und Methoden der Bibliothek sollten in der CFileNavigator Klasse implementiert werden: 

//+----------------------------------------------------------------
//| Klasse für die Erzeugung des Datei-Navigators                                |
//+----------------------------------------------------------------
class CFileNavigator : public CElement
  {
private:
   //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist
   CWindow          *m_wnd;
   //---
public:
                     CFileNavigator(void);
                    ~CFileNavigator(void);
   //--- Abspeichern des pointers
   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) {}
  };

Lassen Sie uns nun die in der Bibliothek verfügbaren Eigenschaften Für die Konfiguration des Datei-Navigators auflisten.

  • Die Breite der Baumansicht
  • Die Breite des Inhalts-Bereichs
  • Die Hintergrundfarbe
  • Die Rahmenfarbe
  • Die Hintergrundfarbe der Adresszeile
  • Die Textfarbe der Adresszeile
  • Die Höhe der Adresszeile
  • Bilder für die Verzeichnisse und Dateien
  • Modus des Navigators (Zeige alles/Nur Verzeichnisse)
  • Datei-Navigator Inhalts-Modus (Gemeinsames Verzeichnis/Lokal/Alles)
//+----------------------------------------------------------------
//| Klasse für die Erzeugung des Datei-Navigators                                |
//+----------------------------------------------------------------
class CFileNavigator : public CElement
  {
private:
   //--- Breite der Baumansicht
   int               m_treeview_area_width;
   //--- Breite des Inhalts-Bereichs
   int               m_content_area_width;
   //--- Hintergrundfarbe und Hintergrunf-Rahmenfarbe
   color             m_area_color;
   color             m_area_border_color;
   //--- Hintergrundfarbe der Adresszeile
   color             m_address_bar_back_color;
   //--- Textfarbe der Adresszeile
   color             m_address_bar_text_color;
   //--- Höhe der Adresszeile
   int               m_address_bar_y_size;
   //--- Bilder für (1) Verzeichnisse und (2) Dateien
   string            m_file_icon;
   string            m_folder_icon;
   //--- Datei-Navigator Inhalts-Modus
   ENUM_FILE_NAVIGATOR_CONTENT m_navigator_content;
   //--- Priorität für die linke Maustaste
   int               m_zorder;
   //---
public:
   //--- (1) Navigator Modus (Zeige alles/Nur Verzeichnisse), (2) Navigator-Inhalt (Gemeinsames Verzeichnsid/Lokal/Alles)
   void              NavigatorMode(const ENUM_FILE_NAVIGATOR_MODE mode)       { m_treeview.NavigatorMode(mode);            }
   void              NavigatorContent(const ENUM_FILE_NAVIGATOR_CONTENT mode) { m_navigator_content=mode;                  }
   //--- (1) Höhe der Adresszeile, (2) Breite der Baumansicht und (3) der Inhaltsliste
   void              AddressBarYSize(const int y_size)                        { m_address_bar_y_size=y_size;               }
   void              TreeViewAreaWidth(const int x_size)                      { m_treeview_area_width=x_size;              }
   void              ContentAreaWidth(const int x_size)                       { m_content_area_width=x_size;               }
   //--- (1) Hintergrund und (2) Hintergrundrahmen Farbe
   void              AreaBackColor(const color clr)                           { m_area_color=clr;                          }
   void              AreaBorderColor(const color clr)                         { m_area_border_color=clr;                   }
   //--- Farbe des (1) Hintergrundes und (2) Des Textes der Adresszeile
   void              AddressBarBackColor(const color clr)                     { m_address_bar_back_color=clr;              }
   void              AddressBarTextColor(const color clr)                     { m_address_bar_text_color=clr;              }
   //--- Festlegen des Dateipfades zu den (1) Dateien und (2) Verzeichnissen
   void              FileIcon(const string file_path)                         { m_file_icon=file_path;                     }
   void              FolderIcon(const string file_path)                       { m_folder_icon=file_path;                   }
  };

Die Initialisierung der Variablen mit Standardwerten, wird in dem Konstruktor der Klasse durchgeführt (Sehen Sie sich dazu das nachfolgende Listing an). Die Bilder für die Dateien und Verzeichnisse werden mit der Bibliothek über resources verknüpft. Sie können im Anhang des Artikels heruntergeladen werden.

#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file.bmp"

//+----------------------------------------------------------------
//| Konstruktor                                                      |
//+----------------------------------------------------------------
CFileNavigator::CFileNavigator(void) : m_address_bar_y_size(20),
                                       m_treeview_area_width(300),
                                       m_content_area_width(0),
                                       m_navigator_content(FN_ONLY_MQL),
                                       m_area_border_color(clrLightGray),
                                       m_address_bar_back_color(clrWhiteSmoke),
                                       m_address_bar_text_color(clrBlack),
                                       m_file_icon("Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file.bmp"),
                                       m_folder_icon("Images\\EasyAndFastGUI\\Icons\\bmp16\\folder.bmp")
  {
//--- 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;
  }

Für die Erzeugung des Datei-Navigators, benötigen wir zwei private und ein öffentliche (public) Methoden. Das hierarchische System der Dateistruktur wird mit der CTreeView Klasse implementiert, welche wir in dem vorherigen Artikel beschrieben haben. Die Datei mit dieser Klasse muss in der FileNavigator.mqh Datei mit einbezogen werden.

//+----------------------------------------------------------------
//|                                                FileNavigator.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+----------------------------------------------------------------
#include "Element.mqh"
#include "Window.mqh"
#include "TreeView.mqh"
//+----------------------------------------------------------------
//| Klasse für die Erzeugung des Datei-Navigators                                |
//+----------------------------------------------------------------
class CFileNavigator : public CElement
  {
private:
   //--- Objekte für die Erzeugung des Elementes
   CRectCanvas       m_address_bar;
   CTreeView         m_treeview;
   //---
public:
   //--- Methoden für die Erzeugung des Datei-Navigators
   bool              CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateAddressBar(void);
   bool              CreateTreeView(void);
  };

Bevor wir mit der Beschreibung der Methoden für die Entwicklung des Datei-Navigators fortschreiten, lassen Sie uns zunächst die Anordnung der Dateistruktur des Terminals analysieren. 

 


Arrangieren der hierarischen Dateistruktur

Bevor wir den Datei-Navigator erzeugen können, müssen wir das Dateisystem des Terminals durchgehen und alle Parameter aller Elemente abspeichern. Alle diese Parameter haben wir im Detail in dem vorangegangenen Artikel besprochen. Daher zeigen wir hier nur eine Array-Liste für die Parameter, die für die Entwicklung der Baumansicht gespeichert werden muss.

  • Der generelle Index
  • Der generelle Index des vorangegangenen Knotenpunktes
  • Verzeichnis oder Dateiname
  • Lokaler Index
  • Nod Level
  • Lokaler Index des vorangegangenen Knotenpunktes
  • Gesamt Anzahl der Elemente in dem Verzeichnis
  • Anzahl der Verzeichnisse in einem Verzeichnis
  • Verzeichnis Flag
  • Status eines Elementes (minimiert/offen)

Für die Vorbereitung der Parameter, benötigen wir zwei Array-listen – main (g prefix) und hilfs (auxiliary) (l prefix):

class CFileNavigator : public CElement
  {
private:
   //--- Haupt Array für das Abspeichern der Daten
   int               m_g_list_index[];           // Genereller Index
   int               m_g_prev_node_list_index[]; // Genereller Index des vorangegangenen Knotenpunktes
   string            m_g_item_text[];            // Datei/Verzeichnisname
   int               m_g_item_index[];           // Lokaler Index
   int               m_g_node_level[];           // Knotenpunkt level
   int               m_g_prev_node_item_index[]; // Lokaler Index des vorherigen Knotenpunktes
   int               m_g_items_total[];          // Gesamtanzahl der Elemente in dem Verzeichnis
   int               m_g_folders_total[];        // Gesamtanzahl der Verzeichnisse in dem Verzeichnis
   bool              m_g_is_folder[];            // folder attribute
   bool              m_g_item_state[];           // Status des Elementes(minimiert/offen)
   //--- Hilftarray für die Datensammlung
   int               m_l_prev_node_list_index[];
   string            m_l_item_text[];
   string            m_l_path[];
   int               m_l_item_index[];
   int               m_l_item_total[];
   int               m_l_folders_total[];
  };

Wir benötigen eine Reihe von zusätzlichen Methoden um das Dateisystem des Terminals zu scannen, alle Daten zu sammeln und sie in einem Array zu speichern. Für das Arbeiten mit Hilfsarrays verwenden wir die Methode CFileNavigator::AuxiliaryArraysResize(). Dieses erlaubt es uns die Größe im Bezug auf den aktuell verwendeten Knotenpunkt zu ändern (Siehe nachfolgenden Code) Mit anderen Worten, die Größe des Arrays entspricht dem Level des aktuellen Knotenpunktes plus 1. Wenn der Level des aktuellen Knotenpunktes 0 ist, dann wird die Größe des Arrays auf 1 gesetzt, wenn der Level 1 ist, dann ist die Größe des Arrays 2, etc. Da sich die Knotenebene während der Datenerfassung erhöhen oder verringern kann, wird die Array-Größe entsprechend angepasst. Die gleiche Methode wird dazu verwendet, um den aktuellen Knoten des Array-Elementes zu initialisieren. 

class CFileNavigator : public CElement
  {
private:
   //--- Vergrößern des Arrays um ein Element
   void              AuxiliaryArraysResize(const int node_level);
  };
//+----------------------------------------------------------------
//| Erhöhe die Größe der zusätzlichen Arrays um ein Element            |
//+----------------------------------------------------------------
void CFileNavigator::AuxiliaryArraysResize(const int node_level)
  {
   int new_size=node_level+1;
   ::ArrayResize(m_l_prev_node_list_index,new_size);
   ::ArrayResize(m_l_item_text,new_size);
   ::ArrayResize(m_l_path,new_size);
   ::ArrayResize(m_l_item_index,new_size);
   ::ArrayResize(m_l_item_total,new_size);
   ::ArrayResize(m_l_folders_total,new_size);
//--- Initialisiere den letzten Wert
   m_l_prev_node_list_index[node_level] =0;
   m_l_item_text[node_level]            ="";
   m_l_path[node_level]                 ="";
   m_l_item_index[node_level]           =-1;
   m_l_item_total[node_level]           =0;
   m_l_folders_total[node_level]        =0;
  }

Die CFileNavigator::AddItem() Methode wird dazu verwendet, ein Element mit Parametern dem Haupt-Array hinzuzufügen. In diesem Fall wird die Größe der Arrays bei jedem Methodenaufruf um einen erhöht. Die Werte der übergebenen Argumente, werden in der letzten Zelle des Elementes gespeichert. 

class CFileNavigator : public CElement
  {
private:
   //--- Fügt ein Element dem Array hinzu
   void              AddItem(const int list_index,const string item_text,const int node_level,const int prev_node_item_index,
                             const int item_index,const int items_total,const int folders_total,const bool is_folder);
  };
//+----------------------------------------------------------------
//| Ein Element mit den angegebenen Parametern dem Array hinzufügen                 |
//+----------------------------------------------------------------
void CFileNavigator::AddItem(const int list_index,const string item_text,const int node_level,const int prev_node_item_index,
                             const int item_index,const int items_total,const int folders_total,const bool is_folder)
  {
//--- Vergrößern des Arrays um ein Element
   int array_size =::ArraySize(m_g_list_index);
   int new_size   =array_size+1;
   ::ArrayResize(m_g_prev_node_list_index,new_size);
   ::ArrayResize(m_g_list_index,new_size);
   ::ArrayResize(m_g_item_text,new_size);
   ::ArrayResize(m_g_item_index,new_size);
   ::ArrayResize(m_g_node_level,new_size);
   ::ArrayResize(m_g_prev_node_item_index,new_size);
   ::ArrayResize(m_g_items_total,new_size);
   ::ArrayResize(m_g_folders_total,new_size);
   ::ArrayResize(m_g_is_folder,new_size);
//--- Abspeichern des Wertes des übergebenen Parameters
   m_g_prev_node_list_index[array_size] =(node_level==0)? -1 : m_l_prev_node_list_index[node_level-1];
   m_g_list_index[array_size]           =list_index;
   m_g_item_text[array_size]            =item_text;
   m_g_item_index[array_size]           =item_index;
   m_g_node_level[array_size]           =node_level;
   m_g_prev_node_item_index[array_size] =prev_node_item_index;
   m_g_items_total[array_size]          =items_total;
   m_g_folders_total[array_size]        =folders_total;
   m_g_is_folder[array_size]            =is_folder;
  }

Beim Scannen des Dateisystems, müssen wir uns zu jedem erkannten Verzeichnis bewegen, um seinen Inhalt einzusehen. Dafür verwenden wir die CFileNavigator::IsFolder() Methode. Diese erkennt, ob es sich bei dem aktuellen Element um ein Verzeichnis oder eine Datei handelt. Der Elementname des Dateisystem sollte als einziger Parameter an Sie übergeben werden. Falls in dem Namen ein "\" Zeichen erkannt wurde, bedeutet das, dass die Methode, true zurück gibt. Falls es sich um eine Datei handelt, gibt die Methode false zurück. 

Die Systemfunktion FileIsExist() bietet eine weitere Möglichkeit zu überprüfen, ob es sich bei dem Element um eine Datei handelt. Die Funktion gibt true zurück, falls es sich bei dem übergebenen Namen des Elementes der letzten Suche um eine Datei handelt. Falls es sich um ein Verzeichnis handelt, dann generiert die Funktion den ERR_FILE_IS_DIRECTORY Fehler.  

class CFileNavigator : public CElement
  {
private:
   //--- Überprüfen, ob ein Datei- oder Verzeichnisname übergeben wurde
   bool              IsFolder(const string file_name);
  };
//+----------------------------------------------------------------
//| Überprüfen, ob ein Datei- oder Verzeichnisname übergeben wurde                   |
//+----------------------------------------------------------------
bool CFileNavigator::IsFolder(const string file_name)
  {
//--- Falls der Name die Zeichen "\\" enthält, dann handelt es sich um ein Verzeichnis
   if(::StringFind(file_name,"\\",0)>-1)
      return(true);
//---
   return(false);
  }

Um eine Baumansicht bilden zu können, müssen wir die Anzahl der Elemente in diesem Element angeben, sowie auch die Anzahl der Elemente, die Verzeichnisse darstellen. Daher müssen wir die entsprechenden Methoden für die Parameterwerte definieren. Die Methoden sind CFileNavigator::ItemsTotal() und CFileNavigator::FoldersTotal(). Sie sind sehr ähnlich, nur die zweite Methode erhöht ihren Zähler nur, falls bei der Überprüfung des Elementes ein Verzeichnis gefunden wurde. Beiden Methoden werden zwei Parameter übergeben: (1) Pfad und (2) Suchbereich. Der Suchbereich ist entweder das gemeinsame Verzeichnis von allen Terminals oder das lokale Verzeichnis eines Terminals. Als nächstes wird die Systemfunktion FileFindFirst() verwendet um ein search handle zu dem angegebenen Pfad zu bekommen, sowie den Namen des ersten Elementes (falls gefunden). Das valid handle und der Objektname liefern den Hinweis darauf, dass das erste Element gefunden worden ist. 

Als nächstes wird mit der Systemfunktion FileFindNext() in einer Schleife versucht, auf alle weiteren Elemente in dem selben Verzeichnis zuzugreifen. Das zuvor erhaltene handle wird als Schlüssel für das Verzeichnis verwendet. Falls ein Element gefunden wurde, gibt die Funktion true zurück und der Zähler wird erhöht.. Die Suche wird gestoppt, sobald die Funktion false zurück gibt. Am Ende beide Methoden sollte das Search handle geschlossen werden. Hierfür wird die Systemfunktion FileFindClose() verwendet. 

class CFileNavigator : public CElement
  {
private:
   //--- Gibt die Anzahl der (1) Elemente und (2) Verzeichnisse in dem angegebenen Verzeichnis zurück
   int               ItemsTotal(const string search_path,const int mode);
   int               FoldersTotal(const string search_path,const int mode);
  };
//+----------------------------------------------------------------
//| Zählt die Anzahl der Dateien in dem aktuellen Verzeichnis               |
//+----------------------------------------------------------------
int CFileNavigator::ItemsTotal(const string search_path,const int search_area)
  {
   int    counter       =0;              // item counter 
   string file_name     ="";             // file name
   long   search_handle =INVALID_HANDLE; // search handle
//--- Abfrage der ersten Datei in dem aktuellen Verzeichnis
   search_handle=::FileFindFirst(search_path,file_name,search_area);
//--- Falls das Verzeichnis nicht leer ist
   if(search_handle!=INVALID_HANDLE && file_name!="")
     {
      //--- Zähle die Anzahl der Objekte in dem aktuellen Verzeichnis
      counter++;
      while(::FileFindNext(search_handle,file_name))
         counter++;
     }
//--- Schließe das Search-handle
   ::FileFindClose(search_handle);
   return(counter);
  }
//+----------------------------------------------------------------
//| Zähle die Anzahl der Verzeichnisse in dem aktuellen Verzeichnis             |
//+----------------------------------------------------------------
int CFileNavigator::FoldersTotal(const string search_path,const int search_area)
  {
   int    counter       =0;              // item counter 
   string file_name     ="";             // file name
   long   search_handle =INVALID_HANDLE; // search handle
//--- Abfrage der ersten Datei in dem aktuellen Verzeichnis
   search_handle=::FileFindFirst(search_path,file_name,search_area);
//--- Falls nicht leer, dann zähle die Anzahl der Objekte in dem aktuellen Verzeichnis
   if(search_handle!=INVALID_HANDLE && file_name!="")
     {
      //--- Falls es sich um ein Verzeichnis handelt, dann erhöhe den Zähler
      if(IsFolder(file_name))
         counter++;
      //--- Die Liste weiter durchgehen und die Anzahl der Verzeichnisse zähle
      while(::FileFindNext(search_handle,file_name))
        {
         if(IsFolder(file_name))
            counter++;
        }
     }
//--- Schließe das Search-handle
   ::FileFindClose(search_handle);
   return(counter);
  }

Für das Sammeln der Parameter der Elemente des Dateisystems, müssen wir den lokalen Index des vorherigen Knotens abfragen. Hierfür verwenden wir die CFileNavigator::PrevNodeItemIndex() Methode. (1) Der aktuelle Element Index in dem Hauptverzeichnis und (2) Der aktuelle Knotenpunkt-level sollten als Parameter übergeben werden. Die Werte dieser beiden Argumente, werden durch die Zähler der externen Schleife der Hauptmethode geliefert, innerhalb welcher diese Methode aufgerufen worden ist. 

class CFileNavigator : public CElement
  {
private:
   //--- Rückgabe des lokalen Index des vorangegangenen Knotens relativ zu den übergebenen Parametern
   int               PrevNodeItemIndex(const int root_index,const int node_level);
  };
//+----------------------------------------------------------------
//| Rückgabe des lokalen Index des vorangegangenen                       |
//| Knotens relativ zu den übergebenen Parametern           |
//+----------------------------------------------------------------
int CFileNavigator::PrevNodeItemIndex(const int root_index,const int node_level)
  {
   int prev_node_item_index=0;
//--- Falls es sich nicht um das Hauptverzeichnis handelt
   if(node_level>1)
      prev_node_item_index=m_l_item_index[node_level-1];
   else
     {
      //--- Falls es sich nicht um das erste Element in der Liste handelt
      if(root_index>0)
         prev_node_item_index=m_l_item_index[node_level-1];
     }
//--- Gib den lokalen Index des vorherigen Knotens zurück
   return(prev_node_item_index);
  }

Jedes Mal wenn ein Verzeichnis gefunden wird, wird dahin gewechselt (Zum Beispiel beim nächsten Knoten-Level). Hierfür wird die CFileNavigator::ToNextNode() Methode verwendet. Einige Parameter werden als Link übergeben um eine Veränderung deren Werte zu ermöglichen

Zu Beginn der Methode wird der Pfad des Verzeichnisses gebildet. Anschließend werden die Element-Parameter in dem Hilfsarray über den aktuellen Knotenpunkt Level Index abgespeichert. Anschließend müssen wir den vorangegangenen Knoten-Element-Index abfragen und das Element mit den angegebenen Parametern dem generellen Arreay hinzufügen. Mit anderen Worten, dieses ist genau die Stelle, wo das Verzeichnis, in welchen die uns gerade befinden, den Arrays mit den vorbereiteten Parametern hinzugefügt wird.

Anschließend wird der Knoten Zähler erhöht und die Größe des Hilfsarrays wird relativ dazu korrigiert. Als nächstes sollten wir ein paar Parameter des nächsten Knoten-Levels speichern: (1) Pfad, (2) Anzahl der Elemente und (3) Anzahl der Verzeichnisse. Am Ende der Methode wird (1) der Zähler der lokalen Indices zurückgesetzt und (2) das aktuelle Search-handle geschlossen.  

class CFileNavigator : public CElement
  {
private:
   //--- Gehe zum nächsten Knoten
   void              ToNextNode(const int root_index,int list_index,int &node_level,
                                int &item_index,long &handle,const string item_text,const int search_area);
  };
//+----------------------------------------------------------------
//| Gehe zum nächsten Knoten                                              |
//+----------------------------------------------------------------
void CFileNavigator::ToNextNode(const int root_index,int list_index,int &node_level,
                                int &item_index,long &handle,const string item_text,const int search_area)
  {
//--- Suchfilter (* - Überprüfe alle Dateien/Verzeichnisse)
   string filter="*";
//--- Erzeuge den Pfad
   string search_path=m_l_path[node_level]+item_text+filter;
//--- Abfrage und Abspeichern von Daten
   m_l_item_total[node_level]           =ItemsTotal(search_path,search_area);
   m_l_folders_total[node_level]        =FoldersTotal(search_path,search_area);
   m_l_item_text[node_level]            =item_text;
   m_l_item_index[node_level]           =item_index;
   m_l_prev_node_list_index[node_level] =list_index;
//--- Abfrage des Index des vorherigen Knoten-Elementes
   int prev_node_item_index=PrevNodeItemIndex(root_index,node_level);
//--- Füge das Element mit dem angegebenen Daten dem generellen Array hinzu
   AddItem(list_index,item_text,node_level,prev_node_item_index,
           item_index,m_l_item_total[node_level],m_l_folders_total[node_level],true);
//--- Erhöhe den Knoten-Zähler
   node_level++;
//--- Vergrößern des Arrays um ein Element
   AuxiliaryArraysResize(node_level);
//--- Abfrage und Abspeichern von Daten
   m_l_path[node_level]          =m_l_path[node_level-1]+item_text;
   m_l_item_total[node_level]    =ItemsTotal(m_l_path[node_level]+filter,search_area);
   m_l_folders_total[node_level] =FoldersTotal(m_l_path[node_level]+item_text+filter,search_area);
//--- Setze den Zähler für die lokalen Indizes zurück
   item_index=0;
//--- Schließe das Search-handle
   ::FileFindClose(handle);
  }

Lassen Sie uns nun die Hauptschleife betrachten, in welcher die wichtigsten Aktionen durchgeführt werden. Für eine bessere Übersichtlichkeit, wird diese Schleife in einer separaten Methode ausgeführt CFileNavigator::FileSystemScan(). Das Auslesen des Dateisystems des Terminals und das Abspeichern der Parameter der erkannten Elemente wird in dieser Methode durchgeführt. Diese Arrays werden später für die Erzeugung der Baumansicht verwendet. Diese Schleife arbeitet so lange, bis der Algorithmus das Ende der Liste erreicht hat oder das Programm von dem Chart entfernt wird. 

Der Algorithmus arbeitet auf die folgende Weise: Zu Beginn wird eine Überprüfung des Anfangs der Liste (Das erste Element der Liste) des aktuellen Verzeichnisses durchgeführt. Falls das geprüfte Element neu ist, erhalten wir das Handle und den Namen des ersten Elementes welches wir in dem Dateisystem ermittelt haben und speichern die Anzahl der Elemente und die Verzeichnisse in dem aktuellen Knoten Level des Hilfs-Arrays. 

Falls es sich hierbei nicht um den ersten Index handelt, dann wird die Sequenz des lokalen Index in dem aktuellen Knoten überprüft. Falls der Index des Knotens bereits existiert, dann wird der Zähler der lokalen Indizes erhöht und es wird zu dem nächsten Element in dem nächsten Verzeihnis übergegangen.

Falls der Algorithmus den nächsten Abschnitt erreicht, überprüfen wir, ob wir die Spanne der Elemente in der Liste des Haupt-Knotenpunktes überschritten haben. Falls dieses der Fall ist, dann wird die Schleife gestoppt, was auch bedeutet, dass das Search-handle geschlossen wird und das Programm die Methode verlässt. Falls wir aber ein Ende einer anderen Knotenliste erreicht haben als das des Haupt-Knotens, dann müssen wir mit den nächsten Level fortfahren. Hier wird (1) der Knoten Zähler um einen Level zurück gezählt, (2) der Zähler für die lokalen Indizes zurückgesetzt, (3) das Search-handle geschlossen (4) und der Übergang zum nächsten Durchlauf wird gestartet.

Falls nicht eine Bedingung in der vorherigen if-else Kostruktion zutrifft, überprüfe, ob es sich bei dem aktuellen Element des Dateisystems um ein Verzeichnis handelt. Falls ja, dann wird die zuvor beschriebene CFileNavigator::ToNextNode() Methode dazu verwendet, um zum nächsten Level zu gehen. Anschließend wird der Zähler für die gesamten Indizes erhöht und das Kommando für das Fortfahren mit dem nächsten Durchlauf wird aktiviert. 

Falls es sich bei dem Element des Dateisystems um eine Datei handelt, dann werden wir zunächst den lokalen Index des vorangegangenen Knotens abfragen. Anschließend fügen wir dieses Element mit den angegebenen Parametern dem generellen Array hinzu. Erhöhung des totalen und lokalen Zählers der Indizes. Als letzte Operation innerhalb der Schleife wird zu dem Dateisystem des nächsten Terminals gewechselt. Anschließend startet der nächste Durchlauf und der Algorithmus geht wieder alle oben aufgeführten Schritte durch. 

class CFileNavigator : public CElement
  {
private:
   //--- Lese das Dateisystem und schreibe die Parameter in die Arrays
   void              FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area);
  };
//+----------------------------------------------------------------
//| Lese das Dateisystem und schreibe die Parameter in                 |
//| die Arrays                                                        |
//+----------------------------------------------------------------
void CFileNavigator::FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area)
  {
   long   search_handle =INVALID_HANDLE; // Folder/file search handle
   string file_name     ="";             // Name of the found item (file/folder)
   string filter        ="*";            // Search filter (* - check all files/folders)
//--- Scanne die Verzeichnisse und speichere Daten in die Arrays
   while(!::IsStopped())
     {
      //--- Falls es sich um den Anfang des Verzeichnisses handelt
      if(item_index==0)
        {
         //--- Der Pfad in welchem nach allen Elementen gesucht werden soll
         string search_path=m_l_path[node_level]+filter;
         //--- Frage das Handle und den Namen der ersten Datei ab
         search_handle=::FileFindFirst(search_path,file_name,search_area);
         //--- Abfrage der Anzahl der Dateien und Verzeichnisse in dem angegebenen Verzeichnis
         m_l_item_total[node_level]    =ItemsTotal(search_path,search_area);
         m_l_folders_total[node_level] =FoldersTotal(search_path,search_area);
        }
      //--- Falls der Index von diesem Knoten bereits verwendet wurde, dann gehe zu der nächsten Datei
      if(m_l_item_index[node_level]>-1 && item_index<=m_l_item_index[node_level])
        {
         //--- Erhöhe den Zähler der lokalen Indizes
         item_index++;
         //--- Gehe zum nächsten Element
         ::FileFindNext(search_handle,file_name);
         continue;
        }
      //--- Falls das Ende der Liste im Hauptverzeichnis erreicht wurde, dann beende die Schleife
      if(node_level==1 && item_index>=m_l_item_total[node_level])
         break;
      //--- Falls ein Ende der Liste innerhalb eines anderen Knoten erreicht wurde
      else if(item_index>=m_l_item_total[node_level])
        {
         //--- Setze den Zähler für die Knoten um einen Level zurück
         node_level--;
         //--- Setze den Zähler für die lokalen Indizes zurück
         item_index=0;
         //--- Schließe das Search-handle
         ::FileFindClose(search_handle);
         continue;
        }
      //--- Falls es sich um ein Verzeichnis handelt
      if(IsFolder(file_name))
        {
         //--- Gehe zum nächsten Knoten
         ToNextNode(root_index,list_index,node_level,item_index,search_handle,file_name,search_area);
         //--- Erhöhe den Zähler für die generellen Indizes und starte einen neuen Durchlauf
         list_index++;
         continue;
        }
      //--- Abfrage des lokalen Index des vorangegangenen Knotens
      int prev_node_item_index=PrevNodeItemIndex(root_index,node_level);
      //--- Füge das Element mit dem angegebenen Daten dem generellen Array hinzu
      AddItem(list_index,file_name,node_level,prev_node_item_index,item_index,0,0,false);
      //--- Erhöhe den Zähler der generellen Indizes
      list_index++;
      //--- Erhöhe den Zähler der lokalen Indizes
      item_index++;
      //--- Gehe zu dem nächsten Element
      ::FileFindNext(search_handle,file_name);
     }
//--- Schließe das Search-handle
   ::FileFindClose(search_handle);
  }

Lassen Sie uns nun die Hauptmethode betrachten CFileNavigator::FillArraysData(), in welcher alle oben beschriebenen Methoden aufgerufen werden.

Zunächst wird die Sequenz des gemeinsamen und des lokalen Verzeichnisses des Terminals gesetzt. Die Sequenz hängt von dem Modus (ENUM_FILE_NAVIGATOR_CONTENT) ab, der in den Eigenschaften des Datei-Navigators angegeben wird. Standardmäßig ist es so eingestellt, dass das gemeinsames Verzeichnis an oberster Stelle steht und das lokale Verzeichnis an zweiter Stelle. Dieses funktioniert aber nur, wenn der Modus FN_BOTH ("Zeige den Inhalt von Weiden Verzeichnissen") gesetzt ist. Falls der Modus "Zeige den Inhalt von von einem Verzeichnis" gesetzt ist, Dann wird der Anfang (begin) und das Ende (end) der Schleife entsprechend gesetzt.

Nachdem der Suchbereich festgelegt wurde, werden die nachfolgenden Schritte der Reihe nach ausgeführt.

  • Der Zähler für die lokalen Indizes wird zurückgesetzt.
  • Die Größe des Hilfsarrays wird relativ zu dem aktuellen Knoten-Level verändert.
  • Die Anzahl der Elemente und Verzeichnisse in dem aktuellen Verzeichnis wird in dem ersten Index des gleichen Arrays gespeichert.
  • Das Element mit den angegebenen Parametern wird dem Haupt-Array hinzugefügt. Da hier das Stammverzeichnis verwendet wird, kann der Name des aktuellen Elementes von einem der zwei Verzeichnisse stammen: Common\\Files\\ oder MQL5\\Files\\
  • Die generellen Indizes und der Zähler für die Knoten-Level werden erhöht.
  • Die Größe des Hilfsarrays wird wieder relativ zu dem aktuellen Knoten-Level verändert.
  • Wenn sich der aktuelle Suchbereich in dem lokalen Ordner des Terminals befindet, dann werden die Werte für die ersten Indizes der Hilfsgruppen korrigiert: (1) Lokale Indices (2) der generelle Index des vorangegangenen Knotens.

Zum Schluss wird die CFileNavigator::FileSystemScan() Methode für das Auslesen des Dateisystems in dem angegebenen Suchbereich und das Speichern der Parameter der darin befindlichen Elemente in die Hauptarrays für das spätere bildendes der Baumansicht, aufgerufen.  

class CFileNavigator : public CElement
  {
private:
   //--- Füllt die Arrays mit den Parametern der Elemente des Terminal-Dateisystems
   void              FillArraysData(void);
  };
//+----------------------------------------------------------------
//| Füllt die Arrays mit den Parametern der Elemente des Terminal-Dateisystems         |
//+----------------------------------------------------------------
void CFileNavigator::FillArraysData(void)
  {
//--- Zähler für die (1) generellen Indexe, (2) Knoten-Levels, (3) lokale indexe
   int list_index =0;
   int node_level =0;
   int item_index =0;
//--- Falls beide Verzeichnisse angezeigt werden sollen (Gemeinsame(0)/Lokale(1))
   int begin=0,end=1;
//--- Wenn nur der Inhalt des lokalen Verzeichnis angezeigt werden soll
   if(m_navigator_content==FN_ONLY_MQL)
      begin=1;
//--- Wenn nur der Inhalt des gemeinsamen Verzeichnisses angezeigt werden soll
   else if(m_navigator_content==FN_ONLY_COMMON)
      begin=end=0;
//--- Durchlaufen der angegebenen Verzeichnisse
   for(int root_index=begin; root_index<=end; root_index++)
     {
      //--- Bestimmen des Verzeichnisses für das Scannen der Dateistruktur
      int search_area=(root_index>0)? 0 : FILE_COMMON;
      //--- Zurücksetzen des Fehlers für den lokalen Index
      item_index=0;
      //--- Erhöhen der Arraygröße um ein Element (Relativ zu dem Knoten-Level)
      AuxiliaryArraysResize(node_level);
      //--- Abfrage der Anzahl der Dateien und Verzeichnisse in dem angegebenen Verzeichnis (* - Scanne alle Dateien/Verzeichnisse)
      string search_path   =m_l_path[0]+"*";
      m_l_item_total[0]    =ItemsTotal(search_path,search_area);
      m_l_folders_total[0] =FoldersTotal(search_path,search_area);
      //--- Füge das Element mit dem Namen des Hauptverzeichnis an die oberste Stelle der Liste
      string item_text=(root_index>0)? "MQL5\\Files\\" : "Common\\Files\\";
      AddItem(list_index,item_text,0,0,root_index,m_l_item_total[0],m_l_folders_total[0],true);
      //--- Erhöhe die Zähler für die generellen Indizes und Knoten-Levels
      list_index++;
      node_level++;
      //--- Erhöhen der Arraygröße um ein Element (Relativ zu dem Knoten-Level)
      AuxiliaryArraysResize(node_level);
      //--- Initialisiere die ersten Elemente des Verzeichnisses des lokalen Ordners des Terminals
      if(root_index>0)
        {
         m_l_item_index[0]           =root_index;
         m_l_prev_node_list_index[0] =list_index-1;
        }
      //--- Scanne die Verzeichnisse und speichere Daten in die Arrays
      FileSystemScan(root_index,list_index,node_level,item_index,search_area);
     }
  }

 


Methoden für die Erzeugung des Controls

Die CFileNavigator::FillArraysData() Methode wird nur einmal vor der Erzeugung des Datei-Navigator-Controls aufgerufen. Tatsächlich kann dieses ein Benutzer der Bibliothek nicht wissen. Dieses ist nur notwendig, um ein paar gemeinsame Eigenschaften, die das Aussehen und den Inhalt des Datei Navigators betreffen, festzulegen.

//+----------------------------------------------------------------
//| Erzeugung des Datei-Navigators                                            |
//+----------------------------------------------------------------
bool CFileNavigator::CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y)
  {
//--- Verlassen, wenn es keinen Pointer zu einer Form gibt
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating the file navigator, "
              "the pointer to the form should be passed to it: CFileNavigator::WindowPointer(CWindow &object).");
      return(false);
     }
//--- Scanne die Verzeichnisse und speichere Daten in die Arrays
   FillArraysData();
//--- Initialisierung der Variablen
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =x;
   m_y        =y;
//--- Ränder von den Kanten
   CElement::XGap(CElement::X()-m_wnd.X());
   CElement::YGap(CElement::Y()-m_wnd.Y());
//--- Erzeugung des Elementes
   if(!CreateAddressBar())
      return(false);
   if(!CreateTreeView())
      return(false);
//--- Verstecke das Element, falls es sich um ein Dialogfenster handelt oder es minimiert ist
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

Die Erzeugung der Adresszeile ist der erste Schritt, wenn man den Datei-Navigator entwickeln möchte. Dieses ist ein einziges Objekt vom Typ OBJ_BITMAP_LABEL. Sein Inhalt wird in vollem Umfang gezeichnet. Zuvor haben wir schon Beispiele betrachtet, wie Controls auf einem Canvas erzeugt werden. Daher werden wir hier nur noch die feinen Unterschiede betrachten. 

Für das Zeichnen der Adresszeile benötigen wir zwei Methoden:

  • Die CFileNavigator::Border() Methode wird für das Zeichnen des Rahmens der Adresszeile verwendet.
  • CFileNavigator::UpdateAddressBar() ist die Hauptmethode für das Zeichnen und Darstellen der letzten Änderungen und des ausgewählten Verzeichnisses in der Baumansicht.

Lassen Sie uns hier nur die CFileNavigator::UpdateAddressBar() Methode betrachten, da wir den Rahmen bereits bei der Entwicklung anderer Controls besprochen haben. Zum Beispiel in dem Artikel Grafische Interfaces IV: Informierende Interface Elemente (Kapitel 1)

Wir haben uns auch bereits schon angeschaut, dass ein User vor der Erzeugung des Datei-Navigators, die Hintergrundfarbe und die Größe über die Y Achse angeben kann. Der Text in dem Canvas wird mit einem Abstand von 5 Pixel vom linken Rand positioniert. Die Ausrichtung an der Y-Achse wird zentriert. Da wir die Größe entlang der Y Achse haben, brauchen wir lediglich die Höhe der Adresszeile durch 2 dividieren, um die Y-Koordinate zu erhalten. In order to call the TextOut() method to draw a text on the canvas, we also need to pass the flags for defining the anchor type to the left and at the center

Nach der ersten Installation des Datei-Navigators, ist der Pfad noch nicht initialisiert und die Variable m_current_path enthält noch einen leeren String. Da es sehr viele Elemente in dem Dateisystem geben kann, kann die Erzeugung der Arrays und der Baumansicht einige Zeit in Anspruch nehmen. Da die Adresszeile zuerst erzeugt wird, kann diese eine Nachricht beinhalten, dass der Anwender ein wenig warten soll. In unserem Beispiel sieht es wie folgt aus: "Loading. Please wait...". 

Am Ende der Methode wird das Canvas aktualisiert damit die letzten Veränderungen sichtbar werden. 

class CFileNavigator : public CElement
  {
private:
   //--- Zeichnet den Rahmen der Adresszeile
   void              Border(void);
   //--- Zeigt den aktuellen Pfad in der Adresszeile an
   void              UpdateAddressBar(void);
  };
//+----------------------------------------------------------------
//| Zeigt den aktuellen Pfad in der Adresszeile an                     |
//+----------------------------------------------------------------
void CFileNavigator::UpdateAddressBar(void)
  {
//--- Koordinaten
   int x=5;
   int y=m_address_bar_y_size/2;
//--- Löschen des Hintergrundes
   m_address_bar.Erase(::ColorToARGB(m_address_bar_back_color,0));
//--- Zeichnen des Rahmens des Hintergrundes
   Border();
//--- Eigenschaften des Textes
   m_address_bar.FontSet("Calibri",14,FW_NORMAL);
//--- Falls kein Pfad angegeben wurde, dann zeige den Standard-String
   if(m_current_full_path=="")
      m_current_full_path="Loading. Please wait...";
//--- Der Adresszeile den Pfad des Datei-Navigators übergeben
   m_address_bar.TextOut(x,y,m_current_path,::ColorToARGB(m_address_bar_text_color),TA_LEFT|TA_VCENTER);
//---Aktualisieren des Canvas
   m_address_bar.Update();
  }

Die Breite der Adresszeile wird vor der Erzeugung in der CFileNavigator::CreateAddressBar() Methode berechnet. Falls der Inhaltsbereich deaktiviert ist, dann ist die Adresszeile genauso breit wie die Baum-Ansicht. In allen anderen Fällen wird für die Berechnung das gleiche Prinzip verwendet, wie in der Klasse für die Baumansicht (CTreeView).

Nach der Erzeugung des Objektes, wird die CFileNavigator::UpdateAddressBar() Methode für das Zeichnen des Hintergrundes, des Rahmens und der Standardnachricht aufgerufen. 

//+----------------------------------------------------------------
//| Erzeugung der Adresszeile                                               |
//+----------------------------------------------------------------
bool CFileNavigator::CreateAddressBar(void)
  {
//--- Den Objektnamen bilden
   string name=CElement::ProgramName()+"_file_navigator_address_bar_"+(string)CElement::Id();
//--- Koordinaten
   int x =CElement::X();
   int y =CElement::Y();
//--- Größen::
//    Berechnung der Breite
   int x_size=0;
//--- Falls es keinen Inhaltsbereich gibt
   if(m_content_area_width<0)
      x_size=m_treeview_area_width;
   else
     {
      //--- Falls eine bestimmte Breite des Inhaltsbereichs definiert wurde
      if(m_content_area_width>0)
         x_size=m_treeview_area_width+m_content_area_width-1;
      //--- Falls die rechte Ecke des Inhalts Bereichs an der rechten Ecke des Formulars sein muss
      else
         x_size=m_wnd.X2()-x-2;
     }
//--- Höhe
   int y_size=m_address_bar_y_size;
//--- Erzeugung des Objektes
   if(!m_address_bar.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,x_size,y_size,COLOR_FORMAT_XRGB_NOALPHA))
      return(false);
//--- Hinzufügen zum Chart
   if(!m_address_bar.Attach(m_chart_id,name,m_subwin,1))
      return(false);
//--- Setzen der Eigenschaften
   m_address_bar.Background(false);
   m_address_bar.Z_Order(m_zorder);
   m_address_bar.Tooltip("\n");
//--- Abspeichern der Größe
   CElement::X(x);
   CElement::Y(y);
//--- Abspeichern der Größe
   CElement::XSize(x_size);
   CElement::YSize(y_size);
//--- Ränder von den Kanten
   m_address_bar.XGap(x-m_wnd.X());
   m_address_bar.YGap(y-m_wnd.Y());
//--- Aktualisieren der Adresszeile
   UpdateAddressBar();
//--- Abspeichern des Objekt-Pointers
   CElement::AddToArray(m_address_bar);
//--- Verstecke das Element, falls es sich um ein Dialogfenster handelt oder es minimiert ist
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      m_address_bar.Timeframes(OBJ_NO_PERIODS);
//---
   return(true);
  }

Nun haben wir den Moment erreicht, wo die CFileNavigator::CreateTreeView() Methode für das Anzeigen der Baumansicht aufgerufen wird. In den vorherigen Artikel habe ich darauf hingewiesen, dass wir vor der Erzeugung des CTreeView Controls erst die Elemente mit den Array-Parametern den Controls-Arrays hinzufügen müssen. An diesem Punkt werden alle Parameter für die Elemente in den CFileNavigator Hauptklassen-Arrays abgelegt. Dafür müssen wir diese nur in einer Schleife der tree view class übergeben. 

Das Bild für jedes Element wird in derselben Schleife festgelegt. Außerdem muss noch das letzte Zeichen (‘\’) in dem Namen des Ordners entfernt werden

//+----------------------------------------------------------------
//| Erzeugt die Baumansicht                                            |
//+----------------------------------------------------------------
bool CFileNavigator::CreateTreeView(void)
  {
//--- Abspeichern des Fenster-Pointers
   m_treeview.WindowPointer(m_wnd);
//--- Setzen der Eigenschaften
   m_treeview.Id(CElement::Id());
   m_treeview.XSize(CElement::XSize());
   m_treeview.YSize(CElement::YSize());
   m_treeview.ResizeListAreaMode(true);
   m_treeview.TreeViewAreaWidth(m_treeview_area_width);
   m_treeview.ContentAreaWidth(m_content_area_width);
//--- Die Arrays für die Baumansicht bilden
   int items_total=::ArraySize(m_g_item_text);
   for(int i=0; i<items_total; i++)
     {
      //--- Das Icon für das Element festlegen (Ordner/Datei)
      string icon_path=(m_g_is_folder[i])? m_folder_icon : m_file_icon;
      //--- Falls es sich um einen Ordner handelt, dann entferne das letzte Zeichen ('\') in dem String 
      if(m_g_is_folder[i])
         m_g_item_text[i]=::StringSubstr(m_g_item_text[i],0,::StringLen(m_g_item_text[i])-1);
      //--- Füge ein Element der Baumansicht hinzu
      m_treeview.AddItem(i,m_g_prev_node_list_index[i],m_g_item_text[i],icon_path,m_g_item_index[i],
                         m_g_node_level[i],m_g_prev_node_item_index[i],m_g_items_total[i],m_g_folders_total[i],false,m_g_is_folder[i]);
     }
//--- Erzeuge die Baumansicht
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x,m_y+m_address_bar_y_size))
      return(false);
//---
   return(true);
  }

 

 


Eventhandler

(1) Der vollständige relative Pfad des in der Baumansicht selektierten Verzeichnisses (zuzüglich der Angabe des Laufwerks) , (2) der relative Pfad zu der terminal Sandbox und (3) und der aktuelle Verzeichnis Bereich werden in den Variablen der Klasse gespeichert. Es werden noch entsprechende Methoden für die Abfrage dieser Werte benötigt. Zudem benötigen wir die CFileNavigator::SelectedFile() Methode, um das ausgewählte Element zu erhalten (Datei).

class CFileNavigator : public CElement
  {
private:
   //--- Der aktuelle Pfad, relativ zu der Datei "Sandbox" des Terminals
   string            m_current_path;
   //--- Der aktuelle Pfad, relativ zu dem Dateisystem, zuzüglich der Laufwerksangabe
   string            m_current_full_path;
   //--- Der Bereich des aktuellen Verzeichnisses
   int               m_directory_area;
   //---
public:
   //--- Gibt (1) den aktuellen Pfad und (2) den vollständigen Pfad, (3) die ausgewählte Datei
   string            CurrentPath(void)                                  const { return(m_current_path);                    }
   string            CurrentFullPath(void)                              const { return(m_current_full_path);               }
   //--- Liefert (1) den Bereich des Verzeichnisses (2) die ausgewählte Datei
   int               DirectoryArea(void)                                const { return(m_directory_area);                  }
   string            SelectedFile(void)                                 const { return(m_treeview.SelectedItemFileName()); }
  };

Der Eventhandler des Datei-Navigators wird so konfiguriert, dass er nur ein Event mit der ID ON_CHANGE_TREE_PATH akzeptiert. Dieser wird generiert, wenn ein Element in der Struktur der Baumansicht ausgewählt wird. Die CFileNavigator::OnChangeTreePath() Methode wurde implementiert, um eine Nachricht mit dieser ID zu verarbeiten. 

Zunächst erhalten wir den Pfad, der in der Baumansicht abgespeichert ist. Anschließend, in Abhängigkeit von der Kategorie des Pfades (common oder local), erhalten wir (1) die Adresse des Daten-Hauptverzeichnisses und (2) bilden den kurzen und den langen Pfad und speichern das Flag für den Verzeichnis-Bereich

class CFileNavigator : public CElement
  {
private:
   //--- Verarbeiten des Events für das Auswählen eines neuen Pfades in der Baumansicht
   void              OnChangeTreePath(void);
  };
//+----------------------------------------------------------------
//| Verarbeiten des Events für das Auswählen eines neuen Pfades in der Baumansicht            |
//+----------------------------------------------------------------
void CFileNavigator::OnChangeTreePath(void)
  {
//--- Abfrage des aktuellen Pfades
   string path=m_treeview.CurrentFullPath();
//--- Falls es sich dabei um den gemeinsamen Ordner der Terminals handelt
   if(::StringFind(path,"Common\\Files\\",0)>-1)
     {
      //--- Abfrage der Adresse des gemeinsamen Ordners der Terminals
      string common_path=::TerminalInfoString(TERMINAL_COMMONDATA_PATH);
      //--- Entfernen des "Common\" prefix in dem String (Von dem Event erhalten)
      path=::StringSubstr(path,7,::StringLen(common_path)-7);
      //--- Erzeugen des Pfades (Die kurze und die vollständige Version)
      m_current_path      =::StringSubstr(path,6,::StringLen(path)-6);
      m_current_full_path =common_path+"\\"+path;
      //--- Abspeichern des Verzeichnis-Bereiches
      m_directory_area=FILE_COMMON;
     }
//--- Falls es sich um den lokalen Ordner des Terminals handelt
   else if(::StringFind(path,"MQL5\\Files\\",0)>-1)
     {
      //--- Abfrage der Daten-Adresse in dem lokalen Ordner des Terminals
      string local_path=::TerminalInfoString(TERMINAL_DATA_PATH);
      //--- Erzeugen des Pfades (Die kurze und die vollständige Version)
      m_current_path      =::StringSubstr(path,11,::StringLen(path)-11);
      m_current_full_path =local_path+"\\"+path;
      //--- Abspeichern des Verzeichnis-Bereiches
      m_directory_area=0;
     }
//--- Darstellung des aktuellen Pfades in der Adresszeile
   UpdateAddressBar();
  }

 

Als Ergebnis, wenn das ON_CHANGE_TREE_PATH Event eintritt, muss die CFileNavigator::OnChangeTreePath() Methode in dem Eventhandler aufgerufen werden:

//+----------------------------------------------------------------
//| Event handler                                                    |
//+----------------------------------------------------------------
void CFileNavigator::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeitendes Events "Änderung des Pfades in der Baumansicht"
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH)
     {
      OnChangeTreePath();
      return;
     }
  }

Das Event mit der gleichen ID kann in dem benutzerdefinierten Klassen-Eventhandler akzeptiert werden. Ein Beispiel hierfür wird nachfolgend diskutiert.

 


Integration des Controls in der Bibliotheks-Engine

Für eine einwandfreie Funktion, sollte dieses in die Bibliotheks-Engine integriert werden. Es müssen hauptsächlich Ergänzungen in der BasisklasseCWndContainer in der WndContainer.mqh Datei vorgenommen werden. Wir müssen folgendes hinzufügen: 

  • Ein privates Array für den Datei-Navigator;
  • Eine Methode für die Abfrage der Anzahl der Datei-Navigator-Anwendungen in dem grafischen Interface (CFileNavigator);
  • Methoden für das Abspeichern der Pointer zu allen Datei-Navigator-Elementen in die Datenbasis.

Eine verkürzte Version der CWndContainer Klasse (nur die Dinge, die hinzugefügt werden müssen) wird in dem nachfolgenden Programmlisting gezeigt:

#include "FileNavigator.mqh"
//+----------------------------------------------------------------
//| Klasse für das Abspeichern der Interface-Objekte                          |
//+----------------------------------------------------------------
class CWndContainer
  {
protected:
   //--- Window Array
   CWindow          *m_windows[];
   //--- Struktur des Elementenarrays
   struct WindowElements
     {
      //--- Datei-Navigatoren
      CFileNavigator   *m_file_navigators[];
     };
   //--- Ein ABC mit Element ABCs für jedes Fenster
   WindowElements    m_wnd[];
   //---
public:
   //--- Die anzahl der Datei-Navigatoren
   int               FileNavigatorsTotal(const int window_index);
   //---
private:
   //--- Speichert die Pointer der Baumansicht-Controls in der Basis
   bool              AddFileNavigatorElements(const int window_index,CElement &object);
  };

Sie können den detaillierten Programmcode dieser Methoden in den angehängten Dateien nachsehen. 



Test des Datei-Navigators

Nun ist alles für einen Test des Datei-Navigators bereit. Lassen Sie uns eine Kopie des Expert Advisors aus dem vorherigen Artikel anfertigen und entfernen Sie alle Elemente, mit Ausnahme des Hauptmenüs und der Status-Bar(CProgram). Um alle Inhalts Modi schnell testen zu können, erzeugen wir zwei externe Parameter, so, wie sie in dem nachfolgenden Programmcode gezeigt werden. Die Typen der Enumerationen und Modi haben wir bereits weiter oben in diesem Artikel besprochen.

//+----------------------------------------------------------------
//|                                                      Program.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+----------------------------------------------------------------
//--- Externe Parameter
input ENUM_FILE_NAVIGATOR_CONTENT NavigatorContent =FN_BOTH;         // Navigator content
input ENUM_FILE_NAVIGATOR_MODE    NavigatorMode    =FN_ONLY_FOLDERS; // Navigator mode

Nun müssen wir die CFileNavigator Datei-Navigator Klassen-Instanz deklarieren, sowie die Methoden für die Erzeugung des Elementes und die Abstände von der Ecke des Formulars, zu welchem diese Instanz hinzugefügt wird. 

class CProgram : public CWndEvents
  {
private:
   //--- Datei-Navigator
   CFileNavigator    m_navigator;
   //---
private:
   //--- Datei-Navigator
#define NAVIGATOR1_GAP_X      (2)
#define NAVIGATOR1_GAP_Y      (43)
   bool              CreateFileNavigator(void);
  };

Die CProgram::CreateFileNavigator() Methode für die Erzeugung des Datei-Navigators wird in den nachfolgenden Programmcode gezeigt. Wir legen die Höhe des Navigators auf 10 Punkte fest. Die Standardgröße (20 pixels) wird durch den CFileNavigator Klassen-konstruktor angegeben. Wie zuvor schon erwähnt, werden die Modi der Inhalte über externe Parameter verwaltet. Das Aussehen der Komponenten kann über Methoden konfiguriert werden, die über die Pointer erhältlich sind. Der nachfolgende Programmcode zeigt dieses anhand eines Beispiels von der Abfrage der Pointer zur Baumansicht und seiner Scrollbars. Der Aufruf der Methode sollte in der Hauptmethode für die Erzeugung des grafischen Interfaces erfolgen. 

//+----------------------------------------------------------------
//| Erzeugung eines Expert-Bedienfeldes (Panel)                                           |
//+----------------------------------------------------------------
bool CProgram::CreateExpertPanel(void)
  {
//--- Erzeugung des Formulars 1 mit Controls
//---Erzeugung der Controls:
//    Hauptmenü
//--- Kontextmenüs
//--- Erzeugen der Statusbar
//--- Erzeugung des Datei-Navigators
   if(!CreateFileNavigator())
      return(false);
//--- Neuzeichnen des Charts
   m_chart.Redraw();
   return(true);
  }
//+----------------------------------------------------------------
//| Erzeugung des Datei-Navigators                                            |
//+----------------------------------------------------------------
bool CProgram::CreateFileNavigator(void)
  {
//--- Abspeichern des Pointers des Formulars
   m_navigator.WindowPointer(m_window1);
//--- Koordinaten
   int x=m_window1.X()+NAVIGATOR1_GAP_X;
   int y=m_window1.Y()+NAVIGATOR1_GAP_Y;
//--- Vor der Erzeugung die Eigenschaften festlegen
   m_navigator.TreeViewPointer().VisibleItemsTotal(10);
   m_navigator.NavigatorMode(NavigatorMode);
   m_navigator.NavigatorContent(NavigatorContent);
   m_navigator.TreeViewAreaWidth(250);
   m_navigator.AddressBarBackColor(clrWhite);
   m_navigator.AddressBarTextColor(clrSteelBlue);
//--- Die Eigenschaften der Scrollbar
   m_navigator.TreeViewPointer().GetScrollVPointer().AreaBorderColor(clrLightGray);
   m_navigator.TreeViewPointer().GetContentScrollVPointer().AreaBorderColor(clrLightGray);
//--- Erzeugen des Controls
   if(!m_navigator.CreateFileNavigator(m_chart_id,m_subwin,x,y))
      return(false);
//--- Hinzufügen der Pointer der Controls zu der Datenbasis
   CWndContainer::AddToElementsArray(0,m_navigator);
   return(true);
  }

Der Eventhandler wird beispielhaft den kurzen und den vollständigen Pfad, sowie den Namen der aktuell selektierten Datei protokollieren. Wenn eine Datei ausgewählt wurde, dann werden wir diese öffnen und die ersten drei Zeilen in das Log schreiben. Bitte beachten Sie, dass wir die CFileNavigator::DirectoryArea() Methode für die Abfrage des Flags entsprechend zu dem Ort der Datei verwenden, damit wir in der Lage sind, den Bereich des Verzeichnisses zu verwalten.  

//+----------------------------------------------------------------
//| Event handler                                                    |
//+----------------------------------------------------------------
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Verarbeiten des Events "Veränderungen des Pfades in der Baumansicht"
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH)
     {
      ::Print(__FUNCTION__," > id: ",id,"; file name: ",m_navigator.SelectedFile());
      ::Print(__FUNCTION__," > id: ",id,"; path: ",m_navigator.CurrentPath()+m_navigator.SelectedFile());
      ::Print(__FUNCTION__," > id: ",id,"; full path: ",m_navigator.CurrentFullPath()+m_navigator.SelectedFile());
      //--- Falls eine Datei ausgewählt worden ist, dann lesen (die ersten drei Zeilen)
      if(m_navigator.SelectedFile()!="")
        {
         //--- Den Pfad zu der Datei bilden
         string path=m_navigator.CurrentPath()+m_navigator.SelectedFile();
         //--- Abfrage des Handles zu der angegebenen Datei
         int filehandle=::FileOpen(path,FILE_READ|FILE_TXT|FILE_ANSI|m_navigator.DirectoryArea(),'\n');
         //--- Falls wir ein Handle erhalten, dann lesen wir die ersten drei Zeilen
         if(filehandle!=INVALID_HANDLE)
           {
            ::Print(__FUNCTION__," > Opened file: ",path);
            ::Print(__FUNCTION__," > Line 01: ",::FileReadString(filehandle));
            ::Print(__FUNCTION__," > Line 02: ",::FileReadString(filehandle));
            ::Print(__FUNCTION__," > Line 03: ",::FileReadString(filehandle));
           }
         //--- Datei schließen
         ::FileClose(filehandle);
        }
      ::Print("---");
     }
  }

Kompilieren Sie dieses Programm und laden Sie es auf einen Chart. Das Ergebnis sollte so aussehen, wie es in dem nachfolgenden Screenshot gezeigt wird. Der Inhalt des Datei-Navigators sollte genauso aussehen, wie der Inhalt des Dateisystems des Terminals auf Ihrem PC.

 Abbildung  1. Test des Datei-Navigators

Abbildung 1. Test des Datei-Navigators


Der nachfolgende Screenshot zeigt die ungeöffnete Datei-Navigator Baumansicht: 

 Abbildung  2. Ungeöffnete Struktur der Baumansicht des Datei-Navigators

Abbildung 2. Ungeöffnete Struktur der Baumansicht des Datei-Navigators


Nachdem eine Datei durch das Protokoll des Eventhandler übergeben wird, erhalten Sie das folgende Ergebnis: 

2016.06.16 02:15:29.994         CProgram::OnEvent > Строка 03: 2,155.66,1028.00,1.04,0.30,0.64,0.24,0.01,2,0,10,10,0
2016.06.16 02:15:29.994         CProgram::OnEvent > Строка 02: 1,260.67,498.00,1.13,1.05,0.26,1.00,0.03,3,0,10,10,0
2016.06.16 02:15:29.994         CProgram::OnEvent > Строка 01: №,PROFIT,TOTAL DEALS,PROFIT FACTOR,EXPECTED PAYOFF,EQUITY DD MAX REL%,RECOVERY FACTOR,SHARPE RATIO,AmountBars,TakeProfit,StopLoss,TrailingSL,ReversePosition
2016.06.16 02:15:29.994         CProgram::OnEvent > Открыт файл: DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv
2016.06.16 02:15:29.994         CProgram::OnEvent > id: 1023; full path: C:\Users\tol64\AppData\Roaming\MetaQuotes\Terminal\Common\Files\DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv
2016.06.16 02:15:29.994         CProgram::OnEvent > id: 1023; path: DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv
2016.06.16 02:15:29.994         CProgram::OnEvent > id: 1023; file name: optimization_results2.csv

Alles arbeitet einwandfrei. 



Schlussfolgerung

Zur Zeit sieht das schematische Diagramm unserer Bibliothek für das Erzeugen von grafischen Interfaces wie folgt aus:

Abbildung 3. Die Struktur der Bibliothek zu den aktuellen Stand der Entwicklung 

Abbildung 3. Die Struktur der Bibliothek zu den aktuellen Stand der Entwicklung

Dieses ist das Ende des 8. Teils der Serie über die Erzeugung von grafischen Interfaces in dem MetaTrader Trading-Terminals. In diesem Teil haben wir Controls wie den statisch und den Dropdown-Kalender, die Baumansicht, den Mauszeiger und den Datei-Navigator besprochen.

Der nächste (neunte) Teil dieser Serie wird sich mit den folgenden Controls beschäftigen:

  • Farbauswahl.
  • Progress Bar.
  • Linien-chart.

Sie können das gesamte Material des achten 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 achten Teils:


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/2541

Beigefügte Dateien |
Grafische Interfaces VIII: Die Baumansicht (Kapitel 2) Grafische Interfaces VIII: Die Baumansicht (Kapitel 2)

Das vorherige Kapitel VIII der grafischen Schnittstellen hat sich mit den Elementen eines statischen und eines Dropdown-Kalenders beschäftigt. Das zweite Kapitel beschäftigt sich mit einem nicht weniger komplexen Element — der Baumansicht, die Teil aller kompletten Bibliotheken graphischer Schnittstellen ist. Die Baumansicht in diesem Artikel beinhaltet mehrere flexible Einstellungen und Modi und erlaubt daher die Elemente ganz Ihren Zielen anzupassen.

Graphische Interfaces VIII: Der Kalender (Kapitel 1) Graphische Interfaces VIII: Der Kalender (Kapitel 1)

Im Kapitel VIII der Reihe von Artikeln, die sich der Erstellung graphischer Oberflächen im MetaTrader widmet, betrachten wir komplexe, zusammengesetzte Steuerelemente wie Kalender, Baumdarstellung und einen Dateinavigator. Aufgrund der umfangreichen Informationen gibt es für jedes Thema eigene Artikel. Das erste Kapitel dieses Teil beschreibt das Kalenderelement und seine erweiterte Version, ein Dropdown-Kalender.

Grafische Interfaces IX: Das Farbauswahl Control (Kapitel 1) Grafische Interfaces IX: Das Farbauswahl Control (Kapitel 1)

Mit diesem Artikel starten wir das Kapitel 9 der Serie der Artikel über die entwicklung von grafischen Interfaces in den Metatrader Trading-Terminals. Diese Serie besteht aus zwei Kapiteln, in welcher neue Elemente und Interfaces vorgestellt werden, wie zum Beispiel das Farbauswahl-Control, farbige Buttons, die Fortschrittsanzeige und Linien Charts.

Grafische Interfaces IX: Die Fortschrittsanzeige und das Linienchart-Control (Kapitel 2) Grafische Interfaces IX: Die Fortschrittsanzeige und das Linienchart-Control (Kapitel 2)

Das zweite Kapitel des neuen Teils dieser Serie widmet sich der Fortschrittsanzeige und dem Linienchart-Control Wie immer, gibt es auch hier detaillierte Beispiele, um deutlich zu machen, wie die Controls in den benutzerdefinierten MQL Anwendungen verwendet werden können.