Von den Grundlagen zur Mittelstufe: Objektereignisse (II)
Einleitung
Im vorherigen Artikel „Von den Grundlagen zur Mittelstufe: Objekt-Ereignisse (I)“ wurde der erste grundlegende Teil erläutert und veranschaulicht, wobei der Schwerpunkt vor allem auf Ereignissen lag, die bei Objekten auftreten. Allerdings müssen wir noch drei weitere Ereignisse besprechen, die ausgelöst werden können, wenn ein Objekt mit dem Benutzer interagiert. Von diesen drei Ereignissen muss eines explizit aktiviert werden, damit MetaTrader 5 tatsächlich damit beginnt, es zu generieren. Andernfalls erhält unser Code, selbst wenn er darauf vorbereitet ist, dieses Ereignis zu verarbeiten, niemals eine Benachrichtigung darüber, dass es eingetreten ist.
Es ist sehr wichtig, alle diese Ereignistypen zu verstehen, bevor man sie gemeinsam einsetzt. Daher gehen wir wie folgt vor: Wir werden kurze Abschnitte anlegen, die ausschließlich erläutern, wie und wann MetaTrader 5 jeden dieser letzten drei Ereignistypen generiert. Sobald Sie sie verstanden haben, können wir uns ansehen, wie man sie gemeinsam nutzt, um eine interessante Funktionalität zu implementieren. Beginnen wir also.
Das Ereignis CHARTEVENT_OBJECT_DELETE
Dieses Ereignis muss, ebenso wie CHARTEVENT_OBJECT_CREATE und andere mausbezogene Ereignisse, von unserer Anwendung aktiviert und deaktiviert werden. Dies geschieht, damit MetaTrader 5 weiß, wann eine auf dem Chart ausgeführte Anwendung eine Benachrichtigung erhalten muss, dass ein auf dem Chart vorhandenes Objekt gelöscht wurde.
Dieses Ereignis hat eindeutig einen vorbeugenden Zweck. Dies ist wichtig, denn wenn wir eine bestimmte Objektkonfiguration in der Anwendung beibehalten möchten, können wir mit diesem Ereignis verhindern, dass der Benutzer ein sensibles oder kritisches Objekt aus dem Chart löscht – sei es versehentlich beim Löschen von Objekten aus dem Chart oder aus einem anderen Grund.
Im Prinzip ist dieses Ereignis sehr einfach. Bei der Programmierung oder Implementierung des Codes müssen jedoch einige Vorsichtsmaßnahmen getroffen werden. Der Grund für diese Vorsichtsmaßnahmen liegt darin, dass es in manchen Fällen zu Problemen im Zusammenhang mit dem Farbschema kommen kann. Dies führt letztendlich dazu, dass es sehr schwierig ist, das Objekt korrekt darzustellen, wenn die Anwendung es automatisch neu erstellen muss.
Aber das, wovon ich hier spreche, ist für uns hier in keiner Weise von Belang. In diesem Material geht es nicht darum, zu erläutern, wie eine universelle Lösung umgesetzt werden kann, die jeden möglichen Fall abdeckt. Außerdem ist jeder Fall anders.
Es gibt keine vollständig integrierte und universelle Methode, um ein Objekt neu zu erstellen, das fälschlicherweise aus dem Chart gelöscht wurde. Als Programmierer fallen Ihnen sicherlich viele Möglichkeiten ein, dies umzusetzen. Ich persönlich empfehle die Verwendung einer kleinen Struktur, in der die Objekteigenschaften der von uns – oder besser gesagt: von unserer Anwendung – erstellten Objekte gespeichert werden. Auf diese Weise wird die Anwendung, wenn sie ein Objekt neu erstellen muss, dies mit dem passenden Farbschema bzw. der passenden Positionierung tun. Abgesehen natürlich von anderen Aspekten wie der Schriftart, der Schriftgröße und der Objektgröße – ich denke, der Gedanke ist klar.
Nach dieser kurzen Einführung sind Sie nun wohl bereit zu sehen, wie der erste Code in diesem Artikel aussieht. Der Code ist unten aufgeführt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Prefix "Demo" 05. //+------------------------------------------------------------------+ 06. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 07. //+------------------------------------------------------------------+ 08. string gl_szObjectName; 09. //+------------------------------------------------------------------+ 10. int OnInit() 11. { 12. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); 13. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 14. 15. gl_szObjectName = macro_NameObject; 16. Proc_Object(); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+ 26. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 27. { 28. switch (id) 29. { 30. case CHARTEVENT_MOUSE_MOVE: 31. ObjectSetString(0, gl_szObjectName, OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam)); 32. break; 33. case CHARTEVENT_OBJECT_DELETE: 34. if (sparam == gl_szObjectName) Proc_Object(); 35. break; 36. } 37. ChartRedraw(); 38. }; 39. //+------------------------------------------------------------------+ 40. void OnDeinit(const int reason) 41. { 42. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 43. ObjectsDeleteAll(0, def_Prefix); 44. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); 45. ChartRedraw(); 46. }; 47. //+------------------------------------------------------------------+ 48. void Proc_Object(void) 49. { 50. ObjectCreate(0, gl_szObjectName, OBJ_LABEL, 0, 0, 0); 51. ObjectSetInteger(0, gl_szObjectName, OBJPROP_SELECTABLE, true); 52. ObjectSetInteger(0, gl_szObjectName, OBJPROP_XDISTANCE, 50); 53. ObjectSetInteger(0, gl_szObjectName, OBJPROP_YDISTANCE, 50); 54. ObjectSetInteger(0, gl_szObjectName, OBJPROP_XSIZE, 150); 55. ObjectSetInteger(0, gl_szObjectName, OBJPROP_COLOR, clrMediumBlue); 56. ObjectSetInteger(0, gl_szObjectName, OBJPROP_FONTSIZE, 20); 57. ObjectSetString(0, gl_szObjectName, OBJPROP_FONT, "Lucida Console"); 58. } 59. //+------------------------------------------------------------------+
Code 01
Der Zweck von Code 01 ist ganz einfach: zu zeigen, was passiert, wenn wir ein Objekt aus dem Chart löschen. Da wir die Benachrichtigung aktivieren und deaktivieren müssen, damit MetaTrader 5 uns mitteilt, ob ein Objekt gelöscht wurde oder nicht, lässt sich anhand dieses Codes viel leichter nachvollziehen, wie das Ganze funktioniert.
Das Schwierigste daran ist vielleicht gar nicht, die Benachrichtigung zu aktivieren, da dies sehr einfach ist und lediglich Zeile 13 erfordert. In der Regel aktivieren wir die Benachrichtigung gleich zu Beginn der Anwendungsausführung, damit wir keine möglicherweise auftretenden Löschereignisse verpassen. Die eigentliche Schwierigkeit besteht darin, genau zu wissen, wann diese Benachrichtigung zu deaktivieren ist. MetaTrader 5 funktioniert auf eine bestimmte Art und Weise, und je nachdem, zu welchem Zeitpunkt die Benachrichtigung deaktiviert wird, kann es zu recht seltsamen, wenn auch in den meisten Fällen völlig harmlosen Ergebnissen kommen.
Um dies zu verstehen, versuchen Sie einmal, die Position des Inhalts aus Zeile 42 zu ändern. Diese Zeile ist dafür zuständig, Benachrichtigungen über Ereignisse zu deaktivieren, wenn ein Objekt aus dem Chart gelöscht wird. Da der Quellcode in der Anwendung enthalten sein wird, können Sie versuchen, den Inhalt von Zeile 42 und Zeile 43 zu vertauschen. Dadurch wird das Ergebnis, das sich aus dem Entfernen des Indikators vom Chart ergibt, recht interessant. Auf diese Weise werden wir feststellen, dass wir diese Aktionen nicht immer in beliebiger Reihenfolge ausführen können. Außerdem wird es Ihnen helfen, verschiedene Sachverhalte zu verstehen, die sich sonst nur schwer erklären lassen würden.
Auf jeden Fall werden wir, wenn Code 01 ausgeführt wird, grundsätzlich zwei Arten von Aktionen ausführen können. Das erste Beispiel ist in der folgenden Animation zu sehen:

Animation 01
In diesem Fall wählen wir das Objekt aus und löschen es mit der Entf-Taste. Bitte beachten Sie, dass das Objekt unmittelbar nach dem Drücken der Entf-Taste neu erstellt wird. Da dies jedoch an ein Mausereignis zur Aktualisierung der Informationen gekoppelt ist, werden die Informationen erst dann korrekt angezeigt, wenn eine Mausbewegung oder ein Mausereignis stattfindet. Der zweite Fall ist der Versuch, das Objekt über das Fenster der Objektliste zu löschen. Dies ist in der folgenden Animation zu sehen:

Animation 02
Wie in Animation 01 zu sehen ist, wird auch hier in Animation 02 deutlich, dass das Objekt genau in dem Moment, in dem wir es löschen, neu erstellt wird. Trotzdem wird dies im Fenster nicht sofort angezeigt. Das Fenster muss erneut geöffnet werden, damit das Objekt wieder in der Liste der im Chart vorhandenen Objekte angezeigt wird.
Dieses Ereignis dient also dazu, uns darüber zu benachrichtigen, dass ein Objekt aus dem Chart gelöscht wurde. Nun müssen wir uns noch zwei weitere Fälle ansehen. Hier treten jedoch einige recht interessante Probleme auf. Um dies besser zu erklären, beginnen wir mit einem neuen Abschnitt.
Das Ereignis CHARTEVENT_OBJECT_CHANGE
Das Ereignis, das wir uns nun näher ansehen werden, ist recht interessant, wenn man versteht, wie es funktioniert. Das liegt daran, dass sich damit – je nach Situation – einige sehr interessante Funktionen umsetzen lassen, da es jedes Mal generiert wird, wenn sich eine Objekteigenschaft ändert.
Im vorigen Abschnitt haben wir erwähnt, dass es keine Möglichkeit gibt, einen 100 % universellen Mechanismus zur Wiederherstellung eines gelöschten Objekts zu erstellen. Und das stimmt. Je nachdem, wie der Code implementiert ist, können wir jedoch einen geeigneten Weg finden, fast jede Art von Objekt auf eine generischere Weise neu zu erstellen. Nach dem zu urteilen, was bisher gezeigt wurde, handelt es sich hierbei um eine recht komplexe Aufgabe. Bleiben Sie also gelassen, denn auch wenn wir mit relativ einfachen Mechanismen gute Anwendungen erstellen können, finden wir in Zukunft vielleicht noch bessere Wege. Wissen baut sich auf, ebenso wie Erfahrung. Deshalb ist es immer sinnvoll, zu lernen und zu üben. Ohne Hast, aber mit dem Ziel, kontinuierlich dazuzulernen.
Um zu zeigen, wie interessant das sein kann und warum es wichtig ist, genau zu verstehen, was implementiert wird, nehmen wir Code 01, den wir im vorigen Abschnitt betrachtet haben, und passen ihn so an, dass wir das Ereignis aus diesem Abschnitt in Aktion sehen können. Dies geschieht im folgenden Code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Prefix "Demo" 05. //+------------------------------------------------------------------+ 06. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 07. //+------------------------------------------------------------------+ 08. struct st_Obj 09. { 10. //+----------------+ 11. private: 12. string szName, 13. font; 14. bool Selectable; 15. int x, 16. y, 17. w, 18. fontSize; 19. color cor; 20. //+----------------+ 21. public: 22. //+----------------+ 23. void SetDefault(const string arg) 24. { 25. szName = arg; 26. font = "Lucida Console"; 27. Selectable = true; 28. x = 50; 29. y = 50; 30. w = 150; 31. cor = clrMediumBlue; 32. fontSize = 20; 33. } 34. //+----------------+ 35. void Create(void) 36. { 37. ObjectCreate(0, szName, OBJ_LABEL, 0, 0, 0); 38. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, Selectable); 39. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x); 40. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, y); 41. ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 42. ObjectSetInteger(0, szName, OBJPROP_COLOR, cor); 43. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, fontSize); 44. ObjectSetString(0, szName, OBJPROP_FONT, font); 45. } 46. //+----------------+ 47. string GetName(void) 48. { 49. return szName; 50. } 51. //+----------------+ 52. }gl_Obj; 53. //+------------------------------------------------------------------+ 54. int OnInit() 55. { 56. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); 57. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 58. 59. gl_Obj.SetDefault(macro_NameObject); 60. gl_Obj.Create(); 61. 62. return INIT_SUCCEEDED; 63. }; 64. //+------------------------------------------------------------------+ 65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 66. { 67. return rates_total; 68. }; 69. //+------------------------------------------------------------------+ 70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 71. { 72. switch (id) 73. { 74. case CHARTEVENT_MOUSE_MOVE: 75. ObjectSetString(0, gl_Obj.GetName(), OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam)); 76. break; 77. case CHARTEVENT_OBJECT_DELETE: 78. if (sparam == gl_Obj.GetName()) gl_Obj.Create(); 79. break; 80. case CHARTEVENT_OBJECT_CHANGE: 81. Comment("An event occurred: CHARTEVENT_OBJECT_CHANGE in object ["+sparam+"]"); 82. break; 83. } 84. ChartRedraw(); 85. }; 86. //+------------------------------------------------------------------+ 87. void OnDeinit(const int reason) 88. { 89. Comment(""); 90. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 91. ObjectsDeleteAll(0, def_Prefix); 92. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); 93. ChartRedraw(); 94. }; 95. //+------------------------------------------------------------------+
Code 02
Wenn wir Code 02 ausführen, werden wir zwei Dinge feststellen. Das erste wird in der folgenden Animation gezeigt.

Animation 03
Beachten Sie, dass genau in dem Moment, in dem wir eine Eigenschaft des Objekts ändern, ein Ereignis erzeugt wird, das in Zeile 80 abgefangen wird. Da wir die Quelle des Ereignisses nicht filtern, führt jede Änderung an einem beliebigen Objekt dazu, dass Zeile 81 in der oberen linken Ecke des Charts angezeigt wird, wie in Animation 03 zu sehen ist. Der interessanteste Teil beginnt jedoch, wenn das zweite Ereignis auftritt, wie in Animation 04 gezeigt:

Animation 04
„Seltsam. Warum hat das Objekt seine Eigenschaft nicht beibehalten? Wir rufen die in Zeile 35 gezeigte Prozedur auf. Im Fall von Code 01 verstehe ich den Grund, aber in diesem Fall bin ich davon ausgegangen, dass die Anwendung das Objekt mit denselben Attributen neu erstellen würde, wenn es aus dem Chart gelöscht würde. Die Funktion „SetDefault“ wurde nicht aufgerufen, um die in Animation 03 geänderten Werte zurückzusetzen. Meiner Meinung nach ist das ziemlich seltsam.“
Lieber Leser, viele Programmieranfänger tappen oft in dieselbe Falle: Sie schreiben Code, verstehen aber einige interne Aspekte des Codes selbst nicht. Das passiert in der Regel genau deshalb, weil sie Code aus anderen Quellen kopieren. Das kann aber auch passieren, wenn wir eine neue Implementierung testen, bei der zunächst alles einwandfrei zu laufen scheint, nur um dann während des Testens festzustellen, dass einige Details noch nicht umgesetzt wurden. In jedem Fall ist das kein Fehler; ganz im Gegenteil. Das ist eine gute Möglichkeit, neue Lösungen zu erproben und zu testen, die sich in Zukunft als interessant erweisen könnten.
Gut, aber worin besteht dann das Problem? Der Punkt ist: Wenn Sie MetaTrader 5 verwenden, um eine Eigenschaft eines Objekts zu aktualisieren, aktualisieren Sie damit nicht die Eigenschaften, mit denen es erstellt wurde. Wir aktualisieren nur die lokalen Eigenschaften des entsprechenden Objekts. Wenn es also gelöscht wird und Zeile 80 von Code 02 es neu erstellt, geschieht dies genau unter Verwendung der Parameter oder Eigenschaften, die definiert wurden und noch in der globalen Variablen gespeichert sind.
Somit wird das Objekt genau so neu erstellt, wie es im Code konfiguriert wurde. Um die Änderungen zu übernehmen, müssen wir das Objekt innerhalb der Anwendung aktualisieren, indem wir die entsprechenden Änderungen daran vornehmen. Und dafür müssen wir das Abfangen des Ereignisses nutzen, das genau in Zeile 82 erfolgt. Deshalb filtern wir das Ereignis in Code 02 nicht. Ich möchte, dass Sie verstehen, wie die Anwendung eine Benachrichtigung darüber erhält, dass sich eine Eigenschaft eines der auf dem Chart vorhandenen Objekte geändert hat.
Da MetaTrader uns nur den Namen des Objekts mitteilt, nicht aber, welche Eigenschaften genau geändert wurden, müssen wir alle diese Eigenschaften erfassen. Zumindest diejenigen, die uns wirklich interessieren. Obwohl viele Objekte ähnliche Eigenschaften aufweisen, gehören manche Eigenschaften ausschließlich zu einem bestimmten Objekt und sind bei anderen nicht vorhanden. Deshalb haben wir gesagt, dass die Entwicklung einer zu 100 % universellen Lösung eine sehr komplexe Aufgabe ist.
Vor diesem Hintergrund können wir Code 02 so anpassen, dass einige Eigenschaften unverändert bleiben. Zumindest solange der Code läuft. Wird die Anwendung aus dem Chart entfernt, verliert sie ihre benutzerdefinierten Eigenschaften und kehrt zu dem zurück, was wir im Code implementiert haben. Es gibt mehrere Möglichkeiten, dieses Problem zu lösen. Aber darüber sprechen wir ein anderes Mal.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_Prefix "Demo" 005. //+------------------------------------------------------------------+ 006. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 007. //+------------------------------------------------------------------+ 008. struct st_Obj 009. { 010. //+----------------+ 011. private: 012. string szName, 013. font; 014. bool Selectable; 015. int x, 016. y, 017. w, 018. fontSize; 019. color cor; 020. //+----------------+ 021. public: 022. //+----------------+ 023. void SetDefault(const string arg) 024. { 025. szName = arg; 026. font = "Lucida Console"; 027. Selectable = true; 028. x = 50; 029. y = 50; 030. w = 150; 031. cor = clrMediumBlue; 032. fontSize = 20; 033. } 034. //+----------------+ 035. void Create(void) 036. { 037. ObjectCreate(0, szName, OBJ_LABEL, 0, 0, 0); 038. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, Selectable); 039. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x); 040. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, y); 041. ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 042. ObjectSetInteger(0, szName, OBJPROP_COLOR, cor); 043. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, fontSize); 044. ObjectSetString(0, szName, OBJPROP_FONT, font); 045. } 046. //+----------------+ 047. string GetName(void) 048. { 049. return szName; 050. } 051. //+----------------+ 052. void Update(void) 053. { 054. font = ObjectGetString(0, szName, OBJPROP_FONT); 055. Selectable = (int)ObjectGetInteger(0, szName, OBJPROP_SELECTABLE); 056. x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 057. y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 058. w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 059. fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 060. cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 061. } 062. //+----------------+ 063. }gl_Obj; 064. //+------------------------------------------------------------------+ 065. int OnInit() 066. { 067. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); 068. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 069. 070. gl_Obj.SetDefault(macro_NameObject); 071. gl_Obj.Create(); 072. 073. return INIT_SUCCEEDED; 074. }; 075. //+------------------------------------------------------------------+ 076. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 077. { 078. return rates_total; 079. }; 080. //+------------------------------------------------------------------+ 081. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 082. { 083. switch (id) 084. { 085. case CHARTEVENT_MOUSE_MOVE: 086. ObjectSetString(0, gl_Obj.GetName(), OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam)); 087. break; 088. case CHARTEVENT_OBJECT_DELETE: 089. if (sparam == gl_Obj.GetName()) gl_Obj.Create(); 090. break; 091. case CHARTEVENT_OBJECT_CHANGE: 092. if (sparam == gl_Obj.GetName()) gl_Obj.Update(); 093. break; 094. } 095. ChartRedraw(); 096. }; 097. //+------------------------------------------------------------------+ 098. void OnDeinit(const int reason) 099. { 100. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 101. ObjectsDeleteAll(0, def_Prefix); 102. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); 103. ChartRedraw(); 104. }; 105. //+------------------------------------------------------------------+
Code 03
Code 03 zeigt, was zu tun ist. Das Ergebnis seiner Ausführung ist in der folgenden Animation zu sehen:

Animation 05
Bitte beachten Sie, dass das Objekt nun gemäß den Eigenschaften neu erstellt wird, die der Benutzer vor einigen Minuten festgelegt hat. Selbst wenn das Objekt aus dem Chart gelöscht wird, wird es aufgrund seiner Bedeutung neu erstellt. Anders als zuvor wird es nun jedoch nicht mit den in der Anwendung definierten Eigenschaften neu erstellt, sondern mit den vom Benutzer festgelegten Eigenschaften, die durch den Aufruf in Zeile 92 gespeichert wurden.
Beachten Sie jedoch, dass wir diesmal eine Filterung verwenden. Das ist aufgrund der Art dessen, was wir hier tun, wichtig. Stellen Sie sich folgendes Szenario vor: Sie haben zwei Objekte im Chart und beschließen, die Eigenschaften eines davon zu ändern. Bis hierhin ist alles in Ordnung. Wenn wir jedoch das von unserer Anwendung gesteuerte Objekt löschen, wird es sofort neu erstellt. Würde die Filterung in Zeile 92 jedoch nicht angewendet, würde die Änderung eines anderen als des von der Anwendung gesteuerten Objekts dazu führen, dass die Eigenschaften dieses anderen Objekts auf das von der Anwendung neu erstellte Objekt angewendet würden, was zu enormer Verwirrung bei allem führen würde, was auf dem Chart mit Objekten geschieht. Deshalb ist uns die Filterung in Zeile 92 so wichtig.
Testen Sie denselben Code 03 einmal ohne die Filterung in Zeile 92 und beobachten Sie die Ergebnisse. Manchmal kann dieser Ansatz interessant sein. Daher könnte es für Sie, lieber Leser, irgendwann einmal nützlich sein, zu verstehen und zu beobachten, was geschieht.
Lernen findet nicht nur statt, wenn wir Code korrekt schreiben.
Es findet auch statt, wenn der Code nicht wie erwartet funktioniert.
In diesem Fall ist es ebenso wichtig, zu verstehen, wie die Lösung gefunden wurde, wie die Lösung selbst.
Großartig, jetzt haben wir genügend Elemente, um den nächsten und letzten Ereignistyp zu untersuchen, der bei einem Objekt auftreten kann. Da wir es in diesem Fall mit einem klar bestimmten Objekt und einem speziell damit verknüpften Ereignis zu tun haben, werden wir ein kleines Experiment durchführen, das meiner Meinung nach sehr spannend und recht lehrreich sein wird. Wir werden etwas tun, was MetaTrader 5 standardmäßig nicht tut. Aber wir können eine Anwendung entwickeln, die dazu in der Lage ist. Angesichts der Art der Umsetzung wird das sehr interessant werden.
Wechseln wir also zu einem neuen Abschnitt, damit alles sauber voneinander getrennt ist.
Das Ereignis CHARTEVENT_OBJECT_ENDEDIT
Ein Punkt, den ich ziemlich bemerkenswert finde, ist, dass viele Nutzer keine Ahnung haben, wie sehr wir MetaTrader 5 dazu bringen können, bestimmte Arten von Aufgaben auszuführen. Oft handelt es sich dabei um Dinge, von denen die Leute hartnäckig behaupten, sie seien unmöglich oder unrealistisch umzusetzen.
Nun, im Grunde gibt es da einen Punkt, der einerseits ziemlich ungewöhnlich ist. Andererseits ist es nicht besonders sinnvoll, es wie bisher zu machen. Aber das Wichtigste ist Geduld. Der Punkt ist: Entgegen der landläufigen Meinung ist MQL5 keine Sprache, deren Schwerpunkt auf der Erstellung von Anwendungen für MetaTrader 5 liegt. Allerdings dient es im Allgemeinen genau diesem Zweck. Im Wesentlichen ist MQL5 eine Sprache, die darauf ausgerichtet ist, uns ein gewisses Maß an Kontrolle darüber zu geben, wie MetaTrader 5 funktionieren soll bzw. wie wir möchten, dass es funktioniert. Natürlich unter Einhaltung bestimmter Regeln. Ich sage das, weil es ohne das Verständnis dieses einfachen und klaren Punkts schwierig sein wird, zu verstehen, was wir in diesem Abschnitt tun werden.
Für die überwiegende Mehrheit der Nutzer, insbesondere für Anfänger, wird das, was hier getan wird, wie etwas nicht von dieser Welt erscheinen. Vielleicht Code, der von künstlicher Intelligenz oder Außerirdischen erstellt wurde. Es gibt jedoch nichts dergleichen. Was wir vorhaben, ist mit sehr wenig Wissen, aber einer guten Portion Fantasie und Kreativität durchaus machbar. Und natürlich darf auch eine gute Portion Beobachtungsgabe nicht fehlen.
Ich weiß nicht, ob Ihnen schon eines aufgefallen ist: Das OBJ_LABEL-Objekt ist dem OBJ_EDIT-Objekt sehr ähnlich. Allgemein gesagt besteht der wichtigste und vielleicht sogar einzige Unterschied zwischen den beiden Objekten darin, dass wir mit OBJ_EDIT Text direkt auf dem Chart bearbeiten können, während dies beim OBJ_LABEL-Objekt nicht möglich ist. Aber ist das wirklich so?
Nun, lieber Leser, tatsächlich hat das OBJ_EDIT-Objekt trotz der großen Ähnlichkeit nicht dieselben Eigenschaften wie das OBJ_LABEL-Objekt und umgekehrt. Das gilt allerdings nur, wenn wir die Dinge aus der Sicht des Nutzers betrachten. Aus Sicht der Programmierung sieht die Sache jedoch nicht ganz so aus. In diesem Fall sind beide Objekte im Grunde gleich und können sogar zusammenarbeiten, wenn wir nur darüber nachdenken, wie sich das umsetzen lässt.
Was wir hier tun werden, ist Folgendes: Wir werden eine Möglichkeit schaffen, das OBJ_LABEL-Objekt direkt auf dem Chart editierbar zu machen, ohne sein Eigenschaftenfenster öffnen zu müssen, um Text einzugeben. Scheint das unmöglich? Dann wollen wir mal sehen, wie das geht. Denken Sie daran, dass es hier um den Lernzweck geht; konzentrieren Sie sich daher nicht zu sehr darauf, wie der Code umgesetzt wird. Versuchen Sie zu verstehen, warum das, was Sie als Nächstes sehen werden, funktioniert. Das ist das Wichtigste.
Zunächst benötigen wir den Quellcode. Da wir nicht zu viel von Grund auf neu entwickeln und viel Zeit mit der Erläuterung unserer Schritte verschwenden wollen, werden wir eine Abwandlung des im vorigen Abschnitt behandelten Codes erstellen. Das sehen Sie unten:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_Prefix "Demo" 005. //+------------------------------------------------------------------+ 006. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 007. //+------------------------------------------------------------------+ 008. struct st_Obj 009. { 010. //+----------------+ 011. private: 012. string szName, 013. font; 014. bool Selectable; 015. int x, 016. y, 017. w, 018. h, 019. fontSize; 020. color cor, 021. backColor; 022. ENUM_OBJECT actual; 023. //+----------------+ 024. public: 025. //+----------------+ 026. void SetDefault(const string arg) 027. { 028. szName = arg; 029. font = "Lucida Console"; 030. Selectable = true; 031. x = 50; 032. y = 50; 033. w = 150; 034. h = 27; 035. cor = clrMediumBlue; 036. backColor = clrWhite; 037. fontSize = 20; 038. } 039. //+----------------+ 040. void Create(const ENUM_OBJECT type) 041. { 042. actual = type; 043. Recreates(); 044. } 045. //+----------------+ 046. void Recreates(void) 047. { 048. ObjectCreate(0, szName, actual, 0, 0, 0); 049. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, (actual != OBJ_EDIT ? Selectable : false)); 050. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x); 051. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, y); 052. ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 053. ObjectSetInteger(0, szName, OBJPROP_YSIZE, h); 054. ObjectSetInteger(0, szName, OBJPROP_COLOR, cor); 055. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, fontSize); 056. ObjectSetString(0, szName, OBJPROP_FONT, font); 057. if (actual == OBJ_EDIT) 058. { 059. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, backColor); 060. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 061. } 062. } 063. //+----------------+ 064. string GetName(void) 065. { 066. return szName; 067. } 068. //+----------------+ 069. void Update(void) 070. { 071. font = ObjectGetString(0, szName, OBJPROP_FONT); 072. Selectable = (int)ObjectGetInteger(0, szName, OBJPROP_SELECTABLE); 073. x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 074. y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 075. w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 076. h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 077. fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 078. cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 079. backColor = (color)((ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_LABEL ? backColor : ObjectGetInteger(0, szName, OBJPROP_BGCOLOR)); 080. } 081. //+----------------+ 082. }gl_Obj; 083. //+------------------------------------------------------------------+ 084. int OnInit() 085. { 086. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); 087. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 088. 089. gl_Obj.SetDefault(macro_NameObject); 090. gl_Obj.Create(OBJ_LABEL); 091. 092. return INIT_SUCCEEDED; 093. }; 094. //+------------------------------------------------------------------+ 095. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 096. { 097. return rates_total; 098. }; 099. //+------------------------------------------------------------------+ 100. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 101. { 102. switch (id) 103. { 104. case CHARTEVENT_MOUSE_MOVE : 105. ObjectSetString(0, gl_Obj.GetName(), OBJPROP_TEXT, StringFormat("%03d : %03d", (ushort)lparam, (ushort)dparam)); 106. break; 107. case CHARTEVENT_OBJECT_DELETE : 108. if (sparam == gl_Obj.GetName())gl_Obj.Recreates(); 109. break; 110. case CHARTEVENT_OBJECT_CHANGE : 111. if (sparam == gl_Obj.GetName()) gl_Obj.Update(); 112. break; 113. } 114. ChartRedraw(); 115. }; 116. //+------------------------------------------------------------------+ 117. void OnDeinit(const int reason) 118. { 119. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 120. ObjectsDeleteAll(0, def_Prefix); 121. ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); 122. ChartRedraw(); 123. }; 124. //+------------------------------------------------------------------+
Code 04
Wir haben hier ein paar Dinge hinzugefügt. Es ist jedoch Folgendes zu beachten: Code 04 funktioniert im Wesentlichen genauso wie Code 03. Beachten Sie jedoch, dass wir in Zeile 40, wenn wir die Prozedur zum Erstellen des Objekts aufrufen, angeben müssen, welcher Objekttyp erstellt werden soll. In diesem Fall werden wir mit OBJ_EDIT und OBJ_LABEL arbeiten. Mit diesen Änderungen schaffen wir die Grundlage für eine weitere Möglichkeit, deren Kern Sie bald verstehen werden: das Manipulieren eines OBJ_LABEL-Objekts, um Text direkt im Chart zu bearbeiten.
Beachten Sie nun den nächsten Punkt in Code 04. Bitte beachten Sie, dass wir in Zeile 49, in der wir das Objekt tatsächlich erstellen, die Eigenschaft OBJPROP_SELECTABLE festlegen. Diese Eigenschaft ist sehr wichtig, und wir müssen sie korrekt definieren. In einem OBJ_LABEL-Objekt müssen wir diese Eigenschaft auf „true“ setzen, wenn wir das Objekt später auswählen möchten, so wie wir es bisher getan haben. Wenn wir dieselbe Eigenschaft jedoch in einem OBJ_EDIT-Objekt auf „true“ setzen, können wir den Text nicht direkt im Chart bearbeiten.
Wie bereits erwähnt, weist jedes Objekt seine eigenen Besonderheiten auf, und je nach Art der Aktion, die wir ausführen möchten, müssen seine Eigenschaften auf eine bestimmte Weise definiert sein. Kurz gesagt: Wenn wir ein OBJ_EDIT-Objekt haben, bei dem die Eigenschaft OBJPROP_SELECTABLE auf „true“ gesetzt ist, können wir den Text zwar nicht bearbeiten, aber das Objekt auf dem Chart verschieben. Wird diese Eigenschaft auf „false“ gesetzt, können wir den Text zwar bearbeiten, das Objekt jedoch nicht auf dem Chart verschieben.
Bitte beachten Sie, dass wir in Zeile 90 den Typ des Objekts angeben, das wir erstellen möchten, nämlich ein OBJ_LABEL-Objekt. In Zeile 108 verwenden wir jedoch nicht mehr den Create-Aufruf; stattdessen rufen wir eine Funktion auf, die das Objekt neu erstellt. Das liegt daran, dass wir bereits wissen, welchen Typ wir erstellen müssen. Die Anwendung weiß das jedoch nicht, denn wenn wir diese Methode verwenden, um den Objekttyp abzufragen, erhalten wir fast immer den falschen Typ. Das liegt daran, dass MetaTrader 5 dieses Objekt bereits gelöscht hat.
Wenn wir das Ereignis CHARTEVENT_OBJECT_DELETE erhalten, wird nicht um Erlaubnis gebeten, das Objekt zu löschen. Zu diesem Zeitpunkt hat MetaTrader 5 das Objekt bereits gelöscht. Folglich werden wir, sofern wir kein Objekt vom Typ 0, also OBJ_VLINE, verwenden, niemals erfahren, welcher Objekttyp gelöscht wurde, wenn wir MetaTrader 5 danach fragen. MetaTrader 5 kann uns das nicht mitteilen, weil das Objekt nicht mehr existiert.
Bisher ist meiner Meinung nach alles sehr einfach und klar. Nun kommen wir zum interessantesten Teil: Wir nehmen Code 04 und passen ihn so an, dass mehrere Ereignisse zusammenwirken, um das gewünschte Ergebnis zu erzielen.
Da es ziemlich kompliziert erscheinen könnte, alles auf einmal zu erklären, werden wir dies in zwei einfachen Schritten tun. Das erste Beispiel ist im folgenden Code dargestellt.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_Prefix "Demo" 005. //+------------------------------------------------------------------+ 006. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 007. //+------------------------------------------------------------------+ 008. struct st_Obj 009. { 010. //+----------------+ 011. private: 012. string szName, 013. font, 014. text; 015. bool Selectable; 016. int x, 017. y, 018. w, 019. h, 020. fontSize; 021. color cor, 022. backColor; 023. ENUM_OBJECT actual; 024. //+----------------+ 025. public: 026. //+----------------+ 027. void SetDefault(const string arg) 028. { 029. szName = arg; 030. font = "Lucida Console"; 031. Selectable = true; 032. x = 50; 033. y = 50; 034. w = 250; 035. h = 27; 036. cor = clrMediumBlue; 037. backColor = clrWhite; 038. fontSize = 20; 039. } 040. //+----------------+ 041. void Create(const ENUM_OBJECT type) 042. { 043. actual = type; 044. text = EnumToString(type); 045. Recreates(); 046. } 047. //+----------------+ 048. void Recreates(void) 049. { 050. ObjectCreate(0, szName, actual, 0, 0, 0); 051. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, (actual != OBJ_EDIT ? Selectable : false)); 052. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x); 053. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, y); 054. ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 055. ObjectSetInteger(0, szName, OBJPROP_YSIZE, h); 056. ObjectSetInteger(0, szName, OBJPROP_COLOR, cor); 057. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, fontSize); 058. ObjectSetString(0, szName, OBJPROP_FONT, font); 059. ObjectSetString(0, szName, OBJPROP_TEXT, text); 060. if (actual == OBJ_EDIT) 061. { 062. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, backColor); 063. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 064. } 065. } 066. //+----------------+ 067. string GetName(void) 068. { 069. return szName; 070. } 071. //+----------------+ 072. void Update(void) 073. { 074. font = ObjectGetString(0, szName, OBJPROP_FONT); 075. Selectable = (int)ObjectGetInteger(0, szName, OBJPROP_SELECTABLE); 076. x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 077. y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 078. w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 079. h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 080. fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 081. cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 082. backColor = (color)((ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_LABEL ? backColor : ObjectGetInteger(0, szName, OBJPROP_BGCOLOR)); 083. } 084. //+----------------+ 085. }gl_Obj; 086. //+------------------------------------------------------------------+ 087. int OnInit() 088. { 089. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 090. 091. gl_Obj.SetDefault(macro_NameObject); 092. gl_Obj.Create(OBJ_LABEL); 093. 094. return INIT_SUCCEEDED; 095. }; 096. //+------------------------------------------------------------------+ 097. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 098. { 099. return rates_total; 100. }; 101. //+------------------------------------------------------------------+ 102. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 103. { 104. switch (id) 105. { 106. case CHARTEVENT_OBJECT_CLICK : 107. if (sparam == gl_Obj.GetName()) 108. { 109. string sz0 = ObjectGetString(0, sparam, OBJPROP_TEXT); 110. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_EDIT) 111. break; 112. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 113. ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true); 114. ObjectDelete(0, sparam); 115. gl_Obj.Create(OBJ_EDIT); 116. ObjectSetString(0, sparam, OBJPROP_TEXT, sz0); 117. } 118. break; 119. case CHARTEVENT_OBJECT_CREATE : 120. if (sparam == gl_Obj.GetName()) 121. { 122. ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, false); 123. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 124. } 125. break; 126. case CHARTEVENT_OBJECT_DELETE : 127. if (sparam == gl_Obj.GetName())gl_Obj.Recreates(); 128. break; 129. case CHARTEVENT_OBJECT_CHANGE : 130. if (sparam == gl_Obj.GetName()) gl_Obj.Update(); 131. break; 132. } 133. ChartRedraw(); 134. }; 135. //+------------------------------------------------------------------+ 136. void OnDeinit(const int reason) 137. { 138. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 139. ObjectsDeleteAll(0, def_Prefix); 140. ChartRedraw(); 141. }; 142. //+------------------------------------------------------------------+
Code 05
Hier, in Code 05, geschieht etwas sehr Seltsames. Das können Sie in der folgenden Animation sehen:

Animation 06
Sehen Sie sich Animation 06 genau an, lieber Leser. Beachten Sie, dass wir in Zeile 92 die Erstellung eines OBJ_LABEL-Objekts anfordern. Aus diesem Grund wird in Zeile 44 der Text so festgelegt, dass er den Typ des verwendeten Objekts angibt. Bislang läuft alles wie gewohnt. Sobald wir jedoch auf dieses OBJ_LABEL-Objekt klicken, wird es in ein OBJ_EDIT-Objekt umgewandelt. Genau das geschieht durch den Code, der das abgefangene Ereignis in Zeile 106 verarbeitet. Bitte beachten Sie, dass wir uns auf das Objekt konzentrieren, das wir anfangs erstellen.
Aufgrund von Zeile 109 wird der im OBJ_LABEL-Objekt enthaltene Text jedoch in Zeile 116 in das OBJ_EDIT-Objekt übertragen. Das ist für uns wichtig, da ohne diese Übertragung ein anderer Text in das OBJ_EDIT-Objekt eingefügt würde. Und das wollen wir nicht. Was wir eigentlich wollen, ist, den Text des OBJ_LABEL-Objekts direkt im Chart ändern zu können.
Beachten Sie nun Folgendes: Im Rahmen des Ereignisses CHARTEVENT_OBJECT_CLICK werden wir das OBJ_LABEL-Objekt löschen und ein OBJ_EDIT-Objekt erstellen. Da wir jedoch in Zeile 89 die Benachrichtigung für das Ereignis CHARTEVENT_OBJECT_DELETE aktiviert haben, müssen wir diese nun deaktivieren. Dies geschieht in Zeile 112. Unmittelbar danach aktivieren wir ein weiteres Ereignis, mit dem MetaTrader 5 angewiesen wird, uns über die Erstellung von Objekten zu benachrichtigen. Dadurch wird das von der Anwendung ausgelöste Objekterstellungsereignis in Zeile 119 abgefangen. In diesem Handler werden wir die Benachrichtigung über die Objekterstellung deaktivieren und die Benachrichtigung über die Löschung wieder aktivieren.
Dadurch wird der Handler in Zeile 126 wieder funktionieren, wenn auch nicht einwandfrei. Der Grund dafür ist, dass das OBJ_EDIT-Objekt zwar neu erstellt wird, wenn wir es löschen, sein Text jedoch nicht mehr mit dem ursprünglichen Text übereinstimmt, der vor der Löschung des Objekts vorhanden war.
Wir überlassen Ihnen diese kleine Korrektur als Übung, damit Sie selbst einen Weg finden können, dieses kleine Problem zu lösen. Das ist ganz einfach. Beachten Sie jedoch, dass einige Daten verarbeitet werden müssen, um den Text wiederherzustellen, der zum Zeitpunkt der Erstellung des OBJ_EDIT-Objekts vorlag. Die Wiederherstellung des Textes, der zwischen der Erstellung des OBJ_EDIT-Objekts und dessen Löschung vorhanden war, wird jedoch einen wesentlich höheren Arbeitsaufwand erfordern. Außerdem müssen Sie Tastatur- und Mausereignisse verarbeiten, um den Text im Speicher auf dem neuesten Stand zu halten und ihn wiederherstellen zu können.
Ich persönlich halte diesen zweiten Teil für völlig unnötig und eher nervig. Denn wenn der Benutzer den Text gelöscht hat, bevor er ihn im OBJ_LABEL-Objekt gespeichert hat, soll er sich eben daran erinnern, welcher Text im OBJ_EDIT-Objekt stand. (GELÄCHTER).
Nun kommt der letzte Teil, der genau in der Verwendung des Ereignisses aus diesem Abschnitt besteht. Dazu nehmen wir noch eine letzte Änderung am Code vor. Das sehen Sie unten.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_Prefix "Demo" 005. //+------------------------------------------------------------------+ 006. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 007. //+------------------------------------------------------------------+ 008. struct st_Obj 009. { 010. //+----------------+ 011. private: 012. string szName, 013. font, 014. text; 015. bool Selectable; 016. int x, 017. y, 018. w, 019. h, 020. fontSize; 021. color cor, 022. backColor; 023. ENUM_OBJECT actual; 024. //+----------------+ 025. public: 026. //+----------------+ 027. void SetDefault(const string arg) 028. { 029. szName = arg; 030. font = "Lucida Console"; 031. Selectable = true; 032. x = 50; 033. y = 50; 034. w = 250; 035. h = 27; 036. cor = clrMediumBlue; 037. backColor = clrWhite; 038. fontSize = 20; 039. } 040. //+----------------+ 041. void Create(const ENUM_OBJECT type) 042. { 043. actual = type; 044. text = EnumToString(type); 045. Recreates(); 046. } 047. //+----------------+ 048. void Recreates(void) 049. { 050. ObjectCreate(0, szName, actual, 0, 0, 0); 051. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, (actual != OBJ_EDIT ? Selectable : false)); 052. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x); 053. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, y); 054. ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 055. ObjectSetInteger(0, szName, OBJPROP_YSIZE, h); 056. ObjectSetInteger(0, szName, OBJPROP_COLOR, cor); 057. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, fontSize); 058. ObjectSetString(0, szName, OBJPROP_FONT, font); 059. ObjectSetString(0, szName, OBJPROP_TEXT, text); 060. if (actual == OBJ_EDIT) 061. { 062. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, backColor); 063. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 064. } 065. } 066. //+----------------+ 067. string GetName(void) 068. { 069. return szName; 070. } 071. //+----------------+ 072. void Update(void) 073. { 074. font = ObjectGetString(0, szName, OBJPROP_FONT); 075. Selectable = (int)ObjectGetInteger(0, szName, OBJPROP_SELECTABLE); 076. x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 077. y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 078. w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 079. h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 080. fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 081. cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 082. backColor = (color)((ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_LABEL ? backColor : ObjectGetInteger(0, szName, OBJPROP_BGCOLOR)); 083. text = ObjectGetString(0, szName, OBJPROP_TEXT); 084. } 085. //+----------------+ 086. }gl_Obj; 087. //+------------------------------------------------------------------+ 088. int OnInit() 089. { 090. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 091. 092. gl_Obj.SetDefault(macro_NameObject); 093. gl_Obj.Create(OBJ_LABEL); 094. 095. return INIT_SUCCEEDED; 096. }; 097. //+------------------------------------------------------------------+ 098. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 099. { 100. return rates_total; 101. }; 102. //+------------------------------------------------------------------+ 103. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 104. { 105. //+----------------+ 106. #define macro_SWAP(A) { \ 107. string sz0 = ObjectGetString(0, sparam, OBJPROP_TEXT); \ 108. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); \ 109. ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true); \ 110. ObjectDelete(0, sparam); \ 111. gl_Obj.Create(A); \ 112. ObjectSetString(0, sparam, OBJPROP_TEXT, sz0); \ 113. gl_Obj.Update(); \ 114. } 115. //+----------------+ 116. switch (id) 117. { 118. case CHARTEVENT_OBJECT_CLICK : 119. if (sparam == gl_Obj.GetName()) 120. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) != OBJ_EDIT) 121. macro_SWAP(OBJ_EDIT); 122. break; 123. case CHARTEVENT_OBJECT_CREATE : 124. if (sparam == gl_Obj.GetName()) 125. { 126. ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, false); 127. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); 128. } 129. break; 130. case CHARTEVENT_OBJECT_DELETE : 131. if (sparam == gl_Obj.GetName())gl_Obj.Recreates(); 132. break; 133. case CHARTEVENT_OBJECT_CHANGE : 134. if (sparam == gl_Obj.GetName()) gl_Obj.Update(); 135. break; 136. case CHARTEVENT_OBJECT_ENDEDIT : 137. if (sparam == gl_Obj.GetName()) macro_SWAP(OBJ_LABEL) 138. break; 139. } 140. ChartRedraw(); 141. //+----------------+ 142. #undef macro_SWAP 143. //+----------------+ 144. }; 145. //+------------------------------------------------------------------+ 146. void OnDeinit(const int reason) 147. { 148. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false); 149. ObjectsDeleteAll(0, def_Prefix); 150. ChartRedraw(); 151. }; 152. //+------------------------------------------------------------------+
Code 06
Code 06 implementiert bereits mehrere Funktionen, darunter auch die Behebung dieses kleinen Problems. Wir haben jedoch bewusst einen Teil des Codes geändert, um zu verbergen, wie wir das Problem gelöst haben. Dies geschah, damit weniger erfahrene Nutzer nicht sofort erkennen konnten, wie die Lösung zustande kam.
Da Code 06 genauso einfach ist wie die anderen und jeder, der sich damit beschäftigt und geübt hat, ihn ohne Schwierigkeiten lösen kann, halte ich es nicht für nötig, viel dazu zu erklären. Ich möchte nur Folgendes anmerken: Wenn die Bearbeitung eines OBJ_EDIT-Objekts abgeschlossen ist – was in der Regel durch Drücken der Eingabetaste geschieht –, generiert MetaTrader 5 ein CHARTEVENT_OBJECT_ENDEDIT-Ereignis. Dieses Ereignis wird in Zeile 136 abgefangen. Dadurch wird das Objekt, das zuvor vom Typ OBJ_EDIT war, wieder zu einem OBJ_LABEL-Objekt.
Ein mögliches Ergebnis ist unten dargestellt:

Animation 07
Aber lassen Sie sich von Animation 07 nicht in die Irre führen, lieber Leser. Versuchen Sie, sich damit auseinanderzusetzen und zu verstehen, was hier wirklich vor sich geht. Was Sie in Animation 07 sehen, ist nur die Spitze eines riesigen Eisbergs. Das liegt daran, dass wir hier nur die grundlegendsten Aspekte des Themas behandelt haben. Wir können noch viel weiter gehen, als hier gezeigt wurde.
Abschließende Gedanken
Vielen dürfte dieser Artikel etwas langweilig vorgekommen sein, da wir stets den vollständigen Code gezeigt und nur kurze Erläuterungen zu dessen Funktionsweise gegeben haben. Ich persönlich halte es jedoch für unnötig, das zu wiederholen, was bereits in anderen Artikeln dieser Reihe erläutert wurde. Natürlich haben wir etwas getan, was viele vielleicht überraschen und sogar skeptisch stimmen könnte. Das ist normal; das gehört zum Lernprozess dazu.
Bevor wir diesen Artikel jedoch abschließen und zum nächsten übergehen, möchte ich Ihnen, lieber Leser, noch einen Rat mit auf den Weg geben. Im letzten Abschnitt haben Sie gesehen, dass wir viele interessante Dinge machen können. Wenn Sie genau hinschauen, werden Sie feststellen, dass wir die Abmessungen des OBJ_LABEL-Objekts angepasst haben, damit ein etwas längerer Text eingefügt werden konnte. Mein Rat lautet: Versuchen Sie, genau zu verstehen, wie Text in einem OBJ_EDIT-Objekt erstellt wird. Dies ist erforderlich, damit Sie die Abmessungen des OBJ_LABEL-Objekts ändern können, wenn Sie Text auf dem Chart anzeigen.
Versuchen Sie außerdem, eine Möglichkeit zu finden, das OBJ_LABEL-Objekt auf dem Chart zu verschieben. Das liegt daran, dass es immer an der Stelle verbleibt, an der es erstellt wurde. Beachten Sie, dass dies genau deshalb geschieht, weil es in dem Moment, in dem es aufgrund des Ereignisses CHARTEVENT_OBJECT_CLICK ausgewählt wird, in ein OBJ_EDIT-Objekt umgewandelt wird. Das hindert uns daran, seine Position zu ändern.
Außerdem gibt es noch etwas, das wir tun können, aber darauf werden wir höchstwahrscheinlich im nächsten Artikel eingehen. Sollte ich zufällig feststellen, dass es nicht besonders interessant ist oder nicht viel zum Artikel beitragen würde, werde ich erklären, worum es dabei geht. Auf jeden Fall wissen Sie jetzt ein wenig mehr über MQL5 und MetaTrader 5 als noch zu Beginn dieses Artikels.
Versuchen Sie also, den im Anhang enthaltenen Code zu studieren und damit zu üben, und wir sehen uns im nächsten Artikel.
| MQ5-Datei | Beschreibung |
|---|---|
| Code 01 | Demonstration von Objektereignissen |
| Code 02 | Demonstration von Objektereignissen |
| Code 03 | Demonstration von Objektereignissen |
| Code 04 | Demonstration von Objektereignissen |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/16050
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
MQL5 meistern – Vom Anfänger zum Profi (Teil VII): Grundlagen des Debuggings von MQL-Anwendungen
Neuronale Netze im Trading: Effektive Merkmalsextraktion für präzise Klassifikation (Objekterstellung)
Von den Grundlagen bis zur Mittelstufe: Direkter Dateizugriff (II)
Neuronale Netze im Handel: Generalisierung von Zeitreihen ohne Abhängigkeit von spezifischen Datensätzen (Fazit)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.