English Русский 中文 Español 日本語 Português
Grafiken in der Bibliothek DoEasy (Teil 88): Grafische Objektkollektion — zweidimensionales dynamisches Array zur Speicherung der sich dynamisch ändernden Objekteigenschaften

Grafiken in der Bibliothek DoEasy (Teil 88): Grafische Objektkollektion — zweidimensionales dynamisches Array zur Speicherung der sich dynamisch ändernden Objekteigenschaften

MetaTrader 5Beispiele | 17 Dezember 2021, 12:42
508 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

In allen bisherigen Artikeln habe ich einfache Arrays zum Speichern von Objekteigenschaftsdaten verwendet. Wir haben drei Arrays — das eindimensionale Array der ganzzahligen, reellen und Text-Eigenschaften. Jede Zelle eines solchen Arrays speichert den Wert einer bestimmten Objekteigenschaft, die der in Defines.mqh festgelegten Aufzählung von Objekteigenschaften entspricht. Bis jetzt hat das ziemlich gut funktioniert. Aber jetzt habe ich beschlossen, dass eine einzelne Eigenschaft Werte für einzelne Einheiten dieser Eigenschaft im Falle von grafischen (und einigen anderen) Objekten zurückgeben kann.

Lassen Sie es mich erklären. Wir haben zum Beispiel die Eigenschaft Zeit des grafischen Objekts. Das grafische Objekt, das durch Preis/Zeit-Koordinaten im Diagramm positioniert wird, verfügt über mehrere Angelpunkte, mit denen es im Diagramm platziert wird. Nehmen wir an, dass ein Objekt, z.B. Arrow, nur einen Angelpunkt hat, den wir mit der Funktion ObjectGetInteger() erhalten können, indem wir OBJPROP_TIME als Eigenschafts-ID angeben. Eine solche Konstruktion gibt die Zeit auf dem Chart zurück, die dem einzigen Angelpunkt des Objekts entspricht:

ObjectGetInteger(0, Name, OBJPROP_TIME);

Was aber, wenn ein Objekt zwei Angelpunkte hat, wie eine Trendlinie? Wie kann man die Zeit beider Angelpunkte ermitteln? Der prop_modifier formale Parameter der Funktion ObjectGetInteger() kann dabei helfen. Sein Standardwert ist 0, was dem ersten Angelpunkt entspricht. Um die Daten des zweiten Punktes zu erhalten, müssen wir den Wert 1 setzen, für den dritten Punkt - 2, usw:

ObjectGetInteger(0, Name, OBJPROP_TIME, 0);
ObjectGetInteger(0, Name, OBJPROP_TIME, 1);
ObjectGetInteger(0, Name, OBJPROP_TIME, 2);
ObjectGetInteger(0, Name, OBJPROP_TIME, n);


Bis jetzt ist alles einfach und überschaubar. Alle Daten, die wir vom Objekt erhalten, werden in Arrays gespeichert: Ganzzahlige Daten gehen in das Array vom Typ long, reelle Daten in das des Typs double und die Text-Daten in das vom Typ string. Das bedeutet, dass wir einfach ein zweidimensionales Array verwenden können, um Daten zu speichern, die durch Angabe des erforderlichen Angelpunkts in prop_modifier erhalten werden können. Alles erscheint logisch. Der Punkt 0 wird in der Dimension Null gespeichert, Punkt 1 in der Eins und 2 in der Zwei usw:

array[TIME][0] = ObjectGetInteger(0, Name, OBJPROP_TIME, 0);
array[TIME][1] = ObjectGetInteger(0, Name, OBJPROP_TIME, 1);
array[TIME][2] = ObjectGetInteger(0, Name, OBJPROP_TIME, 2);
//...
array[TIME][n] = ObjectGetInteger(0, Name, OBJPROP_TIME, n);


Allerdings gibt es einen Haken. Erstens kann die Menge der erhaltenen Daten für jede der vielen Objekteigenschaften unterschiedlich sein. Im Falle des Fibo-Linien-Objekts haben wir beispielsweise zwei Angelpunkte, die zur Lokalisierung des Objekts im Chart verwendet werden. Das Objekt hat jedoch eine unterschiedliche Anzahl von Ebenen. Standardmäßig gibt es neun davon. Außerdem kann sie vom Nutzer jederzeit geändert werden. Zweitens können einige "Multi-Eigenschaften" einer einzelnen Objekteigenschaft dynamisch geändert werden.

Mit anderen Worten, wir können die Größe der zweiten Dimension des Arrays, in dem wir die Objekt-Angelpunkte speichern werden, nicht im Voraus kennen. Außerdem können sich diese Eigenschaften dynamisch ändern. In Anbetracht dessen können wir aus den folgenden Gründen keine zweidimensionalen Arrays für die Speicherung der Eigenschaften verschiedener Objekte verwenden:

  • Alle Objekte sind von ihren Basisobjekten abgeleitet, die die Arrays für die Speicherung von Eigenschaften definieren. Jede Eigenschaft sollte eine vordefinierte Größe der zweiten Array-Dimension haben, die für jedes Objekt und jede seiner Eigenschaften unterschiedlich sein kann. Wenn man ein zweidimensionales Array in MQL erstellt, sollte man den Wert der zweiten Dimension, den man nicht kennt, in der abstrakten Klasse für jede Eigenschaft jedes Objekts angeben.
  • Jede "mehrdimensionale" Eigenschaft dieser Art kann sowohl von einem Nutzer als auch programmatisch dynamisch geändert werden. In MQL können wir jedoch die Dimensionalität eines mehrdimensionalen Arrays, die nicht Null ist, nicht dynamisch ändern.
Es gibt jedoch eine Lösung. Ich werde eine nutzerdefinierte Klasse für ein mehrdimensionales dynamisches Array erstellen, das sich in jeder seiner Dimensionen dynamisch ändern kann.
Um ein solches Array zu erstellen, werde ich die Klasse des dynamischen Arrays von Zeigern auf Instanzen der Klasse CObject und ihrer Nachkommen verwenden — CArrayObj.

Basierend auf der neu erstellten Klasse, erstellen wir eine Objektklasse zum Speichern von Objekteigenschaften anstelle der üblichen Arrays. Dies wird uns erlauben, die Anzahl der gespeicherten Daten in der zweiten Dimension des Arrays jederzeit zu ändern, um die Eigenschaften der Objektklasse zu ändern, wenn wir die Eigenschaften des entsprechenden grafischen Objekts ändern.

Die aktuelle Aufgabe lautet wie folgt: Erstellen der Klasse eines mehrdimensionalen dynamischen Arrays, deren Verwendung, um das zweidimensionale dynamische Array für die Speicherung von Objekteigenschaften zu erstellen und Ersetzen der drei Arrays, die die Objekteigenschaften speichern, durch sie. Wir ordnen die Klasse des abstrakten grafischen Objekts neu an und testen die neuen Möglichkeiten zur Speicherung von Objekteigenschaften im dynamischen Array und zur Verfolgung von Eigenschaftsänderungen.

Die Klasse eines dynamischen, mehrdimensionalen Arrays

Die Klasse CArrayObj ist in der Tat ein Array, das Zeiger auf Instanzen von Objekten speichert, die von der Basisklasse CObject abgeleitet sind. Somit kann das Array jedes von CObject abgeleitete Objekt speichern. Das bedeutet, dass die Zellen des Arrays Daten vom Typ Long, Double oder String enthalten können, sowie ein weiteres CArrayObj-Array, das ebenfalls entweder Daten oder andere Arrays enthalten kann. Während bei den CArrayObj-Arrays alles ziemlich klar ist, ist die Situation bei den Daten etwas komplexer. Die Daten sind nicht von der CObject-Klasse abgeleitet, daher müssen wir die Klassen für die Speicherung solcher Daten erstellen. Außerdem sollte jedes der Objekte (ebenso wie das Array-Objekt selbst) im Array einen bestimmten Typ haben. Dies ist notwendig, um klar zu verstehen, was genau in einer Array-Zelle gespeichert wird — ein einfaches Objekt mit Integer-, Reell- oder String-Daten oder ein weiteres Objekt, das wiederum Objekte mit Daten oder ein weiteres Objekt-Array enthält. Dies ist notwendig, um Methoden zum Kopieren eines Klassenobjekts in ein anderes zu erstellen — um genau zu wissen, ob Daten in eine Zelle kopiert werden sollen (wenn sich in der entsprechenden Zelle des kopierten Arrays Daten befinden) oder ob ein neues Datenarray im Quellarray erstellt werden soll (wenn die entsprechende Zelle des kopierten Arrays das Array enthält) und Daten daraus kopiert werden sollen. Lassen Sie uns gleich die notwendigen Objekttypen einstellen. In \MQL5\Include\DoEasy\Defines.mqh fügen wir die Konstanten der erforderlichen Typen in die Liste der Objekttypen der Bibliothek ein (die vollständige Aufzählung wird zum besseren Verständnis unten gezeigt):

//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of library object types                                     |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
//--- Graphics
   OBJECT_DE_TYPE_GBASE =  COLLECTION_ID_LIST_END+1,              // "Base object of all library graphical objects" object type
   OBJECT_DE_TYPE_GELEMENT,                                       // "Graphical element" object type
   OBJECT_DE_TYPE_GFORM,                                          // Form object type
   OBJECT_DE_TYPE_GSHADOW,                                        // Shadow object type
//--- Animation
   OBJECT_DE_TYPE_GFRAME,                                         // "Single animation frame" object type
   OBJECT_DE_TYPE_GFRAME_TEXT,                                    // "Single text animation frame" object type
   OBJECT_DE_TYPE_GFRAME_QUAD,                                    // "Single rectangular animation frame" object type
   OBJECT_DE_TYPE_GFRAME_GEOMETRY,                                // "Single geometric animation frame" object type
   OBJECT_DE_TYPE_GANIMATIONS,                                    // "Animations" object type
//--- Managing graphical objects
   OBJECT_DE_TYPE_GELEMENT_CONTROL,                               // "Managing graphical objects" object type
//--- Standard graphical objects
   OBJECT_DE_TYPE_GSTD_OBJ,                                       // "Standard graphical object" object type
   OBJECT_DE_TYPE_GSTD_VLINE              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_VLINE,            // "Vertical line" object type
   OBJECT_DE_TYPE_GSTD_HLINE              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_HLINE,            // "Horizontal line" object type
   OBJECT_DE_TYPE_GSTD_TREND              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TREND,            // "Trend line" object type
   OBJECT_DE_TYPE_GSTD_TRENDBYANGLE       =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TRENDBYANGLE,     // "Trend line by angle" object type
   OBJECT_DE_TYPE_GSTD_CYCLES             =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_CYCLES,           // Cyclic lines" object type
   OBJECT_DE_TYPE_GSTD_ARROWED_LINE       =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROWED_LINE,     // "Arrowed line" object type
   OBJECT_DE_TYPE_GSTD_CHANNEL            =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_CHANNEL,          // "Equidistant channel" object type
   OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL      =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_STDDEVCHANNEL,    // "Standard deviation channel" object type
   OBJECT_DE_TYPE_GSTD_REGRESSION         =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_REGRESSION,       // "Linear regression channel" object type
   OBJECT_DE_TYPE_GSTD_PITCHFORK          =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_PITCHFORK,        // "Andrews' pitchfork" object type
   OBJECT_DE_TYPE_GSTD_GANNLINE           =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_GANNLINE,         // "Gann line" object type
   OBJECT_DE_TYPE_GSTD_GANNFAN            =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_GANNFAN,          // "Gann fan" object type
   OBJECT_DE_TYPE_GSTD_GANNGRID           =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_GANNGRID,         // "Gann grid" object type
   OBJECT_DE_TYPE_GSTD_FIBO               =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBO,             // "Fibo levels" object type
   OBJECT_DE_TYPE_GSTD_FIBOTIMES          =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOTIMES,        // "Fibo time zones" object type
   OBJECT_DE_TYPE_GSTD_FIBOFAN            =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOFAN,          // "Fibo fan" object type
   OBJECT_DE_TYPE_GSTD_FIBOARC            =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOARC,          // "Fibo arcs" object type
   OBJECT_DE_TYPE_GSTD_FIBOCHANNEL        =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOCHANNEL,      // "Fibo channel" object type
   OBJECT_DE_TYPE_GSTD_EXPANSION          =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_EXPANSION,        // "Fibo expansion" object type
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5        =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ELLIOTWAVE5,      // "Elliott 5 waves" object type
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3        =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ELLIOTWAVE3,      // "Elliott 3 waves" object type
   OBJECT_DE_TYPE_GSTD_RECTANGLE          =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_RECTANGLE,        // "Rectangle" object type
   OBJECT_DE_TYPE_GSTD_TRIANGLE           =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TRIANGLE,         // "Triangle" object type
   OBJECT_DE_TYPE_GSTD_ELLIPSE            =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ELLIPSE,          // "Ellipse" object type
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP     =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_THUMB_UP,   // "Thumb up" object type
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN   =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_THUMB_DOWN, // "Thumb down" object type
   OBJECT_DE_TYPE_GSTD_ARROW_UP           =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_UP,         // "Arrow up" object type
   OBJECT_DE_TYPE_GSTD_ARROW_DOWN         =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_DOWN,       // "Arrow down" object type
   OBJECT_DE_TYPE_GSTD_ARROW_STOP         =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_STOP,       // "Stop sign" object type
   OBJECT_DE_TYPE_GSTD_ARROW_CHECK        =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_CHECK,      // "Check mark" object type
   OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE   =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_LEFT_PRICE, // "Left price label" object type
   OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE  =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_RIGHT_PRICE,// "Right price label" object type
   OBJECT_DE_TYPE_GSTD_ARROW_BUY          =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_BUY,        // "Buy sign" object type
   OBJECT_DE_TYPE_GSTD_ARROW_SELL         =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_SELL,       // "Sell sign" object type
   OBJECT_DE_TYPE_GSTD_ARROW              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW,            // "Arrow" object type
   OBJECT_DE_TYPE_GSTD_TEXT               =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TEXT,             // "Text" object type
   OBJECT_DE_TYPE_GSTD_LABEL              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_LABEL,            // "Text label" object type
   OBJECT_DE_TYPE_GSTD_BUTTON             =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_BUTTON,           // "Button" object type
   OBJECT_DE_TYPE_GSTD_CHART              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_CHART,            // "Chart" object type
   OBJECT_DE_TYPE_GSTD_BITMAP             =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_BITMAP,           // "Bitmap" object type
   OBJECT_DE_TYPE_GSTD_BITMAP_LABEL       =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_BITMAP_LABEL,     // "Bitmap label" object type
   OBJECT_DE_TYPE_GSTD_EDIT               =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_EDIT,             // "Input field" object type
   OBJECT_DE_TYPE_GSTD_EVENT              =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_EVENT,            // "Event object which corresponds to an event in Economic Calendar" object type
   OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL    =  OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_RECTANGLE_LABEL,  // "Rectangle Label object used to create and design the custom graphical interface" object type
   
//--- Objects
   OBJECT_DE_TYPE_BASE  =  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Base object for all library objects
   OBJECT_DE_TYPE_BASE_EXT,                                       // Extended base object for all library objects
   
   OBJECT_DE_TYPE_ACCOUNT,                                        // "Account" object type
   OBJECT_DE_TYPE_BOOK_ORDER,                                     // "Book order" object type
   OBJECT_DE_TYPE_BOOK_BUY,                                       // "Book buy order" object type
   OBJECT_DE_TYPE_BOOK_BUY_MARKET,                                // "Book buy order at market price" object type
   OBJECT_DE_TYPE_BOOK_SELL,                                      // "Book sell order" object type
   OBJECT_DE_TYPE_BOOK_SELL_MARKET,                               // "Book sell order at market price" object type
   OBJECT_DE_TYPE_BOOK_SNAPSHOT,                                  // "Book snapshot" object type
   OBJECT_DE_TYPE_BOOK_SERIES,                                    // "Book snapshot series" object type
   
   OBJECT_DE_TYPE_CHART,                                          // "Chart" object type
   OBJECT_DE_TYPE_CHART_WND,                                      // "Chart window" object type
   OBJECT_DE_TYPE_CHART_WND_IND,                                  // "Chart window indicator" object type
   
   OBJECT_DE_TYPE_EVENT,                                          // "Event" object type
   OBJECT_DE_TYPE_EVENT_BALANCE,                                  // "Balance operation event" object type
   OBJECT_DE_TYPE_EVENT_MODIFY,                                   // "Pending order/position modification event" object type
   OBJECT_DE_TYPE_EVENT_ORDER_PLASED,                             // "Placing a pending order event" object type
   OBJECT_DE_TYPE_EVENT_ORDER_REMOVED,                            // "Pending order removal event" object type
   OBJECT_DE_TYPE_EVENT_POSITION_CLOSE,                           // "Position closure event" object type
   OBJECT_DE_TYPE_EVENT_POSITION_OPEN,                            // "Position opening event" object type
   
   OBJECT_DE_TYPE_IND_BUFFER,                                     // "Indicator buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_ARROW,                               // "Arrow rendering buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_BAR,                                 // "Bar buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_CALCULATE,                           // "Calculated buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_CANDLE,                              // "Candle buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_FILLING,                             // "Filling buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM,                          // "Histogram buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM2,                         // "Histogram 2 buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_LINE,                                // "Line buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_SECTION,                             // "Section buffer" object type
   OBJECT_DE_TYPE_IND_BUFFER_ZIGZAG,                              // "Zigzag buffer" object type
   OBJECT_DE_TYPE_INDICATOR,                                      // "Indicator" object type
   OBJECT_DE_TYPE_IND_DATA,                                       // "Indicator data" object type
   OBJECT_DE_TYPE_IND_DATA_LIST,                                  // "Indicator data list" object type
   
   OBJECT_DE_TYPE_IND_AC,                                         // "Accelerator Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_AD,                                         // "Accumulation/Distribution indicator" object type
   OBJECT_DE_TYPE_IND_ADX,                                        // "Average Directional Index indicator" object type
   OBJECT_DE_TYPE_IND_ADXW,                                       // "ADX indicator by Welles Wilder" object type
   OBJECT_DE_TYPE_IND_ALLIGATOR,                                  // "Alligator indicator" object type
   OBJECT_DE_TYPE_IND_AMA,                                        // "Adaptive Moving Average indicator" object type
   OBJECT_DE_TYPE_IND_AO,                                         // "Awesome Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_ATR,                                        // "Average True Range" object type
   OBJECT_DE_TYPE_IND_BANDS,                                      // "Bollinger Bands® indicator" object type
   OBJECT_DE_TYPE_IND_BEARS,                                      // "Bears Power indicator" object type
   OBJECT_DE_TYPE_IND_BULLS,                                      // "Bulls Power indicator" object type
   OBJECT_DE_TYPE_IND_BWMFI,                                      // "Market Facilitation Index indicator" object type
   OBJECT_DE_TYPE_IND_CCI,                                        // "Commodity Channel Index indicator" object type
   OBJECT_DE_TYPE_IND_CHAIKIN,                                    // "Chaikin Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_CUSTOM,                                     // "Custom indicator" object type
   OBJECT_DE_TYPE_IND_DEMA,                                       // "Double Exponential Moving Average indicator" object type
   OBJECT_DE_TYPE_IND_DEMARKER,                                   // "DeMarker indicator" object type
   OBJECT_DE_TYPE_IND_ENVELOPES,                                  // "Envelopes indicator" object type
   OBJECT_DE_TYPE_IND_FORCE,                                      // "Force Index indicator" object type
   OBJECT_DE_TYPE_IND_FRACTALS,                                   // "Fractals indicator" object type
   OBJECT_DE_TYPE_IND_FRAMA,                                      // "Fractal Adaptive Moving Average indicator" object type
   OBJECT_DE_TYPE_IND_GATOR,                                      // "Gator Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_ICHIMOKU,                                   // "Ichimoku Kinko Hyo indicator" object type
   OBJECT_DE_TYPE_IND_MA,                                         // "Moving Average indicator" object type
   OBJECT_DE_TYPE_IND_MACD,                                       // "Moving Average Convergence/Divergence indicator" object type
   OBJECT_DE_TYPE_IND_MFI,                                        // "Money Flow Index indicator" object type
   OBJECT_DE_TYPE_IND_MOMENTUM,                                   // "Momentum indicator" object type
   OBJECT_DE_TYPE_IND_OBV,                                        // "On Balance Volume indicator" object type
   OBJECT_DE_TYPE_IND_OSMA,                                       // "Moving Average of Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_RSI,                                        // "Relative Strength Index indicator" object type
   OBJECT_DE_TYPE_IND_RVI,                                        // "Relative Vigor Index indicator" object type
   OBJECT_DE_TYPE_IND_SAR,                                        // "Parabolic SAR indicator" object type
   OBJECT_DE_TYPE_IND_STDEV,                                      // "Standard Deviation indicator" object type
   OBJECT_DE_TYPE_IND_STOCH,                                      // "Stochastic Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_TEMA,                                       // "Triple Exponential Moving Average indicator" object
   OBJECT_DE_TYPE_IND_TRIX,                                       // "Triple Exponential Moving Averages Oscillator indicator" object type
   OBJECT_DE_TYPE_IND_VIDYA,                                      // "Variable Index Dynamic Average indicator" object type
   OBJECT_DE_TYPE_IND_VOLUMES,                                    // "Volumes indicator" object type
   OBJECT_DE_TYPE_IND_WPR,                                        // "Williams' Percent Range indicator" object type
   
   OBJECT_DE_TYPE_MQL5_SIGNAL,                                    // "mql5 signal" object type
   
   OBJECT_DE_TYPE_ORDER_DEAL_POSITION,                            // "Order/Deal/Position" object type
   OBJECT_DE_TYPE_HISTORY_BALANCE,                                // "Historical balance operation" object type
   OBJECT_DE_TYPE_HISTORY_DEAL,                                   // "Historical deal" object type
   OBJECT_DE_TYPE_HISTORY_ORDER_MARKET,                           // "Historical market order" object type
   OBJECT_DE_TYPE_HISTORY_ORDER_PENDING,                          // "Historical removed pending order" object type
   OBJECT_DE_TYPE_MARKET_ORDER,                                   // "Market order" object type
   OBJECT_DE_TYPE_MARKET_PENDING,                                 // "Pending order" object type
   OBJECT_DE_TYPE_MARKET_POSITION,                                // "Market position" object type
   
   OBJECT_DE_TYPE_PENDING_REQUEST,                                // "Pending trading request" object type
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_OPEN,                  // "Pending request to open a position" object type
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_CLOSE,                 // "Pending request to close a position" object type
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_SLTP,                  // "Pending request to modify position stop orders" object type
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_PLACE,                    // "Pending request to place a pending order" object type
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_REMOVE,                   // "Pending request to delete a pending order" object type
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_MODIFY,                   // "Pending request to modify pending order parameters" object type
   
   OBJECT_DE_TYPE_SERIES_BAR,                                     // "Bar" object type
   OBJECT_DE_TYPE_SERIES_PERIOD,                                  // "Period timeseries" object type
   OBJECT_DE_TYPE_SERIES_SYMBOL,                                  // "Symbol timeseries" object type
   
   OBJECT_DE_TYPE_SYMBOL,                                         // "Symbol" object type
   OBJECT_DE_TYPE_SYMBOL_BONDS,                                   // "Bond symbol" object type
   OBJECT_DE_TYPE_SYMBOL_CFD,                                     // "CFD (contract for difference) symbol" object type
   OBJECT_DE_TYPE_SYMBOL_COLLATERAL,                              // "Non-tradable asset symbol" object type" object type
   OBJECT_DE_TYPE_SYMBOL_COMMODITY,                               // "Commodity symbol" object type
   OBJECT_DE_TYPE_SYMBOL_COMMON,                                  // "Common group symbol" object type
   OBJECT_DE_TYPE_SYMBOL_CRYPTO,                                  // "Cryptocurrency symbol" object type
   OBJECT_DE_TYPE_SYMBOL_CUSTOM,                                  // "Custom symbol" object type
   OBJECT_DE_TYPE_SYMBOL_EXCHANGE,                                // "Exchange symbol" object type
   OBJECT_DE_TYPE_SYMBOL_FUTURES,                                 // "Futures symbol" object type
   OBJECT_DE_TYPE_SYMBOL_FX,                                      // "Forex symbol" object type
   OBJECT_DE_TYPE_SYMBOL_FX_EXOTIC,                               // "Exotic Forex symbol" object type
   OBJECT_DE_TYPE_SYMBOL_FX_MAJOR,                                // "Major Forex symbol" object type
   OBJECT_DE_TYPE_SYMBOL_FX_MINOR,                                // "Minor Forex symbol" object type
   OBJECT_DE_TYPE_SYMBOL_FX_RUB,                                  // "RUB Forex symbol" object type
   OBJECT_DE_TYPE_SYMBOL_INDEX,                                   // "Index symbol" object type
   OBJECT_DE_TYPE_SYMBOL_INDICATIVE,                              // "Indicative symbol" object type
   OBJECT_DE_TYPE_SYMBOL_METALL,                                  // "Metal symbol" object type
   OBJECT_DE_TYPE_SYMBOL_OPTION,                                  // "Option symbol" object type
   OBJECT_DE_TYPE_SYMBOL_STOCKS,                                  // "Stock symbol" object type
   
   OBJECT_DE_TYPE_TICK,                                           // "Tick" object type
   OBJECT_DE_TYPE_NEW_TICK,                                       // "New tick" object type
   OBJECT_DE_TYPE_TICKSERIES,                                     // "Tick data series" object type
   
   OBJECT_DE_TYPE_TRADE,                                          // "Trading object" object type
   
   OBJECT_DE_TYPE_LONG,                                           // "Long type data" object type
   OBJECT_DE_TYPE_DOUBLE,                                         // "Double type data" object type
   OBJECT_DE_TYPE_STRING,                                         // "String type data" object type
   OBJECT_DE_TYPE_OBJECT,                                         // "Object type data" object type
  };

//+------------------------------------------------------------------+
//| Search and sorting data                                          |
//+------------------------------------------------------------------+


In \MQL5\Include\DoEasy\Data.mqh ergänzen wir die Nachrichtenindices:

   MSG_LIB_SYS_FAILED_ADD_BUFFER,                     // Failed to add buffer object to the list
   MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ,              // Failed to create \"Indicator buffer\" object
   MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST,                // Failed to add object to the list
   
   MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ,           // Failed to create long data object
   MSG_LIB_SYS_FAILED_CREATE_DOUBLE_DATA_OBJ,         // Failed to create double data object
   MSG_LIB_SYS_FAILED_CREATE_STRING_DATA_OBJ,         // Failed to create string data object
   
   MSG_LIB_SYS_FAILED_DECREASE_LONG_ARRAY,            // Failed to decrease long array size
   MSG_LIB_SYS_FAILED_DECREASE_DOUBLE_ARRAY,          // Failed to decrease double array size
   MSG_LIB_SYS_FAILED_DECREASE_STRING_ARRAY,          // Failed to decrease string array size
   
   MSG_LIB_SYS_FAILED_GET_LONG_DATA_OBJ,              // Failed to get long data object
   MSG_LIB_SYS_FAILED_GET_DOUBLE_DATA_OBJ,            // Failed to get double data object
   MSG_LIB_SYS_FAILED_GET_STRING_DATA_OBJ,            // Failed to get string data object
   
   MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY,            // Request outside long array
   MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY,          // Request outside double array
   MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY,          // Request outside string array
   
   MSG_LIB_TEXT_YES,                                  // Yes
   MSG_LIB_TEXT_NO,                                   // No
   MSG_LIB_TEXT_AND,                                  // and


...

   MSG_GRAPH_OBJ_TEXT_ANCHOR_RIGHT_UPPER,             // Anchor point at the upper right corner
   MSG_GRAPH_OBJ_TEXT_ANCHOR_UPPER,                   // Anchor point at the upper center
   MSG_GRAPH_OBJ_TEXT_ANCHOR_CENTER,                  // Anchor point at the very center of the object
   
   MSG_GRAPH_OBJ_TEXT_TIME_PRICE,                     // Price/time coordinates
   MSG_GRAPH_OBJ_TEXT_PIVOT,                          // Pivot point
   MSG_GRAPH_OBJ_TEXT_LEVELSVALUE_ALL,                // Level values
   MSG_GRAPH_OBJ_TEXT_LEVEL,                          // Level
   MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON,              // On state
   MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF,             // Off state

//--- 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_CREATE_EVN_CTRL_INDICATOR,           // Indicator for controlling and sending events created
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR,    // Failed to create the indicator for controlling and sending events
   MSG_GRAPH_OBJ_CLOSED_CHARTS,                       // Chart windows closed:
   MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS,            // Objects removed together with charts:
   
  };
//+------------------------------------------------------------------+


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

   {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"},
   {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""},
   {"Не удалось добавить объект в список","Failed to add object to the list"},
   
   {"Не удалось создать объект long-данных","Failed to create long-data object"},
   {"Не удалось создать объект double-данных","Failed to create double-data object"},
   {"Не удалось создать объект string-данных","Failed to create string-data object"},
   
   {"Не удалось уменьшить размер long-массива","Failed to reduce the size of the long-array"},
   {"Не удалось уменьшить размер double-массива","Failed to reduce the size of the double-array"},
   {"Не удалось уменьшить размер string-массива","Failed to reduce the size of the string-array"},

   {"Не удалось получить объект long-данных","Failed to get long-data object"},
   {"Не удалось получить объект double-данных","Failed to get double-data object"},
   {"Не удалось получить объект string-данных","Failed to get string-data object"},
   
   {"Запрос за пределами long-массива","Data requested outside the long-array"},
   {"Запрос за пределами double-массива","Data requested outside the double-array"},
   {"Запрос за пределами string-массива","Data requested outside the string-array"},
   

   {"Да","Yes"},
   {"Нет","No"},
   {"и","and"},


...

   {"Точка привязки в правом верхнем углу","Anchor point at the upper right corner"},
   {"Точка привязки сверху по центру","Anchor point above in the center"},
   {"Точка привязки строго по центру объекта","Anchor point strictly in the center of the object"},
   
   {"Координаты цена/время","Price/time coordinates"},
   {"Опорная точка ","Pivot point "},
   {"Значения уровней","Level values"},
   {"Уровень ","Level "},
   {"Состояние \"On\"","State \"On\""},
   {"Состояние \"Off\"","State \"Off\""},
   
//--- CGraphElementsCollection
   {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"},
   {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"},
   
   {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"},
   {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"},
   {"Закрыто окон графиков: ","Closed chart windows: "},
   {"С ними удалено объектов: ","Objects removed with them: "},
   
  };
//+---------------------------------------------------------------------+



Im Ordner \MQL5\Include\DoEasy\Services\ erstellen wir für die Dienstfunktionen eine neue Datei XDimArray.mqh der Klasse CXDimArray.

Da wir einen Ersatz für gewöhnliche Arrays zur Speicherung von integer, reellen und string Eigenschaften einführen wollen, wobei diese Arrays mehrdimensional und in jeder Dimension dynamisch sein sollen, werden wir drei absolut identische Klassen eines mehrdimensionalen dynamischen Arrays erstellen. Jede soll ihren eigenen Typ speichern (long für ganzzahlige Daten, double für reelle Daten und string für Textdaten).

Die Klassenhierarchie sieht wie folgt aus:

  • CObject --> Abstrakte Datentypklasse --> Datentypklasse,
  • CArrayObj-Klasse --> Klasse mit einer Dimension, die die Liste der Datentypklassen speichert,
  • CArrayObj-Klasse --> mehrdimensionale Array-Klasse, die die Liste der Klassenobjekte einer Dimension speichert

Für jeden Datentyp soll es eine eigene Klasse geben, die integer, reellen und string Daten speichert. In jedem Fall wird die Klasse von der Klasse des Basisdatentyps abgeleitet, in der wir den in der abgeleiteten Klasse gespeicherten Datentyp festlegen.

Die Klasse für eine einzelne Array-Dimension wird von der Klasse CArrayObj abgeleitet. Es handelt sich dabei um eine Liste, die Zeiger auf Datenobjekte oder andere Listen speichert.

Die multidimensionale Array-Klasse ist die CArrayObj-Liste, in der die Zeiger auf Instanzen von Klassenobjekten einer einzelnen Array-Dimension gespeichert werden. Mit anderen Worten: Die Liste ist die erste Array-Dimension, während die Klassen einer einzelnen Dimension dynamisch erweiterbare Objekte der ersten Dimension sind. Enthält sie nur ein Objekt einer Dimension mit der Größe 1, entspricht der Zugriff auf sie dem Eintrag array[0][0]. Enthält es zwei Objekte derselben Dimension mit der Größe 1, entspricht der Zugriff auf das erste dem Eintrag array[0][0], während der Zugriff auf das zweite dem Eintrag array[0][1] entspricht.
Wenn die Objekte einer Dimension größer als 1 sind, entspricht der Zugriff auf sie natürlich den Einträgen

array[0][0],  array[0][1],  array[0][2], ...,  array[0][N],
array[1][0],  array[1][1],  array[1][2], ...,  array[1][N].

Der Zugriff erfolgt natürlich über die entsprechenden Methoden. Wir können sowohl die erste als auch die zweite Dimensionalität des Arrays dynamisch ändern.

Auf die dritte und weitere Dimensionen werde ich hier nicht eingehen, da wir nur ein zweidimensionales dynamisches Array benötigen. Auf der Grundlage dieser Klassen ist es möglich, dynamische Arrays mit einer beliebigen Anzahl von Dimensionen zu entwickeln. Es ist möglich, die Dimensionalität zu ändern (erhöhen/verringern) oder eine neue Dimension in jeder Array-Zelle einer beliebigen Dimension hinzuzufügen.

Da ich drei identische Klassen erstellen werde, von denen jede ihren eigenen Datentyp speichern soll, wollen wir die Klassen von nur einem Typ im Detail betrachten.

In die bereits erstellte Datei XDimArray.mqh der Klasse CXDimArray fügen wir die Dateien der Klasse CArrayObj und der Bibliotheksnachrichtenklasse ein, sowie die Klasse der abstrakten Dateneinheit, die vom Basisobjekt CObject abgeleitet ist:

//+------------------------------------------------------------------+
//|                                                    XDimArray.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "Message.mqh"
//+------------------------------------------------------------------+
//| Abstract data unit class                                         |
//+------------------------------------------------------------------+
class CDataUnit : public CObject
  {
private:
   int               m_type;  
protected:
                     CDataUnit(int type)  { this.m_type=type;        }
public:
   virtual int       Type(void)     const { return this.m_type;      }
                     CDataUnit(){ this.m_type=OBJECT_DE_TYPE_OBJECT; }
  };
//+------------------------------------------------------------------+


Hier haben wir eine private Variable zum Speichern eines gespeicherten Datentyps in nachgeordneten Objekten, einen geschützten parametrischen Konstruktor, dem der gespeicherte Datentyp des nachgeordneten Objekts übergeben wird und eine öffentliche virtuelle Methode, die den gespeicherten Datentyp zurückgibt, der in der m_type-Variable festgelegt ist.
Im Standardkonstruktor wird der Datentyp OBJECT_DE_TYPE_OBJECT standardmäßig auf die Variable m_type gesetzt.

Als Nächstes fügen wir die Integer-Dateneinheit unter dieser Klasse hinzu:

//+------------------------------------------------------------------+
//| Integer data unit class                                          |
//+------------------------------------------------------------------+
class CDataUnitLong : public CDataUnit
  {
public:
   long              Value;
                     CDataUnitLong() : CDataUnit(OBJECT_DE_TYPE_LONG){}
  };
//+------------------------------------------------------------------+


Die Klasse hat eine öffentliche Feldvariable zum Speichern eines Integer-Wertes sowie den Klassenkonstruktor. Der Typ OBJECT_DE_TYPE_LONG, der angibt, dass das Objekt Integer-Datentypen speichert, wird in der Initialisierungsliste des Klassenkonstruktors an den Konstruktor der Elternklasse übergeben.

Wir erstellen nun die Klasse des Objekts mit einer einzigen long Array-Dimension:

//+------------------------------------------------------------------+
//| Class of a single long array dimension                           |
//+------------------------------------------------------------------+
class CDimLong : public CArrayObj
  {

  }

Ich werde alle Methoden in den Klassenkörper schreiben. Jede Methode wird in der Auflistung ausführlich kommentiert.

Im privaten Abschnitt der Klasse fügen wir die Methode hinzu, die das lange Datenobjekt aus dem Array empfängt:

//+------------------------------------------------------------------+
//| Class of a single long array dimension                           |
//+------------------------------------------------------------------+
class CDimLong : public CArrayObj
  {
private:
//--- Get long data object from the array
   CDataUnitLong    *GetData(const string source,const int index) const
                       {
                        //--- Get the pointer to the object by the index passed to the method. If the passed index is less than zero, it is set to zero
                        CDataUnitLong *data=this.At(index<0 ? 0 : index);
                        //--- If failed to receive the object
                        if(data==NULL)
                          {
                           //--- if the passed index exceeds the number of objects in the list, display a message informing of exceeding the list size
                           if(index>this.Total()-1)
                              ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY)," (",index,"/",this.Total(),")");
                           //--- otherwise, display the message informing of failing to get the pointer to the object
                           else
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_LONG_DATA_OBJ);
                          }
                        //--- Return the pointer to the object or NULL in case of an error
                        return data;
                       }


Diese Methode implementiert die Kontrolle über den Fehler "Array out of range". Mit anderen Worten, wenn ein übergebener Index eine nicht existierende Array-Zelle anzeigt, wird dies in der Journalmeldung mitgeteilt. Wenn der an die Methode übergebene Index kleiner als Null ist (was nicht vorkommen sollte), wird der Wert auf Null angepasst. Wenn wir auf den gültigen Array-Index zugreifen, aber nicht in der Lage sind, das Objekt zu erhalten, wird die entsprechende Fehlermeldung im Journal angezeigt. Als Ergebnis geben wir entweder den Zeiger auf das Objekt zurück oder NULL im Falle eines Fehlers. Die Protokollierung solcher Fehler hilft dabei, die Indizes der benötigten Daten bei der Verwendung dieser Klasse korrekt anzugeben. Wir werden auf diese Methode von anderen Klassenmethoden aus zugreifen, um Array-Daten zu erhalten. Diese Methode wird über Fehler beim Zugriff auf Array-Objekte informieren.

Als Nächstes implementieren wir die Methode, die die angegebene Anzahl von Zellen mit Objekten am Ende des Arrays hinzufügt:

//--- Add the specified number of cells with objects to the end of the array
   bool              AddQuantity(const string source,const int total,CObject *object)
                       {
                        //--- Declare the variable for storing the result of adding objects to the list
                        bool res=true;
                        //--- in the list by the number of added objects passed to the method
                        for(int i=0;i<total;i++)
                          {
                           //--- if failed to add the object to the list
                           if(!this.Add(object))
                             {
                              //--- display the appropriate message, add 'false' to the variable value
                              //--- and move on to the loop next iteration
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                              res &=false;
                              continue;
                             }
                          }
                        //--- Return the total result of adding the specified number of objects to the list
                        return res;
                       }


Die Methode erhält die Anzahl der Objekte, die zum Arrayende hinzugefügt werden sollen, und den Zeiger auf das Objekt, dessen Instanzen hinzugefügt werden sollen. Anschließend werden in der Schleife die angegebene Anzahl Zeiger auf das Objekt in die Liste eingefügt und das Ergebnis des Hinzufügens von Objekten zur Liste zurückgegeben.

Die Methode hat einen logischen Fehler, der verhindert, dass sie funktioniert, wenn die Anzahl der hinzugefügten Objekte in der Liste zwei übersteigt. Ich werde ihn im nächsten Artikel beheben. Wenn Sie ihn bereits gefunden haben, möchte ich Sie beglückwünschen. Es bedeutet, dass meine Arbeit nicht umsonst war.

Fügen wir im öffentlichen Abschnitt der Klasse noch die Methode zur Initialisierung des Arrays hinzu:

public:
//--- Initialize the array
   void              Initialize(const int total,const long value=0)
                       {
                        //--- Clear the array and increase its size by the value specified in the parameters by setting the default value
                        this.Clear();
                        this.Increase(total,value);
                       }


Wir löschen das Arrays mit der Methode Clear() der übergeordneten Klasse und erhöhen die Größe um den angegebenen Wert mit der Methode Increase():

//--- Increase the number of data cells by the specified value, return the number of added elements
   int               Increase(const int total,const long value=0)
                       {
                        //--- Save the current array size
                        int size_prev=this.Total();
                        //--- Create a new long data object
                        CDataUnitLong *data=new CDataUnitLong();
                        //--- If failed to create an object, inform of that and return zero
                        if(data==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ));
                           return 0;
                          }
                        //--- Set the specified value to a newly created object
                        data.Value=value;
                        //--- Add the specified number of object instances to the list
                        //--- and return the difference between the obtained and previous array size
                        this.AddQuantity(DFUN,total,data);
                        return this.Total()-size_prev;
                       }


Die Methode fügt dem Array die angegebene Menge an langen Daten hinzu und gibt die Menge der hinzugefügten Daten zurück.

Schreiben wir die Methode, die die Anzahl der Datenzellen um den angegebenen Wert verringert:

//--- Decrease the number of data cells by the specified value, return the number of removed elements. The very first element always remains
   int               Decrease(const int total)
                       {
                        //--- If not a single cell remains after removing array cells, return zero
                        if(total>this.Total()-1)
                           return 0;
                        //--- Save the current array size
                        int total_prev=this.Total();
                        //--- Calculate the initial index the array cells should be removed from
                        int from=this.Total()-total;
                        //--- The final index is always an array end
                        int to=this.Total()-1;
                        //--- If failed to remove the specified number of array cells, inform of that in the journal
                        if(!this.DeleteRange(from,to))
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_LONG_ARRAY);
                        //--- return the difference between the previous array size and the one obtained as a result of deleting cells
                        return total_prev-this.Total();
                       }


Da das Array mindestens ein Element haben sollte, stellen wir zunächst sicher, dass beim Entfernen der gewünschten Anzahl mindestens ein oder mehrere Elemente im Array verbleiben. Als Nächstes entfernen wir Elemente vom Ende der Liste in der angegebenen Anzahl mit der DeleteRange()-Methode der Elternklasse. Als Ergebnis wird die Anzahl der entfernten Elemente zurückgegeben.

Schreiben wir die Methode, die eine neue Arraygröße festlegt:

//--- Set a new array size
   bool              SetSize(const int size,const long initial_value=0)
                       {
                        //--- If the zero size is passed, return 'false'
                        if(size==0)
                           return false;
                        //--- Calculate the number of cells to be added or removed for receiving the required array size
                        int total=fabs(size-this.Total());
                        //--- If the passed array size exceeds the current one,
                        //--- return the result of adding the calculated number of arrays to the array
                        if(size>this.Total())
                           return(this.Increase(total,initial_value)==total);
                        //--- otherwise, if the passed array size is less than the current one, 
                        //--- return the result of removing the calculated number of arrays from the array
                        else if(size<this.Total())
                           return(Decrease(total)==total);
                        //--- If the passed size is equal to the current one, simply return 'true'
                        return true;
                       }


Die Methode verwendet die oben genannten Methoden zum Entfernen/Hinzufügen von Elementen zum Array, um dessen Größe auf die angegebene Größe zu bringen.

Fügen wir noch die Methode hinzu, die den Wert in der angegebenen Array-Zelle festlegt:

//--- Set the value to the specified array cell
   bool              Set(const int index,const long value)
                       {
                        //--- Get the pointer to the data object by a specified index
                        CDataUnitLong *data=this.GetData(DFUN,index);
                        //--- If failed to get the object, return 'false'
                        if(data==NULL)
                           return false;
                        //--- Return the value, passed to the method, to the obtained object and return 'true'
                        data.Value=value;
                        return true;
                       }


Hier erhalten wir das Datenobjekt über einen bestimmten Index und setzen den an die Methode übergebenen Wert darauf. Wenn das Objekt erfolgreich empfangen wurde, wird true zurückgegeben. Im Falle eines Fehlers (die private Methode GetData() informiert darüber im Journal), wird false zurückgegeben.

Die Methode gibt die Anzahl der Daten im Array zurück:

//--- Return the amount of data (the array size is defined using the Total() method of the CArrayObj parent class)
   int               Size(void) const { return this.Total(); }


Die Methode gibt einfach die Anzahl der Elemente in der Liste zurück, indem sie die Methode Total() der CArray-Klasse verwendet, die Teil der übergeordneten CArrayObj-Klasse ist.

Schreiben wir die Methode, die den Wert durch den angegebenen Index zurückgibt:

//--- Returns the value at the specified index
   long              Get(const int index) const
                       {
                        //--- Get the pointer to the data object by index
                        CDataUnitLong *data=this.GetData(DFUN,index);
                        //--- If the object is received successfully,
                        //--- return the value set in the object,
                        //--- otherwise, return zero
                        return(data!=NULL ? data.Value : 0);
                       }


Holt den Zeiger auf das Datenobjekt aus der Liste nach Index und gibt den im angegebenen Objekt eingestellten Wert zurück. Im Falle eines Fehlers wird Null zurückgegeben.

Die überladene Methode Get() gibt den booleschen Wert zurück, wenn sie Daten in der Variablen empfängt, die über eine Verknüpfung an die Methode übergeben wurde:

   bool              Get(const int index, long &value) const
                       {
                        //--- Set the initial value to 'value' passed to the method via a link
                        value=0;
                        //--- Get the pointer to the data object by index
                        CDataUnitLong *data=this.GetData(DFUN,index);
                        //--- If failed to get the object, return 'false'
                        if(data==NULL)
                           return false;
                        //--- Set the value stored in the object to 'value' and return 'true'
                        value = data.Value;
                        return true;
                       }



Im Standardkonstruktor initialisieren wir einfach das Array mit der Methode Initialize() und setzen die Arraygröße auf 1 und den Standardwert auf 0.
Im parametrischen Konstruktor geben wir
die erforderliche Array-Größe und den Standardwert an.
Im Klassendestruktor entfernen wir die Array-Elemente und löschen das Array löschen und geben den Array-Speicher vollständig frei.

//--- Constructors
                     CDimLong(void)                               { this.Initialize(1);            }
                     CDimLong(const int total,const long value=0) { this.Initialize(total,value);  }
//--- Destructor
                    ~CDimLong(void)
                       {
                        this.Clear();
                        this.Shutdown();
                       }
  };
//+------------------------------------------------------------------+



Als Nächstes erstellen wir die Klasse des dynamischen mehrdimensionalen langen Arrays in derselben Datei:

//+------------------------------------------------------------------+
//| Dynamic multidimensional long array class                        |
//+------------------------------------------------------------------+
class CXDimArrayLong : public CArrayObj
  {
  
  }

Die Klasse ist eine Liste, in der die Objekte der oben genannten Klasse gespeichert werden, die ihrerseits wiederum Listen sind, die Objekte mit Daten speichern. Die Klasse ist also die erste Dimension, und die in ihr gespeicherten Listen sind die Listen der Daten der Dimension.

Um es noch deutlicher zu machen:

Index 0 der Liste der Klasse CXDimArrayLong zeigt auf das Objekt der Klasse CDimLong, das an erster Stelle der Liste steht; Index 0 der Klasse CDimLong zeigt auf das Objekt der Klasse CDataUnitLong, das an erster Stelle der Liste der Klasse CDimLong steht.

Dies ist gleich dem Eintrag array[0][0];

Index 1 der Klassenliste CXDimArrayLong zeigt auf das Objekt der Klasse CDimLong, das an zweiter Stelle in der Liste steht; Index 0 der Klasse CDimLong zeigt auf das Objekt der Klasse CDataUnitLong, das an erster Stelle in der Liste der Klasse CDimLong steht.

Dies ist gleich dem Eintrag array[1][0];

------

Index 0 der Klassenliste CXDimArrayLong zeigt auf das Objekt der Klasse CDimLong, das sich an erster Stelle in der Liste befindet; Index 1 der Klasse CDimLong zeigt auf das Objekt der Klasse CDataUnitLong, das sich an zweiter Stelle in der Liste der Klasse CDimLong befindet.

Dies ist gleich dem Eintrag array[0][1];

Index 1 der Klassenliste CXDimArrayLong zeigt auf das Objekt der Klasse CDimLong, das an zweiter Stelle in der Liste steht; Index 1 der Klasse CDimLong zeigt auf das Objekt der Klasse CDataUnitLong, das an zweiter Stelle in der Liste der Klasse CDimLong steht.

Dies ist gleich dem Eintrag array[1][1];

usw.


Im privaten Abschnitt der Klasse fügen wir die Methode hinzu, die das Datenarray der ersten Dimension zurückgibt:

//+------------------------------------------------------------------+
//| Dynamic multidimensional long array class                        |
//+------------------------------------------------------------------+
class CXDimArrayLong : public CArrayObj
  {
private:
//--- Return the data array from the first dimensionality
   CDimLong         *GetDim(const string source,const int index) const
                       {
                        //--- Get the first dimension array object by index
                        CDimLong *dim=this.At(index<0 ? 0 : index);
                        //--- If failed to get the pointer to the object,
                        if(dim==NULL)
                          {
                           //--- if the index is outside the array, inform of the request outside the array
                           if(index>this.Total()-1)
                              ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY)," (",index,"/",this.Total(),")");
                           //--- otherwise, inform of the error when receiving the pointer to the array
                           else
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_LONG_DATA_OBJ);
                          }
                        //--- Return either the pointer to the object or NULL in case of an error
                        return dim;
                       }


Die Methode erhält den Zeiger auf das Objekt der Klasse CDimLong mit dem angegebenen Index. Wenn der Index kleiner als Null ist, wird der Index gleich Null verwendet. Wenn der Index außerhalb des Arrays zeigt, informieren wir im Journal über die Anfrage außerhalb des Arrays, oder, wenn das Objekt nicht empfangen werden konnte, informieren im Journal über den Objektempfangsfehler. Als Ergebnis geben wir entweder den Zeiger auf das Objekt zurück oder NULL im Falle eines Fehlers.

Im privaten Bereich der Klasse schreiben wir die Methode, die der ersten Dimensionalität eine neue Dimension hinzufügt:

//--- Add a new dimension to the first dimensionality
   bool              AddNewDim(const string source,const int size,const long initial_value=0)
                       {
                        //--- Create a new array object 
                        CDimLong *dim=new CDimLong(size,initial_value);
                        //--- If failed to create an object, inform of that and return 'false'
                        if(dim==NULL)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ);
                           return false;
                          }
                        //--- If failed to add the object to the list, remove the object, inform of the error in the journal and return 'false'
                        if(!this.Add(dim))
                          {
                           delete dim;
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           return false;
                          }
                        //--- Object successfully created and added to the list
                        return true;
                       }


Die Methode erstellt das neue Objekt der Klasse CDimLong und fügt es an das Ende der Liste an, wodurch die Größe der ersten Dimension erhöht wird.

Im öffentlichen Teil der Klasse schreiben wir die Methoden für die Arbeit mit Arrays. Alle Methoden sind in den Codekommentaren detailliert beschrieben. Ich glaube, es ergibt keinen Sinn, sie hier zu wiederholen. Betrachten wir sie einfach so, wie sie sind:

public:
//--- Increase the number of data cells by the specified 'total' value in the first dimensionality,
//--- return the number of added elements to the dimensionality. Added cells' size is 'size'
   int               IncreaseRangeFirst(const int total,const int size,const long initial_value=0)
                       {
                        //--- Save the current array size
                        int total_prev=this.Total();
                        //--- In the loop by the specified number, add new objects to the array
                        for(int i=0;i<total;i++)
                           this.AddNewDim(DFUN,size,initial_value);
                        //--- Return the difference between the obtained and previous array size
                        return(this.Total()-total_prev);
                       }
//--- Increase the number of data cells by the specified 'total' value in the specified 'range' dimensionality,
//--- return the number of added elements to the changed dimensionality
   int               IncreaseRange(const int range,const int total,const long initial_value=0)
                       {
                        //--- Get the pointer to the array by 'range' index
                        CDimLong *dim=this.GetDim(DFUN,range);
                        //--- Return the result of increasing the array size by 'total' or zero in case of an error
                        return(dim!=NULL ? dim.Increase(total,initial_value) : 0);
                       }
//--- Decrease the number of cells with data in the first dimensionality by the specified value,
//--- return the number of removed elements. The very first element always remains
   int               DecreaseRangeFirst(const int total)
                       {
                        //--- Make sure at least one element remains in the array after the decrease,
                        //--- if not, return 'false'
                        if(total>this.Total()-1)
                           return 0;
                        //--- Save the current array size
                        int total_prev=this.Total();
                        //--- Calculate the initial index to remove the array elements from
                        int from=this.Total()-total;
                        //--- The final index is always the last array element
                        int to=this.Total()-1;
                        //--- If failed to remove the specified number of elements, inform of that in the journal
                        if(!this.DeleteRange(from,to))
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_LONG_ARRAY);
                        //--- Return the number of removed array elements
                        return total_prev-this.Total();
                       }                      
//--- Decrease the number of data cells by the specified value in the specified dimensionality,
//--- return the number of removed elements. The very first element always remains
   int               DecreaseRange(const int range,const int total)
                       {
                        //--- Get the pointer to the array by 'range' index
                        CDimLong *dim=this.GetDim(DFUN,range);
                        //--- Return the result of decreasing the array size by 'total' or zero in case of an error
                        return(dim!=NULL ? dim.Decrease(total) : 0);
                       }
//--- Set the new array size in the specified dimensionality
   bool              SetSizeRange(const int range,const int size,const long initial_value=0)
                       {
                        //--- Get the pointer to the array by 'range' index
                        CDimLong *dim=this.GetDim(DFUN,range);
                        //--- Return the result of setting the array size to 'size' or 'false' in case of an error
                        return(dim!=NULL ? dim.SetSize(size,initial_value) : false);
                       }
//--- Set the value to the specified array cell of the specified dimension
   bool              Set(const int index,const int range,const long value)
                       {
                        //--- Get the pointer to the array by 'index'
                        CDimLong *dim=this.GetDim(DFUN,index);
                        //--- Return the result of setting the value to the array cell by 'range' index or 'false' in case of an error
                        return(dim!=NULL ? dim.Set(range,value) : false);
                       }
//--- Return the value at the specified index of the specified dimension
   long              Get(const int index,const int range) const
                       {
                        //--- Get the pointer to the array by 'index'
                        CDimLong *dim=this.GetDim(DFUN,index);
                        //--- Return the result of receiving the value from the array cell by 'range' index or 0 in case of an error
                        return(dim!=NULL ? dim.Get(range) : 0);
                       }
   bool              Get(const int index,const int range,long &value) const
                       {
                        //--- Get the pointer to the array by 'index'
                        CDimLong *dim=this.GetDim(DFUN,index);
                        //--- Return the result of receiving the value from the array cell by 'range' index to the 'value' variable
                        //--- or 'false' in case of an error ('value' is set to zero)
                        return(dim!=NULL ? dim.Get(range,value) : false);
                       }
//--- Return the amount of data (size of the specified dimension array)
   int               Size(const int range) const
                       {
                        //--- Get the pointer to the array by 'range' index
                        CDimLong *dim=this.GetDim(DFUN,range);
                        //--- Return the size of the obtained array by index or zero in case of an error
                        return(dim!=NULL ? dim.Size() : 0);
                       }
//--- Return the total amount of data (the total size of all dimensions)
   int               Size(void) const
                       {
                        //--- Set the initial size
                        int size=0;
                        //--- In the loop by all arrays in the list,
                        for(int i=0;i<this.Total();i++)
                          {
                           //--- get the next array.
                           CDimLong *dim=this.GetDim(DFUN,i);
                           //--- If failed to get the array, move on to the next one
                           if(dim==NULL)
                              continue;
                           //--- Add the array size to the size value
                           size+=dim.Size();
                          }
                        //--- Return the obtained value
                        return size;
                       }
//--- Constructor
                     CXDimArrayLong()
                       {
                        //--- Clear the list and add a single array to it
                        this.Clear();
                        this.Add(new CDimLong(1));
                       }
                     CXDimArrayLong(int first_dim_size,const int dim_size,const long initial_value=0)
                       {
                        //--- Clear the list
                        this.Clear();
                        int total=(first_dim_size<1 ? 1 : first_dim_size);
                        //--- In the loop by the necessary number of arrays calculated in 'total' from first_dim_size,
                        //--- add new arrays with the specified number of elements in dim_size to the list
                        for(int i=0;i<total;i++)
                           this.Add(new CDimLong(dim_size,initial_value));
                       }
//--- Destructor
                    ~CXDimArrayLong()
                       {
                        //--- Remove array elements and
                        //--- clear the array while completely freeing the array memory
                        this.Clear();
                        this.Shutdown();
                       }
  };
//+------------------------------------------------------------------+


Wie man sehen kann, arbeiten wir hauptsächlich mit den Methoden des erhaltenen CDimLong-Klassenobjekts, das ich bereits oben beim Schreiben der CDimLong-Klasse besprochen habe. Wenn Sie Fragen haben, können Sie diese gerne im Kommentarteil unten stellen.

Nun müssen wir genau die gleichen Klassen für Daten der Typen double und string schreiben. Die Klassen sind völlig identisch mit den oben besprochenen:

//+------------------------------------------------------------------+
//| Real data unit class                                             |
//+------------------------------------------------------------------+
class CDataUnitDouble : public CDataUnit
  {
public:
   double            Value;
//--- Constructor
                     CDataUnitDouble() : CDataUnit(OBJECT_DE_TYPE_DOUBLE){}
  };
//+------------------------------------------------------------------+
//| Class of a single double array dimension                         |
//+------------------------------------------------------------------+
class CDimDouble : public CArrayObj
  {
private:
//--- Get long data object from the array
   CDataUnitDouble  *GetData(const string source,const int index) const
                       {
                        CDataUnitDouble *data=this.At(index<0 ? 0 : index);
                        if(data==NULL)
                          {
                           if(index>this.Total()-1)
                              ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY)," (",index,"/",this.Total(),")");
                           else
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_DOUBLE_DATA_OBJ);
                          }
                        return data;
                       }
//--- Add the specified number of cells with objects to the end of the array
   bool              AddQuantity(const string source,const int total,CObject *object)
                       {
                        bool res=true;
                        for(int i=0;i<total;i++)
                          {
                           if(!this.Add(object))
                             {
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                              res &=false;
                              continue;
                             }
                          }
                        return res;
                       }
public:
//--- Initialize the array
   void              Initialize(const int total,const double value=0)
                       {
                        this.Clear();
                        this.Increase(total,value);
                       }
//--- Increase the number of data cells by the specified value, return the number of added elements
   int               Increase(const int total,const double value=0)
                       {
                        int size_prev=this.Total();
                        CDataUnitDouble *data=new CDataUnitDouble();
                        if(data==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_DOUBLE_DATA_OBJ));
                           return 0;
                          }
                        data.Value=value;
                        this.AddQuantity(DFUN,total,data);
                        return this.Total()-size_prev;
                       }
//--- Decrease the number of data cells by the specified value, return the number of removed elements. The very first element always remains
   int               Decrease(const int total)
                       {
                        if(total>this.Total()-1)
                           return 0;
                        int total_prev=this.Total();
                        int from=this.Total()-total;
                        int to=this.Total()-1;
                        if(!this.DeleteRange(from,to))
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_DOUBLE_ARRAY);
                        return total_prev-this.Total();
                       }
//--- Set a new array size
   bool              SetSize(const int size,const double initial_value=0)
                       {
                        if(size==0)
                           return false;
                        int total=fabs(size-this.Total());
                        if(size>this.Total())
                           return(this.Increase(total,initial_value)==total);
                        else if(size<this.Total())
                           return(Decrease(total)==total);
                        return true;
                       }
//--- Set the value to the specified array cell
   bool              Set(const int index,const double value)
                       {
                        CDataUnitDouble *data=this.GetData(DFUN,index);
                        if(data==NULL)
                           return false;
                        data.Value=value;
                        return true;
                       }
//--- Return the amount of data (array size)
   int               Size(void) const { return this.Total(); }
   
//--- Returns the value at the specified index
   double            Get(const int index) const
                       {
                        CDataUnitDouble *data=this.GetData(DFUN,index);
                        return(data!=NULL ? data.Value : 0);
                       }
   bool              Get(const int index, double &value) const
                       {
                        value=0;
                        CDataUnitDouble *data=this.GetData(DFUN,index);
                        if(data==NULL)
                           return false;
                        value = data.Value;
                        return true;
                       }
//--- Constructors
                     CDimDouble(void)                                { this.Initialize(1);            }
                     CDimDouble(const int total,const double value=0){ this.Initialize(total,value);  }
//--- Destructor
                    ~CDimDouble(void)
                       {
                        this.Clear();
                        this.Shutdown();
                       }
  };
//+------------------------------------------------------------------+
//| Dynamic multidimensional double array class                      |
//+------------------------------------------------------------------+
class CXDimArrayDouble : public CArrayObj
  {
private:
//--- Return the data array from the first dimensionality
   CDimDouble       *GetDim(const string source,const int index) const
                       {
                        CDimDouble *dim=this.At(index<0 ? 0 : index);
                        if(dim==NULL)
                          {
                           if(index>this.Total()-1)
                              ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY)," (",index,"/",this.Total(),")");
                           else
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_DOUBLE_DATA_OBJ);
                          }
                        return dim;
                       }
//--- Add a new dimension to the first dimensionality
   bool              AddNewDim(const string source,const int size,const double initial_value=0)
                       {
                        CDimDouble *dim=new CDimDouble(size,initial_value);
                        if(dim==NULL)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_CREATE_DOUBLE_DATA_OBJ);
                           return false;
                          }
                        if(!this.Add(dim))
                          {
                           delete dim;
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           return false;
                          }
                        return true;
                       }
public:
//--- Increase the number of data cells by the specified 'total' value in the first dimensionality,
//--- return the number of added elements to the dimensionality. Added cells' size is 'size'
   int               IncreaseRangeFirst(const int total,const int size,const long initial_value=0)
                       {
                        int total_prev=this.Total();
                        for(int i=0;i<total;i++)
                           this.AddNewDim(DFUN,size,initial_value);
                        return(this.Total()-total_prev);
                       }
//--- Increase the number of data cells by the specified 'total' value in the specified 'range' dimensionality,
//--- return the number of added elements to the changed dimensionality
   int               IncreaseRange(const int range,const int total,const double initial_value=0)
                       {
                        CDimDouble *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.Increase(total,initial_value) : 0);
                       }
//--- Decrease the number of cells with data in the first dimensionality by the specified value,
//--- return the number of removed elements. The very first element always remains
   int               DecreaseRangeFirst(const int total)
                       {
                        if(total>this.Total()-1)
                           return 0;
                        int total_prev=this.Total();
                        int from=this.Total()-total;
                        int to=this.Total()-1;
                        if(!this.DeleteRange(from,to))
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_DOUBLE_ARRAY);
                        return total_prev-this.Total();
                       }                      
//--- Decrease the number of data cells by the specified value in the specified dimensionality,
//--- return the number of removed elements. The very first element always remains
   int               DecreaseRange(const int range,const int total)
                       {
                        CDimDouble *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.Decrease(total) : 0);
                       }
//--- Set the new array size in the specified dimensionality
   bool              SetSizeRange(const int range,const int size,const double initial_value=0)
                       {
                        CDimDouble *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.SetSize(size,initial_value) : false);
                       }
//--- Set the value to the specified array cell of the specified dimension
   bool              Set(const int index,const int range,const double value)
                       {
                        CDimDouble *dim=this.GetDim(DFUN,index);
                        return(dim!=NULL ? dim.Set(range,value) : false);
                       }
//--- Return the value at the specified index of the specified dimension
   double            Get(const int index,const int range) const
                       {
                        CDimDouble *dim=this.GetDim(DFUN,index);
                        return(dim!=NULL ? dim.Get(range) : 0);
                       }
   bool              Get(const int index,const int range,double &value) const
                       {
                        CDimDouble *dim=this.GetDim(DFUN,index);
                        return(dim!=NULL ? dim.Get(range,value) : false);
                       }
//--- Return the amount of data (size of the specified dimension array)
   int               Size(const int range) const
                       {
                        CDimDouble *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.Size() : 0);
                       }
//--- Return the total amount of data (the total size of all dimensions)
   int               Size(void) const
                       {
                        int size=0;
                        for(int i=0;i<this.Total();i++)
                          {
                           CDimDouble *dim=this.GetDim(DFUN,i);
                           if(dim==NULL)
                              continue;
                           size+=dim.Size();
                          }
                        return size;
                       }
//--- Constructor
                     CXDimArrayDouble()
                       {
                        this.Clear();
                        this.Add(new CDimDouble(1));
                       }
                     CXDimArrayDouble(int first_dim_size,const int dim_size,const double initial_value=0)
                       {
                        this.Clear();
                        int total=(first_dim_size<1 ? 1 : first_dim_size);
                        for(int i=0;i<total;i++)
                           this.Add(new CDimDouble(dim_size,initial_value));
                       }
//--- Destructor
                    ~CXDimArrayDouble()
                       {
                        this.Clear();
                        this.Shutdown();
                       }
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| String data unit class                                           |
//+------------------------------------------------------------------+
class CDataUnitString : public CDataUnit
  {
public:
   string            Value;
                     CDataUnitString() : CDataUnit(OBJECT_DE_TYPE_STRING){}
  };
//+------------------------------------------------------------------+
//| Class of a single string array dimension                         |
//+------------------------------------------------------------------+
class CDimString : public CArrayObj
  {
private:
//--- Get long data object from the array
   CDataUnitString  *GetData(const string source,const int index)
                       {
                        CDataUnitString *data=this.At(index<0 ? 0 : index);
                        if(data==NULL)
                          {
                           if(index>this.Total()-1)
                              ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY)," (",index,"/",this.Total(),")");
                           else
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_STRING_DATA_OBJ);
                          }
                        return data;
                       }
//--- Add the specified number of cells with objects to the end of the array
   bool              AddQuantity(const string source,const int total,CObject *object)
                       {
                        bool res=true;
                        for(int i=0;i<total;i++)
                          {
                           if(!this.Add(object))
                             {
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                              res &=false;
                              continue;
                             }
                          }
                        return res;
                       }
public:
//--- Initialize the array
   void              Initialize(const int total,const string value="")
                       {
                        this.Clear();
                        this.Increase(total,value);
                       }
//--- Increase the number of data cells by the specified value, return the number of added elements
   int               Increase(const int total,const string value="")
                       {
                        int size_prev=this.Total();
                        CDataUnitString *data=new CDataUnitString();
                        if(data==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_STRING_DATA_OBJ));
                           return 0;
                          }
                        data.Value=value;
                        this.AddQuantity(DFUN,total,data);
                        return this.Total()-size_prev;
                       }
//--- Decrease the number of data cells by the specified value, return the number of removed elements. The very first element always remains
   int               Decrease(const int total)
                       {
                        if(total>this.Total()-1)
                           return 0;
                        int total_prev=this.Total();
                        int from=this.Total()-total;
                        int to=this.Total()-1;
                        if(!this.DeleteRange(from,to))
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_STRING_ARRAY);
                        return total_prev-this.Total();
                       }
//--- Set a new array size
   bool              SetSize(const int size,const string initial_value="")
                       {
                        if(size==0)
                           return false;
                        int total=fabs(size-this.Total());
                        if(size>this.Total())
                           return(this.Increase(total,initial_value)==total);
                        else if(size<this.Total())
                           return(Decrease(total)==total);
                        return true;
                       }
//--- Set the value to the specified array cell
   bool              Set(const int index,const string value)
                       {
                        CDataUnitString *data=this.GetData(DFUN,index);
                        if(data==NULL)
                           return false;
                        data.Value=value;
                        return true;
                       }
//--- Return the amount of data (array size)
   int               Size(void) const { return this.Total(); }
   
//--- Returns the value at the specified index
   string            Get(const int index)
                       {
                        CDataUnitString *data=this.GetData(DFUN,index);
                        return(data!=NULL ? data.Value : "");
                       }
   bool              Get(const int index, string &value)
                       {
                        value="";
                        CDataUnitString *data=this.GetData(DFUN,index);
                        if(data==NULL)
                           return false;
                        value = data.Value;
                        return true;
                       }
//--- Constructors
                     CDimString(void)                                   { this.Initialize(1);            }
                     CDimString(const int total,const string value="")  { this.Initialize(total,value);  }
//--- Destructor
                    ~CDimString(void)
                       {
                        this.Clear();
                        this.Shutdown();
                       }
  };
//+------------------------------------------------------------------+
//| Dynamic multidimensional string array class                      |
//+------------------------------------------------------------------+
class CXDimArrayString : public CArrayObj
  {
private:
//--- Return the data array from the first dimensionality
   CDimString       *GetDim(const string source,const int index) const
                       {
                        CDimString *dim=this.At(index<0 ? 0 : index);
                        if(dim==NULL)
                          {
                           if(index>this.Total()-1)
                              ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY)," (",index,"/",this.Total(),")");
                           else
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_STRING_DATA_OBJ);
                          }
                        return dim;
                       }
//--- Add a new dimension to the first dimensionality
   bool              AddNewDim(const string source,const int size,const string initial_value="")
                       {
                        CDimString *dim=new CDimString(size,initial_value);
                        if(dim==NULL)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_CREATE_STRING_DATA_OBJ);
                           return false;
                          }
                        if(!this.Add(dim))
                          {
                           delete dim;
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           return false;
                          }
                        return true;
                       }
public:
//--- Increase the number of data cells by the specified 'total' value in the first dimensionality,
//--- return the number of added elements to the dimensionality. Added cells' size is 'size'
   int               IncreaseRangeFirst(const int total,const int size,const string initial_value="")
                       {
                        int total_prev=this.Total();
                        for(int i=0;i<total;i++)
                           this.AddNewDim(DFUN,size,initial_value);
                        return(this.Total()-total_prev);
                       }
//--- Increase the number of data cells by the specified 'total' value in the specified 'range' dimensionality,
//--- return the number of added elements to the changed dimensionality
   int               IncreaseRange(const int range,const int total,const string initial_value="")
                       {
                        CDimString *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.Increase(total,initial_value) : 0);
                       }
//--- Decrease the number of cells with data in the first dimensionality by the specified value,
//--- return the number of removed elements. The very first element always remains
   int               DecreaseRangeFirst(const int total)
                       {
                        if(total>this.Total()-1)
                           return 0;
                        int total_prev=this.Total();
                        int from=this.Total()-total;
                        int to=this.Total()-1;
                        if(!this.DeleteRange(from,to))
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_STRING_ARRAY);
                        return total_prev-this.Total();
                       }                      
//--- Decrease the number of data cells by the specified value in the specified dimensionality,
//--- return the number of removed elements. The very first element always remains
   int               DecreaseRange(const int range,const int total)
                       {
                        CDimString *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.Decrease(total) : 0);
                       }
//--- Set the new array size in the specified dimensionality
   bool              SetSizeRange(const int range,const int size,const string initial_value="")
                       {
                        CDimString *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.SetSize(size,initial_value) : false);
                       }
//--- Set the value to the specified array cell of the specified dimension
   bool              Set(const int index,const int range,const string value)
                       {
                        CDimString *dim=this.GetDim(DFUN,index);
                        return(dim!=NULL ? dim.Set(range,value) : false);
                       }
//--- Return the value at the specified index of the specified dimension
   string            Get(const int index,const int range) const
                       {
                        CDimString *dim=this.GetDim(DFUN,index);
                        return(dim!=NULL ? dim.Get(range) : "");
                       }
   bool              Get(const int index,const int range, string &value) const
                       {
                        CDimString *dim=this.GetDim(DFUN,index);
                        return(dim!=NULL ? dim.Get(range,value) : false);
                       }
//--- Return the amount of data (size of the specified dimension array)
   int               Size(const int range) const
                       {
                        CDimString *dim=this.GetDim(DFUN,range);
                        return(dim!=NULL ? dim.Size() : 0);
                       }
//--- Return the total amount of data (the total size of all dimensions)
   int               Size(void) const
                       {
                        int size=0;
                        for(int i=0;i<this.Total();i++)
                          {
                           CDimString *dim=this.GetDim(DFUN,i);
                           if(dim==NULL)
                              continue;
                           size+=dim.Size();
                          }
                        return size;
                       }
//--- Constructor
                     CXDimArrayString()
                       {
                        this.Clear();
                        this.Add(new CDimString(1));
                       }
                     CXDimArrayString(int first_dim_size,const int dim_size,const string initial_value="")
                       {
                        this.Clear();
                        int total=(first_dim_size<1 ? 1 : first_dim_size);
                        for(int i=0;i<total;i++)
                           this.Add(new CDimString(dim_size,initial_value));
                       }
//--- Destructor
                    ~CXDimArrayString()
                       {
                        this.Clear();
                        this.Shutdown();
                       }
  };
//+------------------------------------------------------------------+



Nun sind wir bereit, die abstrakte grafische Objektklasse auf eine neue Ebene zu heben, indem wir die Handhabung einfacher Arrays, die integer, reellen und string Eigenschaften speichern, durch die Handhabung dynamischer Arrays ersetzen, die von den oben besprochenen Klassen bereitgestellt werden.

Bevor wir mit der Änderung der grafischen Objektklasse beginnen, müssen wir die neu erstellten Klassen in der Bibliothek genauso sichtbar machen wie die übrigen Klassen der Bibliothek. Um dies zu tun, fügen wir die Datei mit den Klassen in die Datei der Bibliotheksdienstfunktionen ein \MQL5\Include\DoEasy\Services\DELib.mqh:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property strict  // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\Defines.mqh"
#include "Message.mqh"
#include "TimerCounter.mqh"
#include "Pause.mqh"
#include "Colors.mqh"
#include "XDimArray.mqh"
//+------------------------------------------------------------------+
//| Service functions                                                |
//+------------------------------------------------------------------+


Jetzt sind diese Klassen für alle Bibliotheksklassen sichtbar.

Zweidimensionales dynamisches Array zur Speicherung von Objekteigenschaften

Fast alle Bibliotheksobjekte verfügen über Arrays, die integer, reellen und string Objekteigenschaften speichern. Arrays sind eindimensional und haben eine starr festgelegte Größe, die der Anzahl der benötigten Eigenschaften entspricht. Diese Struktur war praktisch, bis wir mit der Notwendigkeit konfrontiert wurden, die sich dynamisch ändernde Anzahl von Objekteigenschaften zu verwalten. Ich werde die Anpassungen in den Bibliotheksobjekten anhand der abstrakten grafischen Standardobjektklasse vornehmen. Alle nachfolgenden Objekte werden mit der neuen Funktionalität auf der Grundlage der dynamischen mehrdimensionalen Array-Klasse erstellt. Höchstwahrscheinlich werde ich die zuvor erstellten Objekte so umbauen, dass sie auch dynamische Arrays verarbeiten können.

Der private Teil der abstrakten grafischen Standardobjektdatei \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh enthält die Arrays zum Speichern von Eigenschaften (die aktuelle und die vorherige) und die Methoden zum Empfangen eines realen Index der angegebenen Eigenschaft im Array :

//+------------------------------------------------------------------+
//|                                                 GStdGraphObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\GBaseObj.mqh"
//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   long              m_long_prop[GRAPH_OBJ_PROP_INTEGER_TOTAL];         // Integer properties
   double            m_double_prop[GRAPH_OBJ_PROP_DOUBLE_TOTAL];        // Real properties
   string            m_string_prop[GRAPH_OBJ_PROP_STRING_TOTAL];        // String properties

   long              m_long_prop_prev[GRAPH_OBJ_PROP_INTEGER_TOTAL];    // Integer properties before change
   double            m_double_prop_prev[GRAPH_OBJ_PROP_DOUBLE_TOTAL];   // Real properties before change
   string            m_string_prop_prev[GRAPH_OBJ_PROP_STRING_TOTAL];   // String properties before change

//--- Return the index of the array the (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property)  const { return(int)property-GRAPH_OBJ_PROP_INTEGER_TOTAL;                              }
   int               IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property)  const { return(int)property-GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_DOUBLE_TOTAL;  }

public:


Nun sollen all diese Arrays und Methoden in eine neue Klasse übertragen werden, die ein dynamisches zweidimensionales Array zur Speicherung der Eigenschaften des Objekts sein soll, dessen zweite Dimension sich dynamisch ändern soll, um die Änderung einer einzelnen Objekteigenschaft zu verfolgen, wie z. B. den Preis und die Zeit des Objekt-Angelpunkts oder Farbe, Stil, Breite und Beschreibung der Objektebenen, deren Anzahl sich dynamisch ändern kann.

Bis die Klasse vollständig debugged ist, werde ich sie direkt in den privaten Bereich der abstrakten grafischen Standardobjektklasse einfügen. Nachdem die gesamte Funktionalität debugged und fertig ist, werde ich die Klasse des dynamischen zweidimensionalen Objekt-Eigenschaftsarrays in eine separate Datei übertragen, um in anderen Objekten darauf zugreifen zu können, anstatt sie innerhalb des Objekts neu zu schreiben.

Alle oben angegebenen Arrays und Methoden werden aus dem privaten Bereich entfernt und an ihrer Stelle die neue Klasse der Objekteigenschaften definiert:

//+------------------------------------------------------------------+
//|                                                 GStdGraphObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\GBaseObj.mqh"
//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   //--- Object property class
   class CDataPropObj
     {
      
     }

Im privaten Abschnitt der neuen Klasse deklarieren wir die Liste für die Speicherung von Eigenschaftsobjekten, die von der dynamischen multidimensionalen Array-Klasse CXDimArrayLong eingeführt wird, die Variablen für die Speicherung der Anzahl von integer, reellen und string Objekteigenschaften und zwei Methoden, die den Index des Arrays zurückgeben, das eigentlich reelle und string Objekteigenschaften speichert:

//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   //--- Object property class
   class CDataPropObj
     {
   private:
      CArrayObj         m_list;        // list of property objects
      int               m_total_int;   // Number of integer parameters
      int               m_total_dbl;   // Number of real parameters
      int               m_total_str;   // Number of string parameters
      //--- Return the index of the array the (1) double and (2) string properties are actually located at
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property)              const { return(int)property-this.m_total_int;                     }
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property)              const { return(int)property-this.m_total_int-this.m_total_dbl;    }


Im öffentlichen Abschnitt der Klasse deklarieren wir die Methode, die die Liste der Eigenschaftsobjekte zurückgibt, die Zeiger auf Objekte mit integer, reellen und string Eigenschaften und die Methoden zum Setzen und Empfangen der angegebenen Objekteigenschaften:

   //--- Object property class
   class CDataPropObj
     {
   private:
      CArrayObj         m_list;        // list of property objects
      int               m_total_int;   // Number of integer parameters
      int               m_total_dbl;   // Number of real parameters
      int               m_total_str;   // Number of string parameters
      //--- Return the index of the array the (1) double and (2) string properties are actually located at
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property)              const { return(int)property-this.m_total_int;                     }
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property)              const { return(int)property-this.m_total_int-this.m_total_dbl;    }
   public:
      //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties
      CArrayObj        *GetList(void)                                                     { return &this.m_list;                                      }
      CXDimArrayLong   *Long()                                                      const { return this.m_list.At(0);                                 }
      CXDimArrayDouble *Double()                                                    const { return this.m_list.At(1);                                 }
      CXDimArrayString *String()                                                    const { return this.m_list.At(2);                                 }
      //--- Set object's (1) integer, (2) real and (3) string properties
      void              Set(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value)    { this.Long().Set(property,index,value);                    }
      void              Set(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value)   { this.Double().Set(this.IndexProp(property),index,value);  }
      void              Set(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value)   { this.String().Set(this.IndexProp(property),index,value);  }
      //--- Return object’s (1) integer, (2) real and (3) string property from the properties array
      long              Get(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)         const { return this.Long().Get(property,index);                   }
      double            Get(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)          const { return this.Double().Get(this.IndexProp(property),index); }
      string            Get(ENUM_GRAPH_OBJ_PROP_STRING property,int index)          const { return this.String().Get(this.IndexProp(property),index); }


Die CArrayObj-Liste soll drei Zeiger auf die Instanzen der Klassenobjekte des oben erstellten dynamischen Arrays speichern — integer, reell und string, die im Klassenkonstruktor erstellt und in die Liste eingefügt werden. Die Set- und Get-Methoden ähneln den zuvor verwendeten Methoden, nur dass sie jetzt einen weiteren Parameter haben. Neben dem Index der Objekteigenschaft geben wir auch den Index der Eigenschaft in der zweiten Dimension des Arrays an. Für die meisten Eigenschaften ist der Index immer gleich Null. Aber für mehrere gleichartige Objekteigenschaften gebe ich den Eigenschaftsmodifikatorindex an, um die erste, zweite, dritte und folgende Eigenschaften von Preis, Zeit usw. zu erhalten.

Außerdem schreiben wir im öffentlichen Abschnitt der Klasse die Methode, die die Größe des angegebenen Datenarrays der ersten Dimension zurückgibt:

      //--- Return the size of the specified first dimension data array
      int               Size(const int range) const
                          {
                           if(range<this.m_total_int)
                              return this.Long().Size(range);
                           else if(range<this.m_total_int+this.m_total_dbl)
                              return this.Double().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range));
                           else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str)
                              return this.String().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range));
                           return 0;
                          }


Die Methode übergibt den Index der erforderlichen Eigenschaft der ersten Dimension des Eigenschaftsfeldes (Bereich). Danach wird folgendes geprüft:
Wenn der Eigenschaftswert kleiner ist als die Gesamtzahl der integer Eigenschaften, handelt es sich um eine integer Eigenschaftsanforderung — geben wir die Größe des integer Eigenschaftsarrays mit der Methode Size() des Long-Objekts zurück.
Wenn der Eigenschaftswert kleiner ist als die Gesamtzahl der ganzzahligen Eigenschaften + die Anzahl der reellen Eigenschaften, handelt es sich um eine Anfrage für eine reelle Eigenschaft — gib die Größe des Arrays der reellen Eigenschaften mit der Methode Size() des Double-Objekts zurück.
Wenn der Eigenschaftswert kleiner ist als die Gesamtzahl der integer Eigenschaften + die Anzahl der reellen Eigenschaften + die Anzahl der string Eigenschaften, handelt es sich um eine Anforderung für eine string Eigenschaft — geben wir die Größe des string Eigenschaftsarrays des String-Objekts zurück.
Wir haben die Methode Size() oben bei der Erstellung der Objektklassen von dynamischen Arrays besprochen.

Im öffentlichen Abschnitt der Klasse schreiben wir die Methode, die die Größe des Arrays in der angegebenen Dimensionalität festlegt:

      //--- Set the array size in the specified dimensionality
      bool              SetSizeRange(const int range,const int size)
                          {
                           if(range<this.m_total_int)
                              return this.Long().SetSizeRange(range,size);
                           else if(range<this.m_total_int+this.m_total_dbl)
                              return this.Double().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range),size);
                           else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str)
                              return this.String().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range),size);
                           return false;
                          }


Die Logik der Methode ist identisch mit der oben besprochenen Methode zum Empfang der Größe.

Der Klassenkonstruktor empfängt die Anzahl der integer, reellen und string Objekteigenschaften. Diese Werte werden den entsprechenden Klassenvariablen zugewiesen. Danach werden neue Objekte des dynamischen mehrdimensionalen Arrays integer, reell und string zur Liste hinzugefügt. Das Array hat die Dimensionalität der ersten Dimension gleich der Anzahl der entsprechenden in den Parametern übergebenen Eigenschaften, während die Größe der zweiten Dimension für jedes Eigenschafts-Array auf 1 gesetzt wird.

      //--- Constructor
                        CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string)
                          {
                           this.m_total_int=prop_total_integer;
                           this.m_total_dbl=prop_total_double;
                           this.m_total_str=prop_total_string;
                           this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1));
                           this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1));
                           this.m_list.Add(new CXDimArrayString(this.m_total_str,1));
                          }


Im Destruktor der Klasse entfernen wir die Elemente aus der Liste und geben den Array-Speicher frei:

      //--- Destructor
                       ~CDataPropObj()
                          {
                           m_list.Clear();
                           m_list.Shutdown();
                          }


Die Klasse der Objekt-Eigenschaften sieht dann wie folgt aus:

   //--- Object property class
   class CDataPropObj
     {
   private:
      CArrayObj         m_list;        // list of property objects
      int               m_total_int;   // Number of integer parameters
      int               m_total_dbl;   // Number of real parameters
      int               m_total_str;   // Number of string parameters
      //--- Return the index of the array the (1) double and (2) string properties are actually located at
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property)              const { return(int)property-this.m_total_int;                     }
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property)              const { return(int)property-this.m_total_int-this.m_total_dbl;    }
   public:
      //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties
      CArrayObj        *GetList(void)                                                     { return &this.m_list;                                      }
      CXDimArrayLong   *Long()                                                      const { return this.m_list.At(0);                                 }
      CXDimArrayDouble *Double()                                                    const { return this.m_list.At(1);                                 }
      CXDimArrayString *String()                                                    const { return this.m_list.At(2);                                 }
      //--- Set object's (1) integer, (2) real and (3) string properties
      void              Set(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value)    { this.Long().Set(property,index,value);                    }
      void              Set(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value)   { this.Double().Set(this.IndexProp(property),index,value);  }
      void              Set(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value)   { this.String().Set(this.IndexProp(property),index,value);  }
      //--- Return object’s (1) integer, (2) real and (3) string property from the properties array
      long              Get(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)         const { return this.Long().Get(property,index);                   }
      double            Get(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)          const { return this.Double().Get(this.IndexProp(property),index); }
      string            Get(ENUM_GRAPH_OBJ_PROP_STRING property,int index)          const { return this.String().Get(this.IndexProp(property),index); }
      
      //--- Return the size of the specified first dimension data array
      int               Size(const int range) const
                          {
                           if(range<this.m_total_int)
                              return this.Long().Size(range);
                           else if(range<this.m_total_int+this.m_total_dbl)
                              return this.Double().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range));
                           else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str)
                              return this.String().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range));
                           return 0;
                          }
      //--- Set the array size in the specified dimensionality
      bool              SetSizeRange(const int range,const int size)
                          {
                           if(range<this.m_total_int)
                              return this.Long().SetSizeRange(range,size);
                           else if(range<this.m_total_int+this.m_total_dbl)
                              return this.Double().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range),size);
                           else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str)
                              return this.String().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range),size);
                           return false;
                          }
      //--- Constructor
                        CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string)
                          {
                           this.m_total_int=prop_total_integer;
                           this.m_total_dbl=prop_total_double;
                           this.m_total_str=prop_total_string;
                           this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1));
                           this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1));
                           this.m_list.Add(new CXDimArrayString(this.m_total_str,1));
                          }
      //--- Destructor
                       ~CDataPropObj()
                          {
                           m_list.Clear();
                           m_list.Shutdown();
                          }
     };


Um jedoch Änderungen in den Objekteigenschaften zu kontrollieren, müssen wir uns den früheren Zustand aller Eigenschaften merken und ihn mit dem aktuellen vergleichen. Um dies zu erreichen, habe ich zuvor zwei Sätze von Objekteigenschaften gespeichert. Jetzt werde ich einfach eine weitere Klasse erstellen, die zwei Objekte der neu erstellten Klasse enthält. Das erste Objekt soll die aktuellen Eigenschaften speichern, während das zweite Objekt die vorherigen Eigenschaften speichern soll.

Außerdem deklarieren wir im privaten Abschnitt der Klasse eine neue Datenklasse für die aktuellen und vorherigen Eigenschaften:

   class CProperty
     {
      
     }

Alle Felder und Methoden der Klasse sollen öffentlich sein. Werfen wir einen Blick auf den Code der Klasse. Es ist relativ klein und speichert Objekte der zuvor besprochenen Klassen:

   //--- Data class of the current and previous properties
   class CProperty
     {
   public:
      CDataPropObj     *Curr;    // Pointer to the current properties object
      CDataPropObj     *Prev;    // Pointer to the previous properties object
      //--- Set the array size ('size') in the specified dimension ('range')
      bool              SetSizeRange(const int range,const int size)
                          {
                           return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false);
                          }
      //--- Return the size of the specified array of the (1) current and (2) previous first dimension data
      int               CurrSize(const int range)  const { return Curr.Size(range); }
      int               PrevSize(const int range)  const { return Prev.Size(range); }
      //--- Copy the current data to the previous one
      void              CurrentToPrevious(void)
                          {
                           //--- Copy all integer properties
                           for(int i=0;i<this.Curr.Long().Total();i++)
                              for(int r=0;r<this.Curr.Long().Size(i);r++)
                                 this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r));
                           //--- Copy all real properties
                           for(int i=0;i<this.Curr.Double().Total();i++)
                              for(int r=0;r<this.Curr.Double().Size(i);r++)
                                 this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r));
                           //--- Copy all string properties
                           for(int i=0;i<this.Curr.String().Total();i++)
                              for(int r=0;r<this.Curr.String().Size(i);r++)
                                 this.Prev.String().Set(i,r,this.Curr.String().Get(i,r));
                          }
      //--- Constructor
                        CProperty(const int prop_int_total,const int prop_double_total,const int prop_string_total)
                          {
                           this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                           this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                          }
     };


Neben der Behandlung der Methoden der zuvor erstellten Klassen habe ich die Methode zum Kopieren der aktuellen Eigenschaften in die vorherigen hinzugefügt. Das Kopieren erfolgt Element für Element, da wir zwei unabhängige Kopien desselben Objekts benötigen. Hätten wir auf die Methode AssignArray der Klasse CArrayObj zurückgegriffen, hätten wir die Zeiger und nicht die Eigenschaftswerte kopiert. Dies hätte dazu geführt, dass eine exakte Kopie des Objekts erstellt worden wäre, während die Änderung einer Eigenschaft in einem Objekt zu einer Änderung in einem anderen geführt hätte, und das brauche ich nicht.

Das Ersetzen einfacher Arrays durch dynamische Array-Klassenobjekte, die ich soeben (wenn auch noch nicht vollständig) implementiert habe, impliziert also die Verwendung des CProperty-Klassenobjekts als Zeiger auf die Eigenschaften des grafischen Objekts sowie mehrere Änderungen in den Bibliotheksklassen, da wir jetzt zusätzlich zu einem Eigenschaftsindex auch einen Index des Preis- und Zeit-Angelpunkts oder einen Index des Niveaus angeben müssen, dessen Eigenschaft wir erhalten oder setzen wollen.


Verbesserung der Klassenbibliothek

Im privaten Abschnitt der abstrakten grafischen Objektklasse (in der wir gerade arbeiten) deklarieren wir den Zeiger auf das Objekt der grafischen Objekteigenschaften, die Variable zum Speichern der Anzahl der Objektdrehpunkte und die Methoden zum Setzen mehrerer Werte einer einzelnen Objekteigenschaft unmittelbar nach der neu geschriebenen Eigenschaftsklasse:

   CProperty        *Prop;                                              // Pointer to the properties object
   int               m_pivots;                                          // Number of object reference points
//--- Read and set (1) the time and (2) the price of the specified object pivot point
   void              SetTimePivot(const int index);
   void              SetPricePivot(const int index);
//--- Read and set (1) color, (2) style, (3) width, (4) value, (5) text of the specified object level
   void              SetLevelColor(const int index);
   void              SetLevelStyle(const int index);
   void              SetLevelWidth(const int index);
   void              SetLevelValue(const int index);
   void              SetLevelText(const int index);
//--- Read and set the BMP file name for the "Bitmap Level" object. Index: 0 - ON, 1 - OFF
   void              SetBMPFile(const int index);


Im öffentlichen Teil der Klasse ergänzen wir alle Get- und Set-Methoden um eine weitere Variable, die den Index des Property-Arrays zweiter Dimension angibt. Die Eigenschaften werden mit den Get- und Set-Methoden aus den Feldern Curr und Prev der Klasse CProperty, die die Arrays der aktuellen und vorherigen Eigenschaften speichern, zurückgegeben und empfangen:

public:
//--- Set object's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value)     { this.Prop.Curr.Set(property,index,value);  }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value)    { this.Prop.Curr.Set(property,index,value);  }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value)    { this.Prop.Curr.Set(property,index,value);  }
//--- Return object’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)          const { return this.Prop.Curr.Get(property,index); }
   double            GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)           const { return this.Prop.Curr.Get(property,index); }
   string            GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index)           const { return this.Prop.Curr.Get(property,index); }
      
//--- Set object's previous (1) integer, (2) real and (3) string properties
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.Set(property,index,value);  }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.Set(property,index,value);  }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.Set(property,index,value);  }
//--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array
   long              GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)      const { return this.Prop.Prev.Get(property,index); }
   double            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)       const { return this.Prop.Prev.Get(property,index); }
   string            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index)       const { return this.Prop.Prev.Get(property,index); }


Die Änderungen sollten im gesamten Klassencode vorgenommen werden, in dem die Methoden GetProperty(), SetProperty(), GetPropertyPrev() und SetPropertyPrev() verwendet werden, da der Index der zweiten Dimension nun in diesen Methoden angegeben wird. Ich werde hier nicht alle mehrfachen Änderungen beschreiben, da dies zu viel Platz im Artikel einnehmen würde. Sie wurden alle in den unten beigefügten Klassencode aufgenommen. Besprechen wir stattdessen neue und einige alte Methoden.

Im öffentlichen Teil der Klasse werden die Methoden deklariert, die die Beschreibungen der Angelpunkte und der Objektebenen zurückgeben:

//--- Return the description of the (1) (ENUM_OBJECT) graphical object type
   virtual string    TypeDescription(void)                                    const { return CMessage::Text(MSG_GRAPH_STD_OBJ_ANY);          }
//--- Return the description of the coordinate of the specified graphical object (1) price and (2) time
   virtual string    PriceDescription(const int index)      const { return ::DoubleToString(this.GetProperty(GRAPH_OBJ_PROP_PRICE,index),this.m_digits);       }
   virtual string    TimeDescription(const int index)       const { return ::TimeToString(this.GetProperty(GRAPH_OBJ_PROP_TIME,index),TIME_DATE|TIME_MINUTES); }
//--- Return the description of the specified level (1) color, (2) style, (3) width, (4) value
   virtual string    LevelColorDescription(const int index) const { return ::ColorToString((color)this.GetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,index),true); }
   virtual string    LevelStyleDescription(const int index) const { return LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,index)); }
   virtual string    LevelWidthDescription(const int index) const { return (string)this.GetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,index); }
   virtual string    LevelValueDescription(const int index) const { return ::DoubleToString(this.GetProperty(GRAPH_OBJ_PROP_LEVELVALUE,index),5); }

//--- Return the descriptions of all (1) times, (2) prices, (3) pivot point times and prices,
//--- (4) level values, (6) all properties of all levels, (5) BMP files of the graphical object
   string            TimesDescription(void)        const;
   string            PricesDescription(void)       const;
   string            TimePricesDescription(void)   const;
   string            LevelColorsDescription(void)  const;
   string            LevelStylesDescription(void)  const;
   string            LevelWidthsDescription(void)  const;
   string            LevelValuesDescription(void)  const;
   string            LevelTextsDescription(void)   const;
   string            LevelsAllDescription(void)    const;
   string            BMPFilesDescription(void)     const;


Dem Konstruktor der geschützten parametrischen Klasse wird ein weiteren Parameter übergeben — die Anzahl der für die Konstruktion des Objekts erforderlichen Angelpunkte (das bedeutet, dass wir die nachgeordneten Objektklassen zusätzlich verbessern müssen, damit sie die Anzahl der Angelpunkte an diesen Konstruktor übergeben):

//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; }
protected:
//--- Protected parametric constructor
                     CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                                   const ENUM_GRAPH_OBJ_BELONG belong,
                                   const ENUM_GRAPH_OBJ_GROUP group,
                                   const long chart_id, const int pivots,
                                   const string name);


Werfen wir einen Blick auf die Implementierung des Klassenkonstruktors:

//+------------------------------------------------------------------+
//| Protected parametric constructor                                 |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_GROUP group,
                             const long chart_id,const int pivots,
                             const string name)
  {
   //--- Create the property object with the default values
   this.Prop=new CProperty(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL);
   
//--- Set the number of pivot points and object levels
   this.m_pivots=pivots;
   int levels=(int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS);

//--- Set the property array dimensionalities according to the number of pivot points and levels
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,2);
   
//--- Set the object (1) type, type of graphical (2) object, (3) element, (4) subwindow affiliation and (5) index, as well as (6) chart symbol Digits
   this.m_type=obj_type;
   this.SetName(name);
   CGBaseObj::SetChartID(chart_id);
   CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type));
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_STANDARD);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetGroup(group);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));
   
//--- Save the integer properties inherent in all graphical objects but not present in the current one
   this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,0,CGBaseObj::ChartID());                // Chart ID
   this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow());               // Chart subwindow index
   this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,CGBaseObj::TypeGraphObject());            // Graphical object type (ENUM_OBJECT)
   this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,CGBaseObj::TypeGraphElement());   // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,CGBaseObj::Belong());                   // Graphical object affiliation
   this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,CGBaseObj::Group());                     // Graphical object group
   this.SetProperty(GRAPH_OBJ_PROP_ID,0,0);                                         // Object ID
   this.SetProperty(GRAPH_OBJ_PROP_NUM,0,0);                                        // Object index in the list
   
//--- Save the properties inherent in all graphical objects and present in a graphical object
   this.PropertiesRefresh();
   
//--- Save basic properties in the parent object
   this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME,0);
   this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0);
   this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0);
   this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0);
   this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0);

//--- Save the current properties to the previous ones
   this.PropertiesCopyToPrevData();
  }
//+-------------------------------------------------------------------+


Hier habe ich zunächst das Objekt der Klasse Eigenschaften des grafischen Objekts erstellt, indem ich die Anzahl der integer, reellen und string Eigenschaften angegeben habe. Danach habe ich für die Eigenschaften mit mehreren Parametern (Angelpunktpreis und -zeit, Anzahl der Ebenen und Eigenschaften der einzelnen Ebenen) die Größe der Arrays in der zweiten Dimension festgelegt. Beim Setzen und Abrufen von Eigenschaften mit nur einem Parameter, geben wir den Parameterindex in der zweiten Dimension gleich Null an.

Diesmal, in der Methode, die CGStdGraphObj-Objekte nach allen Eigenschaften vergleicht, vergleichen wir die Eigenschaften in der Schleife nach der Größe der zweiten Dimension:

//+------------------------------------------------------------------+
//| Compare CGStdGraphObj objects by all properties                  |
//+------------------------------------------------------------------+
bool CGStdGraphObj::IsEqual(CGStdGraphObj *compared_obj) const
  {
   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;
      for(int j=0;j<Prop.CurrSize(prop);j++)
         if(this.GetProperty(prop,j)!=compared_obj.GetProperty(prop,j)) return false; 
     }
   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;
      for(int j=0;j<Prop.CurrSize(prop);j++)
         if(this.GetProperty(prop,j)!=compared_obj.GetProperty(prop,j)) return false; 
     }
   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;
      for(int j=0;j<Prop.CurrSize(prop);j++)
         if(this.GetProperty(prop,j)!=compared_obj.GetProperty(prop,j)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+


In den Methoden zum Abrufen und Speichern von grafischen Objekteigenschaften werden nun alle Eigenschaften mit mehreren Werten in der Schleife mit der Anzahl der Eigenschaften eingetragen:

//+------------------------------------------------------------------+
//| Get and save the integer properties                              |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveINT(void)
  {
   //--- Properties inherent in all graphical objects and present in a graphical object
   this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME));
   this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME));  // Object creation time
   this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIMEFRAMES));  // Object visibility on timeframes
   this.SetProperty(GRAPH_OBJ_PROP_BACK,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BACK));              // Background object
   this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ZORDER));          // Priority of a graphical object for receiving the event of clicking on a chart
   this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_HIDDEN));          // Disable displaying the name of a graphical object in the terminal object list
   this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTED));      // Object selection
   this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTABLE));  // Object availability
   for(int i=0;i<this.m_pivots;i++)                                                                                  // Point time coordinates
      this.SetTimePivot(i);
   this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_COLOR));            // Color
   this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STYLE));            // Style
   this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_WIDTH));            // Line width
   //--- Properties belonging to different graphical objects
   this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL));              // Fill an object with color
   this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY));      // Ability to edit text in the Edit object
   this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS));          // Number of levels
   for(int i=0;i<this.Levels();i++)                                                                                  // Level data
     {
      this.SetLevelColor(i);
      this.SetLevelStyle(i);
      this.SetLevelWidth(i);
     }
   this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN));            // Horizontal text alignment in the Edit object (OBJ_EDIT)
   this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FONTSIZE));      // Font size
   this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_LEFT));      // Ray goes to the left
   this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_RIGHT));    // Ray goes to the right
   this.SetProperty(GRAPH_OBJ_PROP_RAY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY));                // Vertical line goes through all windows of a chart
   this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ELLIPSE));        // Display the full ellipse of the Fibonacci Arc object
   this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ARROWCODE));    // Arrow code for the "Arrow" object
   this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ANCHOR));          // Position of the binding point of the graphical object
   this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XDISTANCE));    // Distance from the base corner along the X axis in pixels
   this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YDISTANCE));    // Distance from the base corner along the Y axis in pixels
   this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DIRECTION));    // Gann object trend
   this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DEGREE));          // Elliott wave marking level
   this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DRAWLINES));    // Display lines for Elliott wave marking
   this.SetProperty(GRAPH_OBJ_PROP_STATE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STATE));            // Button state (pressed/released)
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_ID));// Chart object ID (OBJ_CHART).
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PERIOD));    // Chart object period
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DATE_SCALE));  // Time scale display flag for the Chart object
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PRICE_SCALE));// Price scale display flag for the Chart object
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_SCALE));// Chart object scale
   this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XSIZE));            // Object width along the X axis in pixels.
   this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YSIZE));            // Object height along the Y axis in pixels.
   this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XOFFSET));        // X coordinate of the upper-left corner of the visibility area.
   this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YOFFSET));        // Y coordinate of the upper-left corner of the visibility area.
   this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BGCOLOR));        // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CORNER));          // Chart corner for binding a graphical object
   this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_TYPE));// Border type for "Rectangle border"
   this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_COLOR));// Border color for OBJ_EDIT and OBJ_BUTTON
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Get and save the real properties                                 |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveDBL(void)
  {
   for(int i=0;i<this.m_pivots;i++)                                                                               // Point prices coordinates
      SetPricePivot(i);
   for(int i=0;i<this.Levels();i++)                                                                               // Level values
      this.SetLevelValue(i);
   this.SetProperty(GRAPH_OBJ_PROP_SCALE,0,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_SCALE));          // Scale (property of Gann objects and Fibonacci Arcs objects)
   this.SetProperty(GRAPH_OBJ_PROP_ANGLE,0,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_ANGLE));          // Angle
   this.SetProperty(GRAPH_OBJ_PROP_DEVIATION,0,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_DEVIATION));  // Deviation of the standard deviation channel
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Get and save the string properties                               |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveSTR(void)
  {
   this.SetProperty(GRAPH_OBJ_PROP_TEXT,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_TEXT));            // Object description (the text contained in the object)
   this.SetProperty(GRAPH_OBJ_PROP_TOOLTIP,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_TOOLTIP));      // Tooltip text
   for(int i=0;i<this.Levels();i++)                                                                               // Level descriptions
      this.SetLevelText(i);
   this.SetProperty(GRAPH_OBJ_PROP_FONT,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_FONT));            // Font
   this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_BMPFILE));      // BMP file name for the "Bitmap Level" object
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_SYMBOL));// Chart object symbol 
  }
//+------------------------------------------------------------------+


In der Methode, die die aktuellen Daten in die vorherigen kopiert, rufen wir die Methode des aktuellen Eigenschaftsobjekts auf, die speziell zum Kopieren der aktuellen Eigenschaften in die vorherigen erstellt wurde:

//+------------------------------------------------------------------+
//| Copy the current data to the previous one                        |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCopyToPrevData(void)
  {
   this.Prop.CurrentToPrevious();
  }
//+------------------------------------------------------------------+


In der Methode, die die Änderungen der Objekteigenschaften prüft, wird zuerst geprüft, ob die Eigenschaft mehrfach existiert. Wenn ja, prüfe die Änderung in der Schleife anhand der Anzahl der Eigenschaftsinstanzen. Ansonsten vergleicht man einfach die Eigenschaften zweier Objekte:

//+------------------------------------------------------------------+
//| Check object property changes                                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCheckChanged(void)
  {
   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;
      if(prop==GRAPH_OBJ_PROP_TIME || (prop>=GRAPH_OBJ_PROP_LEVELCOLOR && prop<=GRAPH_OBJ_PROP_LEVELWIDTH))
        {
         int total=(prop==GRAPH_OBJ_PROP_TIME ? this.m_pivots : this.Levels());
         for(int j=0;j<Prop.CurrSize(prop);j++)
           {
            if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
              {
               changed=true;
               ::Print(DFUN,this.Name(),", property index: ",j," ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
              }
           }
        }
      else if(this.GetProperty(prop,0)!=this.GetPropertyPrev(prop,0))
        {
         changed=true;
         ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
        }
     }

   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;
      if(prop==GRAPH_OBJ_PROP_PRICE || GRAPH_OBJ_PROP_LEVELVALUE)
        {
         int total=(prop==GRAPH_OBJ_PROP_PRICE ? this.m_pivots : this.Levels());
         for(int j=0;j<Prop.CurrSize(prop);j++)
           {
            if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
              {
               changed=true;
               ::Print(DFUN,this.Name(),", property index: ",j," ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
              }
           }
        }
      else if(this.GetProperty(prop,0)!=this.GetPropertyPrev(prop,0))
        {
         changed=true;
         ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop));
        }
     }

   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;
      if(prop==GRAPH_OBJ_PROP_LEVELTEXT || prop==GRAPH_OBJ_PROP_BMPFILE)
        {
         int total=(prop==GRAPH_OBJ_PROP_LEVELTEXT ? this.Levels() : 2);
         for(int j=0;j<Prop.CurrSize(prop);j++)
           {
            if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
              {
               changed=true;
               ::Print(DFUN,this.Name(),", property index: ",j," ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
              }
           }
        }
      else if(this.GetProperty(prop,0)!=this.GetPropertyPrev(prop,0))
        {
         changed=true;
         ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop));
        }
     }
   if(changed)
      PropertiesCopyToPrevData();
  }
//+------------------------------------------------------------------+



Die Methoden zum Lesen und Setzen von Objekteigenschaften und zur Rückgabe von Eigenschaftsbeschreibungen:

//+------------------------------------------------------------------+
//| Read and set the time of the specified pivot point               |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetTimePivot(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_TIME,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIME,index));
  }
//+------------------------------------------------------------------+
//| Read and set the price of the specified pivot point              |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetPricePivot(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_PRICE,index,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_PRICE,index));
  }
//+------------------------------------------------------------------+
//| Read and set the color of the specified level                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetLevelColor(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELCOLOR,index));
  }
//+------------------------------------------------------------------+
//| Read and set the style of the specified level                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetLevelStyle(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELSTYLE,index));
  }
//+------------------------------------------------------------------+
//| Read and set the width of the specified level                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetLevelWidth(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELWIDTH,index));
  }
//+------------------------------------------------------------------+
//| Read and set the value of the specified level                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetLevelValue(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_LEVELVALUE,index,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_LEVELVALUE,index));
  }
//+------------------------------------------------------------------+
//| Read and set the text of the specified level                     |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetLevelText(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_LEVELTEXT,index,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_LEVELTEXT,index));
  }
//+------------------------------------------------------------------+
//| Read and set the BMP file name                                   |
//| for the Bitmap Label object.                                     |
//| Index: 0 - ON, 1 - OFF                                           |
//+------------------------------------------------------------------+
void CGStdGraphObj::SetBMPFile(const int index)
  {
   this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,index,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_BMPFILE,index));
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all pivot point times                 |
//+------------------------------------------------------------------+
string CGStdGraphObj::TimesDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.m_pivots;i++)
      txt+=" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_PIVOT)+(string)i+": "+this.TimeDescription(i)+(i<this.m_pivots-1 ? "\n" : "");
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the prices of all pivot points                            |
//+------------------------------------------------------------------+
string CGStdGraphObj::PricesDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.m_pivots;i++)
      txt+=" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_PIVOT)+(string)i+": "+this.PriceDescription(i)+(i<this.m_pivots-1 ? "\n" : "");
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all pivot points                      |
//+------------------------------------------------------------------+
string CGStdGraphObj::TimePricesDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.m_pivots;i++)
      txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_PIVOT)+(string)i+": "+this.TimeDescription(i)+" / "+this.PriceDescription(i)+(i<this.m_pivots-1 ? "\n" : ""));
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all level colors                      |
//+------------------------------------------------------------------+
string CGStdGraphObj::LevelColorsDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.Levels();i++)
      txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelColorDescription(i)+(i<this.Levels()-1 ? "\n" : ""));
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all level styles                      |
//+------------------------------------------------------------------+
string CGStdGraphObj::LevelStylesDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.Levels();i++)
      txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+LineStyleDescription(this.LevelStyle(i))+(i<this.Levels()-1 ? "\n" : ""));
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all level width                       |
//+------------------------------------------------------------------+
string CGStdGraphObj::LevelWidthsDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.Levels();i++)
      txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelWidthDescription(i)+(i<this.Levels()-1 ? "\n" : ""));
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all level values                      |
//+------------------------------------------------------------------+
string CGStdGraphObj::LevelValuesDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.Levels();i++)
      txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelValueDescription(i)+(i<this.Levels()-1 ? "\n" : ""));
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all level texts                       |
//+------------------------------------------------------------------+
string CGStdGraphObj::LevelTextsDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.Levels();i++)
      txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelText(i)+(i<this.Levels()-1 ? "\n" : ""));
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of the graphical object BMP files        |
//+------------------------------------------------------------------+
string CGStdGraphObj::BMPFilesDescription(void) const
  {
   string txt=
     (
      " - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON) +" (0): \""+BMPFile(0)+"\"\n"+
      " - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF)+" (1): \""+BMPFile(1)+"\""
     );
   return txt;
  }
//+------------------------------------------------------------------+
//| Return the descriptions of all values of all levels              |
//+------------------------------------------------------------------+
string CGStdGraphObj::LevelsAllDescription(void) const
  {
   string txt="";
   for(int i=0;i<this.Levels();i++)
     {
      txt+=
        (
         " - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+" "+(string)i+"\n"+
         "    "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELCOLOR)+": "+LevelColorDescription(i)+"\n"+
         "    "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELSTYLE)+": "+LevelStyleDescription(i)+"\n"+
         "    "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELWIDTH)+": "+LevelWidthDescription(i)+"\n"+
         "    "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELVALUE)+": "+LevelValueDescription(i)+"\n"+
         "    "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELTEXT) +": "+LevelText(i)
        );
     }
   return txt;
  }
//+------------------------------------------------------------------+

Ich glaube, diese Methoden bedürfen kaum eines Kommentars. Abgesehen von den besprochenen Methoden und Änderungen weist die Klasse mehrere Änderungen für die Angabe des Index der zweiten Dimension auf. Ich werde sie hier nicht beschreiben. Sie finden sie in den unten angehängten Dateien.

Da der Klassenkonstruktor eine neue Variable hat, die die Anzahl der Angelpunkte bei der Erstellung einer Kindklasse übergibt, müssen wir die Konstruktoren jedes solchen Objekts überarbeiten. Ich werde nur zwei grafische Objektklassendateien besprechen.

Wir öffnen die Klassendatei des grafischen Objekts des Kaufsymbols aus \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdArrowBuyObj.mqh und fügen die Übergabe des Parameters für die Anzahl der Objektdrehpunkte an den Konstruktor der Elternklasse hinzu:

//+------------------------------------------------------------------+
//| Buy graphical object                                             |
//+------------------------------------------------------------------+
class CGStdArrowBuyObj : public CGStdGraphObj
  {
private:

public:
   //--- Constructor
                     CGStdArrowBuyObj(const long chart_id,const string name) : 
                        CGStdGraphObj(OBJECT_DE_TYPE_GSTD_ARROW_BUY,GRAPH_OBJ_BELONG_NO_PROGRAM,GRAPH_OBJ_GROUP_ARROWS,chart_id,1,name)
                          {
                           //--- Specify the object property
                           CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,ANCHOR_TOP);
                          }
   //--- Supported object properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property);
//--- Return the graphical object anchor point position
   ENUM_ARROW_ANCHOR Anchor(void)            const { return (ENUM_ARROW_ANCHOR)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); }
//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);
//--- Return the object short name
   virtual string    Header(const bool symbol=false);
//--- Return the description of the (ENUM_OBJECT) graphical object type
   virtual string    TypeDescription(void)   const { return StdGraphObjectTypeDescription(OBJ_ARROW_BUY);  }
//--- Return the description of the graphical object anchor point position
   virtual string    AnchorDescription(void) const { return AnchorForArrowObjDescription(this.Anchor()); }

  };
//+------------------------------------------------------------------+


Dieses Objekt hat nur einen Angelpunkt. Besprechen wir nun das Objekt mit mehreren Angelpunkten.

Wir öffnen die Klassendatei des Objekts "Trendlinie" aus \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdTrendObj.mqh und übergeben die Anzahl der Angelpunkte (es sind zwei) an den Konstruktor der übergeordneten Klasse:

//+------------------------------------------------------------------+
//| "Trend line" graphical object                                    |
//+------------------------------------------------------------------+
class CGStdTrendObj : public CGStdGraphObj
  {
private:

public:
   //--- Constructor
                     CGStdTrendObj(const long chart_id,const string name) :
                        CGStdGraphObj(OBJECT_DE_TYPE_GSTD_TREND,GRAPH_OBJ_BELONG_NO_PROGRAM,GRAPH_OBJ_GROUP_LINES,chart_id,2,name)
                          {
                           //--- Get and save the object properties
                           CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,::ObjectGetInteger(chart_id,name,OBJPROP_RAY_LEFT));
                           CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,::ObjectGetInteger(chart_id,name,OBJPROP_RAY_RIGHT));
                          }
   //--- Supported object properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property);
//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);
//--- Return the object short name
   virtual string    Header(const bool symbol=false);
//--- Return the description of the (ENUM_OBJECT) graphical object type
   virtual string    TypeDescription(void)   const { return StdGraphObjectTypeDescription(OBJ_TREND); }
  };
//+------------------------------------------------------------------+


Solche Änderungen wurden für jede Klasse in \MQL5\Include\DoEasy\Objects\Graph\Standard\ vorgenommen. Jedes Objekt hat die genaue Anzahl von Angelpunkten, die für seine Konstruktion verwendet wurden. Sie können alle Änderungen in den unten angehängten Dateien sehen.

Nun wollen wir die Klasse CSelect in \MQL5\Include\DoEasy\Services\Select.mqh verbessern. Hier müssen wir einfach die Indizes der zweiten Dimension zu jeder Methode hinzufügen, die auf die GetProperty()-Methoden zugreifen soll:

//+------------------------------------------------------------------+
//| Methods of working with standard graphical object data           |
//+------------------------------------------------------------------+
   //--- Return the list of objects with one of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the graphical object index in the list with the maximum value of the (1) integer, (2) real and (3) string properties
   static int        FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index);
   static int        FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index);
   static int        FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_STRING property,int index);
   //--- Return the graphical object index in the list with the minimum value of the (1) integer, (2) real and (3) string properties
   static int        FindGraphicStdObjectMin(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index);
   static int        FindGraphicStdObjectMin(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index);
   static int        FindGraphicStdObjectMin(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_STRING property,int index);
//---
  };
//+------------------------------------------------------------------+


Kommen wir zur Umsetzung mehrerer Methoden:

//+------------------------------------------------------------------+
//| Return the list of objects with one integer                      |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CGStdGraphObj *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property,index);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int idx=0;
   CGStdGraphObj *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGStdGraphObj *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property,index);
      max_obj=list_source.At(idx);
      long obj2_prop=max_obj.GetProperty(property,index);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) idx=i;
     }
   return idx;
  }
//+------------------------------------------------------------------+


Alle Änderungen sind farblich hervorgehoben. Die übrigen Methoden sind identisch mit den oben vorgestellten geändert. Sie können sich mit ihnen in den unten angehängten Dateien vertraut machen.

Ändern wir nun die Klasse der grafischen Objektkollektion in \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

In den öffentlichen Methoden GetList() fügen wir die Angabe des Indexes der zweiten Dimension des Arrays hinzu:

public:
//--- Return itself
   CGraphElementsCollection *GetObject(void)                                                             { return &this;                        }
//--- Return the full collection list of standard graphical objects "as is"
   CArrayObj        *GetListGraphObj(void)                                                               { return &this.m_list_all_graph_obj;   }
//--- Return the full collection list of graphical elements on canvas "as is"
   CArrayObj        *GetListCanvElm(void)                                                                { return &this.m_list_all_canv_elm_obj;}
//--- Return the list of graphical elements by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);  }
//--- Return the list of graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); }
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); }
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); }


Alle Methoden, die Zugang zu den verbesserten Methoden der Klasse CSelect haben, fügen die Angabe des Index der zweiten Dimension des Arrays hinzu:

//+------------------------------------------------------------------+
//| Return the first free graphical object ID                        |
//+------------------------------------------------------------------+
long CGraphElementsCollection::GetFreeGraphObjID(void)
  {
   int index=CSelect::FindGraphicStdObjectMax(this.GetListGraphObj(),GRAPH_OBJ_PROP_ID,0);
   CGStdGraphObj *obj=this.m_list_all_graph_obj.At(index);
   return(obj!=NULL ? obj.ObjectID()+1 : 1);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|Find an object present in the collection but not on a chart       |
//+------------------------------------------------------------------+
CGStdGraphObj *CGraphElementsCollection::FindMissingObj(const long chart_id)
  {
   CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
   if(list==NULL)
      return NULL;
   for(int i=0;i<list.Total();i++)
     {
      CGStdGraphObj *obj=list.At(i);
      if(obj==NULL)
         continue;
      if(!this.IsPresentGraphObjOnChart(obj.ChartID(),obj.Name()))
         return obj;
     }
   return NULL;
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------------------+
//| Return the flag indicating the presence of the graphical object class object |
//| in the graphical object collection list                                      |
//+------------------------------------------------------------------------------+
bool CGraphElementsCollection::IsPresentGraphObjInList(const long chart_id,const string name)
  {
   CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
   list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,name,EQUAL);
   return(list==NULL || list.Total()==0 ? false : true);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Remove a graphical object by a chart ID                          |
//| from the graphical object collection list                        |
//+------------------------------------------------------------------+
void CGraphElementsCollection::DeleteGraphObjectsFromList(const long chart_id)
  {
   CArrayObj *list=CSelect::ByGraphicStdObjectProperty(GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL);
   if(list==NULL)
      return;
   for(int i=list.Total();i>WRONG_VALUE;i--)
     {
      CGStdGraphObj *obj=list.At(i);
      if(obj==NULL)
         continue;
      this.DeleteGraphObjFromList(obj);
     }
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Return a graphical object by chart name and ID                   |
//+------------------------------------------------------------------+
CGStdGraphObj *CGraphElementsCollection::GetStdGraphObject(const string name,const long chart_id)
  {
   CArrayObj *list=this.GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id);
   list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,name,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+


In der Methode, die das grafische Objekt zur Kollektion hinzufügt, zeigen wir die vollständige Objektbeschreibung anstelle der kurzen, damit wir in der Lage sind, die vollständige Liste der Objekteigenschaften während des Tests zu sehen:

//+------------------------------------------------------------------+
//| Add a graphical object to the collection                         |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control)
  {
   //--- Get the list of the last added graphical objects from the class for managing graphical objects
   CArrayObj *list=obj_control.GetListNewAddedObj();
   //--- If failed to obtain the list, inform of that and return 'false'
   if(list==NULL)
     {
      CMessage::ToLog(DFUN_ERR_LINE,MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST);
      return false;
     }
   //--- If the list is empty, return 'false'
   if(list.Total()==0)
      return false;
   //--- Declare the variable for storing the result
   bool res=true;
   //--- In the loop by the list of newly added standard graphical objects,
   for(int i=0;i<list.Total();i++)
     {
      //--- retrieve the next object from the list and
      CGStdGraphObj *obj=list.Detach(i);
      //--- if failed to get the object, inform of that, add 'false' to the resulting variable and move on to the next one
      if(obj==NULL)
        {
         CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST);
         res &=false;
         continue;
        }
      //--- if failed to add the object to the collection list, inform of that,
      //--- remove the object, add 'false' to the resulting variable and move on to the next one
      if(!this.m_list_all_graph_obj.Add(obj))
        {
         CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
         delete obj;
         res &=false;
         continue;
        }
      //--- The object has been successfully retrieved from the list of newly added graphical objects and introduced into the collection -
      //--- find the next free object ID, write it to the property and display the short object description in the journal
      else
        {
         obj.SetObjectID(this.GetFreeGraphObjID());
         obj.Print();
        }
     }
   //--- Return the result of adding the object to the collection
   return res;
  }
//+------------------------------------------------------------------+


Dies sind alle Verbesserungen, die ich hier umsetzen wollte. Kleine, aber zahlreiche Änderungen, die ich in diesem Artikel nicht besprochen habe, finden Sie in den folgenden Dateien.


Testen von Objektereignissen mit zwei Ankerpunkten

Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Part88\ als TestDoEasyPart88.mq5.

Es sind keine Änderungen im EA erforderlich, da sie in den Bibliotheksdateien vorgenommen wurden.

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


Beim Hinzufügen von Objekten mit zwei Angelpunkten und beim Ändern eines der beiden Angelpunkte zeigt der EA die entsprechenden Ereigniseinträge im Journal an. Wenn wir ein Objekt mit mehr als zwei Angelpunkten zum Chart hinzufügen, gibt es keine Einträge zur Änderung eines Punktes oder die Daten eines Punktes werden geändert, wenn ein anderer geändert wird. Dies ist auf den oben erwähnten logischen Fehler zurückzuführen. Ich werde ihn im nächsten Artikel beheben.


Was kommt als Nächstes?

Danach werde ich meine Arbeit an den Standard-Ereignissen für grafische Objekte und den dynamischen Array-Klassen fortsetzen.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit der Test-EA-Datei für MQL5 zum Testen und Herunterladen angehängt. Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit der Test-EA-Datei für MQL5 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 82): Die Umgestaltung von Bibliotheksobjekten und Kollektion von grafischen Objekten
Grafiken in der Bibliothek DoEasy (Teil 83): Die Klasse des abstrakten grafischen Standardobjekts
/> Grafiken in der Bibliothek DoEasy (Teil 84): Abgeleitete Klassen des abstrakten grafischen Standardobjekts
Grafiken in der Bibliothek DoEasy (Teil 85): Grafische Objektkollektion - Hinzufügen neu erstellter Objekte
Grafiken in der Bibliothek DoEasy (Teil 86): Grafische Objektkollektion - Verwaltung der Eigenschaftsänderungen
Grafiken in der Bibliothek DoEasy (Teil 87): Grafische Kollektion - Verwaltung der Änderungen von Eigenschaften von Objekten auf allen offenen Charts

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

Beigefügte Dateien |
MQL5.zip (4164.99 KB)
Ein Versuch, einen EA-Konstruktor zu entwickeln Ein Versuch, einen EA-Konstruktor zu entwickeln
In diesem Artikel biete ich eine Reihe von Handelsfunktionen in Form eines fertigen EA an. Diese Methode ermöglicht es, durch einfaches Hinzufügen von Indikatoren und Ändern von Eingaben mehrere Handelsstrategien zu erstellen.
Die Verwendung von AutoIt mit MQL5 Die Verwendung von AutoIt mit MQL5
Kurzbeschreibung. In diesem Artikel befassen wir uns mit dem Skripting des MetraTrader 5-Terminals durch die Integration von MQL5 mit AutoIt. Es wird gezeigt, wie man verschiedene Aufgaben durch Manipulation der Benutzeroberfläche des Terminals automatisieren kann, und es wird eine Klasse vorgestellt, die die AutoItX-Bibliothek verwendet.
MQL5 Kochbuch – Der Wirtschaftskalender MQL5 Kochbuch – Der Wirtschaftskalender
Der Artikel hebt die Programmierfunktionen des Wirtschaftskalenders hervor und befasst sich mit der Erstellung einer Klasse für einen vereinfachten Zugriff auf die Kalendereigenschaften und den Empfang der Ereigniswerte. Als praktisches Beispiel dient die Entwicklung eines Indikators, der die nicht-kommerziellen Nettopositionen der CFTC verwendet.
Die Verwendung von Kanälen und Gruppenchats der MQL5.community Die Verwendung von Kanälen und Gruppenchats der MQL5.community
Die Website MQL5.com bringt Händler aus der ganzen Welt zusammen. Die Nutzer veröffentlichen Artikel, teilen kostenlose Codes, verkaufen Produkte auf dem Markt, führen Freelance-Aufträge aus und kopieren Handelssignale. Sie können mit ihnen im Forum, in Händler-Chats und in MetaTrader-Kanälen kommunizieren.