Grafische Interfaces VIII: Das Datei-Navigator Control (Kapitel 3)
Anatoli Kazharski | 14 September, 2016
Inhalt
- Einleitung
- Das Datei-Navigator Control
- Entwicklung der CFileNavigator Klasse
- Arrangieren der hierarischen Dateistruktur
- Methoden für die Erzeugung des Controls
- Eventhandler
- Integration des Controls in der Bibliotheks-Engine
- Test des Datei-Navigators
- Schlussfolgerung
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
Der nachfolgende Screenshot zeigt die ungeöffnete Datei-Navigator Baumansicht:
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
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:
- Grafische Interfaces VIII: Das Kalender-Control (Kapitel 1)
- Grafische Interfaces VIII: Das Baumansicht-Control (Kapitel 2)
- Grafische Interface VIII: Das Datei-Navigator-Control (Kapitel 3)