Implementazione di un modello di tabella in MQL5: Applicazione del concetto MVC
Contenuto
- Introduzione
- Un po' di cose sul concetto MVC (Model-View-Controller)
- Scrivere classi per costruire un modello di tabella
- Elenchi collegati come base per l'archiviazione di dati tabellari
- Classe della Cella della Tabella
- Classe della Riga della Tabella
- Classe del Modello della Tabella
- Verifica del risultato
- Conclusioni
Introduzione
Nella programmazione, l'architettura delle applicazioni svolge un ruolo fondamentale nel garantire affidabilità, scalabilità e facilità di supporto. Uno degli approcci che aiuta a raggiungere tali obiettivi è quello di sfruttare il modello di architettura chiamato MVC (Model-View-Controller).
Il concetto MVC consente di dividere un'applicazione in tre componenti interconnessi: model (gestione dei dati e della logica), view (visualizzazione dei dati) e controller (elaborazione delle azioni dell'utente). Questa separazione semplifica lo sviluppo, il test e la manutenzione del codice, rendendolo più strutturato e flessibile.
In questo articolo, consideriamo come applicare i principi MVC per implementare un modello di tabella nel linguaggio MQL5. Le tabelle sono uno strumento importante per l'archiviazione, l'elaborazione e la visualizzazione dei dati e la loro corretta organizzazione può facilitare il lavoro con le informazioni. Creeremo classi per lavorare con le tabelle: celle, righe e modello di tabella. Per memorizzare le celle all'interno delle righe e le righe all'interno del modello di tabella, utilizzeremo le classi degli elenchi collegati della Libreria Standard MQL5, che consentono di memorizzare e utilizzare i dati in modo efficiente.
Un po' di cose sul concetto MVC: cos'è e perché lo vogliamo?
Immaginate l'applicazione come una produzione teatrale. Esiste uno scenario che descrive ciò che dovrebbe accadere (questo è il modello). C'è il palcoscenico - ciò che lo spettatore vede (questa è la vista). Infine, c'è il regista che gestisce l'intero processo e collega gli altri elementi (è il controllore). Questo è il modo in cui opera il modello architettonico MVC — Model-View-Controller.
Questo concetto aiuta a separare le responsabilità all'interno dell'applicazione. Il modello è responsabile dei dati e della logica, la vista è responsabile della visualizzazione e dell'aspetto e il controllore è responsabile dell'elaborazione delle azioni dell'utente. Questa separazione rende il codice più chiaro, più flessibile e più comodo per il lavoro di squadra.
Supponiamo di creare una tabella. Il modello sa quali righe e celle contiene e sa come modificarle. La vista disegna una tabella sullo schermo. Il controllore reagisce quando l'utente fa clic su "Aggiungi riga" e passa il compito al modello, per poi dire alla vista di aggiornare.
MVC è particolarmente utile quando l'applicazione diventa più complessa: vengono aggiunte nuove funzionalità, l'interfaccia cambia e lavorano diversi sviluppatori. Con un'architettura chiara, è più facile apportare modifiche, testare i singoli componenti e riutilizzare il codice.
Questo approccio presenta anche alcuni svantaggi. Per progetti molto semplici, MVC potrebbe essere superfluo: si dovrà separare anche ciò che potrebbe rientrare in un paio di funzioni. Tuttavia, per le applicazioni scalabili e serie, questa struttura si ripaga rapidamente.
In sintesi:
MVC è un potente modello di architettura che aiuta a organizzare il codice, a renderlo più comprensibile, testabile e scalabile. È particolarmente utile per le applicazioni complesse in cui è importante la separazione tra logica dei dati, interfaccia utente e gestione. Per i progetti di piccole dimensioni, il suo utilizzo è superfluo.
Il paradigma Model-View-Controller si adatta molto bene al nostro compito. La tabella verrà creata da oggetti indipendenti:
- Cella della tabella.
Un oggetto che memorizza un valore di uno dei tipi - reale, intero o stringa - è dotato di strumenti per gestire il valore, impostarlo e recuperarlo; - Riga della tabella.
Un oggetto che memorizza un elenco di oggetti nelle celle della tabella è dotato di strumenti per la gestione delle celle, la loro posizione, l'aggiunta e la cancellazione; - Un modello di tabella.
Un oggetto che memorizza un elenco di oggetti stringa della tabella è dotato di strumenti per gestire le stringhe e le colonne della tabella, la loro posizione, l'aggiunta e l'eliminazione e ha anche accesso ai controlli delle stringhe e delle celle.
La figura seguente mostra schematicamente la struttura di un modello di tabella 4x4:

Fig.1 Modello di tabella 4x4
Passiamo ora dalla teoria alla pratica.
Scrivere classi per costruire un modello di tabella
Utilizzeremo la Libreria Standard MQL5 per creare tutti gli oggetti.
Ogni oggetto sarà un erede della classe base della libreria. Ciò consente di memorizzare questi oggetti in elenchi di oggetti.
Scriveremo tutte le classi in un unico file di script di test, in modo che tutto sia in un unico file, visibile e rapidamente accessibile. In futuro, distribuiremo le classi scritte nei loro file include separatamente.
1. Elenchi collegati come base per l'archiviazione di dati tabellari
L'elenco collegato CList è molto adatto alla memorizzazione di dati tabellari. A differenza dell'analogo elenco CArrayObj, implementa metodi di accesso agli oggetti elenco vicini, situati a sinistra e a destra di quello corrente. In questo modo sarà facile spostare le celle di una riga o le righe di una tabella, aggiungerle ed eliminarle. Allo stesso tempo, l'elenco stesso si occuperà della corretta indicizzazione degli oggetti spostati, aggiunti o cancellati nell'elenco.
Ma qui c'è una sfumatura. Se si fa riferimento ai metodi per caricare e salvare un elenco in un file, si può notare che quando si carica da un file, la classe elenco deve creare un nuovo oggetto nel metodo virtuale CreateElement().
Questo metodo in questa classe restituisce semplicemente NULL:
//--- method of creating an element of the list virtual CObject *CreateElement(void) { return(NULL); }
Ciò significa che per lavorare con gli elenchi collegati e a condizione che abbiamo bisogno di operazioni sui file, dobbiamo ereditare dalla classe CList e implementare questo metodo nella nostra classe.
Se si esaminano i metodi di salvataggio degli oggetti della Libreria Standard in un file, possiamo vedere il seguente algoritmo per il salvataggio delle proprietà degli oggetti:
- Il marcatore di inizio dati (-1) viene scritto nel file,
- Il tipo di oggetto viene scritto nel file,
- Tutte le proprietà dell'oggetto vengono scritte nel file una per una.
Il primo e il secondo punto sono inerenti a tutti i metodi di salvataggio/caricamento implementati che gli oggetti della Libreria Standard possiedono. Di conseguenza, seguendo la stessa logica, vogliamo conoscere il tipo dell'oggetto salvato nell'elenco, in modo che, leggendo da un file, possiamo creare un oggetto con questo tipo nel metodo virtuale CreateElement() della classe elenco ereditata da CList.
Inoltre, tutti gli oggetti che possono essere caricati nell'elenco - le loro classi devono essere dichiarate o create prima che la classe dell'elenco sia implementata. In questo caso, l'elenco "saprà" quali oggetti sono "in questione" e quali devono essere creati.
Nella directory del terminale \MQL5\Scripts\, create una nuova cartella TableModel\, e in essa un nuovo file dello script di test TableModelTest.mq5.
Includere il file dell'elenco collegato e dichiarare le future classi del modello di tabella:
//+------------------------------------------------------------------+ //| TableModelTest.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include <Arrays\List.mqh> //--- Форвард-декларация классов class CTableCell; // Класс ячейки таблицы class CTableRow; // Класс строки таблицы class CTableModel; // Класс модели таблицы
La dichiarazione di classi future è necessaria per far sì che la classe degli elenchi collegati che eredita da CList conosca questi tipi di classi e i tipi di oggetti che dovrà creare. Per farlo, scriveremo enumerazioni di tipi di oggetti, macro ausiliarie e enumerazioni di modi per ordinare gli elenchi:
//+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include <Arrays\List.mqh> //--- Форвард-декларация классов class CTableCell; // Класс ячейки таблицы class CTableRow; // Класс строки таблицы class CTableModel; // Класс модели таблицы //+------------------------------------------------------------------+ //| Макросы | //+------------------------------------------------------------------+ #define MARKER_START_DATA -1 // Маркер начала данных в файле #define MAX_STRING_LENGTH 128 // Максимальная длина строки в ячейке //+------------------------------------------------------------------+ //| Перечисления | //+------------------------------------------------------------------+ enum ENUM_OBJECT_TYPE // Перечисление типов объектов { OBJECT_TYPE_TABLE_CELL=10000, // Ячейка таблицы OBJECT_TYPE_TABLE_ROW, // Строка таблицы OBJECT_TYPE_TABLE_MODEL, // Модель таблицы }; enum ENUM_CELL_COMPARE_MODE // Режимы сравнения ячеек таблицы { CELL_COMPARE_MODE_COL, // Сравнение по номеру колонки CELL_COMPARE_MODE_ROW, // Сравнение по номеру строки CELL_COMPARE_MODE_ROW_COL, // Сравнение по строке и колонке }; //+------------------------------------------------------------------+ //| Функции | //+------------------------------------------------------------------+ //--- Возвращает тип объекта как строку string TypeDescription(const ENUM_OBJECT_TYPE type) { string array[]; int total=StringSplit(EnumToString(type),StringGetCharacter("_",0),array); string result=""; for(int i=2;i<total;i++) { array[i]+=" "; array[i].Lower(); array[i].SetChar(0,ushort(array[i].GetChar(0)-0x20)); result+=array[i]; } result.TrimLeft(); result.TrimRight(); return result; } //+------------------------------------------------------------------+ //| Классы | //+------------------------------------------------------------------+
La funzione che restituisce la descrizione del tipo di oggetto si basa sull'ipotesi che tutti i nomi delle costanti del tipo di oggetto inizino con la sottostringa "OBJECT_TYPE_". Quindi è possibile prendere la sottostringa successiva a questa, convertire tutti i caratteri della riga risultante in minuscolo, convertire il primo carattere in maiuscolo e cancellare tutti gli spazi e controllare i caratteri dalla stringa finale a sinistra e a destra.
Scriviamo la nostra classe di liste collegate. Continueremo a scrivere il codice nello stesso file:
//+------------------------------------------------------------------+ //| Классы | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс связанного списка объектов | //+------------------------------------------------------------------+ class CListObj : public CList { protected: ENUM_OBJECT_TYPE m_element_type; // Тип создаваемого объекта в CreateElement() public: //--- Виртуальный метод (1) загрузки списка из файла, (2) создания элемента списка virtual bool Load(const int file_handle); virtual CObject *CreateElement(void); };
La classe CListObj è la nostra nuova classe di elenchi collegati, ereditata dalla classe CList della Libreria Standard.
L'unica variabile della classe sarà quella in cui verrà scritto il tipo dell'oggetto creato. Poiché il metodo CreateElement() è virtuale e deve avere esattamente la stessa firma del metodo della classe madre, non possiamo passargli il tipo dell'oggetto che viene creato. Ma possiamo scrivere questo tipo, in una variabile dichiarata e leggere da essa il tipo dell'oggetto che viene creato.
Dobbiamo ridefinire due metodi virtuali della classe madre: il metodo di caricamento da un file e il metodo di creazione di un nuovo oggetto. Prendiamoli in considerazione.
Metodo di caricamento dell'elenco da un file:
//+------------------------------------------------------------------+ //| Загрузка списка из файла | //+------------------------------------------------------------------+ bool CListObj::Load(const int file_handle) { //--- Переменные CObject *node; bool result=true; //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загрузка и проверка маркера начала списка - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загрузка и проверка типа списка if(::FileReadInteger(file_handle,INT_VALUE)!=Type()) return(false); //--- Чтение размера списка (количество объектов) uint num=::FileReadInteger(file_handle,INT_VALUE); //--- Последовательно заново создаём элементы списка с помощью вызова метода Load() объектов node this.Clear(); for(uint i=0; i<num; i++) { //--- Читаем и проверяем маркер начала данных объекта - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return false; //--- Читаем тип объекта this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE); node=this.CreateElement(); if(node==NULL) return false; this.Add(node); //--- Сейчас файловый указатель смещён относительно начала маркера объекта на 12 байт (8 - маркер, 4 - тип) //--- Поставим указатель на начало данных объекта и загрузим свойства объекта из файла методом Load() элемента node. if(!::FileSeek(file_handle,-12,SEEK_CUR)) return false; result &=node.Load(file_handle); } //--- Результат return result; }
In questo caso, viene prima controllato l'inizio dell'elenco, il suo tipo e la sua dimensione, cioè il numero di elementi nell'elenco; quindi, in un ciclo per il numero di elementi, vengono letti dal file i marcatori di inizio dati di ogni oggetto e il suo tipo. Il tipo risultante viene scritto nella variabile m_element_type e viene richiamato un metodo per creare un nuovo elemento. In questo metodo, un nuovo elemento con il tipo ricevuto viene creato e scritto nella variabile puntatore node, che a sua volta viene aggiunta all'elenco. L'intera logica del metodo è spiegata in dettaglio nei commenti. Consideriamo un metodo per creare un nuovo elemento dell'elenco.
Metodo di creazione di un elemento dell'elenco:
//+------------------------------------------------------------------+ //| Метод создания элемента списка | //+------------------------------------------------------------------+ CObject *CListObj::CreateElement(void) { //--- В зависимости от типа объекта в m_element_type, создаём новый объект switch(this.m_element_type) { case OBJECT_TYPE_TABLE_CELL : return new CTableCell(); case OBJECT_TYPE_TABLE_ROW : return new CTableRow(); case OBJECT_TYPE_TABLE_MODEL : return new CTableModel(); default : return NULL; } }
Ciò significa che prima di chiamare il metodo, il tipo dell'oggetto che viene creato è già scritto nella variabile m_element_type . In base al tipo di elemento, viene creato un nuovo oggetto del tipo appropriato e viene restituito un puntatore ad esso. In futuro, quando si svilupperanno nuovi controlli, i loro tipi saranno scritti nell'enumerazione ENUM_OBJECT_TYPE. E qui verranno aggiunti nuovi casi per creare nuovi tipi di oggetti. La classe di elenchi collegati basata su CList standard è pronta. Ora è in grado di memorizzare tutti gli oggetti di tipo conosciuti, salvare gli elenchi in un file, caricarli dal file e ripristinarli correttamente.
2. Classe della Cella della Tabella
La cella di una tabella è l'elemento più semplice di una tabella che memorizza un determinato valore. Le celle compongono elenchi, rappresentando le righe della tabella. Ogni elenco rappresenta una riga della tabella. Nella nostra tabella, le celle potranno memorizzare solo un valore di diversi tipi alla volta - un valore reale, intero o stringa.
Oltre a un semplice valore a una cella può essere assegnato un oggetto di un tipo conosciuto dall'enumerazione ENUM_OBJECT_TYPE. In questo caso, la cella può memorizzare un valore di uno qualsiasi dei tipi elencati, più un puntatore a un oggetto, il cui tipo viene scritto in una variabile speciale. In futuro, quindi, il componente View potrà essere istruito a visualizzare tale oggetto in una cella per interagire con esso tramite il componente Controller.
Poiché in una cella possono essere memorizzati diversi tipi di valori, utilizzeremo union per scriverli, memorizzarli e restituirli. Union è un tipo speciale di dati che memorizza diversi campi nella stessa area di memoria. L’unione è simile a una struttura, ma in questo caso, a differenza di una struttura, i diversi termini dell'unione appartengono alla stessa area di memoria. Mentre nella struttura, a ogni campo viene allocata una propria area di memoria.
Continuiamo a scrivere il codice nel file già creato. Iniziamo a scrivere una nuova classe. Nella sezione protected, scriviamo un'unione e dichiariamo le variabili:
//+------------------------------------------------------------------+ //| Класс ячейки таблицы | //+------------------------------------------------------------------+ class CTableCell : public CObject { protected: //--- Объединение для хранения значений ячейки (double, long, string) union DataType { protected: double double_value; long long_value; ushort ushort_value[MAX_STRING_LENGTH]; public: //--- Установка значений void SetValueD(const double value) { this.double_value=value; } void SetValueL(const long value) { this.long_value=value; } void SetValueS(const string value) { ::StringToShortArray(value,ushort_value); } //--- Возврат значений double ValueD(void) const { return this.double_value; } long ValueL(void) const { return this.long_value; } string ValueS(void) const { string res=::ShortArrayToString(this.ushort_value); res.TrimLeft(); res.TrimRight(); return res; } }; //--- Переменные DataType m_datatype_value; // Значение ENUM_DATATYPE m_datatype; // Тип данных CObject *m_object; // Объект в ячейке ENUM_OBJECT_TYPE m_object_type; // Тип объекта в ячейке int m_row; // Номер строки int m_col; // Номер столбца int m_digits; // Точность представления данных uint m_time_flags; // Флаги отображения даты/времени bool m_color_flag; // Флаг отображения наименования цвета bool m_editable; // Флаг редактируемой ячейки public:
Nella sezione public, scrivete i metodi di accesso alle variabili protette, i metodi virtuali e i costruttori di classe per i vari tipi di dati memorizzati in una cella:
public: //--- Возврат координат и свойств ячейки uint Row(void) const { return this.m_row; } uint Col(void) const { return this.m_col; } ENUM_DATATYPE Datatype(void) const { return this.m_datatype; } int Digits(void) const { return this.m_digits; } uint DatetimeFlags(void) const { return this.m_time_flags; } bool ColorNameFlag(void) const { return this.m_color_flag; } bool IsEditable(void) const { return this.m_editable; } //--- Возвращает (1) double, (2) long, (3) string значение double ValueD(void) const { return this.m_datatype_value.ValueD(); } long ValueL(void) const { return this.m_datatype_value.ValueL(); } string ValueS(void) const { return this.m_datatype_value.ValueS(); } //--- Возвращает значение в виде форматированной строки string Value(void) const { switch(this.m_datatype) { case TYPE_DOUBLE : return(::DoubleToString(this.ValueD(),this.Digits())); case TYPE_LONG : return(::IntegerToString(this.ValueL())); case TYPE_DATETIME: return(::TimeToString(this.ValueL(),this.m_time_flags)); case TYPE_COLOR : return(::ColorToString((color)this.ValueL(),this.m_color_flag)); default : return this.ValueS(); } } string DatatypeDescription(void) const { string type=::StringSubstr(::EnumToString(this.m_datatype),5); type.Lower(); return type; } //--- Установка значений переменных void SetRow(const uint row) { this.m_row=(int)row; } void SetCol(const uint col) { this.m_col=(int)col; } void SetDatatype(const ENUM_DATATYPE datatype) { this.m_datatype=datatype; } void SetDigits(const int digits) { this.m_digits=digits; } void SetDatetimeFlags(const uint flags) { this.m_time_flags=flags; } void SetColorNameFlag(const bool flag) { this.m_color_flag=flag; } void SetEditable(const bool flag) { this.m_editable=flag; } void SetPositionInTable(const uint row,const uint col) { this.SetRow(row); this.SetCol(col); } //--- Назначает объект в ячейку void AssignObject(CObject *object) { if(object==NULL) { ::PrintFormat("%s: Error. Empty object passed",__FUNCTION__); return; } this.m_object=object; this.m_object_type=(ENUM_OBJECT_TYPE)object.Type(); } //--- Снимает назначение объекта void UnassignObject(void) { this.m_object=NULL; this.m_object_type=-1; } //--- Устанавливает double-значение void SetValue(const double value) { this.m_datatype=TYPE_DOUBLE; if(this.m_editable) this.m_datatype_value.SetValueD(value); } //--- Устанавливает long-значение void SetValue(const long value) { this.m_datatype=TYPE_LONG; if(this.m_editable) this.m_datatype_value.SetValueL(value); } //--- Устанавливает datetime-значение void SetValue(const datetime value) { this.m_datatype=TYPE_DATETIME; if(this.m_editable) this.m_datatype_value.SetValueL(value); } //--- Устанавливает color-значение void SetValue(const color value) { this.m_datatype=TYPE_COLOR; if(this.m_editable) this.m_datatype_value.SetValueL(value); } //--- Устанавливает string-значение void SetValue(const string value) { this.m_datatype=TYPE_STRING; if(this.m_editable) this.m_datatype_value.SetValueS(value); } //--- Очищает данные void ClearData(void) { if(this.Datatype()==TYPE_STRING) this.SetValue(""); else this.SetValue(0.0); } //--- (1) Возвращает, (2) выводит в журнал описание объекта string Description(void); void Print(void); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_TABLE_CELL);} //--- Конструкторы/деструктор CTableCell(void) : m_row(0), m_col(0), m_datatype(-1), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueD(0); } //--- Принимает double-значение CTableCell(const uint row,const uint col,const double value,const int digits) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DOUBLE), m_digits(digits), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueD(value); } //--- Принимает long-значение CTableCell(const uint row,const uint col,const long value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_LONG), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает datetime-значение CTableCell(const uint row,const uint col,const datetime value,const uint time_flags) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DATETIME), m_digits(0), m_time_flags(time_flags), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает color-значение CTableCell(const uint row,const uint col,const color value,const bool color_names_flag) : m_row((int)row), m_col((int)col), m_datatype(TYPE_COLOR), m_digits(0), m_time_flags(0), m_color_flag(color_names_flag), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает string-значение CTableCell(const uint row,const uint col,const string value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_STRING), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueS(value); } ~CTableCell(void) {} };
Nei metodi di impostazione dei valori, si imposta prima il tipo di valore memorizzato nella cella e poi si controlla il flag della funzione di modifica dei valori nella cella. Solo quando il flag è impostato, il nuovo valore viene salvato nella cella:
//--- Устанавливает double-значение void SetValue(const double value) { this.m_datatype=TYPE_DOUBLE; if(this.m_editable) this.m_datatype_value.SetValueD(value); }
Perché si fa così? Quando si crea una nuova cella, questa viene creata con il tipo reale del valore memorizzato. Se si desidera cambiare il tipo di valore, ma allo stesso tempo la cella non è modificabile, è possibile richiamare il metodo per impostare il valore del tipo desiderato con un valore qualsiasi. Il tipo di valore memorizzato viene modificato, ma il valore della cella stessa non sarà interessato.
Il metodo di pulizia dei dati imposta i valori numerici a zero e inserisce uno spazio nei valori stringa:
//--- Очищает данные void ClearData(void) { if(this.Datatype()==TYPE_STRING) this.SetValue(""); else this.SetValue(0.0); }
In seguito, faremo diversamente, in modo che nessun dato venga visualizzato nelle celle pulite. Per mantenere la cella vuota, creeremo un valore "vuoto" per la cella e poi, quando la cella verrà pulita, tutti i valori registrati e visualizzati al suo interno verranno cancellati. Dopo tutto, anche lo zero è un valore a tutti gli effetti e ora, quando la cella viene pulita, i dati digitali vengono riempiti di zeri. Questo non è corretto.
Nei costruttori parametrici della classe, vengono passate le coordinate delle celle della tabella - numero di riga e colonna e il valore del tipo richiesto (double, long, datetime, color, string). Alcuni tipi di valori richiedono informazioni aggiuntive:
- double - precisione del valore di uscita (numero di cifre decimali),
- datetime - flag di uscita dell'ora (data/ore-minuti/secondi),
- color- flag per visualizzare i nomi dei colori standard conosciuti.
Nei costruttori con questi tipi di valori memorizzati nelle celle vengono passati parametri aggiuntivi per impostare il formato dei valori visualizzati nelle celle:
//--- Принимает double-значение CTableCell(const uint row,const uint col,const double value,const int digits) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DOUBLE), m_digits(digits), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueD(value); } //--- Принимает long-значение CTableCell(const uint row,const uint col,const long value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_LONG), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает datetime-значение CTableCell(const uint row,const uint col,const datetime value,const uint time_flags) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DATETIME), m_digits(0), m_time_flags(time_flags), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает color-значение CTableCell(const uint row,const uint col,const color value,const bool color_names_flag) : m_row((int)row), m_col((int)col), m_datatype(TYPE_COLOR), m_digits(0), m_time_flags(0), m_color_flag(color_names_flag), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает string-значение CTableCell(const uint row,const uint col,const string value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_STRING), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueS(value); }
Metodo per il confronto di due oggetti:
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CTableCell::Compare(const CObject *node,const int mode=0) const { const CTableCell *obj=node; switch(mode) { case CELL_COMPARE_MODE_COL : return(this.Col()>obj.Col() ? 1 : this.Col()<obj.Col() ? -1 : 0); case CELL_COMPARE_MODE_ROW : return(this.Row()>obj.Row() ? 1 : this.Row()<obj.Row() ? -1 : 0); //---CELL_COMPARE_MODE_ROW_COL default : return ( this.Row()>obj.Row() ? 1 : this.Row()<obj.Row() ? -1 : this.Col()>obj.Col() ? 1 : this.Col()<obj.Col() ? -1 : 0 ); } }
Il metodo consente di confrontare i parametri di due oggetti in base a uno dei tre criteri di confronto - per numero di colonna, per numero di riga e contemporaneamente per numero di riga e di colonna.
Questo metodo è necessario per poter ordinare le righe della tabella in base ai valori delle colonne della tabella. Questo aspetto sarà trattato dal Controller negli articoli successivi.
Metodo per salvare le proprietà delle celle in un file:
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableCell::Save(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long)) return(false); //--- Сохраняем тип объекта if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем тип данных if(::FileWriteInteger(file_handle,this.m_datatype,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем тип объекта в ячейке if(::FileWriteInteger(file_handle,this.m_object_type,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем номер строки if(::FileWriteInteger(file_handle,this.m_row,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем номер столбца if(::FileWriteInteger(file_handle,this.m_col,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем точность представления данных if(::FileWriteInteger(file_handle,this.m_digits,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем флаги отображения даты/времени if(::FileWriteInteger(file_handle,this.m_time_flags,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем флаг отображения наименования цвета if(::FileWriteInteger(file_handle,this.m_color_flag,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем флаг редактируемой ячейки if(::FileWriteInteger(file_handle,this.m_editable,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем значение if(::FileWriteStruct(file_handle,this.m_datatype_value)!=sizeof(this.m_datatype_value)) return(false); //--- Всё успешно return true; }
Dopo aver scritto nel file il marcatore dei dati di partenza e il tipo di oggetto, vengono salvate a turno tutte le proprietà delle celle. L'unione deve essere salvata come struttura utilizzando FileWriteStruct().
Metodo per caricare le proprietà delle celle da un file:
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableCell::Load(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загружаем тип объекта if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type()) return(false); //--- Загружаем тип данных this.m_datatype=(ENUM_DATATYPE)::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем тип объекта в ячейке this.m_object_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем номер строки this.m_row=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем номер столбца this.m_col=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем точность представления данных this.m_digits=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем флаги отображения даты/времени this.m_time_flags=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем флаг отображения наименования цвета this.m_color_flag=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем флаг редактируемой ячейки this.m_editable=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем значение if(::FileReadStruct(file_handle,this.m_datatype_value)!=sizeof(this.m_datatype_value)) return(false); //--- Всё успешно return true; }
Dopo aver letto e controllato i marcatori di inizio dati e il tipo di oggetto, tutte le proprietà dell'oggetto vengono caricate a turno nello stesso ordine in cui sono state salvate. Le unioni vengono lette usando FileReadStruct().
Metodo che restituisce la descrizione dell'oggetto:
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableCell::Description(void) { return(::StringFormat("%s: Row %u, Col %u, %s <%s>Value: %s", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Row(),this.Col(), (this.m_editable ? "Editable" : "Uneditable"),this.DatatypeDescription(),this.Value())); }
Qui viene creata una riga a partire da alcuni parametri della cella e restituita, ad esempio, per double, in questo formato:
Table Cell: Row 2, Col 2, Uneditable <double>Value: 0.00
Metodo che restituisce la descrizione dell'oggetto al log:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableCell::Print(void) { ::Print(this.Description()); }
In questo caso, la descrizione dell'oggetto viene semplicemente stampata nel log.
//+------------------------------------------------------------------+ //| Класс строки таблицы | //+------------------------------------------------------------------+ class CTableRow : public CObject { protected: CTableCell m_cell_tmp; // Объект ячейки для поиска в списке CListObj m_list_cells; // Список ячеек uint m_index; // Индекс строки //--- Добавляет указанную ячейку в конец списка bool AddNewCell(CTableCell *cell); public: //--- (1) Устанавливает, (2) возвращает индекс строки void SetIndex(const uint index) { this.m_index=index; } uint Index(void) const { return this.m_index; } //--- Устанавливает позиции строки и колонки всем ячейкам void CellsPositionUpdate(void); //--- Создаёт новую ячейку и добавляет в конец списка CTableCell *CreateNewCell(const double value); CTableCell *CreateNewCell(const long value); CTableCell *CreateNewCell(const datetime value); CTableCell *CreateNewCell(const color value); CTableCell *CreateNewCell(const string value); //--- Возвращает (1) ячейку по индексу, (2) количество ячеек CTableCell *GetCell(const uint index) { return this.m_list_cells.GetNodeAtIndex(index); } uint CellsTotal(void) const { return this.m_list_cells.Total(); } //--- Устанавливает значение в указанную ячейку void CellSetValue(const uint index,const double value); void CellSetValue(const uint index,const long value); void CellSetValue(const uint index,const datetime value); void CellSetValue(const uint index,const color value); void CellSetValue(const uint index,const string value); //--- (1) назначает в ячейку, (2) снимает с ячейки назначенный объект void CellAssignObject(const uint index,CObject *object); void CellUnassignObject(const uint index); //--- (1) Удаляет (2) перемещает ячейку bool CellDelete(const uint index); bool CellMoveTo(const uint cell_index, const uint index_to); //--- Обнуляет данные ячеек строки void ClearData(void); //--- (1) Возвращает, (2) выводит в журнал описание объекта string Description(void); void Print(const bool detail, const bool as_table=false, const int cell_width=10); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_TABLE_ROW); } //--- Конструкторы/деструктор CTableRow(void) : m_index(0) {} CTableRow(const uint index) : m_index(index) {} ~CTableRow(void){} };
3. Classe della Riga della Tabella
Una riga di tabella è essenzialmente un elenco collegato di celle. La classe della riga deve supportare l'aggiunta, la cancellazione e il riordino delle celle dell'elenco in una nuova posizione. La classe deve avere un insieme di metodi minimo-sufficiente per gestire le celle dell'elenco.
Continuiamo a scrivere il codice nello stesso file. Solo una variabile è disponibile nei parametri della classe - l'indice della riga della tabella. Tutti gli altri sono metodi per lavorare con le proprietà delle righe e con un elenco delle sue celle.
Consideriamo i metodi della classe.
Metodo per confrontare due righe di tabella:
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CTableRow::Compare(const CObject *node,const int mode=0) const { const CTableRow *obj=node; return(this.Index()>obj.Index() ? 1 : this.Index()<obj.Index() ? -1 : 0); }
Poiché le righe possono essere confrontate solo in base al loro singolo parametro - l'indice della riga - questo confronto viene implementato qui. Questo metodo è necessario per ordinare le righe della tabella.
Metodi di sovraccarico per creare celle con diversi tipi di dati memorizzati:
//+------------------------------------------------------------------+ //| Создаёт новую double-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const double value) { //--- Создаём новый объект ячейки, хранящей значение с типом double CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,2); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую long-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const long value) { //--- Создаём новый объект ячейки, хранящей значение с типом long CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую datetime-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const datetime value) { //--- Создаём новый объект ячейки, хранящей значение с типом datetime CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,TIME_DATE|TIME_MINUTES|TIME_SECONDS); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую color-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const color value) { //--- Создаём новый объект ячейки, хранящей значение с типом color CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,true); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую string-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const string value) { //--- Создаём новый объект ячейки, хранящей значение с типом string CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; }
Cinque metodi per creare una nuova cella (double, long, datetime, color, string) e aggiungerla alla fine dell'elenco. I parametri aggiuntivi del formato di output dei dati nella cella sono impostati con valori predefiniti. Possono essere modificati dopo la creazione della cella. Innanzitutto, viene creato un nuovo oggetto cella, che viene poi aggiunto alla fine dell'elenco. Se l'oggetto non è stato creato, viene eliminato per evitare perdite di memoria.
Metodo che aggiunge una cella alla fine dell'elenco:
//+------------------------------------------------------------------+ //| Добавляет ячейку в конец списка | //+------------------------------------------------------------------+ bool CTableRow::AddNewCell(CTableCell *cell) { //--- Если передан пустой объект - сообщаем и возвращаем false if(cell==NULL) { ::PrintFormat("%s: Error. Empty CTableCell object passed",__FUNCTION__); return false; } //--- Устанавливаем индекс ячейки в списке и добавляем созданную ячейку в конец списка cell.SetPositionInTable(this.m_index,this.CellsTotal()); if(this.m_list_cells.Add(cell)==WRONG_VALUE) { ::PrintFormat("%s: Error. Failed to add cell (%u,%u) to list",__FUNCTION__,this.m_index,this.CellsTotal()); return false; } //--- Успешно return true; }
Ogni nuova cella creata viene sempre aggiunta alla fine dell'elenco. Quindi, può essere spostata nella posizione appropriata utilizzando i metodi della classe del modello della tabella, che verrà creata in seguito.
Metodi sovraccaricati per impostare i valori nella cella specificata:
//+------------------------------------------------------------------+ //| Устанавливает double-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const double value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает long-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const long value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает datetime-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const datetime value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает color-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const color value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает string-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const string value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); }
Utilizzando l'indice, otteniamo la cella desiderata dall'elenco e se ne imposta il valore. Se la cella non è modificabile, il metodo SetValue() dell'oggetto cella imposterà solo il tipo di valore che è stato impostato. Il valore stesso non verrà impostato.
Un metodo che assegna un oggetto a una cella:
//+------------------------------------------------------------------+ //| Назначает в ячейку объект | //+------------------------------------------------------------------+ void CTableRow::CellAssignObject(const uint index,CObject *object) { //--- Получаем из списка нужную ячейку и записываем в неё указатель на объект CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.AssignObject(object); }
Otteniamo un oggetto cella in base al suo indice e utilizziamo il metodo AssignObject() per assegnare un puntatore all'oggetto cella.
Metodo che annulla un oggetto assegnato per una cella:
//+------------------------------------------------------------------+ //| Отменяет для ячейки назначенный объект | //+------------------------------------------------------------------+ void CTableRow::CellUnassignObject(const uint index) { //--- Получаем из списка нужную ячейку и отменяем в ней указатель на объект и его тип CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.UnassignObject(); }
Otteniamo l'oggetto cella in base al suo indice e utilizziamo il metodo UnassignObject() per rimuovere il puntatore all'oggetto assegnato alla cella.
Metodo che elimina una cella:
//+------------------------------------------------------------------+ //| Удаляет ячейку | //+------------------------------------------------------------------+ bool CTableRow::CellDelete(const uint index) { //--- Удаляем ячейку в списке по индексу if(!this.m_list_cells.Delete(index)) return false; //--- Обновляем индексы для оставшихся ячеек в списке this.CellsPositionUpdate(); return true; }
Utilizzando il metodo Delete() della classe CList, cancelliamo la cella dall'elenco. Dopo l'eliminazione di una cella dall'elenco, gli indici delle celle rimanenti vengono modificati. Utilizzando il metodo CellsPositionUpdate(), aggiorniamo gli indici di tutte le celle rimanenti nell'elenco.
Metodo che sposta una cella nella posizione specificata:
//+------------------------------------------------------------------+ //| Перемещает ячейку на указанную позицию | //+------------------------------------------------------------------+ bool CTableRow::CellMoveTo(const uint cell_index,const uint index_to) { //--- Выбираем нужную ячейку по индексу в списке, делая её текущей CTableCell *cell=this.GetCell(cell_index); //--- Перемещаем текущую ячейку на указанную позицию в списке if(cell==NULL || !this.m_list_cells.MoveToIndex(index_to)) return false; //--- Обновляем индексы всех ячеек в списке this.CellsPositionUpdate(); return true; }
Affinché la classe CList possa operare su un oggetto, questo oggetto dell'elenco deve essere quello attuale. Diventa attuale, ad esempio, quando viene selezionato. Pertanto, per prima cosa si ottiene l'oggetto cella dall'elenco in base all'indice. La cella diventa quella corrente e quindi, utilizzando il metodo MoveToIndex() della classe CList, si sposta l'oggetto nella posizione desiderata dell'elenco. Dopo aver spostato con successo un oggetto in una nuova posizione, è necessario regolare gli indici degli oggetti rimanenti, utilizzando il metodo CellsPositionUpdate().
Metodo che imposta le posizioni di riga e colonna per tutte le celle dell'elenco:
//+------------------------------------------------------------------+ //| Устанавливает позиции строки и колонки всем ячейкам | //+------------------------------------------------------------------+ void CTableRow::CellsPositionUpdate(void) { //--- В цикле по всем ячейкам в списке for(int i=0;i<this.m_list_cells.Total();i++) { //--- получаем очередную ячейку и устанавливаем в неё индексы строки и столбца CTableCell *cell=this.GetCell(i); if(cell!=NULL) cell.SetPositionInTable(this.Index(),this.m_list_cells.IndexOf(cell)); } }
La classe CList consente di trovare l'indice dell'oggetto corrente nell'elenco. A tal fine, l'oggetto deve essere selezionato. In questo caso, cicliamo tutti gli oggetti cella dell'elenco, si seleziona ciascuno di essi e si scopre il suo indice utilizzando il metodo IndexOf() della classe CList. L'indice della riga e l'indice della cella trovata vengono impostati sull'oggetto cella utilizzando il metodo SetPositionInTable().
Metodo che reimposta i dati delle celle della riga:
//+------------------------------------------------------------------+ //| Обнуляет данные ячеек строки | //+------------------------------------------------------------------+ void CTableRow::ClearData(void) { //--- В цикле по всем ячейкам в списке for(uint i=0;i<this.CellsTotal();i++) { //--- получаем очередную ячейку и устанавливаем в неё пустое значение CTableCell *cell=this.GetCell(i); if(cell!=NULL) cell.ClearData(); } }
Nel ciclo, azzerare ogni cella successiva dell'elenco utilizzando il metodo dell'oggetto cella ClearData(). Per i dati stringa, nella cella viene scritta una riga vuota, mentre per i dati numerici viene scritto zero.
Metodo che restituisce la descrizione dell'oggetto:
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableRow::Description(void) { return(::StringFormat("%s: Position %u, Cells total: %u", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Index(),this.CellsTotal())); }
Una riga viene raccolta dalle proprietà e dai dati dell'oggetto e restituita nel seguente formato, ad esempio:
Table Row: Position 1, Cells total: 4:
Metodo che restituisce la descrizione dell'oggetto al log:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableRow::Print(const bool detail, const bool as_table=false, const int cell_width=10) { //--- Количество ячеек int total=(int)this.CellsTotal(); //--- Если вывод в табличном виде string res=""; if(as_table) { //--- создаём строку таблицы из значений всех ячеек string head=" Row "+(string)this.Index(); string res=::StringFormat("|%-*s |",cell_width,head); for(int i=0;i<total;i++) { CTableCell *cell=this.GetCell(i); if(cell==NULL) continue; res+=::StringFormat("%*s |",cell_width,cell.Value()); } //--- Выводим строку в журнал ::Print(res); return; } //--- Выводим заголовок в виде описания строки ::Print(this.Description()+(detail ? ":" : "")); //--- Если детализированное описание if(detail) { //--- Вывод не в табличном виде //--- В цикле по спискук ячеек строки for(int i=0; i<total; i++) { //--- получаем текущую ячейку и добавляем в итоговую строку её описание CTableCell *cell=this.GetCell(i); if(cell!=NULL) res+=" "+cell.Description()+(i<total-1 ? "\n" : ""); } //--- Выводим в журнал созданную в цикле строку ::Print(res); } }
Per la visualizzazione di dati non tabellari nel log, l'intestazione viene visualizzata per prima nel log come descrizione della riga. Quindi, se il flag di visualizzazione dettagliata è impostato, le descrizioni di ciascuna cella vengono visualizzate nel log in un ciclo attraverso l'elenco delle celle.
Di conseguenza, la visualizzazione dettagliata di una riga della tabella nel log appare, ad esempio (per una vista non tabellare):
Table Row: Position 0, Cells total: 4: Table Cell: Row 0, Col 0, Editable <long>Value: 10 Table Cell: Row 0, Col 1, Editable <long>Value: 21 Table Cell: Row 0, Col 2, Editable <long>Value: 32 Table Cell: Row 0, Col 3, Editable <long>Value: 43
Per la visualizzazione tabellare, il risultato sarà, ad esempio, il seguente:
| Row 0 | 0 | 1 | 2 | 3 |
Metodo che salva una riga della tabella in un file:
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableRow::Save(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long)) return(false); //--- Сохраняем тип объекта if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем индекс if(::FileWriteInteger(file_handle,this.m_index,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем список ячеек if(!this.m_list_cells.Save(file_handle)) return(false); //--- Успешно return true; }
Salvare i marcatori di inizio dati, quindi il tipo di oggetto. È l'intestazione standard di ogni oggetto nel file. Segue nel file una voce delle proprietà dell'oggetto. In questo caso, viene salvato nel file l'indice della riga e poi l'elenco delle celle.
Metodo di caricamento della riga da un file:
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableRow::Load(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загружаем тип объекта if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type()) return(false); //--- Загружаем индекс this.m_index=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем список ячеек if(!this.m_list_cells.Load(file_handle)) return(false); //--- Успешно return true; }
Qui tutto è nello stesso ordine in cui è stato salvato. Innanzitutto, vengono caricati e controllati il marcatore di inizio dati e il tipo di oggetto. Quindi vengono caricati l'indice di riga e l'intero elenco di celle.
4. Classe modelli di tabella
Nella sua forma più semplice, il modello di tabella è un elenco collegato di righe, che a loro volta contengono elenchi collegati di celle. Il nostro modello, che creeremo oggi, riceverà in ingresso un array bidimensionale di uno dei cinque tipi (double, long, datetime, color, string) e costruirà una tabella virtuale a partire da esso. Inoltre, estenderemo questa classe per accettare altri argomenti per creare tabelle da altri dati di input. Lo stesso modello sarà considerato il modello predefinito.
Continuiamo a scrivere il codice nello stesso file \MQL5\Scripts\TableModel\TableModelTest.mq5.
La classe del modello della tabella è essenzialmente un elenco collegato di righe con metodi per la gestione delle righe, colonne e celle. Scrivere il corpo della classe con tutte le variabili e i metodi, quindi considerare i metodi dichiarati della classe:
//+------------------------------------------------------------------+ //| Класс модели таблицы | //+------------------------------------------------------------------+ class CTableModel : public CObject { protected: CTableRow m_row_tmp; // Объект строки для поиска в списке CListObj m_list_rows; // Список строк таблицы //--- Создаёт модель таблицы из двумерного массива template<typename T> void CreateTableModel(T &array[][]); //--- Возвращает корректный тип данных ENUM_DATATYPE GetCorrectDatatype(string type_name) { return ( //--- Целочисленное значение type_name=="bool" || type_name=="char" || type_name=="uchar" || type_name=="short"|| type_name=="ushort" || type_name=="int" || type_name=="uint" || type_name=="long" || type_name=="ulong" ? TYPE_LONG : //--- Вещественное значение type_name=="float"|| type_name=="double" ? TYPE_DOUBLE : //--- Значение даты/времени type_name=="datetime" ? TYPE_DATETIME : //--- Значение цвета type_name=="color" ? TYPE_COLOR : /*--- Строковое значение */ TYPE_STRING ); } //--- Создаёт и добавляет новую пустую строку в конец списка CTableRow *CreateNewEmptyRow(void); //--- Добавляет строку в конец списка bool AddNewRow(CTableRow *row); //--- Устанавливает позиции строки и колонки всем ячейкам таблицы void CellsPositionUpdate(void); public: //--- Возвращает (1) ячейку, (2) строку по индексу, количество (3) строк, ячеек (4) в указанной строке, (5) в таблице CTableCell *GetCell(const uint row, const uint col); CTableRow *GetRow(const uint index) { return this.m_list_rows.GetNodeAtIndex(index); } uint RowsTotal(void) const { return this.m_list_rows.Total(); } uint CellsInRow(const uint index); uint CellsTotal(void); //--- Устанавливает (1) значение, (2) точность, (3) флаги отображения времени, (4) флаг отображения имён цветов в указанную ячейку template<typename T> void CellSetValue(const uint row, const uint col, const T value); void CellSetDigits(const uint row, const uint col, const int digits); void CellSetTimeFlags(const uint row, const uint col, const uint flags); void CellSetColorNamesFlag(const uint row, const uint col, const bool flag); //--- (1) Назначает, (2) отменяет объект в ячейке void CellAssignObject(const uint row, const uint col,CObject *object); void CellUnassignObject(const uint row, const uint col); //--- (1) Удаляет (2) перемещает ячейку bool CellDelete(const uint row, const uint col); bool CellMoveTo(const uint row, const uint cell_index, const uint index_to); //--- (1) Возвращает, (2) выводит в журнал описание ячейки, (3) назначенный в ячейку объект string CellDescription(const uint row, const uint col); void CellPrint(const uint row, const uint col); CObject *CellGetObject(const uint row, const uint col); public: //--- Создаёт новую строку и (1) добавляет в конец списка, (2) вставляет в указанную позицию списка CTableRow *RowAddNew(void); CTableRow *RowInsertNewTo(const uint index_to); //--- (1) Удаляет (2) перемещает строку, (3) очищает данные строки bool RowDelete(const uint index); bool RowMoveTo(const uint row_index, const uint index_to); void RowResetData(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание строки string RowDescription(const uint index); void RowPrint(const uint index,const bool detail); //--- (1) Удаляет (2) перемещает столбец, (3) очищает данные столбца bool ColumnDelete(const uint index); bool ColumnMoveTo(const uint row_index, const uint index_to); void ColumnResetData(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание таблицы string Description(void); void Print(const bool detail); void PrintTable(const int cell_width=10); //--- (1) Очищает данные, (2) уничтожает модель void ClearData(void); void Destroy(void); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_TABLE_MODEL); } //--- Конструкторы/деструктор CTableModel(void){} CTableModel(double &array[][]) { this.CreateTableModel(array); } CTableModel(long &array[][]) { this.CreateTableModel(array); } CTableModel(datetime &array[][]) { this.CreateTableModel(array); } CTableModel(color &array[][]) { this.CreateTableModel(array); } CTableModel(string &array[][]) { this.CreateTableModel(array); } ~CTableModel(void){} };
In pratica, esiste un solo oggetto per un elenco collegato di righe di tabelle e metodi per gestire righe, celle e colonne. E costruttori che accettano diversi tipi di array bidimensionali.
Al costruttore della classe viene passato un array e viene richiamato un metodo per creare un modello di tabella:
//+------------------------------------------------------------------+ //| Создаёт модель таблицы из двумерного массива | //+------------------------------------------------------------------+ template<typename T> void CTableModel::CreateTableModel(T &array[][]) { //--- Получаем из свойств массива количество строк и столбцов таблицы int rows_total=::ArrayRange(array,0); int cols_total=::ArrayRange(array,1); //--- В цикле по индексам строк for(int r=0; r<rows_total; r++) { //--- создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); //--- Если строка создана и добавлена в список, if(row!=NULL) { //--- В цикле по количеству ячеек в строке //--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки for(int c=0; c<cols_total; c++) row.CreateNewCell(array[r][c]); } } }
La logica del metodo è spiegata nei commenti. La prima dimensione dell'array è costituita dalle righe della tabella, la seconda dalle celle di ciascuna riga. Quando si creano le celle, esse utilizzano lo stesso tipo di dati che viene passato al metodo.
In questo modo, possiamo creare diversi modelli di tabella, le cui celle inizialmente memorizzano diversi tipi di dati (double, long, datetime, color e string). In seguito, dopo aver creato il modello di tabella, è possibile modificare i tipi di dati memorizzati nelle celle.
Un metodo che crea una nuova riga vuota e la aggiunge alla fine dell'elenco:
//+------------------------------------------------------------------+ //| Создаёт новую пустую строку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableRow *CTableModel::CreateNewEmptyRow(void) { //--- Создаём новый объект строки CTableRow *row=new CTableRow(this.m_list_rows.Total()); if(row==NULL) { ::PrintFormat("%s: Error. Failed to create new row at position %u",__FUNCTION__, this.m_list_rows.Total()); return NULL; } //--- Если строку не удалось добавить в список - удаляем созданный новый объект и возвращаем NULL if(!this.AddNewRow(row)) { delete row; return NULL; } //--- Успешно - возвращаем указатель на созданный объект return row; }
Il metodo crea un nuovo oggetto della classe CTableRow e lo aggiunge alla fine dell'elenco delle righe utilizzando il metodo AddNewRow(). Se si verifica un errore di aggiunta, il nuovo oggetto creato viene eliminato e viene restituito NULL. In caso di successo, il metodo restituisce un puntatore alla riga appena aggiunta all'elenco.
Metodo che aggiunge un oggetto riga alla fine dell'elenco:
//+------------------------------------------------------------------+ //| Добавляет строку в конец списка | //+------------------------------------------------------------------+ bool CTableModel::AddNewRow(CTableRow *row) { //--- Если передан пустой объект - сообщаем об этом и возвращаем false if(row==NULL) { ::PrintFormat("%s: Error. Empty CTableRow object passed",__FUNCTION__); return false; } //--- Устанавливаем строке её индекс в списке и добавляем её в конец списка row.SetIndex(this.RowsTotal()); if(this.m_list_rows.Add(row)==WRONG_VALUE) { ::PrintFormat("%s: Error. Failed to add row (%u) to list",__FUNCTION__,row.Index()); return false; } //--- Успешно return true; }
Entrambi i metodi discussi sopra si trovano nella sezione protected della classe, funzionano in coppia e sono utilizzati internamente quando si aggiungono nuove righe alla tabella.
Metodo per creare una nuova riga e aggiungerla alla fine dell'elenco:
//+------------------------------------------------------------------+ //| Создаёт новую строку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableRow *CTableModel::RowAddNew(void) { //--- Создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); if(row==NULL) return NULL; //--- Создаём ячейки по количеству ячеек первой строки for(uint i=0;i<this.CellsInRow(0);i++) row.CreateNewCell(0.0); row.ClearData(); //--- Успешно - возвращаем указатель на созданный объект return row; }
Questo è un metodo pubblico. Viene utilizzato per aggiungere una nuova riga con celle alla tabella. Il numero di celle per la riga creata viene preso dalla prima riga della tabella.
Metodo per la creazione e l'aggiunta di una nuova riga in una posizione specifica dell'elenco:
//+------------------------------------------------------------------+ //| Создаёт и добавляет новую строку в указанную позицию списка | //+------------------------------------------------------------------+ CTableRow *CTableModel::RowInsertNewTo(const uint index_to) { //--- Создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); if(row==NULL) return NULL; //--- Создаём ячейки по количеству ячеек первой строки for(uint i=0;i<this.CellsInRow(0);i++) row.CreateNewCell(0.0); row.ClearData(); //--- Смещаем строку на позицию index_to this.RowMoveTo(this.m_list_rows.IndexOf(row),index_to); //--- Успешно - возвращаем указатель на созданный объект return row; }
A volte è necessario inserire una nuova riga non alla fine dell'elenco, ma tra quelle esistenti. Questo metodo crea prima una nuova riga alla fine dell'elenco, la riempie con le celle, le pulisce e quindi sposta la riga nella posizione desiderata.
Metodo che imposta i valori nella cella specificata:
//+------------------------------------------------------------------+ //| Устанавливает значение в указанную ячейку | //+------------------------------------------------------------------+ template<typename T> void CTableModel::CellSetValue(const uint row,const uint col,const T value) { //--- Получаем ячейку по индексам строки и столбца CTableCell *cell=this.GetCell(row,col); if(cell==NULL) return; //--- Получаем корректный тип устанавливаемых данных (double, long, datetime, color, string) ENUM_DATATYPE type=this.GetCorrectDatatype(typename(T)); //--- В зависимости от типа данных вызываем соответствующий типу данных //--- метод ячейки для установки значения, явно указывая требуемый тип switch(type) { case TYPE_DOUBLE : cell.SetValue((double)value); break; case TYPE_LONG : cell.SetValue((long)value); break; case TYPE_DATETIME: cell.SetValue((datetime)value); break; case TYPE_COLOR : cell.SetValue((color)value); break; case TYPE_STRING : cell.SetValue((string)value); break; default : break; } }
Per prima cosa, si ottiene un puntatore alla cella desiderata tramite le coordinate della riga e della colonna, quindi si imposta un valore su di essa. Qualunque sia il valore passato al metodo per installarlo nella cella, il metodo selezionerà solo il tipo corretto per l'installazione - double, long, datetime, color o string.
Metodo che imposta la precisione della visualizzazione dei dati nella cella specificata:
//+------------------------------------------------------------------+ //| Устанавливает точность отображения данных в указанную ячейку | //+------------------------------------------------------------------+ void CTableModel::CellSetDigits(const uint row,const uint col,const int digits) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.SetDigits(digits); }
Il metodo è rilevante solo per le celle che memorizzano il tipo di valore reale. Viene utilizzato per specificare il numero di cifre decimali per il valore visualizzato dalla cella.
Metodo che imposta i flag di visualizzazione dell'ora nella cella specificata:
//+------------------------------------------------------------------+ //| Устанавливает флаги отображения времени в указанную ячейку | //+------------------------------------------------------------------+ void CTableModel::CellSetTimeFlags(const uint row,const uint col,const uint flags) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.SetDatetimeFlags(flags); }
Rilevante per le celle che visualizzano i valori datetime. Imposta il formato di visualizzazione dell'ora per la cella (uno tra TIME_DATE | TIME_MINUTES | TIME_SECONDS, o una loro combinazione).
TIME_DATE ottiene il risultato come " yyyy.mm.dd " ,
TIME_MINUTES ottiene il risultato come " hh:mi " ,
TIME_SECONDS ottiene il risultato come " hh:mi:ss ".
Metodo che imposta i flag di visualizzazione del nome del colore alla cella specificata:
//+------------------------------------------------------------------+ //| Устанавливает флаг отображения имён цветов в указанную ячейку | //+------------------------------------------------------------------+ void CTableModel::CellSetColorNamesFlag(const uint row,const uint col,const bool flag) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.SetColorNameFlag(flag); }
Rilevante solo per le celle che visualizzano valori color. Indica la necessità di visualizzare i nomi dei colori se il colore memorizzato nella cella è presente nella tabella dei colori.
Metodo che assegna un oggetto a una cella:
//+------------------------------------------------------------------+ //| Назначает объект в ячейку | //+------------------------------------------------------------------+ void CTableModel::CellAssignObject(const uint row,const uint col,CObject *object) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.AssignObject(object); }
Metodo che annulla l'assegnazione di un oggetto ad una cella:
//+------------------------------------------------------------------+ //| Отменяет назначение объекта в ячейке | //+------------------------------------------------------------------+ void CTableModel::CellUnassignObject(const uint row,const uint col) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.UnassignObject(); }
I due metodi presentati sopra consentono di assegnare un oggetto a una cella o di rimuoverne l'assegnazione. L'oggetto deve essere un discendente della classe CObject. Nel contesto degli articoli sulle tabelle, un oggetto può essere, ad esempio, uno degli oggetti noti dell'enumerazione ENUM_OBJECT_TYPE. Al momento, l'elenco contiene solo oggetti cella, righe e modelli di tabella. Assegnarli a una cella non ha senso. Ma l'enumerazione si espanderà quando scriveremo articoli sul componente View, dove verranno creati i controlli. È opportuno assegnare a una cella, ad esempio, il controllo "elenco a discesa".
Metodo che elimina la cella specificata:
//+------------------------------------------------------------------+ //| Удаляет ячейку | //+------------------------------------------------------------------+ bool CTableModel::CellDelete(const uint row,const uint col) { //--- Получаем строку по индексу и возвращаем результат удаления ячейки из списка CTableRow *row_obj=this.GetRow(row); return(row_obj!=NULL ? row_obj.CellDelete(col) : false); }
Il metodo ottiene l'oggetto riga in base al suo indice e chiama il suo metodo per eliminare la cella specificata. Per una singola cella in una singola riga, il metodo non ha ancora senso, poiché ridurrà il numero di celle in una sola riga della tabella. In questo modo le celle non saranno più sincronizzate con le righe vicine. Finora non esiste un'elaborazione di questo tipo di cancellazione, in cui è necessario "espandere" la cella accanto a quella cancellata fino alla dimensione di due celle, in modo da non interrompere la struttura della tabella. Tuttavia, questo metodo viene utilizzato come parte del metodo di eliminazione delle colonne della tabella, in cui le celle di tutte le righe vengono eliminate in una sola volta senza violare l'integrità dell'intera tabella.
Metodo per spostare una cella di tabella:
//+------------------------------------------------------------------+ //| Перемещает ячейку | //+------------------------------------------------------------------+ bool CTableModel::CellMoveTo(const uint row,const uint cell_index,const uint index_to) { //--- Получаем строку по индексу и возвращаем результат перемещения ячейки на новую позицию CTableRow *row_obj=this.GetRow(row); return(row_obj!=NULL ? row_obj.CellMoveTo(cell_index,index_to) : false); }
Ottenere l'oggetto riga in base al suo indice e richiamare il suo metodo per eliminare la cella specificata.
Metodo che restituisce il numero di celle nella riga specificata:
//+------------------------------------------------------------------+ //| Возвращает количество ячеек в указанной строке | //+------------------------------------------------------------------+ uint CTableModel::CellsInRow(const uint index) { CTableRow *row=this.GetRow(index); return(row!=NULL ? row.CellsTotal() : 0); }
Ottenere la riga per indice e restituire il numero di celle in essa contenute richiamando il metodo CellsTotal() row.
Metodo che restituisce il numero di celle della tabella:
//+------------------------------------------------------------------+ //| Возвращает количество ячеек в таблице | //+------------------------------------------------------------------+ uint CTableModel::CellsTotal(void) { //--- подсчёт ячеек в цикле по строкам (медленно при большом количестве строк) uint res=0, total=this.RowsTotal(); for(int i=0; i<(int)total; i++) { CTableRow *row=this.GetRow(i); res+=(row!=NULL ? row.CellsTotal() : 0); } return res; }
Il metodo passa in rassegna tutte le righe della tabella e aggiunge il numero di celle di ogni riga al risultato totale, che viene restituito. Con un numero elevato di righe nella tabella, tale conteggio può essere lento. Dopo aver creato tutti i metodi che influenzano il numero di celle nella tabella, tenerli in considerazione solo quando il loro numero cambia.
Metodo che restituisce la cella di tabella specificata:
//+------------------------------------------------------------------+ //| Возвращает указанную ячейку таблицы | //+------------------------------------------------------------------+ CTableCell *CTableModel::GetCell(const uint row,const uint col) { //--- Получаем строку по индексу row и возвращаем по индексу col ячейку строки CTableRow *row_obj=this.GetRow(row); return(row_obj!=NULL ? row_obj.GetCell(col) : NULL); }
Ottenere la riga in base all'indice di riga e restituire il puntatore all'oggetto cella in base all'indice di colonna utilizzando il metodo GetCell() dell'oggetto riga.
Metodo che restituisce la descrizione della cella:
//+------------------------------------------------------------------+ //| Возвращает описание ячейки | //+------------------------------------------------------------------+ string CTableModel::CellDescription(const uint row,const uint col) { CTableCell *cell=this.GetCell(row,col); return(cell!=NULL ? cell.Description() : ""); }
Ottenere la riga in base all'indice, ricava la cella dalla riga e restituisce la sua descrizione.
Metodo che visualizza la descrizione della cella nel log:
//+------------------------------------------------------------------+ //| Выводит в журнал описание ячейки | //+------------------------------------------------------------------+ void CTableModel::CellPrint(const uint row,const uint col) { //--- Получаем ячейку по индексу строки и колонки и возвращаем её описание CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.Print(); }
Ottenere un puntatore alla cella tramite gli indici di riga e colonna e, utilizzando il metodo Print() dell'oggetto cella, visualizzarne la descrizione nel log.
Metodo che elimina la riga specificata:
//+------------------------------------------------------------------+ //| Удаляет строку | //+------------------------------------------------------------------+ bool CTableModel::RowDelete(const uint index) { //--- Удаляем строку из списка по индексу if(!this.m_list_rows.Delete(index)) return false; //--- После удаления строки необходимо обновить все индексы всех ячеек таблицы this.CellsPositionUpdate(); return true; }
Utilizzando il metodo Delete() della classe CList, si elimina dall'elenco l'oggetto riga per indice. Dopo l'eliminazione della riga, gli indici delle righe rimanenti e delle celle in esse contenute non corrispondono alla realtà e devono essere modificati con il metodo CellsPositionUpdate().
Metodo che sposta una riga nella posizione specificata:
//+------------------------------------------------------------------+ //| Перемещает строку на указанную позицию | //+------------------------------------------------------------------+ bool CTableModel::RowMoveTo(const uint row_index,const uint index_to) { //--- Получаем строку по индексу, делая её текущей CTableRow *row=this.GetRow(row_index); //--- Перемещаем текущую строку на указанную позицию в списке if(row==NULL || !this.m_list_rows.MoveToIndex(index_to)) return false; //--- После перемещения строки необходимо обновить все индексы всех ячеек таблицы this.CellsPositionUpdate(); return true; }
Nella classe CList, molti metodi lavorano con l'oggetto dell’elenco corrente. Ottenere un puntatore alla riga desiderata, rendendola quella corrente e spostarla nella posizione desiderata utilizzando il metodo MoveToIndex() della classe CList. Dopo aver spostato la riga in una nuova posizione, è necessario aggiornare gli indici per le righe rimanenti, che viene fatto con il metodo CellsPositionUpdate().
Metodo che imposta le posizioni di riga e colonna per tutte le celle:
//+------------------------------------------------------------------+ //| Устанавливает позиции строки и колонки всем ячейкам | //+------------------------------------------------------------------+ void CTableModel::CellsPositionUpdate(void) { //--- В цикле по списку строк for(int i=0;i<this.m_list_rows.Total();i++) { //--- получаем очередную строку CTableRow *row=this.GetRow(i); if(row==NULL) continue; //--- устанавливаем строке индекс, найденный методом IndexOf() списка row.SetIndex(this.m_list_rows.IndexOf(row)); //--- Обновляем индексы позиций ячеек строки row.CellsPositionUpdate(); } }
Scorrere l'elenco di tutte le righe della tabella, selezionare ogni riga successiva e impostare un indice corretto, trovato con il metodo IndexOf() della classe CList. Richiamare quindi il metodo di riga CellsPositionUpdate(), che imposta l'indice corretto per ogni cella della riga.
Metodo che cancella i dati di tutte le celle di una riga:
//+------------------------------------------------------------------+ //| Очищает строку (только данные в ячйках) | //+------------------------------------------------------------------+ void CTableModel::RowResetData(const uint index) { //--- Получаем строку из списка и очищаем данные ячеек строки методом ClearData() CTableRow *row=this.GetRow(index); if(row!=NULL) row.ClearData(); }
Ogni cella della riga viene reimpostata su un valore "vuoto". Per ora, a scopo di semplificazione, il valore vuoto per le celle numeriche è zero, ma questo verrà modificato in seguito, poiché anche lo zero è un valore che deve essere visualizzato nella cella. E la reimpostazione di un valore implica la visualizzazione di un campo di cella vuoto.
Metodo che cancella i dati di tutte le celle della tabella:
//+------------------------------------------------------------------+ //| Очищает таблицу (данные всех ячеек) | //+------------------------------------------------------------------+ void CTableModel::ClearData(void) { //--- В цикле по всем строкам таблицы очищаем данные каждой строки for(uint i=0;i<this.RowsTotal();i++) this.RowResetData(i); }
Passare attraverso tutte le righe della tabella e per ogni riga richiamare il metodo RowResetData() discusso in precedenza.
Metodo che restituisce la descrizione della riga:
//+------------------------------------------------------------------+ //| Возвращает описание строки | //+------------------------------------------------------------------+ string CTableModel::RowDescription(const uint index) { //--- Получаем строку по индексу и возвращаем её описание CTableRow *row=this.GetRow(index); return(row!=NULL ? row.Description() : ""); }
Ottenere un puntatore alla riga per indice e restituire la sua descrizione.
Metodo che visualizza la descrizione della riga nel log:
//+------------------------------------------------------------------+ //| Выводит в журнал описание строки | //+------------------------------------------------------------------+ void CTableModel::RowPrint(const uint index,const bool detail) { CTableRow *row=this.GetRow(index); if(row!=NULL) row.Print(detail); }
Ottenere un puntatore alla riga e chiamare il metodo Print() dell'oggetto ricevuto.
Metodo che elimina le colonne della tabella:
//+------------------------------------------------------------------+ //| Удаляет столбец | //+------------------------------------------------------------------+ bool CTableModel::ColumnDelete(const uint index) { bool res=true; for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) res &=row.CellDelete(index); } return res; }
In un ciclo che attraversa tutte le righe della tabella, ottenere ogni riga successiva ed eliminare la cella richiesta in essa in base all'indice colonna. Questo elimina tutte le celle della tabella che hanno gli stessi indici colonna.
Metodo che sposta una colonna della tabella:
//+------------------------------------------------------------------+ //| Перемещает столбец | //+------------------------------------------------------------------+ bool CTableModel::ColumnMoveTo(const uint col_index,const uint index_to) { bool res=true; for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) res &=row.CellMoveTo(col_index,index_to); } return res; }
In un ciclo che attraversa tutte le righe della tabella, si ottiene ogni riga successiva e si sposta la cella richiesta in una nuova posizione. Questo sposta tutte le celle della tabella che hanno lo stesso indice colonna.
Metodo che cancella i dati delle celle delle colonne:
//+------------------------------------------------------------------+ //| Очищает данные столбца | //+------------------------------------------------------------------+ void CTableModel::ColumnResetData(const uint index) { //--- В цикле по всем строкам таблицы for(uint i=0;i<this.RowsTotal();i++) { //--- получаем из каждой строки ячейку с индексом столбца и очищаем её CTableCell *cell=this.GetCell(i, index); if(cell!=NULL) cell.ClearData(); } }
In un ciclo che attraversa tutte le righe della tabella, ottenere ogni riga e cancellare i dati nella cella richiesta. Questo cancella tutte le celle della tabella che hanno lo stesso indice colonna.
Metodo che restituisce la descrizione dell'oggetto:
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableModel::Description(void) { return(::StringFormat("%s: Rows %u, Cells in row %u, Cells Total %u", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.RowsTotal(),this.CellsInRow(0),this.CellsTotal())); }
Una riga viene creata e restituisce alcuni parametri del modello della tabella in questo formato:
Table Model: Rows 4, Cells in row 4, Cells Total 16:
Metodo che restituisce la descrizione dell'oggetto al log:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableModel::Print(const bool detail) { //--- Выводим в журнал заголовок ::Print(this.Description()+(detail ? ":" : "")); //--- Если детализированное описание, if(detail) { //--- В цикле по всем строкам таблицы for(uint i=0; i<this.RowsTotal(); i++) { //--- получаем очередную строку и выводим в журнал её детализированное описание CTableRow *row=this.GetRow(i); if(row!=NULL) row.Print(true,false); } } }
In primo luogo, viene stampata l'intestazione come descrizione del modello e poi, se il flag di output è impostato su dettagliato, nel ciclo vengono stampate le descrizioni dettagliate di tutte le righe del modello di tabella.
Metodo che restituisce la descrizione dell'oggetto nel registro sotto forma di tabella:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта в табличном виде | //+------------------------------------------------------------------+ void CTableModel::PrintTable(const int cell_width=10) { //--- Получаем указатель на первую строку (индекс 0) CTableRow *row=this.GetRow(0); if(row==NULL) return; //--- По количеству ячеек первой строки таблицы создаём строку заголовка таблицы uint total=row.CellsTotal(); string head=" n/n"; string res=::StringFormat("|%*s |",cell_width,head); for(uint i=0;i<total;i++) { if(this.GetCell(0, i)==NULL) continue; string cell_idx=" Column "+(string)i; res+=::StringFormat("%*s |",cell_width,cell_idx); } //--- Выводим строку заголовка в журнал ::Print(res); //--- Пройдём в цикле по всем строкам таблицы и распечатаем их в табличном виде for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) row.Print(true,true,cell_width); } }
Per prima cosa, in base al numero di celle della prima riga della tabella, creare e stampare l'intestazione della tabella con i nomi delle colonne della tabella nel log. Quindi, passare in rassegna tutte le righe della tabella in un ciclo e stampare ciascuna di esse in forma tabellare.
Metodo che distrugge il modello di tabella:
//+------------------------------------------------------------------+ //| Уничтожает модель | //+------------------------------------------------------------------+ void CTableModel::Destroy(void) { //--- Очищаем список строк m_list_rows.Clear(); }
L'elenco delle righe della tabella viene semplicemente cancellato e tutti gli oggetti vengono distrutti con il metodo Clear() della classe CList.
Metodo per salvare il modello di tabella su file:
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableModel::Save(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long)) return(false); //--- Сохраняем тип объекта if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем список строк if(!this.m_list_rows.Save(file_handle)) return(false); //--- Успешно return true; }
Dopo aver salvato il marcatore di inizio dati e il tipo di elenco, salvare l'elenco di righe nel file utilizzando il metodo Save() della classe CList.
Metodo per caricare il modello di tabella da un file:
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableModel::Load(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загружаем тип объекта if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type()) return(false); //--- Загружаем список строк if(!this.m_list_rows.Load(file_handle)) return(false); //--- Успешно return true; }
Dopo aver caricato e controllato il marcatore di inizio dati e il tipo di elenco, caricare l'elenco di righe dal file usando il metodo Load() della classe CListObj, discusso all'inizio dell'articolo.
Tutte le classi per la creazione di un modello di tabella sono pronte. Ora scriviamo uno script per verificare il funzionamento del modello.
Verifica del risultato.
Continuare a scrivere il codice nello stesso file. Scrivete uno script in cui creeremo un array bidimensionale 4x4 (4 righe di 4 celle ciascuna) con il tipo, ad esempio, long. Quindi, aprire un file per scrivervi i dati del modello di tabella e caricare i dati nella tabella dal file. Creare un modello di tabella e verificare il funzionamento di alcuni dei suoi metodi.
Ogni volta che la tabella viene modificata, si registrerà il risultato ricevuto utilizzando la funzione TableModelPrint(), che seleziona come stampare il modello della tabella. Se il valore della macro PRINT_AS_TABLE è true, la registrazione viene eseguita con il metodo PrintTable() della classe CTableModel, se il valore è false - con il metodo Print() della stessa classe.
In uno script, creare un modello di tabella, stamparlo in forma tabellare e salvare il modello in un file. Quindi aggiungere righe, eliminare colonne e modificare i permessi di modifica...
Quindi scaricare nuovamente dal file la versione originale della tabella e stampare il risultato.
#define PRINT_AS_TABLE true // Распечатывать модель как таблицу //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем и заполняем массив с размерностью 4x4 //--- Тип массива может быть double, long, datetime, color, string long array[4][4]={{ 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, {13, 14, 15, 16}}; //--- Создаём модель таблицы из вышесозданного long-массива array 4x4 CTableModel *tm=new CTableModel(array); //--- Если модель не создана - уходим if(tm==NULL) return; //--- Распечатаем модель в табличном виде Print("The table model has been successfully created:"); tm.PrintTable(); //--- Удалим объект модели таблицы delete tm; }
Di conseguenza, nel log si ottiene il seguente risultato dello script:
The table model has been successfully created: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 |
Per testare il lavoro con il modello di tabella, l'aggiunta, l'eliminazione e lo spostamento di righe e colonne, il lavoro con un file, completate lo script:
#define PRINT_AS_TABLE true // Распечатывать модель как таблицу //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем и заполняем массив с размерностью 4x4 //--- Тип массива может быть double, long, datetime, color, string long array[4][4]={{ 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, {13, 14, 15, 16}}; //--- Создаём модель таблицы из вышесозданного long-массива array 4x4 CTableModel *tm=new CTableModel(array); //--- Если модель не создана - уходим if(tm==NULL) return; //--- Распечатаем модель в табличном виде Print("The table model has been successfully created:"); tm.PrintTable(); //--- Проверим работу с файлами и функционал модели таблицы //--- Открываем файл для записи в него данных модели таблицы int handle=FileOpen(MQLInfoString(MQL_PROGRAM_NAME)+".bin",FILE_READ|FILE_WRITE|FILE_BIN|FILE_COMMON); if(handle==INVALID_HANDLE) return; //--- Сохраним в файл оригинальную созданную таблицу if(tm.Save(handle)) Print("\nThe table model has been successfully saved to file."); //--- Теперь вставим в таблицу новую строку в позицию 2 //--- Получим последнюю ячейку созданной строки и сделаем её нередактируемой //--- Распечатаем в журнале изменённую модель таблицы if(tm.RowInsertNewTo(2)) { Print("\nInsert a new row at position 2 and set cell 3 to non-editable"); CTableCell *cell=tm.GetCell(2,3); if(cell!=NULL) cell.SetEditable(false); TableModelPrint(tm); } //--- Теперь удалим столбец таблицы с индексом 1 и //--- распечатаем в журнале полученную модель таблицы if(tm.ColumnDelete(1)) { Print("\nRemove column from position 1"); TableModelPrint(tm); } //--- При сохранении данных таблицы файловый указатель был смещён на последние записанные данные //--- Поставим указатель в начало файла, загрузим ранее сохранённую оригинальную таблицу и распечатаем её if(FileSeek(handle,0,SEEK_SET) && tm.Load(handle)) { Print("\nLoad the original table view from the file:"); TableModelPrint(tm); } //--- Закроем открытый файл и удалим объект модели таблицы FileClose(handle); delete tm; } //+------------------------------------------------------------------+ //| Распечатывает модель таблицы | //+------------------------------------------------------------------+ void TableModelPrint(CTableModel *tm) { if(PRINT_AS_TABLE) tm.PrintTable(); // Распечатать модель как таблицу else tm.Print(true); // Распечатать детализированные данные таблицы }
Otterrete questo risultato nel log:
The table model has been successfully created: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 | The table model has been successfully saved to file. Insert a new row at position 2 and set cell 3 to non-editable | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 0.00 | 0.00 | 0.00 | 0.00 | | Row 3 | 9 | 10 | 11 | 12 | | Row 4 | 13 | 14 | 15 | 16 | Remove column from position 1 | n/n | Column 0 | Column 1 | Column 2 | | Row 0 | 1 | 3 | 4 | | Row 1 | 5 | 7 | 8 | | Row 2 | 0.00 | 0.00 | 0.00 | | Row 3 | 9 | 11 | 12 | | Row 4 | 13 | 15 | 16 | Load the original table view from the file: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 |
Se si desidera inviare al log i dati non in forma tabellare dal modello di tabella, impostare false nella macro PRINT_AS_TABLE :
#define PRINT_AS_TABLE false // Распечатывать модель как таблицу
In questo caso, nel log viene visualizzato quanto segue:
The table model has been successfully created: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 | The table model has been successfully saved to file. Insert a new row at position 2 and set cell 3 to non-editable Table Model: Rows 5, Cells in row 4, Cells Total 20: Table Row: Position 0, Cells total: 4: Table Cell: Row 0, Col 0, Editable <long>Value: 1 Table Cell: Row 0, Col 1, Editable <long>Value: 2 Table Cell: Row 0, Col 2, Editable <long>Value: 3 Table Cell: Row 0, Col 3, Editable <long>Value: 4 Table Row: Position 1, Cells total: 4: Table Cell: Row 1, Col 0, Editable <long>Value: 5 Table Cell: Row 1, Col 1, Editable <long>Value: 6 Table Cell: Row 1, Col 2, Editable <long>Value: 7 Table Cell: Row 1, Col 3, Editable <long>Value: 8 Table Row: Position 2, Cells total: 4: Table Cell: Row 2, Col 0, Editable <double>Value: 0.00 Table Cell: Row 2, Col 1, Editable <double>Value: 0.00 Table Cell: Row 2, Col 2, Editable <double>Value: 0.00 Table Cell: Row 2, Col 3, Uneditable <double>Value: 0.00 Table Row: Position 3, Cells total: 4: Table Cell: Row 3, Col 0, Editable <long>Value: 9 Table Cell: Row 3, Col 1, Editable <long>Value: 10 Table Cell: Row 3, Col 2, Editable <long>Value: 11 Table Cell: Row 3, Col 3, Editable <long>Value: 12 Table Row: Position 4, Cells total: 4: Table Cell: Row 4, Col 0, Editable <long>Value: 13 Table Cell: Row 4, Col 1, Editable <long>Value: 14 Table Cell: Row 4, Col 2, Editable <long>Value: 15 Table Cell: Row 4, Col 3, Editable <long>Value: 16 Remove column from position 1 Table Model: Rows 5, Cells in row 3, Cells Total 15: Table Row: Position 0, Cells total: 3: Table Cell: Row 0, Col 0, Editable <long>Value: 1 Table Cell: Row 0, Col 1, Editable <long>Value: 3 Table Cell: Row 0, Col 2, Editable <long>Value: 4 Table Row: Position 1, Cells total: 3: Table Cell: Row 1, Col 0, Editable <long>Value: 5 Table Cell: Row 1, Col 1, Editable <long>Value: 7 Table Cell: Row 1, Col 2, Editable <long>Value: 8 Table Row: Position 2, Cells total: 3: Table Cell: Row 2, Col 0, Editable <double>Value: 0.00 Table Cell: Row 2, Col 1, Editable <double>Value: 0.00 Table Cell: Row 2, Col 2, Uneditable <double>Value: 0.00 Table Row: Position 3, Cells total: 3: Table Cell: Row 3, Col 0, Editable <long>Value: 9 Table Cell: Row 3, Col 1, Editable <long>Value: 11 Table Cell: Row 3, Col 2, Editable <long>Value: 12 Table Row: Position 4, Cells total: 3: Table Cell: Row 4, Col 0, Editable <long>Value: 13 Table Cell: Row 4, Col 1, Editable <long>Value: 15 Table Cell: Row 4, Col 2, Editable <long>Value: 16 Load the original table view from the file: Table Model: Rows 4, Cells in row 4, Cells Total 16: Table Row: Position 0, Cells total: 4: Table Cell: Row 0, Col 0, Editable <long>Value: 1 Table Cell: Row 0, Col 1, Editable <long>Value: 2 Table Cell: Row 0, Col 2, Editable <long>Value: 3 Table Cell: Row 0, Col 3, Editable <long>Value: 4 Table Row: Position 1, Cells total: 4: Table Cell: Row 1, Col 0, Editable <long>Value: 5 Table Cell: Row 1, Col 1, Editable <long>Value: 6 Table Cell: Row 1, Col 2, Editable <long>Value: 7 Table Cell: Row 1, Col 3, Editable <long>Value: 8 Table Row: Position 2, Cells total: 4: Table Cell: Row 2, Col 0, Editable <long>Value: 9 Table Cell: Row 2, Col 1, Editable <long>Value: 10 Table Cell: Row 2, Col 2, Editable <long>Value: 11 Table Cell: Row 2, Col 3, Editable <long>Value: 12 Table Row: Position 3, Cells total: 4: Table Cell: Row 3, Col 0, Editable <long>Value: 13 Table Cell: Row 3, Col 1, Editable <long>Value: 14 Table Cell: Row 3, Col 2, Editable <long>Value: 15 Table Cell: Row 3, Col 3, Editable <long>Value: 16
Questo output fornisce ulteriori informazioni di debug. Ad esempio, qui vediamo che l'impostazione del flag di divieto di modifica di una cella viene visualizzata nei registri del programma.
Tutte le funzionalità indicate funzionano come previsto, le righe vengono aggiunte, spostate, le colonne eliminate, possiamo modificare le proprietà delle celle e lavorare con i file.
Conclusioni
Questa è la prima versione di un semplice modello di tabella che consente di creare una tabella da un array bidimensionale di dati. In seguito, creeremo altri modelli di tabelle specializzate, creeremo il componente View della tabella e, infine, lavoreremo a pieno titolo con i dati tabellari come uno dei controlli dell'interfaccia grafica dell'utente.
Il file dello script creato oggi con le classi incluse è allegato all'articolo. È possibile scaricarlo per lo studio autonomo.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/17653
Avvertimento: Tutti i diritti su questi materiali sono riservati a MetaQuotes Ltd. La copia o la ristampa di questi materiali in tutto o in parte sono proibite.
Questo articolo è stato scritto da un utente del sito e riflette le sue opinioni personali. MetaQuotes Ltd non è responsabile dell'accuratezza delle informazioni presentate, né di eventuali conseguenze derivanti dall'utilizzo delle soluzioni, strategie o raccomandazioni descritte.
Classi di Tabelle e Intestazioni basate su un modello di tabella in MQL5: Applicazione del concetto MVC
Passaggio a MQL5 Algo Forge (Parte 4): Lavorare con le Versioni e i Rilasci
I metodi di William Gann (Parte I): Creazione dell'indicatore Angoli di Gann
Previsioni economiche: Esplorare il potenziale di Python
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso
Quando una classe di SomeObject viene caricata da un file chiamando il metodo Load() di questo stesso SomeObject, esso controlla "mi sono davvero letto dal file?" (è quello che ci si chiede). In caso contrario, significa che qualcosa è andato storto, quindi è inutile continuare a caricare.
Si tratta di una LISTA (CListObj) che legge un tipo di oggetto da un file. La lista non sa cosa c'è (quale oggetto) nel file. Ma deve conoscere questo tipo di oggetto per crearlo nel suo metodo CreateElement(). Ecco perché non controlla il tipo dell'oggetto caricato dal file. Dopo tutto, ci sarà un confronto con Type(), che in questo metodo restituisce il tipo di una lista, non di un oggetto.
Grazie, ho capito, ho capito.
L'ho letto e poi riletto.
è tutto tranne che un "modello" in MVC. Alcuni ListStorage per esempio
Mi chiedo. È possibile ottenere un analogo dei dataframe di python e R in questo modo? Si tratta di tabelle in cui le diverse colonne possono contenere dati di tipo diverso (da un insieme limitato di tipi, ma che includono le stringhe).
È possibile. Se stiamo parlando di diverse colonne di una tabella, nell'implementazione descritta ogni cella della tabella può avere un tipo di dati diverso.