Von der Grundstufe bis zur Mittelstufe: Objektereignisse (III)
Einführung
Im vorigen Artikel „Von der Grundstufe zur Mittelstufe: Objektereignisse (II)“ haben wir gezeigt, wie man eine sehr interessante und sogar ziemlich kuriose Art von Anwendung implementiert, deren Zweck es ist, Benutzern das Bearbeiten des Textes eines OBJ_LABEL-Objekts direkt auf dem Chart zu ermöglichen, ohne das Dialogfenster „Objekteigenschaften“ zu öffnen und den angezeigten Text dort zu bearbeiten.
Obwohl das sowohl aus praktischer als auch aus didaktischer Sicht sehr interessant und nützlich war, können wir etwas noch Interessanteres tun als das, was im vorigen Artikel gezeigt wurde. Ich denke, Sie, lieber Leser, haben es wahrscheinlich getestet und sich vielleicht sogar gefragt, ob sich dieselbe Anwendung so erweitern ließe, dass sie mit beliebigen OBJ_LABEL-Objekten arbeitet, nicht nur mit denjenigen, die wir in unserem Programm erstellt haben. Aus diesem Grund habe ich beschlossen zu zeigen, wie dies gemacht werden kann. Dies kann tatsächlich eine sehr interessante Lösung sein, gerade wegen eines weiteren Punktes, den wir später untersuchen werden.
Wenden wir uns nun dem ersten Thema dieses Artikels zu. Es ist also an der Zeit, alles beiseite zu legen, was Sie ablenken könnte, und sich auf das zu konzentrieren, was wir hier behandeln werden, denn wir werden einige sehr interessante Punkte untersuchen.
Verbesserung dessen, was bereits gut war
Im vorigen Artikel haben wir also gesehen, dass es möglich ist, das zu tun, was in der folgenden Animation gezeigt wird.

Animation 01
Dies ermöglicht die Implementierung eines Mechanismus zur direkten Bearbeitung des Textes, der in einem OBJ_LABEL-Objekt auf dem Chart enthalten ist. Wenn wir jedoch versuchen, manuell ein neues OBJ_LABEL hinzuzufügen, werden wir sehen, dass das, was in Animation 01 gezeigt wird, unmöglich ist. Warum? Der Grund liegt in den Filtern, die bei der Ereignisverarbeitung verwendet werden. In gewisser Weise würde das einfache Entfernen der Filter das Problem zunächst tatsächlich lösen. Das wäre natürlich zunächst der Fall. Wenn wir dies jedoch versuchen, werden wir feststellen, dass die Anwendung ein wenig verrücktspielt.
Aus diesem Grund werden wir kurz zeigen, wie der Quellcode aus dem vorherigen Artikel geändert werden sollte, um den Mechanismus aus Animation 01 zur Bearbeitung eines beliebigen OBJ_LABEL-Objekts zu verwenden.
Dies mag kompliziert erscheinen, aber tatsächlich ist alles viel einfacher, als es aussieht, vorausgesetzt natürlich, dass bei der Änderung des Codes bestimmte Vorsichtsmaßnahmen getroffen werden. Das meiste von dem, was getan werden muss, wurde bereits in früheren Artikeln erklärt, daher werden wir uns nur auf das konzentrieren, was wirklich wichtig ist. Dies ist im Folgenden dargestellt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. switch (id) 17. { 18. case CHARTEVENT_OBJECT_CLICK : 19. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_LABEL) Swap_ObjLabel_ObjEdit(sparam); 20. break; 21. case CHARTEVENT_OBJECT_ENDEDIT : 22. Swap_ObjLabel_ObjEdit(sparam); 23. break; 24. } 25. ChartRedraw(); 26. }; 27. //+------------------------------------------------------------------+ 28. void Swap_ObjLabel_ObjEdit(const string szName) 29. { 30. struct st_Mem 31. { 32. string font, text; 33. int x, y, w, h, fontSize; 34. color cor; 35. }mem; 36. ENUM_OBJECT type = (ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_EDIT ? OBJ_LABEL : OBJ_EDIT; 37. 38. mem.font = ObjectGetString(0, szName, OBJPROP_FONT); 39. mem.x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 40. mem.y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 41. mem.w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 42. mem.h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 43. mem.fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 44. mem.cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 45. mem.text = ObjectGetString(0, szName, OBJPROP_TEXT); 46. 47. ObjectDelete(0, szName); 48. ChartRedraw(); 49. ObjectCreate(0, szName, type, 0, 0, 0); 50. 51. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, (type != OBJ_EDIT ? true : false)); 52. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, mem.x); 53. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, mem.y); 54. ObjectSetInteger(0, szName, OBJPROP_XSIZE, mem.w); 55. ObjectSetInteger(0, szName, OBJPROP_YSIZE, mem.h); 56. ObjectSetInteger(0, szName, OBJPROP_COLOR, mem.cor); 57. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, mem.fontSize); 58. ObjectSetString(0, szName, OBJPROP_FONT, mem.font); 59. ObjectSetString(0, szName, OBJPROP_TEXT, mem.text); 60. if (type == OBJ_EDIT) 61. { 62. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, clrWhite); 63. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 64. } 65. } 66. //+------------------------------------------------------------------+
Code 01
Jetzt möchte ich, dass Sie sich Code 01 ansehen und mir sagen: Was ist daran schwierig? Wenn Sie dieser Artikelserie gefolgt sind und gleichzeitig das Material in der Praxis studiert und angewendet haben, wird Ihre Antwort wahrscheinlich lauten: „Hier ist nichts schwierig; alles ist sehr einfach und klar.“ Lieber Leser, Code 01 ist so einfach und klar, dass ich keine Notwendigkeit sehe, zu erklären, wie er funktioniert. Allerdings gibt es hier ein kleines Problem. Nichts Kompliziertes. Bevor wir jedoch zeigen, wie man dies behebt, lassen Sie uns sehen, was passiert, wenn Code 01 auf einen beliebigen Chart angewendet wird. Dazu führen wir eine kurze Abfolge von Schritten durch, beginnend mit dem ersten, der unten dargestellt ist.

Animation 02
In Animation 02 haben wir nur Code 01 zum Chart hinzugefügt. Beachten Sie, dass nichts passiert ist, da das Ziel nicht darin besteht, etwas zu erstellen, sondern mit etwas zu arbeiten, das bereits erstellt wurde. Unmittelbar danach werden wir mehrere OBJ_LABEL-Objekte hinzufügen. Dazu verwenden wir das, was als Nächstes gezeigt wird:

Abbildung 01
Dieses Bild zeigt, wie man Objekte des erforderlichen Typs zum Chart hinzufügt. Fügen Sie so viele hinzu, wie Sie möchten. In diesem Fall werde ich nur zwei hinzufügen, da das Ziel genau darin besteht, den bestehenden Fehler aufzuzeigen. Der Chart wird dann wie folgt aussehen:

Abbildung 02
Sehr gut. Jetzt kommen wir zu dem Teil, in dem alles einen Sinn ergibt. Sobald die Anwendung auf dem Chart läuft und wir ein Objekt des korrekten Typs haben, können wir nicht mehr alles wie zuvor tun. Dies liegt daran, dass beim Anklicken eines der OBJ_LABEL-Objekte dieses in ein OBJ_EDIT-Objekt umgewandelt wird, wodurch wir den angezeigten Text direkt eingeben können. Und all dies geschieht, ohne das Dialogfeld „Objekteigenschaften“ für das OBJ_LABEL-Objekt öffnen zu müssen.

Animation 03
Beachten Sie, dass wir, wie in Animation 03 zu sehen ist, diese Objekte tatsächlich bearbeiten können, wie im vorherigen Artikel versprochen und demonstriert. Aber das ist noch nicht alles: Jetzt können wir dies mit jedem OBJ_LABEL-Objekt tun. Darüber hinaus können wir noch viel mehr tun, wenn auch auf etwas andere Weise. Dazu müssen wir das Fenster „Objektliste“ öffnen. Dies ermöglicht es uns, bestimmte Eigenschaften zu ändern, wie die Farbe, die Schriftart, die Schriftgröße und sogar den Namen des Objekts selbst.
Die Eigenschaften, die wir auf diese Weise ändern, sind jedoch sekundäre Eigenschaften. Auf jeden Fall verwenden die Objekte OBJ_EDIT und OBJ_LABEL denselben Satz an Eigenschaften, da die Prozedur in Zeile 28, die in Code 01 gezeigt wird, die Übertragung dieser Eigenschaften selbst übernimmt.
Was ist also der Fehler, der zu Beginn dieses Themas erwähnt wurde? Nun, lieber Leser, es ist kein schrecklicher oder alarmierender Fehler. Es ist eher ein Problem beim Auswählen und Aufheben der Auswahl. Bei der Verwendung der in Code 01 gezeigten Anwendung werden wir oft nur ein OBJ_EDIT-Objekt auf dem Chart benötigen. Das liegt daran, dass es in manchen Situationen nicht viel Sinn ergibt, mehrere OBJ_EDIT-Objekte auf dem Chart zu haben. Aber schauen Sie sich aufgrund eines Fehlers im Auswahl- und Abwahlprozess an, was passiert.

Animation 04
Beachten Sie, dass wir anfangs zwei OBJ_LABEL-Objekte hatten und dann zwei OBJ_EDIT-Objekte erschienen. Das kann behoben werden. Ich sage nicht, dass dies völlig falsch ist, aber eine Situation wie die in Animation 04 gezeigte ergibt keinen Sinn, wenn das Ziel einfach darin besteht, den OBJ_LABEL-Text direkt auf dem Chart bearbeiten zu können.
Die Behebung dieser Art von Fehler ist sehr einfach. Um dieses Problem zu lösen, müssen wir nur den Event-Handling-Code ändern. In unserem speziellen Fall reicht es aus, den Code wie unten gezeigt zu ändern:
. . . 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. static string szOldEdit = ""; 17. 18. switch (id) 19. { 20. case CHARTEVENT_OBJECT_CLICK : 21. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_LABEL) 22. { 23. if (szOldEdit != "") Swap_ObjLabel_ObjEdit(szOldEdit); 24. Swap_ObjLabel_ObjEdit(szOldEdit = sparam); 25. } 26. break; 27. case CHARTEVENT_OBJECT_ENDEDIT : 28. Swap_ObjLabel_ObjEdit(sparam); 29. szOldEdit = ""; 30. break; 31. } 32. ChartRedraw(); 33. }; 34. //+------------------------------------------------------------------+ . . .
Code 02
Bitte beachten Sie, wie einfach es ist, diese Lösung anzuwenden, da wir nur die im Code 02-Fragment gezeigten Punkte ändern müssen. Das Ergebnis ist in Animation 05 unten zu sehen:

Animation 05
„Alles klar, jetzt glaube ich zu verstehen, wie wichtig es ist, das Material besser zu üben und zu studieren, bevor man etwas wiederholt, das jeder einfach deshalb wiederholt, weil es richtig erscheint. Aber ich habe eine Frage zu dieser Anwendung, deren Erstellung Sie so freundlicherweise demonstriert haben. Gibt es keine Möglichkeit, andere Parameter zu steuern, ohne das Fenster der Objektliste öffnen zu müssen? Ich frage, weil wir anscheinend keine Möglichkeit haben, die Position des Objekts auf dem Chart zu ändern. Nachdem das Objekt platziert wurde und die Anwendung auf dem Chart läuft, wird das Verschieben des Objekts praktisch unmöglich.
Gibt es also eine Möglichkeit, mit dieser Art von Situation umzugehen? Denn das stört mich doch ziemlich, obwohl die gezeigte Manipulationsmethode für mich sehr interessant erscheint."
Ja, lieber Leser, es gibt Möglichkeiten, dieses Problem zu lösen. Deshalb ist es wichtig, dass Sie bestimmte Dinge sehr gut verstehen. Versuchen Sie immer, das Konzept selbst zu verstehen, nicht den spezifischen Code, der verwendet wurde. Das liegt daran, dass Code geändert werden kann, das Konzept jedoch nicht.
Es gibt verschiedene Möglichkeiten, das zu tun, was Sie vorschlagen, das heißt, die Position des Objekts zu ändern bzw. zu verwalten, während die hier gezeigte Anwendung auf dem Chart läuft. Eine der einfachsten Möglichkeiten besteht darin, die Funktionsweise des OBJ_EDIT-Objekts zu ändern.
Im vorherigen Artikel haben wir erwähnt, dass eine seiner Eigenschaften einen bestimmten Wert haben muss, um den Inhalt eines OBJ_EDIT-Objekts zu bearbeiten. Wenn dieselbe Eigenschaft einen anderen Wert enthält, ist die Bearbeitung blockiert. „Aber warum ist das für uns wichtig? War es nicht die Idee, eine Möglichkeit zu schaffen, das Objekt auf dem Chart zu verschieben?“ Wie kann uns diese Information über eine bestimmte Eigenschaft und wie sie sich auf ein OBJ_EDIT-Objekt auswirkt, dabei helfen, das Problem des Verschiebens zu lösen? Das verstehe ich nicht. Könnten Sie das genauer erklären?
Natürlich kann ich genauer erklären, worauf ich hinaus will. Wenn Sie sich erinnern, haben wir in den letzten beiden Artikeln gezeigt, wie unser Code Objektereignisse abfangen kann, wenn wir wissen müssen, was passiert. Da wir uns nicht zu viel zusätzliche Arbeit aufhalsen wollen, bis hin dazu, Objekten vorzugeben, wie sie sich bewegen sollen, übertragen wir diese Aufgabe an MetaTrader 5. Nun, dieser Teil ist leicht zu verstehen. Der schwierige Teil, und der Grund, warum es wichtig ist, die Funktionsweise von Objekten sehr gut zu verstehen, ist vielleicht, dass wir die Verantwortung für das Verschieben des Objekts an MetaTrader 5 übergeben können, wenn ein OBJ_EDIT-Objekt nicht bearbeitet werden kann, weil es ausgewählt ist.
Der schwierigste Teil, den die allermeisten Programmieranfänger verstehen müssen, ist, dass unser Code 01, selbst nach dem Hinzufügen des Fragments aus Code 02, ein OBJ_LABEL-Objekt immer noch in ein OBJ_EDIT-Objekt umwandelt. Dies geschieht, um den Text zu bearbeiten, der später angezeigt wird. Was passiert jedoch, wenn Sie erneut auf das OBJ_EDIT-Objekt klicken? Basierend auf dem, womit wir es im Code zu tun haben, lautet die Antwort: NICHTS. Aber denken Sie einen Moment darüber nach: Wenn wir das Verschieben des Objekts abschließen, generiert MetaTrader 5 ein Ereignis. Es benachrichtigt unsere Anwendung darüber, dass der Benutzer das Objekt losgelassen hat und es sich nun an einer neuen Position befindet.
Wenn wir daher eine kleine Strategie anwenden, können wir das OBJ_EDIT-Objekt in einen Zustand versetzen, der es MetaTrader 5 ermöglicht, dessen Bewegung zu verwalten. Dies ermöglicht es dem Benutzer, das Objekt auf dem Chart zu verschieben. Wenn der Benutzer das Objekt loslässt, schließen wir die Bearbeitung ab, oder wir können das OBJ_EDIT-Objekt einfach wieder in ein OBJ_LABEL-Objekt umwandeln. Sie, lieber Leser, müssen entscheiden, welche Option am besten zu Ihren Zielen passt.
Auf diese Weise erklärt, mag es etwas kompliziert erscheinen. Wenn wir jedoch Ideen in Code umsetzen, werden die Dinge viel leichter verständlich. Aber bevor wir das tun, muss ich Ihnen etwas zeigen, lieber Leser. Es wird ein einfaches Beispiel sein, aber es wird ein entscheidendes Detail für uns viel klarer machen.
Als wir im vorherigen Artikel sagten, dass wir die Eigenschaft OBJPROP_SELECTABLE des Objekts OBJ_EDIT auf false setzen müssen, haben wir nie gezeigt, warum. Obwohl wir in einigen Fällen OBJPROP_SELECTABLE auf true setzen können und trotzdem in der Lage sind, den Text im Objekt OBJ_EDIT zu bearbeiten, würde die Demonstration eines jeden Falls, in dem wir dies tun können, die Dinge zweifellos viel komplizierter machen. Um das Gesagte in der Praxis zu sehen, schauen Sie sich den Code unten an.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. static string szOldEdit = ""; 17. 18. switch (id) 19. { 20. case CHARTEVENT_OBJECT_CLICK : 21. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_LABEL) 22. { 23. if (szOldEdit != "") Swap_ObjLabel_ObjEdit(szOldEdit); 24. Swap_ObjLabel_ObjEdit(szOldEdit = sparam); 25. } 26. break; 27. case CHARTEVENT_OBJECT_ENDEDIT : 28. Swap_ObjLabel_ObjEdit(sparam); 29. szOldEdit = ""; 30. break; 31. } 32. ChartRedraw(); 33. }; 34. //+------------------------------------------------------------------+ 35. void Swap_ObjLabel_ObjEdit(const string szName, bool Selectable = true) 36. { 37. struct st_Mem 38. { 39. string font, text; 40. int x, y, w, h, fontSize; 41. color cor; 42. }mem; 43. ENUM_OBJECT type = (ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_EDIT ? OBJ_LABEL : OBJ_EDIT; 44. 45. mem.font = ObjectGetString(0, szName, OBJPROP_FONT); 46. mem.x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 47. mem.y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 48. mem.w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 49. mem.h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 50. mem.fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 51. mem.cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 52. mem.text = ObjectGetString(0, szName, OBJPROP_TEXT); 53. 54. ObjectDelete(0, szName); 55. ChartRedraw(); 56. ObjectCreate(0, szName, type, 0, 0, 0); 57. 58. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, Selectable); 59. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, mem.x); 60. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, mem.y); 61. ObjectSetInteger(0, szName, OBJPROP_XSIZE, mem.w); 62. ObjectSetInteger(0, szName, OBJPROP_YSIZE, mem.h); 63. ObjectSetInteger(0, szName, OBJPROP_COLOR, mem.cor); 64. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, mem.fontSize); 65. ObjectSetString(0, szName, OBJPROP_FONT, mem.font); 66. ObjectSetString(0, szName, OBJPROP_TEXT, mem.text); 67. if (type == OBJ_EDIT) 68. { 69. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, clrWhite); 70. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 71. } 72. } 73. //+------------------------------------------------------------------+
Code 03
Code 03 ähnelt Code 02, bei dem wir nur einen Ausschnitt des Gesamtcodes gesehen haben, aber diesmal wird er hier fast vollständig gezeigt. Dies ist in Zeile 35 zu sehen. Der im zweiten Argument angegebene Wert wird nur an einer Stelle verwendet, nämlich in Zeile 58. Passen Sie jetzt auf, denn dies kann je nach Art der Konfiguration, die Sie in Ihrem Code implementieren, etwas verwirrend werden. Im aktuellen Zustand verhält sich Code 03 wie folgt:

Animation 06
„Warten Sie eine Sekunde, Moment mal. Dieser Code funktioniert nicht wie Code 02. Wie ist das möglich? Hat das einfache Ändern einer Eigenschaft den Code wirklich auf diese Weise verändert? Das ist sehr seltsam. Aber kann das Bearbeiten nicht gleichzeitig mit der Objektauswahl erlaubt sein?“ In manchen Fällen ja, lieber Leser. Da einige Probleme jedoch recht schwer zu erklären sind, weil sie Details darüber erfordern, wie bestimmte Eigenschaften funktionieren, ziehe ich einen anderen Ansatz vor. Es ist einfacher, dies auf diese Weise zu erklären und zu demonstrieren, ohne Sie zu verwirren.
Ich denke also, Sie haben Folgendes bemerkt: Wenn die Eigenschaft OBJPROP_SELECTABLE für ein OBJ_EDIT-Objekt aktiviert ist, sollten wir davon ausgehen, dass wir den Text nicht direkt im Chart bearbeiten können. Das Verschieben des Objekts ist jedoch problemlos möglich.
Unter Berücksichtigung dieser Fakten können wir nun Code 03 ändern, um das erwartete Verhalten zu erzielen. Dies wird erreicht, indem der Code wie unten gezeigt geändert wird.
. . . 18. switch (id) 19. { 20. case CHARTEVENT_OBJECT_CLICK : 21. { 22. ENUM_OBJECT type = (ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE); 23. if (type == OBJ_EDIT) ObjectSetInteger(0, sparam, OBJPROP_SELECTABLE, false); 24. else if (type == OBJ_LABEL) 25. { 26. if (szOldEdit != "") Swap_ObjLabel_ObjEdit(szOldEdit); 27. Swap_ObjLabel_ObjEdit(szOldEdit = sparam); 28. ObjectSetInteger(0, sparam, OBJPROP_SELECTED, true); 29. } 30. } 31. break; . . .
Code 04
In diesem Fall konzentrieren wir uns in Code 04 nur auf den Teil, der in Code 03 wirklich geändert werden muss. Mit anderen Worten: Wir sehen nur das Fragment, das eine Änderung erforderte. Wenn wir dieses Fragment nun in Code 03 oder, wenn Sie möchten, in Code 02 verwenden, ist das Ergebnis wie folgt:

Animation 07
„Kommen Sie, was für eine verrückte und absurde Lösung wird hier implementiert. Sie müssen verrückt sein. Es wäre unmöglich gewesen, auf eine solche Art und Weise das Problem des Verschiebens und Bearbeitens eines OBJ_LABEL-Objekts direkt im Chart zu lösen." Es ist wirklich eine verrückte Idee.
Aber abgesehen von der verrückten Seite: Ist das, was ich Ihnen gerade gezeigt habe, nicht amüsant? Schließlich ist es uns mit nur wenig Aufwand gelungen, eine Möglichkeit zu schaffen, ein OBJ_LABEL-Objekt direkt im Chart zu bearbeiten. Natürlich ist diese Lösung nicht zu 100 % perfekt. Das liegt daran, dass Sie die Anwendung eine Weile benutzen müssen, um sich an ihre Funktionsweise zu gewöhnen. Es handelt sich um eine Abfolge von Vorgängen, die zu bestimmten Zeiten durchgeführt werden müssen, wie in Animation 07 gezeigt. Daher besteht Zeile 24 die Prüfung, und wir konvertieren das OBJ_LABEL-Objekt in ein OBJ_EDIT-Objekt. Da wir jedoch verschieben möchten, aktivieren wir in Zeile 28 die Objektauswahl. Dies ermöglicht es uns, das OBJ_EDIT-Objekt im Chart zu verschieben.
Sobald wir erneut auf dasselbe Objekt klicken, deaktiviert die Prüfung in Zeile 23 die Auswahl. Daher wählt MetaTrader 5 beim nächsten Klick nicht das OBJ_EDIT-Objekt aus, sondern den Text, wodurch dieser geändert werden kann. Das ist gut geworden, aber es lässt sich noch verbessern. Da das Ziel jedoch NICHT darin besteht, eine Anwendung zu erstellen, sondern zu zeigen, was wir und wie wir es tun können, bin ich mit dem hier präsentierten Ergebnis bereits zufrieden und kann zum nächsten Thema übergehen.
Größen direkt im Chart verwalten
Ich kann ohne falsche Bescheidenheit sagen, dass alles, was wir bis zu diesem Punkt gesehen haben, sehr unterhaltsam und interessant war. Aber nichts ist vergleichbar mit dem, was wir jetzt sehen werden. Lassen Sie uns beginnen. Es sollte gesagt werden, dass es je nach Ihrer Kreativität und Ihrer Fähigkeit, sich vorzustellen, wie alles zusammenpassen kann, mehr oder weniger Optionen geben wird. Dies liegt einfach daran, dass wir bereits verstanden haben, wie MetaTrader 5 und unsere Anwendung miteinander interagieren, was es uns ermöglicht, eine bestimmte Art von Ergebnis zu erzielen und ein spezielles Modell für die Funktionsweise der Anwendung zu erstellen.
Meiner Ansicht nach ist es sehr interessant, MetaTrader 5 nicht nur für den Handel, sondern auch zur Erstellung anderer Dinge zu verwenden. Zum Beispiel einen Grafikeditor, einen Texteditor oder sogar eine kleine Plattform für einfache Spiele wie Puzzles oder klassische Spiele im Stil alter Spielautomaten.
Viele mögen denken, dass solche Dinge unmöglich sind, da wir anscheinend nicht über die notwendigen Mechanismen dafür verfügen. Entgegen der Meinung vieler Leute und dem, was sie über die Fähigkeiten von MetaTrader 5 zu wissen glauben, haben wir jedoch tatsächlich das Potenzial, diese Art von Funktionalität auf der Plattform zu implementieren. Die Hauptsache ist, Fantasie und das notwendige Wissen zu haben. Noch wichtiger ist, dass wir die richtigen Konzepte haben und wissen müssen, wie wir sie zu unserem Vorteil nutzen können. Wenn dies geschieht, werden Sie in der Lage sein, viele interessante Dinge in MetaTrader 5 zu entwickeln, anstatt sich nur langweilige Kurscharts anzusehen. Vielleicht kommen Sie sogar auf eine etwas interessantere Art, ein Preisdiagramm zu zeichnen, indem Sie unter anderem 3D-Elemente verwenden.
Aber im Moment sind Sie wahrscheinlich noch nicht bereit, sich das vorzustellen. Mit anderen Worten: Um so etwas zu tun, befinden wir uns in diesem Stadium noch am absoluten Anfang von alledem. Und ja, entgegen dem, was Sie sich vielleicht vorstellen, befinden wir uns immer noch auf dem grundlegenden Niveau dessen, was MQL5-Programmierung darstellt; man könnte sogar sagen, auf dem Anfängerniveau. Und schauen Sie sich an, wie viel wir bereits auf dieser grundlegenden Ebene erreicht haben.
Nun gut, jetzt werden wir uns etwas anderes ansehen, das recht einfach umzusetzen ist, auch wenn es etwas schwierig zu verstehen sein mag, wenn Sie die Artikel nicht verfolgt haben. Da wir einige Techniken verwenden werden, mag es etwas komplex erscheinen. Ich möchte Ihre Vorstellungskraft jedoch nicht auf das grundlegende Niveau unserer Fähigkeiten beschränken. Wenn wir etwas tun können, lassen Sie es uns tun, auch wenn es einige Zeit dauert, wirklich zu verstehen, was passiert und welches Konzept verwendet wird.
Das Ziel dieses Themas ist es, eine Möglichkeit zu entwickeln, ein Objekt im Chart mit der Maus in der Größe zu verändern. Aber wir werden versuchen, dies zu tun, ohne direkt Mausereignisse zu verwenden. Das scheint ziemlich schwierig, nicht wahr? Die Idee ist, weiterhin MetaTrader 5 zu verwenden, um bestimmte Ereignisse zu verwalten, während wir sie so steuern, dass sie ein bestimmtes Ergebnis erzielen.
Das erscheint wirklich wie eine sehr schwierige Aufgabe. Etwas, das nur einem Verrückten einfallen würde, der aus einer Heilanstalt entkommen ist und davon besessen ist, alle anderen genauso zu machen. Aber Sie werden sehen, dass es sehr einfach ist – Sie müssen nur weitermachen. Vielleicht ist es sogar noch einfacher als das. Da dieses Thema jedoch recht umfangreich ist und mehr als einen Artikel für eine ordnungsgemäße Erklärung und ein Verständnis erfordert, beginnen wir mit der folgenden Tatsache: Im Gegensatz zu dem, was im restlichen Code geschah, werden wir Ihnen oder jedem anderen Benutzer hier bereits erlauben, die Größe jeder Art von Objekt zu manipulieren.
Obwohl dies in der ersten Phase nicht für jeden Typ gilt. Es könnte von Ihrer Anwendung erstellt oder vom Benutzer zum Chart hinzugefügt worden sein. Das spielt keine Rolle; wir werden sie alle auf die gleiche Weise behandeln. Es gibt einige Einschränkungen, aber wir werden sie untersuchen, während wir den Code implementieren. Lassen Sie uns also zum Anfang den folgenden Code betrachten:
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. int OnInit() 09. { 10. IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix); 11. 12. return INIT_SUCCEEDED; 13. }; 14. //+------------------------------------------------------------------+ 15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 16. { 17. return rates_total; 18. }; 19. //+------------------------------------------------------------------+ 20. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 21. { 22. switch (id) 23. { 24. case CHARTEVENT_KEYDOWN : 25. break; 26. case CHARTEVENT_OBJECT_CLICK : 27. break; 28. case CHARTEVENT_OBJECT_DRAG : 29. break; 30. } 31. ChartRedraw(); 32. }; 33. //+------------------------------------------------------------------+ 34. void OnDeinit(const int reason) 35. { 36. ObjectsDeleteAll(0, def_Prefix); 37. ChartRedraw(); 38. }; 39. //+------------------------------------------------------------------+
Code 05
Code 05 ist unsere Basisvorlage. Bitte beachten Sie, dass Code 05 drei Ereignisse spezifiziert, die behandelt werden müssen. Dies liegt daran, dass wir hier keine „fantabulöse“ Anwendung erstellen werden („fantabulös“ als ein Kofferwort aus „fantastisch“ und „fabulös“). Hier werden wir einfach etwas Einfaches und Verständliches tun: die Größe und möglicherweise die Position eines beliebigen Objekts direkt im Chart ändern. Was die Position betrifft, überlege ich noch, ob es sich lohnt, sie zu implementieren.
„In Ordnung, Sie haben hier das Sagen. Was werden wir also als Nächstes machen?“ Nun, lieber Leser, der nächste Schritt besteht darin, das Ereignis CHARTEVENT_OBJECT_CLICK zu verwenden, um Daten über das Objekt zu erhalten, mit dem wir arbeiten werden. Im Prinzip werden wir nur mit dieser Information arbeiten, nämlich dem Objektnamen, da dies die einzige Information ist, die für uns wirklich von Bedeutung ist, und da alles andere daraus abgeleitet werden kann. Wir konzentrieren uns jetzt also auf die Prozedur OnChartEvent, damit wir nicht den gesamten Code erneut zeigen müssen. Die ersten Änderungen sind daher nachstehend zu sehen:
. . . 19. //+------------------------------------------------------------------+ 20. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 21. { 22. static string szNameObj = NULL; 23. 24. switch (id) 25. { 26. case CHARTEVENT_KEYDOWN : 27. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) szNameObj = NULL; 28. break; 29. case CHARTEVENT_OBJECT_CLICK : 30. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break; 31. if (szNameObj != NULL); 32. if (szNameObj != sparam); else szNameObj = NULL; 33. break; 34. case CHARTEVENT_OBJECT_DRAG : 35. break; 36. } 37. ChartRedraw(); 38. }; 39. //+------------------------------------------------------------------+ . . .
Code 06
„Wir wissen immer noch nicht, worauf das hinausläuft. Könnten Sie ein kleines Codefragment teilen? Ich fange an, mir ein wenig Sorgen zu machen.“ Nun, liebe Leser. Sie haben danach gefragt. (GELÄCHTER). Aber bevor wir dazu kommen, schauen Sie sich die Logik an, die im Codefragment aus Code 06 eingebettet ist. Das Verständnis dieser Logik ist sehr wichtig, um die nächsten Schritte vorherzusehen. Dieser nächste Schritt ist im nachstehenden Code dargestellt:
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_BoxResize 09. { 10. private : 11. ushort x, y, w, h; 12. bool isBack; 13. string szObjects[4]; 14. //+----------------+ 15. void CreateEdge(const char what) 16. { 17. ushort p1 = x, p2 = y, p3 = w, p4 = h; 18. 19. switch (what) 20. { 21. case 0: p4 = 1; break; 22. case 1: p3 = 1; break; 23. case 2: p2 = y + h; p4 = 1; break; 24. case 3: p1 = x + w; p3 = 1; break; 25. } 26. szObjects[what] = macro_NameObject; 27. ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0); 28. ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime); 29. ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1); 30. ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2); 31. ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3); 32. ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4); 33. } 34. //+----------------+ 35. public : 36. void CreateBoxResize(const string szArg, const uchar adjust = 2) 37. { 38. isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK); 39. ObjectSetInteger(0, szArg, OBJPROP_BACK, true); 40. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 41. x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust; 42. y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust; 43. w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust; 44. h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust; 45. for (uchar c = 0; c < 4; c++) 46. CreateEdge(c); 47. } 48. //+----------------+ 49. void RemoveBoxResize(const string szArg) 50. { 51. ObjectSetInteger(0, szArg, OBJPROP_BACK, isBack); 52. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 53. ObjectsDeleteAll(0, def_Prefix); 54. } 55. //+----------------+ 56. }gl_RZ; 57. //+------------------------------------------------------------------+ 58. int OnInit() 59. { 60. IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix); 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. static string szNameObj = NULL; 73. 74. switch (id) 75. { 76. case CHARTEVENT_KEYDOWN : 77. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) 78. { 79. gl_RZ.RemoveBoxResize(szNameObj); 80. szNameObj = NULL; 81. } 82. break; 83. case CHARTEVENT_OBJECT_CLICK : 84. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break; 85. if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj); 86. if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL; 87. break; 88. case CHARTEVENT_OBJECT_DRAG : 89. break; 90. } 91. ChartRedraw(); 92. }; 93. //+------------------------------------------------------------------+ 94. void OnDeinit(const int reason) 95. { 96. ObjectsDeleteAll(0, def_Prefix); 97. ChartRedraw(); 98. }; 99. //+------------------------------------------------------------------+
Code 07
„Wow! Bleiben Sie ganz entspannt, es besteht kein Grund, so weit zu gehen.“ Nun, Sie haben danach gefragt, und hier ist das Ergebnis. Obwohl es sehr komplex erscheinen mag, ist dieser Code selbst sehr einfach und extrem grundlegend. Um es zu verstehen, reicht es aus, in Ruhe zu beobachten, wie alles gemacht wird. Auf jeden Fall werde ich kurz erklären, was hier gemacht wird, da dies der erste Teil dessen ist, was wir später tun werden.
In Zeile 08 erstellen wir eine Struktur, deren Zweck es ist, einen Rahmen um das Objekt zu bilden, das wir ausgewählt haben. Passen Sie jetzt gut auf, liebe Leserin, lieber Leser. Das Objekt, um das herum wir diesen Umriss erstellen werden, muss kartesische Koordinaten haben. Koordinaten, die an die Preisskala gebunden sind, werden hier noch nicht berücksichtigt. Da MetaTrader 5 nur wenige Objekte dieser Art implementiert und nur zwei davon von Interesse sein könnten – die Objekte OBJ_TEXT und OBJ_BITMAP –, sehe ich derzeit keinen Grund, mit ihnen zu arbeiten. Die übrigen sind für uns jedoch ebenfalls relevant.
Bitte beachten Sie, dass wir im Ereignishandler OnChartEvent auf mehrere Prozeduren innerhalb der Struktur verweisen. Aber die Struktur selbst enthält eine interne private Prozedur. Diese Prozedur ist für das Erzeugen der Linien verantwortlich, die das ausgewählte Objekt umgeben werden.
In diesem Fall verwenden wir eine ziemlich helle Farbe. Dies ist in Zeile 28 definiert. Wenn Sie möchten, können Sie sie durch eine andere Farbe ersetzen, die Ihnen geeigneter erscheint. Vermeiden Sie jedoch das Ändern anderer Teile des Codes, es sei denn, Sie experimentieren, um zu verstehen, wie er funktioniert. Sehr gut. In Zeile 38 speichern wir, ob sich das ausgewählte Objekt im Vordergrund oder im Hintergrund befindet. Dies ist für uns wichtig, da wir das Objekt in jedem Fall in den Hintergrund verschieben und die Auswahl aufheben werden, wodurch gezeigt wird, dass es nicht ausgewählt ist.
Aber warum machen wir das? Der Grund dafür ist, zu verhindern, dass das Objekt das stört, was wir als Nächstes tun werden. Denken Sie daran, dass wir, wenn ein Objekt ausgewählt ist, einen seiner spezifischen Punkte verwenden können, um es im Chartfenster zu verschieben. Dies wird direkt von MetaTrader 5 erledigt. In diesem Fall benötigen wir dieses Verhalten jedoch nicht, da wir die Kontrolle übernehmen und es selbst verwalten werden. Zuerst müssen Sie vollständig verstehen, wie dieser Code funktioniert. Aber bevor wir diesen Artikel abschließen, lassen Sie uns sehen, wozu Code 07 fähig ist. Dies ist in der folgenden Animation dargestellt. Aber schauen Sie sich zuerst die Objekte an, die auf dem Chart vorhanden sind. Versuchen Sie, sich auf die Objektnamen zu konzentrieren, da dies wichtig ist, um zu verstehen, wie der Ereignishandler jedes einzelne verarbeiten wird. Dies ist in Abbildung 03 zu sehen:

Abbildung 03
Lassen Sie uns nun zur Demonstrationsanimation übergehen.

Animation 08
Schlussfolgerung
In diesem Artikel haben wir gezeigt, wie man mit OBJ_LABEL-Objekten zusammen mit OBJ_EDIT-Objekten arbeitet, sodass Text direkt im Chart bearbeitet werden kann. Darüber hinaus haben wir gesehen, wie man eine Logik hinzufügt, die es ermöglicht, das Objekt zu verschieben, auch wenn dies ursprünglich nicht möglich war. Dies liegt daran, dass wir beim Wechsel von einem OBJ_LABEL-Objekt zu einem OBJ_EDIT-Objekt und wieder zurück in Bezug auf die Implementierung der Objektbewegung in eine Sackgasse geraten sind.
Wir haben auch den Anfang dessen gesehen, was im nächsten Artikel detaillierter behandelt wird. Da ich jedoch möchte, dass Sie versuchen, sehr gut zu verstehen, was hier getan wurde, da dies später benötigt wird, habe ich beschlossen, kurz vorzugreifen und etwas zu zeigen, das Ihnen meiner Ansicht nach verschiedene Ideen zur Steuerung von MetaTrader 5 mittels MQL5 geben wird.
Das ist alles für den Moment. Wir sehen uns im nächsten Artikel. Viel Erfolg beim weiteren Lernen und bis bald!
| 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/16075
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.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Neuronale Netze im Trading: Zeitreihenprognose mittels adaptiver Modenzerlegung (ACEFormer)
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Entwicklung eines Multi-Currency Expert Advisors (Teil 28): Hinzufügen eines Managers zum Schließen von Positionen
- 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.