English Русский 中文 Español 日本語 Português
Grafiken in der Bibliothek DoEasy (Teil 94): Bewegen und Löschen zusammengesetzter grafischer Objekte

Grafiken in der Bibliothek DoEasy (Teil 94): Bewegen und Löschen zusammengesetzter grafischer Objekte

MetaTrader 5Beispiele | 14 März 2022, 11:30
245 1
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

Im vorigen Artikel habe ich mit der Entwicklung von zusammengesetzten grafischen Objekten begonnen. Um ein zusammengesetztes grafisches Objekt zu definieren, habe ich einen neuen Typ von grafischem Element eingeführt — ein erweitertes grafisches Standardobjekt. Alle grafischen Objekte, die an der Erstellung eines zusammengesetzten grafischen Objekts beteiligt sind, müssen von diesem Typ sein. Im Moment erstelle ich keine Klassen für die Erstellung bestimmter zusammengesetzter grafischer Objekte. Stattdessen werde ich eine Funktionsweise implementieren, die es ermöglicht, vordefinierte, zusammengesetzte grafische Objekte zu erstellen, was natürlich die Möglichkeit nicht ausschließt, nutzerdefinierte, zusammengesetzte grafische Objekte sowohl programmatisch als auch "on the fly" zu erstellen — direkt auf dem Chart.


Ich werde meine Arbeit an dieser Funktionalität in mehrere Teile unterteilen. Zunächst werde ich die notwendigen Mittel für die Verwaltung und Erstellung zusammengesetzter grafischer Objekte erstellen. Als Nächstes werde ich vordefinierte Klassen solcher Objekte hinzufügen (in der Tat hängt hier alles von den individuellen Bedürfnissen des Nutzers ab, die Klassen der vordefinierten zusammengesetzten grafischen Objekte werden hier nur als Beispiel verwendet). Als Nächstes werde ich mit der Implementierung der Funktionalität beginnen, die es uns ermöglicht, zusammengesetzte grafische Objekte visuell, manuell, in Echtzeit und direkt auf dem Diagramm zu erstellen.

In der Tat werde ich hier eine Feinabstimmung der Dinge vornehmen, die ich im vorherigen Artikel implementiert habe. Ich werde das Setzen der Koordinatenankerpunkte auf untergeordnete Objekte und das Empfangen der Koordinaten vorstellen. Außerdem werde ich das Verschieben des Basisobjekts mit angehängten untergeordneten Objekten testen (in diesem Stadium stellt sich heraus, dass ich auch die Funktionalität zum Verschieben von Koordinatenpunkten eines zusammengesetzten Objekts in einer komplexeren Form benötige, als nur ein einzelnes Objektereignis zu verfolgen), sowie die Funktionalität zum Entfernen eines zusammengesetzten grafischen Objekts erstellen.

Das Verschieben von Koordinatenpunkten eines grafischen Objekts führt nur dann zu dem Ereignis CHARTEVENT_OBJECT_DRAG, wenn die Maustaste losgelassen wird. Wenn wir also nur dieses Ereignis verfolgen, führt das Verschieben eines grafischen Basisobjekts (während die Maustaste nicht losgelassen wird) dazu, dass alle mit ihm verbundenen Objekte unverändert bleiben. Wenn die Maustaste losgelassen wird und das Ereignis eintritt, werden die gebundenen Objekte zu ihren Basisobjektankerpunkten bewegt. Das bedeutet, dass wir die Mausbewegung mit der gedrückten Maustaste verfolgen sollten. Außerdem müssen wir wissen, dass die Taste auf einem grafischen Basisobjekt gedrückt wurde, und zwar in seinem Koordinaten- (oder zentralen) Ankerpunkt. Wir sollten auch in der Lage sein, die Lage der Koordinatenpunkte des Objekts und der Ankerpunkte seiner untergeordneten Objekte neu zu berechnen.
Das Ereignis CHARTEVENT_OBJECT_DRAG sollte auch ganz am Ende der Verschiebung behandelt werden, um die Endkoordinaten des Basisobjekts zu fixieren und sie zu verwenden, um die Koordinaten aller untergeordneten grafischen Objekte, die an dieses gebunden sind, neu zu berechnen.

In diesem Artikel werde ich die Behandlung des Ereignisses CHARTEVENT_OBJECT_DRAG und die Neuberechnung der Koordinaten der gebundenen Objekte entsprechend der neuen Position der Koordinaten des Basisobjekts implementieren. Wenn das Basisobjekt entfernt wird, wird auch ein zusammengesetztes grafisches Objekt entfernt. Im Falle eines solchen Ereignisses müssen alle daran gebundenen grafischen Objekte entfernt werden. Für den Moment mache ich es mir einfach, indem ich die Möglichkeit ausschalte, alle grafischen Objekte, die an ein Basisobjekt gebunden sind, mit der Maus auszuwählen. Um ein zusammengesetztes grafisches Objekt zu entfernen, müssen wir also das Basisobjekt auswählen und es löschen. Es ist nicht mehr möglich, eines der gebundenen Objekte mit der Maus auszuwählen. Dies ist der erste und einfachste Weg, um sich gegen die Zerstörung eines zusammengesetzten grafischen Objekts zu schützen.
Es ist jedoch möglich, die Objektliste zu öffnen (Strg+B), die Eigenschaften eines beliebigen gebundenen Objekts auszuwählen und es zu erlauben, es auszuwählen oder es sofort aus dem Fenster der grafischen Objektliste zu entfernen. Später werde ich auch die Behandlung einer beabsichtigten Zerstörung eines zusammengesetzten grafischen Objekts implementieren. Beim Entfernen eines an das Basisobjekt gebundenen grafischen Objekts werden wir alle Objekte entfernen, die an der Konstruktion eines zusammengesetzten grafischen Objekts beteiligt sind. Mit anderen Worten, ich werde dafür sorgen, dass das gesamte zusammengesetzte Objekt gelöscht wird, wenn eines der Objekte, aus denen es besteht, entfernt wird. In den kommenden Artikeln werde ich auch die Funktionalität zum Ablösen eines gebundenen grafischen Objekts vom Basisobjekt vorstellen.


Verbesserung der Klassenbibliothek

Wie üblich implementieren wir zuerst die neuen Bibliotheksmeldungen.
In \MQL5\Include\DoEasy\Data.mqh fügen wir die Indizes der neuen Nachrichten ein:

//--- CGraphElementsCollection
   MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST,           // Failed to get the list of newly added objects
   MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST,         // Failed to remove a graphical object from the list
   MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_LIST,         // Failed to remove a graphical object from the list
   MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART,        // Failed to remove a graphical object from the chart
   MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_DEL_LIST,          // Failed to set a graphical object to the list of removed objects
   MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_RNM_LIST,          // Failed to set a graphical object to the list of renamed objects

...

//--- CLinkedPivotPoint
   MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X,                // Not a single pivot point is set for the object along the X axis
   MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y,                // Not a single pivot point is set for the object along the Y axis
   MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE,             // The object is not attached to the basic graphical object
   MSG_GRAPH_OBJ_EXT_FAILED_CREATE_PP_DATA_OBJ,       // Failed to create a data object for the X and Y pivot points
   MSG_GRAPH_OBJ_EXT_NUM_BASE_PP_TO_SET_X,            // Number of base object pivot points for calculating the X coordinate: 
   MSG_GRAPH_OBJ_EXT_NUM_BASE_PP_TO_SET_Y,            // Number of base object pivot points for calculating the Y coordinate: 
   
  };
//+------------------------------------------------------------------+

und die Textmeldungen, die den neu hinzugefügten Indizes entsprechen:

//--- CGraphElementsCollection
   {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"},
   {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"},
   {"Не удалось удалить графический объект из списка","Failed to delete graphic object from the list"},
   {"Не удалось удалить графический объект с графика","Failed to delete graphic object from the chart"},
   {"Не удалось поместить графический объект в список удалённых объектов","Failed to place graphic object in the list of deleted objects"},
   {"Не удалось поместить графический объект в список переименованных объектов","Failed to place graphic object in the list of renamed objects"},

...

//--- CLinkedPivotPoint
   {"Для объекта не установлено ни одной опорной точки по оси X","The object does not have any pivot points set along the x-axis"},
   {"Для объекта не установлено ни одной опорной точки по оси Y","The object does not have any pivot points set along the y-axis"},
   {"Объект не привязан к базовому графическому объекту","The object is not attached to the base graphical object"},
   {"Не удалось создать объект данных опорной точки X и Y.","Failed to create X and Y reference point data object"},
   {"Количество опорных точек базового объекта для расчёта координаты X: ","Number of reference points of the base object to set the X coordinate: "},
   {"Количество опорных точек базового объекта для расчёта координаты Y: ","Number of reference points of the base object to set the Y coordinate: "},
   
  };
//+---------------------------------------------------------------------+


Lassen Sie uns die folgende kleine Verbesserung in allen abhängigen Klassendateien des abstrakten grafischen Standardobjekts, das in \MQL5\Include\DoEasy\Objects\Graph\Standard\ gespeichert ist, vornehmen, nämlich in seinen Methoden zur Anzeige einer kurzen Objektbeschreibung:

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CGStdArrowBuyObj::PrintShort(const bool dash=false,const bool symbol=false)
  {
   ::Print
     (
      (dash ? " - " : "")+this.Header(symbol)," \"",CGBaseObj::Name(),"\": ID ",(string)this.GetProperty(GRAPH_OBJ_PROP_ID,0),
      ", ",::TimeToString(CGBaseObj::TimeCreate(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
     );
  }
//+------------------------------------------------------------------+

Da alle virtuellen Methoden, die einen kurzen Objektnamen anzeigen, eine Reihe von Eingaben haben sollten, die der Methode der Elternklasse ähnlich sind, hatten diese Methoden (und haben immer noch) ungenutzte Eingaben. Ich habe gerade eine davon implementiert — die Anzeige eines Bindestrichs vor dem von der Methode zurückgegebenen Text. Wenn die Methode das Bindestrich-Flag gleich wahr erhält, wird ein Bindestrich gesetzt, bevor ein kurzer Objektname angezeigt wird (ein Beispiel finden Sie im aktuellen Artikel weiter unten). Dies ist praktisch, wenn man eine Überschrift schreiben und darunter die Aufzählung der Objektnamen anzeigen möchte.
Solche Änderungen (völlig identisch mit der betrachteten) wurden bereits in allen Klassendateien vorgenommen, die von der Klasse des abstrakten grafischen Standardobjekts abgeleitet sind. Sie sind in den unten angehängten Dateien zu finden.

Alle hier besprochenen grundlegenden Änderungen haben mit der Klasse des abstrakten, grafischen Standardobjekts \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh zu tun.

In der Klasse des abhängigen Objekts Drehpunktdaten, die sich in derselben Datei befindet, wurde das Array mit dem Namen mit der Koordinate deklariert: m_property_x[][2], das nach den Experimenten mit zwei Arrays in einer einzigen Klasse für X- und Y-Koordinaten übrig blieb. Später habe ich diese Idee aufgegeben, während der Name des Arrays falsch blieb. Daher wurde es in m_property[][2] umbenannt.

Der öffentliche Teil der Klasse enthält nun die Methode zur Anzeige des Namens der Achse, deren Koordinaten in der Klasse gespeichert sind, die Methode zur Rückgabe einer Eigenschaft und den Modifikator einer im Array gespeicherten Eigenschaft, sowie die Methode zur Rückgabe der Beschreibung einer Anzahl von Pivotpunkten des Basisobjekts, die zur Berechnung des Koordinatenpunkts verwendet werden, an den das abhängige grafische Objekt angehängt ist — die Methode wird zur Fehlersuche verwendet:

//+------------------------------------------------------------------+
//| Class of the dependent object pivot point data                   |
//+------------------------------------------------------------------+
class CPivotPointData
  {
private:
   bool              m_axis_x;
   int               m_property[][2];
public:
//--- (1) Set and (2) return the flag indicating that the pivot point belongs to the X coordinate
   void              SetAxisX(const bool axis_x)         { this.m_axis_x=axis_x;             }
   bool              IsAxisX(void)                 const { return this.m_axis_x;             }
   string            AxisDescription(void)         const { return(this.m_axis_x ? "X" : "Y");}
//--- Return the number of base object pivot points for calculating the coordinate of the dependent one
   int               GetBasePivotsNum(void)  const { return ::ArrayRange(this.m_property,0);  }
//--- Add the new pivot point of the base object for calculating the coordinate of a dependent one
   bool              AddNewBasePivotPoint(const string source,const int pivot_prop,const int pivot_num)
                       {
                        //--- Get the array size 
                        int pivot_index=this.GetBasePivotsNum();
                        //--- if failed to increase the array size, inform of that and return 'false'
                        if(::ArrayResize(this.m_property,pivot_index+1)!=pivot_index+1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
                           return false;
                          }
                        //--- Return the result of changing the values of a newly added new array dimension
                        return this.ChangeBasePivotPoint(source,pivot_index,pivot_prop,pivot_num);
                       }
//--- Change the specified pivot point of the base object for calculating the coordinate of a dependent one
   bool              ChangeBasePivotPoint(const string source,const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Get the array size. If it is zero, inform of that and return 'false'
                        int n=this.GetBasePivotsNum();
                        if(n==0)
                          {
                           CMessage::ToLog(source,(this.IsAxisX() ? MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X : MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y));
                           return false;
                          }
                        //--- If the specified index goes beyond the array range, inform of that and return 'false'
                        if(pivot_index<0 || pivot_index>n-1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY);
                           return false;
                          }
                        //--- Set the values, passed to the method, in the specified array cells by index
                        this.m_property[pivot_index][0]=pivot_prop;
                        this.m_property[pivot_index][1]=pivot_num;
                        return true;
                       }

//--- Return(1) a property and (2) a modifier of the property from the array
   int               GetProperty(const string source,const int index)     const
                       {
                        if(index<0 || index>this.GetBasePivotsNum()-1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY);
                           return WRONG_VALUE;
                          }
                        return this.m_property[index][0];   
                       }
   int               GetPropertyModifier(const string source,const int index)  const
                       {
                        if(index<0 || index>this.GetBasePivotsNum()-1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY);
                           return WRONG_VALUE;
                          }
                        return this.m_property[index][1];   
                       }

//--- Return the description of the number of pivot points for setting the coordinate
   string            GetBasePivotsNumDescription(void) const
                       {
                        return CMessage::Text(IsAxisX() ? MSG_GRAPH_OBJ_EXT_NUM_BASE_PP_TO_SET_X : MSG_GRAPH_OBJ_EXT_NUM_BASE_PP_TO_SET_Y)+
                               (string)this.GetBasePivotsNum();
                       }

//--- Constructor/destructor
                     CPivotPointData(void){;}
                    ~CPivotPointData(void){;}
  };
//+------------------------------------------------------------------+

Alle Methoden sind sehr einfach. Ihre Logik sollte aus dem Code ersichtlich sein. Ich werde hier nicht näher auf sie eingehen.


In der Klasse der Daten fügen wir zu den X- und Y-Pivotpunkten eines zusammengesetzten Objekts die Methoden hinzu, die das Ergebnis des Aufrufs der neuen Methoden zurückgeben, die ich gerade besprochen habe:

//+------------------------------------------------------------------+
//| Class of data on X and Y pivot points of a composite object      |
//+------------------------------------------------------------------+
class CPivotPointXY : public CObject
  {
private:
   CPivotPointData   m_pivot_point_x;            // X coordinate pivot point
   CPivotPointData   m_pivot_point_y;            // Y coordinate pivot point
public:
//--- Return the pointer to the (1) X and (2) Y coordinate pivot point data object
   CPivotPointData  *GetPivotPointDataX(void)      { return &this.m_pivot_point_x;                    }
   CPivotPointData  *GetPivotPointDataY(void)      { return &this.m_pivot_point_y;                    }
//--- Return the number of base object pivot points for calculating the (1) X and (2) Y coordinate
   int               GetBasePivotsNumX(void) const { return this.m_pivot_point_x.GetBasePivotsNum();  }
   int               GetBasePivotsNumY(void) const { return this.m_pivot_point_y.GetBasePivotsNum();  }
//--- Add the new pivot point of the base object for calculating the X coordinate of a dependent one
   bool              AddNewBasePivotPointX(const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_x.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Add the new pivot point of the base object for calculating the Y coordinate of a dependent one
   bool              AddNewBasePivotPointY(const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_y.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Add new pivot points of the base object for calculating the X and Y coordinates of a dependent one
   bool              AddNewBasePivotPointXY(const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        bool res=true;
                        res &=this.m_pivot_point_x.AddNewBasePivotPoint(DFUN,pivot_prop_x,pivot_num_x);
                        res &=this.m_pivot_point_y.AddNewBasePivotPoint(DFUN,pivot_prop_y,pivot_num_y);
                        return res;
                       }
//--- Change the specified pivot point of the base object for calculating the X coordinate of a dependent one
   bool              ChangeBasePivotPointX(const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_x.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num);
                       }
//--- Change the specified pivot point of the base object for calculating the Y coordinate of a dependent one
   bool              ChangeBasePivotPointY(const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_y.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num);
                       }
//--- Change specified pivot points of the base object for calculating the X and Y coordinates
   bool              ChangeBasePivotPointXY(const int pivot_index,
                                            const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        bool res=true;
                        res &=this.m_pivot_point_x.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_x,pivot_num_x);
                        res &=this.m_pivot_point_y.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_y,pivot_num_y);
                        return res;
                       }
//--- Return (1) the property for calculating the X coordinate and (2) the X coordinate property modifier
   int               GetPropertyX(const string source,const int index) const
                       {
                        return this.m_pivot_point_x.GetProperty(source,index);
                       }
   int               GetPropertyModifierX(const string source,const int index) const
                       {
                       return this.m_pivot_point_x.GetPropertyModifier(source,index);
                       }
//--- Return (1) the property for calculating the Y coordinate and (2) the Y coordinate property modifier
   int               GetPropertyY(const string source,const int index) const
                       {
                        return this.m_pivot_point_y.GetProperty(source,index);
                       }
   int               GetPropertyModifierY(const string source,const int index) const
                       {
                       return this.m_pivot_point_y.GetPropertyModifier(source,index);
                       }
//--- Return the description of the number of pivot points for setting the (1) X and (2) Y coordinates
   string            GetBasePivotsNumXDescription(void) const
                       {
                        return this.m_pivot_point_x.GetBasePivotsNumDescription();
                       }
   string            GetBasePivotsNumYDescription(void) const
                       {
                        return this.m_pivot_point_y.GetBasePivotsNumDescription();
                       }
                       
//--- Constructor/destructor
                     CPivotPointXY(void){ this.m_pivot_point_x.SetAxisX(true); this.m_pivot_point_y.SetAxisX(false); }
                    ~CPivotPointXY(void){;}
  };  
//+------------------------------------------------------------------+

Jede dieser Methoden gibt das Ergebnis des Aufrufs einer gleichnamigen Methode der entsprechenden Klasse zurück, die die Daten der X- und Y-Achsenkoordinaten speichert.
Die Namen der Methoden geben nun die genaue Koordinate an, deren Daten von der Methode zurückgegeben werden, zum Beispiel GetPropertyX oder GetPropertyY.

Die Klasse der gebundenen Daten der Pivotpunkte des zusammengesetzten Objekts wurde vor allem in Bezug auf die Methodennamen erheblich verbessert. Während der Fehlersuche wurde ich zunehmend durch die Namen der Methoden verwirrt, die nicht selbsterklärend genug waren. Daher habe ich sie umbenannt, um mehr Klarheit zu schaffen. Zum Beispiel war der Name der Methode CreateNewLinkedPivotPoint(), die einen neuen Ankerpunkt eines abhängigen Objekts durch X- und Y-Koordinaten hinzufügt, ziemlich verwirrend, da PivotPoint ein Ankerpunkt ist, der verwendet wird, um die X- oder Y-Koordinaten des Basisobjekts für die Berechnung der Koordinate zu setzen, an die das abhängige Objekt angehängt werden soll. Der Koordinatenpunkt selbst kann über mehrere PivotPoint berechnet werden. Daher wurde die Methode in CreateNewLinkedCoord() umbenannt, was das Hinzufügen eines neuen Koordinatenpunktes anzeigt.

Die ternären Operatoren wurden hinzugefügt, um den Methodencode zu verkürzen. Zum Beispiel schaut die Methode

   CPivotPointData  *GetBasePivotPointDataX(const int index) const
                       {
                        CPivotPointXY *obj=this.GetLinkedPivotPointXY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetPivotPointDataX();
                       }

nun wie folgt aus:

   CPivotPointData  *GetBasePivotPointDataX(const int index_coord_point) const
                       {
                        CPivotPointXY *obj=this.GetLinkedCoord(index_coord_point);
                        return(obj!=NULL ? obj.GetPivotPointDataX() : NULL);
                       }

Das ist völlig identisch aber kürzer.

Außerdem enthält der öffentliche Teil der Klasse jetzt die Methoden, die das Ergebnis des Aufrufs der gleichnamigen Klassenmethoden zurückgeben, die der gewünschten Koordinate entsprechen, was die Beschaffung der erforderlichen Daten vereinfacht:

//--- Add the new pivot point of the base object for calculating the X coordinate for a specified anchor point of the dependent one
   bool              AddNewBasePivotPointX(const int index_coord_point,const int pivot_prop,const int pivot_num)
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index_coord_point);
                        return(obj!=NULL ? obj.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num) : false);
                       }
//--- Add the new pivot point of the base object for calculating the Y coordinate for a specified anchor point of the dependent one
   bool              AddNewBasePivotPointY(const int index_coord_point,const int pivot_prop,const int pivot_num)
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index_coord_point);
                        return(obj!=NULL ? obj.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num) : false);
                       }
//--- Add the new pivot points of the base object for calculating the X and Y coordinates for a specified anchor point of the dependent one
   bool              AddNewBasePivotPointXY(const int index_coord_point,
                                            const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        CPivotPointData *objx=this.GetBasePivotPointDataX(index_coord_point);
                        if(objx==NULL)
                           return false;
                        CPivotPointData *objy=this.GetBasePivotPointDataY(index_coord_point);
                        if(objy==NULL)
                           return false;
                        bool res=true;
                        res &=objx.AddNewBasePivotPoint(DFUN,pivot_prop_x,pivot_num_x);
                        res &=objy.AddNewBasePivotPoint(DFUN,pivot_prop_y,pivot_num_y);
                        return res;
                       }

//--- Change the specified pivot point of the base object for calculating the X coordinate for a specified anchor point of the dependent one
   bool              ChangeBasePivotPointX(const int index_coord_point,const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index_coord_point);
                        return(obj!=NULL ? obj.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num) : false);
                       }
//--- Change the specified pivot point of the base object for calculating the Y coordinate for a specified anchor point of the dependent one
   bool              ChangeBasePivotPointY(const int index_coord_point,const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index_coord_point);
                        return(obj!=NULL ? obj.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num) : false);
                       }
//--- Change the specified pivot points of the base object for calculating the X and Y coordinates for a specified anchor point
   bool              ChangeBasePivotPointXY(const int index_coord_point,
                                            const int pivot_index,
                                            const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        CPivotPointData *objx=this.GetBasePivotPointDataX(index_coord_point);
                        if(objx==NULL)
                           return false;
                        CPivotPointData *objy=this.GetBasePivotPointDataY(index_coord_point);
                        if(objy==NULL)
                           return false;
                        bool res=true;
                        res &=objx.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_x,pivot_num_x);
                        res &=objy.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_y,pivot_num_y);
                        return res;
                       }

//--- Return the property for calculating the X coordinate for a specified anchor point
   int               GetPropertyX(const int index_coord_point,const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index_coord_point);
                        return(obj!=NULL ? obj.GetProperty(DFUN,index) : WRONG_VALUE);
                       }
//--- Return the modifier of the X coordinate property for a specified anchor point
   int               GetPropertyModifierX(const int index_coord_point,const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index_coord_point);
                        return(obj!=NULL ? obj.GetPropertyModifier(DFUN,index) : WRONG_VALUE);
                       }

//--- Return the property for calculating the Y coordinate for a specified anchor point
   int               GetPropertyY(const int index_coord_point,const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index_coord_point);
                        return(obj!=NULL ? obj.GetProperty(DFUN,index) : WRONG_VALUE);
                       }
//--- Return the modifier of the Y coordinate property for a specified anchor point
   int               GetPropertyModifierY(const int index_coord_point,const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index_coord_point);
                       return(obj!=NULL ? obj.GetPropertyModifier(DFUN,index) : WRONG_VALUE);
                       }

//--- Return the description of the number of base object pivot points for calculating the X coordinate by index
   string            GetBasePivotsNumXDescription(const int index_coord_point) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index_coord_point);
                        return(obj!=NULL ? obj.GetBasePivotsNumDescription() : "WRONG_VALUE");
                       }
//--- Return the description of the number of base object pivot points for calculating the Y coordinate by index
   string            GetBasePivotsNumYDescription(const int index_coord_point) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index_coord_point);
                        return(obj!=NULL ? obj.GetBasePivotsNumDescription() : "WRONG_VALUE");
                       }

//--- Constructor/destructor
                     CLinkedPivotPoint(void){;}
                    ~CLinkedPivotPoint(void){;}
  };  
//+------------------------------------------------------------------+


In den Methoden, die die Eigenschaftsbeschreibung der Klasse des abstrakten grafischen Standardobjekts zurückgeben, fügen wir den Index der gewünschten Eigenschaft hinzu:

//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property) { return true;             }
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)  { return true;             }
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property)  { return true;             }

//--- Get description of (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER property,const int index=0);
   string            GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_DOUBLE property,const int index=0);
   string            GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_STRING property,const int index=0);

//--- Return the description of the graphical object anchor point position
   virtual string    AnchorDescription(void)                const { return (string)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); }

So können wir dafür sorgen, dass die Methoden die Liste der benötigten grafischen Objekteigenschaften anzeigen, anstatt sie alle anzuzeigen.

Lassen Sie es mich erklären. Nehmen wir an, dass eine Trendlinie zwei Ankerpunkte hat. Der Eigenschaftsmodifikator (der Index in den oben betrachteten Methoden) wird verwendet, um Zeit (X-Koordinate) oder Preis (Y-Koordinate) festzulegen, um die Koordinaten des Punktes anzugeben, der benötigt wird (links oder rechts). Im Moment zeigt die Methode die vollständige Liste aller Eigenschaften an — auf die Kopfzeile folgen die Werte der beiden Ankerpunkte:

OnChartEvent: Time coordinate: 
 - Pivot point 0: 2022.01.24 20:59
 - Pivot point 1: 2022.01.26 22:00

...

OnChartEvent: Price coordinate: 
 - Pivot point 0: 1.13284
 - Pivot point 1: 1.11846

Aber wenn wir einen einzelnen Punkt anzeigen müssten, gibt es im Moment keine Möglichkeit, dies zu tun. Wir müssen den Eigenschaftsnamen und seinen Wert schreiben. Später werde ich eine einfache Möglichkeit implementieren, den Namen und den Wert eines erforderlichen Ankerpunkts anzuzeigen, wenn die Indizes verwendet werden. Für den Moment werde ich die Standardwerte für Indizes festlegen. Dies schützt vor Mehrfachfehlern und erleichtert das Einfügen von Änderungen, da wir lediglich einen Standardwert entfernen und die notwendige Behandlung von auftretenden Fehlern hinzufügen müssen, um entweder eine vollständige Beschreibung (wie jetzt) oder eine selektive für einen einzelnen Ankerpunkt anzeigen zu können.

Im öffentlichen Abschnitt fügen wir die Methode, die die Anzahl der angehängten Objekte zurückgibt, der Basismethode hinzu und korrigieren die Methodennamen:

//--- Return (1) the list of dependent objects, (2) dependent graphical object by index and (3) the number of dependent objects
   CArrayObj        *GetListDependentObj(void)        { return &this.m_list;           }
   CGStdGraphObj    *GetDependentObj(const int index) { return this.m_list.At(index);  }
   int               GetNumDependentObj(void)         { return this.m_list.Total();    }
//--- Return the name of the dependent object by index
   string            NameDependent(const int index);
//--- Add the dependent graphical object to the list
   bool              AddDependentObj(CGStdGraphObj *obj);

//--- Return the object of data on pivot points
   CLinkedPivotPoint*GetLinkedPivotPoint(void)        { return &this.m_linked_pivots;  }
   
//--- Add a new pivot point for calculating X and Y coordinates to the current object
   bool              AddNewLinkedCoord(const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- If the current object is not bound to the base one, display the appropriate message and return 'false'
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Return the result of adding a new connected pivot point from the CLinkedPivotPoint class to the current object
                        return this.m_linked_pivots.CreateNewLinkedCoord(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y);
                       }
//--- Add a new pivot point for calculating X and Y coordinates to the specified object
   bool              AddNewLinkedCoord(CGStdGraphObj *obj,const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- If the current object is not an extended one, display the appropriate message and return 'false'
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- If a zero pointer to the object is passed, return 'false'
                        if(obj==NULL)
                           return false;
                        //--- Return the result of adding a new connected pivot point from the CLinkedPivotPoint class to the specified object
                        return obj.AddNewLinkedCoord(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y);
                       }


Wir ändern die Methodennamen von GetLinkedPivotsNum() und deklarieren neue private Methoden zum Setzen der Koordinaten für die untergeordneten grafischen Objekte:

//--- Return the number of base object pivot points for calculating the (1) X and (2) Y coordinate in the current object
   int               GetBasePivotsNumX(const int index)           { return this.m_linked_pivots.GetBasePivotsNumX(index);  }
   int               GetBasePivotsNumY(const int index)           { return this.m_linked_pivots.GetBasePivotsNumY(index);  }
//--- Return the number of base object pivot points for calculating the (1) X and (2) Y coordinate in the specified object
   int               GetBasePivotsNumX(CGStdGraphObj *obj,const int index) const { return(obj!=NULL ? obj.GetBasePivotsNumX(index): 0); }
   int               GetBasePivotsNumY(CGStdGraphObj *obj,const int index) const { return(obj!=NULL ? obj.GetBasePivotsNumY(index): 0); }
//--- Return the number of base object pivot points for calculating the coordinates in the (1) current (2) object
   int               GetLinkedCoordsNum(void)               const { return this.m_linked_pivots.GetNumLinkedCoords();      }
   int               GetLinkedPivotsNum(CGStdGraphObj *obj) const { return(obj!=NULL ? obj.GetLinkedCoordsNum() : 0);      }
   
private:
//--- Set the X coordinate (1) from the specified property of the base object to the specified subordinate object, (2) from the base object
   void              SetCoordXToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to);
   void              SetCoordXFromBaseObj(const int prop_from,const int modifier_from,const int modifier_to);
//--- Set the Y coordinate (1) from the specified property of the base object to the specified subordinate object, (2) from the base object
   void              SetCoordYToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to);
   void              SetCoordYFromBaseObj(const int prop_from,const int modifier_from,const int modifier_to);
//--- Set the (1) integer, (2) real and (3) string property to the specified subordinate property
   void              SetDependentINT(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier);
   void              SetDependentDBL(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier);
   void              SetDependentSTR(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier);

public:
//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=WRONG_VALUE; }
//--- Destructor
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:
//--- Protected parametric constructor
                     CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                                   const ENUM_GRAPH_ELEMENT_TYPE elm_type,
                                   const ENUM_GRAPH_OBJ_BELONG belong,
                                   const ENUM_GRAPH_OBJ_SPECIES species,
                                   const long chart_id, const int pivots,
                                   const string name);
                     
public:
//+--------------------------------------------------------------------+ 
//|Methods of simplified access and setting graphical object properties|
//+--------------------------------------------------------------------+


In der Methode, die das untergeordnete grafische Standardobjekt zur Liste der an das Basisobjekt gebundenen Objekte hinzufügt, fügen wir die Einstellung der Eigenschaften hinzu:

//+------------------------------------------------------------------+
//| Add a subordinate standard graphical object to the list          |
//+------------------------------------------------------------------+
bool CGStdGraphObj::AddDependentObj(CGStdGraphObj *obj)
  {
   //--- If the current object is not an extended one, inform of that and return 'false'
   if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
     {
      CMessage::ToLog(MSG_GRAPH_OBJ_NOT_EXT_OBJ);
      return false;
     }
   //--- If failed to add the pointer to the passed object into the list, inform of that and return 'false'
   if(!this.m_list.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_DEP_EXT_OBJ_TO_LIST);
      return false;
     }
   //--- Object added to the list - set its number in the list,
   //--- name and ID of the current object as the base one,
   //--- set the flags of object availability and selection
   //--- and the graphical element type - standard extended graphical object
   obj.SetNumber(this.m_list.Total()-1);
   obj.SetBaseName(this.Name());
   obj.SetBaseObjectID(this.ObjectID());
   obj.SetFlagSelected(false,false);
   obj.SetFlagSelectable(false,false);
   obj.SetTypeElement(GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED);
   return true;
  }
//+------------------------------------------------------------------+

Wir setzen das Objektauswahl-Flag auf false, um die Auswahl eines neu hinzugefügten Objekts zu vermeiden. Die Objektverfügbarkeit deaktivieren wir sofort, indem wir das entsprechende Flag ebenfalls auf false setzen. Als Nächstes stellen wir den Typ "erweitertes grafisches Standardobjekt" für das Objekt ein. Die Möglichkeit, die Objekte mit der Maus auszuwählen, wird deaktiviert und sie werden in der Liste der erweiterten grafischen Standardobjekte verfügbar, so dass es möglich ist, sie programmatisch nach Typ und Name eines grafischen Basisobjekts auszuwählen.

Die Methode setzt die X-Koordinate von der angegebenen Eigenschaft des Basisobjekts auf das angegebene untergeordnete Objekt:

//+--------------------------------------------------------------------+
//| Set the X coordinate from the specified property of the base object|
//| to the specified subordinate object                                |
//+--------------------------------------------------------------------+
void CGStdGraphObj::SetCoordXToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to)
  {
   int prop=WRONG_VALUE;
   switch(obj.TypeGraphObject())
     {
      case OBJ_LABEL             :
      case OBJ_BUTTON            :
      case OBJ_BITMAP_LABEL      :
      case OBJ_EDIT              :
      case OBJ_RECTANGLE_LABEL   :
      case OBJ_CHART             :
        prop=GRAPH_OBJ_PROP_XDISTANCE; 
        break;
      default:
        prop=GRAPH_OBJ_PROP_TIME;
        break;
     }
   if(prop_from<GRAPH_OBJ_PROP_INTEGER_TOTAL)
     {
      this.SetDependentINT(obj,(ENUM_GRAPH_OBJ_PROP_INTEGER)prop,this.GetProperty((ENUM_GRAPH_OBJ_PROP_INTEGER)prop_from,modifier_from),modifier_to);
     }
   else if(prop_from<GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL)
     {
      //--- Assigning a real property value to the integer value of the X coordinate is a bad idea unless you know what you are doing
      this.SetDependentINT(obj,(ENUM_GRAPH_OBJ_PROP_INTEGER)prop,(long)this.GetProperty((ENUM_GRAPH_OBJ_PROP_DOUBLE)prop_from,modifier_from),modifier_to);
     }
   else if(prop_from<GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL+GRAPH_OBJ_PROP_STRING_TOTAL)
     {
      //--- Assigning a string property value to the integer value of the X coordinate is a bad idea unless you know what you are doing
      this.SetDependentINT(obj,(ENUM_GRAPH_OBJ_PROP_INTEGER)prop,(long)this.GetProperty((ENUM_GRAPH_OBJ_PROP_STRING)prop_from,modifier_from),modifier_to);
     }
  }
//+------------------------------------------------------------------+

Wir wählen die erforderliche Eigenschaft je nach Objekttyp. Dies kann eine Zeitkoordinate oder eine Koordinate in Bildschirmpixeln sein. Als Nächstes setzen wir die Eigenschaft, deren Koordinaten in den Eingaben der Methode übergeben wurden, auf die Koordinateneigenschaft des Objekts, das durch den Zeiger an die Methode übergeben wurde — die Eigenschaft selbst und ihren Modifikator. Schließlich geben wir den Modifikator der Eigenschaft an, die im Objekt selbst eingestellt ist. Als Ergebnis weist das grafische Objekt die notwendigen Koordinaten des Ankerpunktes auf, dessen Parameter an die Methode übergeben wurden.

Die Methode setzt die Y-Koordinate von der angegebenen Eigenschaft des Basisobjekts auf das angegebene untergeordnete Objekt:

//+--------------------------------------------------------------------+
//| Set the Y coordinate from the specified property of the base object|
//| to the specified subordinate object                                |
//+--------------------------------------------------------------------+
void CGStdGraphObj::SetCoordYToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to)
  {
   int prop=WRONG_VALUE;
   switch(obj.TypeGraphObject())
     {
      case OBJ_LABEL             :
      case OBJ_BUTTON            :
      case OBJ_BITMAP_LABEL      :
      case OBJ_EDIT              :
      case OBJ_RECTANGLE_LABEL   :
      case OBJ_CHART             :
        prop=GRAPH_OBJ_PROP_YDISTANCE;
        break;
      default:
        prop=GRAPH_OBJ_PROP_PRICE;
        break;
     }
   if(prop_from<GRAPH_OBJ_PROP_INTEGER_TOTAL)
     {
      if(prop==GRAPH_OBJ_PROP_YDISTANCE)
         this.SetDependentINT(obj,(ENUM_GRAPH_OBJ_PROP_INTEGER)prop,this.GetProperty((ENUM_GRAPH_OBJ_PROP_INTEGER)prop_from,modifier_from),modifier_to);
      else
         //--- Assigning an integer property value to the real value of the Y coordinate is allowed only if you know what you are doing
         this.SetDependentDBL(obj,(ENUM_GRAPH_OBJ_PROP_DOUBLE)prop,this.GetProperty((ENUM_GRAPH_OBJ_PROP_INTEGER)prop_from,modifier_from),modifier_to);
     }
   else if(prop_from<GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL)
     {
      if(prop==GRAPH_OBJ_PROP_YDISTANCE)
         //--- Assigning a real property value to the integer value of the Y coordinate is a bad idea unless you know what you are doing
         this.SetDependentINT(obj,(ENUM_GRAPH_OBJ_PROP_INTEGER)prop,(long)this.GetProperty((ENUM_GRAPH_OBJ_PROP_DOUBLE)prop_from,modifier_from),modifier_to);
      else
         this.SetDependentDBL(obj,(ENUM_GRAPH_OBJ_PROP_DOUBLE)prop,this.GetProperty((ENUM_GRAPH_OBJ_PROP_DOUBLE)prop_from,modifier_from),modifier_to);
     }
   else if(prop_from<GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL+GRAPH_OBJ_PROP_STRING_TOTAL)
     {
      //--- Assigning a string property value to the integer or real value of the Y coordinate is a bad idea unless you know what you are doing
      if(prop==GRAPH_OBJ_PROP_YDISTANCE)
         this.SetDependentINT(obj,(ENUM_GRAPH_OBJ_PROP_INTEGER)prop,(long)this.GetProperty((ENUM_GRAPH_OBJ_PROP_STRING)prop_from,modifier_from),modifier_to);
      else
         this.SetDependentDBL(obj,(ENUM_GRAPH_OBJ_PROP_DOUBLE)prop,(double)this.GetProperty((ENUM_GRAPH_OBJ_PROP_STRING)prop_from,modifier_from),modifier_to);
     }
  }
//+------------------------------------------------------------------+

Hier ist alles ähnlich wie bei der Methode zur Festlegung der X-Koordinate. Es gibt jedoch eine Ausnahme: Die X-Koordinate ist immer eine Ganzzahl — entweder die Zeit oder die Anzahl der Pixel, während die Y-Koordinate entweder eine Ganzzahl (die Anzahl der Pixel) oder ein reeller Wert (Preis) sein kann. Daher sollten wir hier prüfen, welche Eigenschaft gesetzt werden soll. Je nachdem setzen wir den Wert entweder als eine Integer-Eigenschaft oder als einen reellen Wert.

Die Methode setzt einen Integer-Wert auf das angegebene untergeordnete Objekt:

//+------------------------------------------------------------------+
//| Set the integer property                                         |
//| to the specified dependent object                                |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetDependentINT(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier)
  {
   if(obj==NULL || obj.BaseObjectID()==0)
      return;
   switch(prop)
     {
      case GRAPH_OBJ_PROP_TIMEFRAMES            :  obj.SetVisibleOnTimeframes((int)value,false);         break;   // Object visibility on timeframes
      case GRAPH_OBJ_PROP_BACK                  :  obj.SetFlagBack(value,false);                         break;   // Background object
      case GRAPH_OBJ_PROP_ZORDER                :  obj.SetZorder(value,false);                           break;   // Priority of a graphical object for receiving the event of clicking on a chart
      case GRAPH_OBJ_PROP_HIDDEN                :  obj.SetFlagHidden(value,false);                       break;   // Disable displaying the name of a graphical object in the terminal object list
      case GRAPH_OBJ_PROP_SELECTED              :  obj.SetFlagSelected(value,false);                     break;   // Object selection
      case GRAPH_OBJ_PROP_SELECTABLE            :  obj.SetFlagSelectable(value,false);                   break;   // Object availability
      case GRAPH_OBJ_PROP_TIME                  :  obj.SetTime(value,modifier);                          break;   // Time coordinate
      case GRAPH_OBJ_PROP_COLOR                 :  obj.SetColor((color)value);                           break;   // Color
      case GRAPH_OBJ_PROP_STYLE                 :  obj.SetStyle((ENUM_LINE_STYLE)value);                 break;   // Style
      case GRAPH_OBJ_PROP_WIDTH                 :  obj.SetWidth((int)value);                             break;   // Line width
      case GRAPH_OBJ_PROP_FILL                  :  obj.SetFlagFill(value);                               break;   // Filling an object with color
      case GRAPH_OBJ_PROP_READONLY              :  obj.SetFlagReadOnly(value);                           break;   // Ability to edit text in the Edit object
      case GRAPH_OBJ_PROP_LEVELS                :  obj.SetLevels((int)value);                            break;   // Number of levels
      case GRAPH_OBJ_PROP_LEVELCOLOR            :  obj.SetLevelColor((color)value,modifier);             break;   // Level line color
      case GRAPH_OBJ_PROP_LEVELSTYLE            :  obj.SetLevelStyle((ENUM_LINE_STYLE)value,modifier);   break;   // Level line style
      case GRAPH_OBJ_PROP_LEVELWIDTH            :  obj.SetLevelWidth((int)value,modifier);               break;   // Level line width
      case GRAPH_OBJ_PROP_ALIGN                 :  obj.SetAlign((ENUM_ALIGN_MODE)value);                 break;   // Horizontal text alignment in the Edit object (OBJ_EDIT)
      case GRAPH_OBJ_PROP_FONTSIZE              :  obj.SetFontSize((int)value);                          break;   // Font size
      case GRAPH_OBJ_PROP_RAY_LEFT              :  obj.SetFlagRayLeft(value);                            break;   // Ray goes to the left
      case GRAPH_OBJ_PROP_RAY_RIGHT             :  obj.SetFlagRayRight(value);                           break;   // Ray goes to the right
      case GRAPH_OBJ_PROP_RAY                   :  obj.SetFlagRay(value);                                break;   // Vertical line goes through all windows of a chart
      case GRAPH_OBJ_PROP_ELLIPSE               :  obj.SetFlagEllipse(value);                            break;   // Display the full ellipse of the Fibonacci Arc object
      case GRAPH_OBJ_PROP_ARROWCODE             :  obj.SetArrowCode((uchar)value);                       break;   // Arrow code for the Arrow object
      case GRAPH_OBJ_PROP_ANCHOR                :  obj.SetAnchor((int)value);                            break;   // Position of the binding point of the graphical object
      case GRAPH_OBJ_PROP_XDISTANCE             :  obj.SetXDistance((int)value);                         break;   // Distance from the base corner along the X axis in pixels
      case GRAPH_OBJ_PROP_YDISTANCE             :  obj.SetYDistance((int)value);                         break;   // Distance from the base corner along the Y axis in pixels
      case GRAPH_OBJ_PROP_DIRECTION             :  obj.SetDirection((ENUM_GANN_DIRECTION)value);         break;   // Gann object trend
      case GRAPH_OBJ_PROP_DEGREE                :  obj.SetDegree((ENUM_ELLIOT_WAVE_DEGREE)value);        break;   // Elliott wave markup level
      case GRAPH_OBJ_PROP_DRAWLINES             :  obj.SetFlagDrawLines(value);                          break;   // Display lines for Elliott wave markup
      case GRAPH_OBJ_PROP_STATE                 :  obj.SetFlagState(value);                              break;   // Button state (pressed/released)
      case GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID    :  obj.SetChartObjChartID(value);                        break;   // Chart object ID (OBJ_CHART)
      case GRAPH_OBJ_PROP_CHART_OBJ_PERIOD      :  obj.SetChartObjPeriod((ENUM_TIMEFRAMES)value);        break;   // Chart object period
      case GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE  :  obj.SetChartObjChartScale((int)value);                break;   // Time scale display flag for the Chart object
      case GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE :  obj.SetFlagChartObjPriceScale(value);                 break;   // Price scale display flag for the Chart object
      case GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE :  obj.SetFlagChartObjDateScale(value);                  break;   // Chart object scale
      case GRAPH_OBJ_PROP_XSIZE                 :  obj.SetXSize((int)value);                             break;   // Object distance along the X axis in pixels
      case GRAPH_OBJ_PROP_YSIZE                 :  obj.SetYSize((int)value);                             break;   // Object height along the Y axis in pixels
      case GRAPH_OBJ_PROP_XOFFSET               :  obj.SetXOffset((int)value);                           break;   // X coordinate of the upper-left corner of the visibility area
      case GRAPH_OBJ_PROP_YOFFSET               :  obj.SetYOffset((int)value);                           break;   // Y coordinate of the upper-left corner of the visibility area
      case GRAPH_OBJ_PROP_BGCOLOR               :  obj.SetBGColor((color)value);                         break;   // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
      case GRAPH_OBJ_PROP_CORNER                :  obj.SetCorner((ENUM_BASE_CORNER)value);               break;   // Chart corner for binding a graphical object
      case GRAPH_OBJ_PROP_BORDER_TYPE           :  obj.SetBorderType((ENUM_BORDER_TYPE)value);           break;   // Border type for "Rectangle border"
      case GRAPH_OBJ_PROP_BORDER_COLOR          :  obj.SetBorderColor((color)value);                     break;   // Border color for the OBJ_EDIT and OBJ_BUTTON objects
      case GRAPH_OBJ_PROP_BASE_ID               :  obj.SetBaseObjectID(value);                           break;   // Base object ID
      case GRAPH_OBJ_PROP_GROUP                 :  obj.SetGroup((int)value);                             break;   // Graphical object group
      case GRAPH_OBJ_PROP_CHANGE_HISTORY        :  obj.SetAllowChangeMemory((bool)value);                break;   // Flag of storing the change history
      case GRAPH_OBJ_PROP_ID                    :  // Object ID
      case GRAPH_OBJ_PROP_TYPE                  :  // Graphical object type (ENUM_OBJECT)
      case GRAPH_OBJ_PROP_ELEMENT_TYPE          :  // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
      case GRAPH_OBJ_PROP_SPECIES               :  // Graphical object species (ENUM_GRAPH_OBJ_SPECIES)
      case GRAPH_OBJ_PROP_BELONG                :  // Graphical object affiliation
      case GRAPH_OBJ_PROP_CHART_ID              :  // Chart ID
      case GRAPH_OBJ_PROP_WND_NUM               :  // Chart subwindow index
      case GRAPH_OBJ_PROP_NUM                   :  // Object index in the list
      case GRAPH_OBJ_PROP_CREATETIME            :  // Object creation time
      default  : break;
     }
  }
//+------------------------------------------------------------------+

Wenn ein ungültiger Zeiger auf das Objekt übergeben wurde oder es sich nicht um ein untergeordnetes Objekt handelt (nicht an das Basisobjekt gebunden) — exit. Als Nächstes setzen wir einfach die an die Methode übergebene Eigenschaft des Objekts. Einige Objekteigenschaften können nicht geändert werden. Deshalb stehen sie am Ende der 'switch'-Liste und werden in keiner Weise behandelt.

Die Methode setzt eine reelle Eigenschaft auf das angegebene untergeordnete Objekt:

//+------------------------------------------------------------------+
//|Set a real property to the specified subordinate object           |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetDependentDBL(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier)
  {
   if(obj==NULL || obj.BaseObjectID()==0)
      return;
   switch(prop)
     {
      case GRAPH_OBJ_PROP_PRICE                 : obj.SetPrice(value,modifier);        break;   // Price coordinate
      case GRAPH_OBJ_PROP_LEVELVALUE            : obj.SetLevelValue(value,modifier);   break;   // Level value
      case GRAPH_OBJ_PROP_SCALE                 : obj.SetScale(value);                 break;   // Scale (property of Gann objects and Fibonacci Arcs objects)
      case GRAPH_OBJ_PROP_ANGLE                 : obj.SetAngle(value);                 break;   // Angle
      case GRAPH_OBJ_PROP_DEVIATION             : obj.SetDeviation(value);             break;   // Deviation of the standard deviation channel
      default: break;
     }
  }
//+------------------------------------------------------------------+

Die Methode setzt eine String-Eigenschaft auf das angegebene untergeordnete Objekt:

//+------------------------------------------------------------------+
//| Set a string property to the specified subordinate object        |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetDependentSTR(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier)
  {
   if(obj==NULL || obj.BaseObjectID()==0)
      return;
   obj.SetProperty(prop,modifier,value);
   switch(prop)
     {
      case GRAPH_OBJ_PROP_TEXT                  : obj.SetText(value);                  break;   // Object description (the text contained in the object)
      case GRAPH_OBJ_PROP_TOOLTIP               : obj.SetTooltip(value);               break;   // Tooltip text
      case GRAPH_OBJ_PROP_LEVELTEXT             : obj.SetLevelText(value,modifier);    break;   // Level description
      case GRAPH_OBJ_PROP_FONT                  : obj.SetFont(value);                  break;   // Font
      case GRAPH_OBJ_PROP_BMPFILE               : obj.SetBMPFile(value,modifier);      break;   // BMP file name for the "Bitmap Level" object
      case GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL      : obj.SetChartObjSymbol(value);        break;   // Chart object symbol
      case GRAPH_OBJ_PROP_BASE_NAME             : obj.SetBaseName(value);              break;   // Base object name
      case GRAPH_OBJ_PROP_NAME                  : // Object name
      default :  break;
     }
  }
//+------------------------------------------------------------------+

Beide Methoden sind identisch mit der Methode zum Setzen einer Integer-Eigenschaft.


Verschieben und Löschen eines zusammengesetzten grafischen Objekts

Beim Verschieben eines zusammengesetzten grafischen Objekts (das nur verschoben werden kann, wenn auch das Basisobjekt verschoben wird) müssen wir auch alle untergeordneten grafischen Objekte, die mit dem Basisobjekt verbunden sind, verschieben. Wie ich bereits erwähnt habe, kann dies nicht durch einfache Ereignisverfolgung geschehen — das Ereignis tritt ein, wenn die Maustaste nach dem Ziehen des grafischen Objekts losgelassen wird. Das Objekt erhält seine endgültigen geänderten Eigenschaften, die den an es gebundenen Objekten zugewiesen werden sollten, damit sie ebenfalls an die Positionen verschoben werden, die ihren Positionsankerkoordinaten entsprechen. Dies ist der letzte Schritt beim Verschieben eines zusammengesetzten grafischen Objekts. Während wir ein Objekt mit der Maus ziehen und es noch nicht losgelassen haben, müssen wir auch die Änderung der Position eines grafischen Objekts auf dem Chart verfolgen, um seine Koordinaten interaktiv zu verfolgen und untergeordnete Objekte, die an das Basisobjekt gebunden sind, entsprechend zu verschieben. Dies werde ich später nachholen. Derzeit werde ich die Neuberechnung des Standortes der untergeordneten Objekte nach der Verschiebung des Basisobjekts in einem zusammengesetzten grafischen Objekt implementieren.

Um dies zu erreichen, fügen wir den folgenden Codeblock zu derselben abstrakten grafischen Objektklasse hinzu, der die Änderungen in den Objekteigenschaften überprüft:

//+------------------------------------------------------------------+
//| Check object property changes                                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCheckChanged(void)
  {
   CGBaseObj::ClearEventsList();
   bool changed=false;
   int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME)
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }
   if(changed)
     {
      for(int i=0;i<this.m_list_events.Total();i++)
        {
         CGBaseEvent *event=this.m_list_events.At(i);
         if(event==NULL)
            continue;
         ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam());
        }
      if(this.AllowChangeHistory())
        {
         int total=HistoryChangesTotal();
         if(this.CreateNewChangeHistoryObj(total<1))
            ::Print
              (
               DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_SUCCESS_CREATE_SNAPSHOT)," #",(total==0 ? "0-1" : (string)total),
               ": ",this.HistoryChangedObjTimeChangedToString(total-1)
              );
        }
      //--- If subordinate objects are attached to the base one (in a composite graphical object)
      if(this.m_list.Total()>0)
        {
         //--- In the loop by the number of added graphical objects,
         for(int i=0;i<this.m_list.Total();i++)
           {
            //--- get the next graphical object,
            CGStdGraphObj *dep=m_list.At(i);
            if(dep==NULL)
               continue;
            //--- get the data object of its pivot points,
            CLinkedPivotPoint *pp=dep.GetLinkedPivotPoint();
            if(pp==NULL)
               continue;
            //--- get the number of coordinate points the object is attached to
            int num=pp.GetNumLinkedCoords();
            //--- In the loop by the object coordinate points,
            for(int j=0;j<num;j++)
              {
               //--- get the number of coordinate points of the base object for setting the X coordinate
               int numx=pp.GetBasePivotsNumX(j);
               //--- In the loop by each coordinate point for setting the X coordinate,
               for(int nx=0;nx<numx;nx++)
                 {
                  //--- get the property for setting the X coordinate, its modifier
                  //--- and set it in the object selected as the current one in the main loop
                  int prop_from=pp.GetPropertyX(j,nx);
                  int modifier_from=pp.GetPropertyModifierX(j,nx);
                  this.SetCoordXToDependentObj(dep,prop_from,modifier_from,nx);
                 }
               //--- Get the number of coordinate points of the base object for setting the Y coordinate
               int numy=pp.GetBasePivotsNumY(j);
               //--- In the loop by each coordinate point for setting the Y coordinate,
               for(int ny=0;ny<numy;ny++)
                 {
                  //--- get the property for setting the Y coordinate, its modifier
                  //--- and set it in the object selected as the current one in the main loop
                  int prop_from=pp.GetPropertyY(j,ny);
                  int modifier_from=pp.GetPropertyModifierY(j,ny);
                  this.SetCoordYToDependentObj(dep,prop_from,modifier_from,ny);
                 }
              }
           }
         //--- Upon completion of the loop of handling all bound objects, redraw the chart to display all the changes
         ::ChartRedraw(m_chart_id);
        }
      //--- Save the current properties as the previous ones
      this.PropertiesCopyToPrevData();
     }
  }
//+------------------------------------------------------------------+

Wird bei einem grafischen Objekt eine Änderung festgestellt, wird geprüft, ob das Objekt untergeordnete Objekte hat. Wenn ja (die Liste ist nicht leer), bewegen wir uns in der Schleife entlang jedes untergeordneten Objekts und setzen neue Werte für seine Standortkoordinaten, die im Objekt angegeben sind und die Koordinaten des Basisobjekts identifizieren. Anhand dieser Koordinaten erhalten wir die Werte und setzen sie in die Koordinaten des untergeordneten Objekts. Nach Abschluss der Schleife aktualisieren wir das Chart, um alle Änderungen sofort anzuzeigen und nicht auf einen neuen Tick zu warten.

Das zusammengesetzte grafische Objekt kann aus dem Chart entfernt werden, indem das Basisobjekt, an das alle untergeordneten Objekte gebunden sind, entfernt wird.
Dieser Fall (Entfernen eines Basisobjekts) wird in der Kollektionsklasse für grafische Elemente in \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh behandelt.

Im privaten Abschnitt der Klasse deklarieren wir die Methode, die das Entfernen eines erweiterten grafischen Standardobjekts behandelt:

//--- Update the list of (1) all graphical objects, (2) on the specified chart, fill in the data on the number of new ones and set the event flag
   void              Refresh(void);
   void              Refresh(const long chart_id);
//--- Event handler
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

private:
//--- Handle the removal of extended graphical objects
   void              DeleteExtendedObj(CGStdGraphObj *obj);
//--- Create a new graphical object, return the pointer to the chart management object

Schreiben wir seine Implementierung außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Handle the removal of extended graphical objects                 |
//+------------------------------------------------------------------+
void CGraphElementsCollection::DeleteExtendedObj(CGStdGraphObj *obj)
  {
   if(obj==NULL)
      return;
   //--- Save the ID of the graphical object chart and the number of subordinate objects in its list
   long chart_id=obj.ChartID();
   int total=obj.GetNumDependentObj();
   //--- If the list of subordinate objects is not empty (this is the base object)
   if(total>0)
     {
      //--- In the loop, move along all dependent objects and remove them
      for(int n=total-1;n>WRONG_VALUE;n--)
        {
         //--- Get the next graphical object
         CGStdGraphObj *dep=obj.GetDependentObj(n);
         if(dep==NULL)
            continue;
         //--- If failed to remove it from the chart, display the appropriate message in the journal
         if(!::ObjectDelete(dep.ChartID(),dep.Name()))
            CMessage::ToLog(DFUN+dep.Name()+": ",MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART);
        }
      //--- Upon the loop completion, update the chart to display the changes and exit the method
      ::ChartRedraw(chart_id);
      return;
     }
   //--- If this is a subordinate object
   else if(obj.BaseObjectID()>0)
     {
      //--- Get the base object name and its ID
      string base_name=obj.BaseName();
      long base_id=obj.BaseObjectID();
      //--- Get the base object from the graphical object collection list
      CGStdGraphObj *base=GetStdGraphObject(base_name,chart_id);
      if(base==NULL)
         return;
      //--- get the number of dependent objects in its list
      int count=base.GetNumDependentObj();
      //--- In the loop, move along all its dependent objects and remove them
      for(int n=count-1;n>WRONG_VALUE;n--)
        {
         //--- Get the next graphical object
         CGStdGraphObj *dep=base.GetDependentObj(n);
         //--- If failed to get the pointer or the object has already been removed from the chart, move on to the next one
         if(dep==NULL || !this.IsPresentGraphObjOnChart(dep.ChartID(),dep.Name()))
            continue;
         //--- If failed to delete the graphical object from the chart,
         //--- display the appropriate message in the journal and move on to the next one
         if(!::ObjectDelete(dep.ChartID(),dep.Name()))
           {
            CMessage::ToLog(DFUN+dep.Name()+": ",MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART);
            continue;
           }
        }
      //--- Remove the base object from the chart and from the list
      if(!::ObjectDelete(base.ChartID(),base.Name()))
         CMessage::ToLog(DFUN+base.Name()+": ",MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART);
     }
   //--- Update the chart for displaying the changes
   ::ChartRedraw(chart_id);
  }
//+------------------------------------------------------------------+

Die gesamte Methodenlogik ist in den Kommentaren zum Code beschrieben. Kurz gesagt, wenn das Basisobjekt entfernt wird (seine Liste enthält gebundene Objekte), werden alle an dieses Objekt gebundenen Objekte aus dem Chart entfernt. Wenn stattdessen ein untergeordnetes grafisches Objekt entfernt wird, müssen wir das Objekt kennen, an das es gebunden war (das Basisobjekt eines zusammengesetzten grafischen Objekts finden), dann die Liste der abhängigen Objekte, die an es gebunden sind, durchgehen und sie alle entfernen.

Diese Methode wird in der Methode zur Aktualisierung der Liste aller grafischen Objekte im Block zum Entfernen eines grafischen Objekts aufgerufen:

//+------------------------------------------------------------------+
//| Update the list of all graphical objects                         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(void)
  {
   this.RefreshForExtraObjects();
//--- Declare variables to search for charts
   long chart_id=0;
   int i=0;
//--- In the loop by all open charts in the terminal (no more than 100)
   while(i<CHARTS_MAX)
     {
      //--- Get the chart ID
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Get the pointer to the object for managing graphical objects
      //--- and update the list of graphical objects by chart ID
      CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id);
      //--- If failed to get the pointer, move on to the next chart
      if(obj_ctrl==NULL)
         continue;
      //--- If the number of objects on the chart changes
      if(obj_ctrl.IsEvent())
        {
         //--- If a graphical object is added to the chart
         if(obj_ctrl.Delta()>0)
           {
            //--- Get the list of added graphical objects and move them to the collection list
            //--- (if failed to move the object to the collection, move on to the next object)
            if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl))
               continue;
           }
         //--- If the graphical object has been removed
         else if(obj_ctrl.Delta()<0)
           {
            int index=WRONG_VALUE;
            //--- In the loop by the number of removed graphical objects
            for(int j=0;j<-obj_ctrl.Delta();j++)
              {
               // Find an extra object in the list
               CGStdGraphObj *obj=this.FindMissingObj(chart_id,index);
               if(obj!=NULL)
                 {
                  //--- Get the removed object parameters
                  long   lparam=obj.ChartID();
                  string sparam=obj.Name();
                  double dparam=(double)obj.TimeCreate();
                  //--- If this is an extended graphical object
                  if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                    {                            
                     this.DeleteExtendedObj(obj);
                    }                            
                  //--- Move the graphical object class object to the list of removed objects
                  //--- and send the event to the control program chart
                  if(this.MoveGraphObjToDeletedObjList(index))
                     ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,lparam,dparam,sparam);
                 }
              }
           }
        }
      //--- Increase the loop index
      i++;
     }
  }
//+------------------------------------------------------------------+

Dies reicht aus, um das Entfernen eines zusammengesetzten grafischen Standardobjekts zu ermöglichen.

Lassen Sie uns die Ergebnisse testen.


Test

Um den Test durchzuführen, verwende ich den EA aus dem vorherigen Artikel und speichere ihn in \MQL5\Experts\TestDoEasy\Part94\ als TestDoEasyPart94.mq5.

Es gibt keine Änderungen im EA, außer dem Entfernen der Anzeige von Journaleinträgen bezüglich der erstellten Objekte, die das zusammengesetzte grafische Objekt im Block der Chart-Klickbehandlung im OnChartEvent()-Handler bilden:

   if(id==CHARTEVENT_CLICK)
     {
      if(!IsCtrlKeyPressed())
         return;
      //--- Get the chart click coordinates
      datetime time=0;
      double price=0;
      int sw=0;
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price))
        {
         //--- Get the right point coordinates for a trend line
         datetime time2=iTime(Symbol(),PERIOD_CURRENT,1);
         double price2=iOpen(Symbol(),PERIOD_CURRENT,1);
         
         //--- Create the "Trend line" object
         string name_base="TrendLineExt";
         engine.CreateLineTrend(name_base,0,true,time,price,time2,price2);
         //--- Get the object from the list of graphical objects by chart name and ID and pass its properties to the journal
         CGStdGraphObj *obj=engine.GraphGetStdGraphObjectExt(name_base,ChartID());
         
         //--- Create the "Left price label" object
         string name_dep="PriceLeft";
         engine.CreatePriceLabelLeft(name_dep,0,false,time,price);
         //--- Get the object from the list of graphical objects by chart name and ID and
         CGStdGraphObj *dep=engine.GraphGetStdGraphObject(name_dep,ChartID());
         //--- add it to the list of graphical objects bound to the "Trend line" object
         obj.AddDependentObj(dep);
         //--- Set its pivot point by X and Y axis to the trend line left point
         dep.AddNewLinkedCoord(GRAPH_OBJ_PROP_TIME,0,GRAPH_OBJ_PROP_PRICE,0);
         
         //--- Create the "Right price label" object
         name_dep="PriceRight";
         engine.CreatePriceLabelRight(name_dep,0,false,time2,price2);
         //--- Get the object from the list of graphical objects by chart name and ID and
         dep=engine.GraphGetStdGraphObject(name_dep,ChartID());
         //--- add it to the list of graphical objects bound to the "Trend line" object
         obj.AddDependentObj(dep);
         //--- Set its pivot point by X and Y axis to the trend line right point
         dep.AddNewLinkedCoord(GRAPH_OBJ_PROP_TIME,1,GRAPH_OBJ_PROP_PRICE,1);
        }
     }

Die Tatsache, dass wir die Objekte "Linke Preislabel" und "Rechte Preislabel" als nicht erweiterte Objekte anlegen, sollte keine Rolle spielen, da alle angehängten Objekte nun den Status eines erweiterten grafischen Objekts in der Methode AddDependentObj() erhalten.

Kompilieren Sie den EA und starten Sie ihn auf dem Chart:


Wie man sieht, werden die untergeordneten Objekte erst beim Loslassen der Maustaste auf ihre Zielpositionen gesetzt. Ich werde dies in den kommenden Artikeln beheben. Das Entfernen eines Objekts funktioniert korrekt — alle untergeordneten Objekte werden ebenfalls entfernt. Das absichtliche Entfernen eines der untergeordneten Objekte führt zum Entfernen des gesamten zusammengesetzten grafischen Objekts.

Was kommt als Nächstes?

Im nächsten Artikel werde ich meine Arbeit an zusammengesetzten grafischen Objekten fortsetzen.

Alle Dateien der aktuellen Bibliotheksversion, des Test-EA und des Chart-Event-Control-Indikators für MQL5 sind unten zum Testen und Herunterladen angehängt. Stellen Sie Ihre Fragen, Kommentare und Vorschläge bitte im Kommentarteil.

Zurück zum Inhalt

*Frühere Artikel dieser Serie:

Grafiken in der Bibliothek DoEasy (Teil 89): Programmieren von grafischen Standardobjekten, grundlegende Funktionsweise
Grafiken in der DoEasy-Bibliothek (Teil 90): Standard-Ereignisse für grafische Objekte. grundlegende Funktionsweise
Grafiken in der DoEasy-Bibliothek (Teil 91): Standard-Ereignisse für grafische Objekte. Geschichte der Objektnamensänderung
Grafiken in der DoEasy-Bibliothek (Teil 92): Speicherklasse der grafischen Standardobjekte. Änderungsverlauf der Objekteigenschaften
Grafiken in der Bibliothek DoEasy (Teil 93): Vorbereiten der Funktionen zur Erstellung zusammengesetzter grafischer Objekte

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

Beigefügte Dateien |
MQL5.zip (4202.7 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Tobias Johannes Zimmer
Tobias Johannes Zimmer | 14 März 2022 in 13:15
Jetzt wird mir klar, warum Sie eine Historie der Veränderung der Objekte wollten.
Lernen Sie, wie man ein Handelssystem mit dem RSI entwickelt Lernen Sie, wie man ein Handelssystem mit dem RSI entwickelt
In diesem Artikel werde ich Ihnen einen der beliebtesten und am häufigsten verwendeten Indikatoren in der Welt des Handels vorstellen: den RSI. Sie werden lernen, wie Sie ein Handelssystem mit diesem Indikator entwerfen können.
Der richtige Weg zur Auswahl eines Expert Advisors vom Markt Der richtige Weg zur Auswahl eines Expert Advisors vom Markt
In diesem Artikel werden wir einige der wesentlichen Punkte besprechen, auf die Sie beim Kauf eines Expert Advisors achten sollten. Wir werden auch nach Wegen suchen, um den Gewinn zu steigern, das Geld klug auszugeben und an diesen Ausgaben zu verdienen. Außerdem werden Sie nach der Lektüre des Artikels sehen, dass es möglich ist, auch mit einfachen und kostenlosen Produkten Geld zu verdienen.
Datenwissenschaft und maschinelles Lernen (Teil 01): Lineare Regression Datenwissenschaft und maschinelles Lernen (Teil 01): Lineare Regression
Es ist an der Zeit, dass wir als Händler unsere Systeme und uns selbst darauf trainieren, Entscheidungen auf der Grundlage von Zahlen zu treffen. Nicht unsere Augen oder wenn unser Bauchgefühl uns glauben macht, dass die Welt sich in diese Richtung bewegt, also lassen Sie uns senkrecht zur Richtung der Welle gehen.
Lernen Sie, wie Sie ein Handelssystem mit Hilfe der Envelopes entwickelt Lernen Sie, wie Sie ein Handelssystem mit Hilfe der Envelopes entwickelt
In diesem Artikel werde ich Ihnen eine der Methoden vorstellen, wie man mit Bändern handeln kann. Dieses Mal werden wir uns mit Envelopes (Hüllkurve) beschäftigen und sehen, wie einfach es ist, einige Strategien auf der Grundlage der Envelopes zu erstellen.