English Русский 中文 Español Deutsch 日本語
DoEasy. Controles (Parte 32): "ScrollBar" horizontal, rolagem com a roda do mouse

DoEasy. Controles (Parte 32): "ScrollBar" horizontal, rolagem com a roda do mouse

MetaTrader 5Exemplos | 16 outubro 2023, 15:56
292 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


Ideia

Desde a última vez que escrevemos sobre a biblioteca, muito tempo se passou, durante o qual o MetaTrader 5 adicionou suporte para uma nova política de execução de ordens - Passiva / Book or Cancel (BOC), e no MQL5, novos códigos de erro de tempo de execução foram introduzidos para métodos de matrizes, vetores e modelos ONNX. Hoje, vamos adicionar todas essas atualizações à biblioteca.

No último artigo, paramos depois de criar o funcional do objeto de barra de rolagem WinForms, que permite rolar o conteúdo do contêiner usando as setas localizadas nas extremidades da barra de rolagem. Isso, é claro, não é suficiente para trabalhar completamente com esse objeto, e hoje vamos habilitar a capacidade de rolar o conteúdo do contêiner arrastando o controle deslizante da barra de rolagem com o mouse. Os métodos criados para isso permitirão que você conecte funcionalidades à barra de rolagem que permitem deslocar o conteúdo do contêiner usando a rolagem do mouse.

Quando você coloca o cursor sobre a área com a barra de rolagem horizontal e roda o botão da roda do mouse, isso causará um deslocamento do conteúdo do contêiner para a direita quando você rolar o botão para cima (afastando-se de você) e para a esquerda quando você rolar o botão para baixo (em sua direção). Pelo menos é assim que a rolagem horizontal funciona no Adobe PhotoShop, e é isso que faremos aqui. Posteriormente, adicionaremos a capacidade de especificar na biblioteca a direção da rolagem horizontal (e vertical também) do conteúdo do contêiner usando a roda do mouse.


Modificando as classes da biblioteca

Há algum tempo, depois de atualizar o terminal, apareceu um erro irritante: a biblioteca e os arquivos de exemplo dos artigos pararam de ser compilados. O motivo foi minha desatenção ao especificar uma seção privada para alguns métodos da classe CTrading no arquivo Trading.mqh. Por isso, a classe CTradingControl, que é uma classe derivada da CTrading, não conseguia acessar esses métodos. Antes, o compilador ignorava esse erro, mas após a atualização, ele começou a detectá-lo. A correção é simples: precisamos especificar uma seção protegida para os métodos privados inacessíveis nas classes derivadas, tornando-os assim acessíveis na classe herdada.

Abrimos o arquivo \MQL5\Include\DoEasy\Trading.mqh e adicionamos uma seção protegida ao método SetPrices(), que está atualmente na seção privada:

//--- Set the desired sound for a trading object
   void                 SetSoundByMode(const ENUM_MODE_SET_SOUND mode,const ENUM_ORDER_TYPE action,const string sound,CTradeObj *trade_obj);

protected:
//--- Set trading request prices
   template <typename PR,typename SL,typename TP,typename PL> 
   bool                 SetPrices(const ENUM_ORDER_TYPE action,const PR price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj);

private:
//--- Return the flag checking the permission to trade by (1) StopLoss, (2) TakeProfit distance, (3) order placement level by a StopLevel-based price
   bool                 CheckStopLossByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const double sl,const CSymbol *symbol_obj);
   bool                 CheckTakeProfitByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const double tp,const CSymbol *symbol_obj);
   bool                 CheckPriceByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj,const double limit=0);

Após a declaração do método, retornamos a seção privada ao seu lugar, para que outros métodos não sejam incluídos na seção protegida.

A propósito, eu já havia feito a mesma coisa um pouco antes com dois outros métodos na mesma classe:

//--- Return the error handling method
   ENUM_ERROR_CODE_PROCESSING_METHOD   ResultProccessingMethod(const uint result_code);
//--- Correct errors
   ENUM_ERROR_CODE_PROCESSING_METHOD   RequestErrorsCorrecting(MqlTradeRequest &request,const ENUM_ORDER_TYPE order_type,const uint spread_multiplier,CSymbol *symbol_obj,CTradeObj *trade_obj);
   
protected:
//--- (1) Open a position, (2) place a pending order

   template<typename SL,typename TP> 
   bool                 OpenPosition(const ENUM_POSITION_TYPE type,
                                    const double volume,
                                    const string symbol,
                                    const ulong magic=ULONG_MAX,
                                    const SL sl=0,
                                    const TP tp=0,
                                    const string comment=NULL,
                                    const ulong deviation=ULONG_MAX,
                                    const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceOrder( const ENUM_ORDER_TYPE order_type,
                                    const double volume,
                                    const string symbol,
                                    const PR price,
                                    const PL price_limit=0,
                                    const SL sl=0,
                                    const TP tp=0,
                                    const ulong magic=ULONG_MAX,
                                    const string comment=NULL,
                                    const datetime expiration=0,
                                    const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                    const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
                                    
private                                   
//--- Return the request object index in the list by (1) ID,
//--- (2) order ticket, (3) position ticket in the request
   int                  GetIndexPendingRequestByID(const uchar id);
   int                  GetIndexPendingRequestByOrder(const ulong ticket);
   int                  GetIndexPendingRequestByPosition(const ulong ticket);

public:

Se, por acaso, ao compilar os arquivos de exemplo dos artigos anteriores, você encontrar um erro de acesso proibido aos métodos privados da classe CTrading, poderá corrigi-lo por conta própria, seguindo as modificações mencionadas acima.


Agora que temos uma nova política de execução de ordens e novos códigos de erro de tempo de execução, precisamos adicionar as descrições dessas novas adições aos arrays de mensagens de texto da biblioteca.

Abrimos o arquivo \MQL5\Include\DoEasy\Data.mqh e inserimos um novo índice para a mensagem sobre a nova política de execução:

   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK,            // Order is executed in the specified volume only, otherwise it is canceled
   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK,            // Order is filled within an available volume, while the unfilled one is canceled
   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_BOK,            // Order is placed in the market depth and cannot be executed immediately. If the order can be executed immediately when placed, it is canceled
   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN,         // Order is filled within an available volume, while the unfilled one remains

e as mensagens de texto correspondentes (em russo e inglês) para o novo índice:

   {"Ордер исполняется исключительно в указанном объеме, иначе отменяется (FOK)","The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)"},
   {"Ордер исполняется на доступный объем, неисполненный отменяется (IOK)","The order is executed on the available volume, the unfulfilled is canceled (IOK)"},
   {
    "Ордер выставляется в стакан цен и не может быть исполнен немедленно. Если ордер может быть исполнен немедленно при выставлении, то он снимается (BOK)",
    "The order is placed in the Depth of Market and cannot be executed immediately. If the order can be executed immediately when placed, then it is canceled"
   },
   {"Ордер исполняется на доступный объем, неисполненный остаётся (Return)","The order is executed at an available volume, unfulfilled remains in the market (Return)"},


No array de mensagens de erro de tempo de execução (códigos de erro 0, 4001 - 4019), adicionamos os novos códigos de erro que foram introduzidos na linguagem MQL5, de 4020 a 4025:

//+------------------------------------------------------------------+
//| Array of execution time error messages (0, 4001 - 4025)          |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime[][TOTAL_LANG]=
  {
   {"Операция выполнена успешно","Operation successful"},                                                                          // 0
   {"Неожиданная внутренняя ошибка","Unexpected internal error"},                                                                                  // 4001
   {"Ошибочный параметр при внутреннем вызове функции клиентского терминала","Wrong parameter in inner call of client terminal function"}, // 4002
   {"Ошибочный параметр при вызове системной функции","Wrong parameter when calling system function"},                                         // 4003
   {"Недостаточно памяти для выполнения системной функции","Not enough memory to perform system function"},                                    // 4004
   {
    "Структура содержит объекты строк и/или динамических массивов и/или структуры с такими объектами и/или классы",                                // 4005
    "The structure contains objects of strings and/or dynamic arrays and/or structure of such objects and/or classes"
   },                                                                                                                                                    
   {
    "Массив неподходящего типа, неподходящего размера или испорченный объект динамического массива",                                               // 4006
    "Array of a wrong type, wrong size, or a damaged object of a dynamic array"
   },
   {
    "Недостаточно памяти для перераспределения массива либо попытка изменения размера статического массива",                                       // 4007
    "Not enough memory for the relocation of an array, or an attempt to change the size of a static array"
   },
   {"Недостаточно памяти для перераспределения строки","Not enough memory for relocation of string"},                                          // 4008
   {"Неинициализированная строка","Not initialized string"},                                                                                       // 4009
   {"Неправильное значение даты и/или времени","Invalid date and/or time"},                                                                        // 4010
   {"Общее число элементов в массиве не может превышать 2147483647","Total amount of elements in array cannot exceed 2147483647"},             // 4011
   {"Ошибочный указатель","Wrong pointer"},                                                                                                        // 4012
   {"Ошибочный тип указателя","Wrong type of pointer"},                                                                                            // 4013
   {"Системная функция не разрешена для вызова","Function not allowed for call"},                                                               // 4014
   {"Совпадение имени динамического и статического ресурсов","Names of dynamic and static resource match"},                            // 4015
   {"Ресурс с таким именем в EX5 не найден","Resource with this name not found in EX5"},                                                  // 4016
   {"Неподдерживаемый тип ресурса или размер более 16 MB","Unsupported resource type or its size exceeds 16 Mb"},                                  // 4017
   {"Имя ресурса превышает 63 символа","Resource name exceeds 63 characters"},                                                                 // 4018
   {"При вычислении математической функции произошло переполнение ","Overflow occurred when calculating math function "},                          // 4019
   {"Выход за дату окончания тестирования после вызова Sleep()","Out of test end date after calling Sleep()"},                                     // 4020
   {"Неизвестный код ошибки (4021)","Unknown error code (4021)"},                                                                         // 4021
   {
    "Тестирование было прекращено принудительно извне. Например, прервана оптимизацию, или закрыто окно визуального тестирования, или остановлен агент тестирования",
    "Test forcibly stopped from the outside. For example, optimization interrupted, visual testing window closed or testing agent stopped"},       // 4022
   {"Неподходящий тип","Invalid type"},                                                                                                            // 4023
   {"Невалидный хендл","Invalid handle"},                                                                                                          // 4024
   {"Пул объектов заполнен","Object pool filled out"},                                                                                             // 4025
  };
//+------------------------------------------------------------------+

Alguns códigos de erro não estão em uso (ainda?), mas seus valores estão diretamente entre os códigos de erro em uso. Para facilitar a identificação de qual código de erro está sendo usado no MQL5, simplesmente adicionaremos o código de erro ao texto da mensagem sobre o código de erro desconhecido.

Se a biblioteca gerar "Unknown error code" (código de erro desconhecido) (como acontece atualmente, antes das alterações), é difícil entender qual código de erro não utilizado agora está em uso. Ao adicionar o código de erro à mensagem, receberemos imediatamente uma mensagem sobre o código de erro desconhecido com o número desse erro. Em seguida, basta consultar a documentação atualizada e complementar a biblioteca com a descrição desse erro, da mesma forma que estamos fazendo agora. Tais adições a este arquivo já foram feitas para todas as mensagens sobre código de erro desconhecido.

Para os códigos de erro dos métodos de matrizes e vetores e modelos ONNX, criaremos dois novos arrays, pois os valores iniciais dos códigos começam em 5700 e 5800, e preencher o "espaço vazio" com mil mensagens idênticas sobre código de erro não utilizado não é prático nem eficiente. Especialmente porque a biblioteca usa diferentes arrays para diferentes grupos de códigos de erro, devido à mesma razão, adicionaremos mais dois arrays após o array de mensagens de erro de tempo de execução com códigos de 5601 a 5626:

//+------------------------------------------------------------------+
//| Array of execution time error messages  (5601 - 5626)            |
//| (Working with databases)                                         |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_sqlite[][TOTAL_LANG]=
  {
   {"Общая ошибка","Generic error"},                                                                                                               // 5601
   {"Внутренняя логическая ошибка в SQLite","SQLite internal logic error"},                                                                        // 5602
   {"Отказано в доступе","Access denied"},                                                                                                         // 5603
   {"Процедура обратного вызова запросила прерывание","Callback routine requested abort"},                                                         // 5604
   {"Файл базы данных заблокирован","Database file locked"},                                                                                       // 5605
   {"Таблица в базе данных заблокирована ","Database table locked"},                                                                               // 5606
   {"Сбой malloc()","Insufficient memory for completing operation"},                                                                               // 5607
   {"Попытка записи в базу данных, доступной только для чтения ","Attempt to write to readonly database"},                                         // 5608
   {"Операция прекращена с помощью sqlite3_interrupt() ","Operation terminated by sqlite3_interrupt()"},                                           // 5609
   {"Ошибка дискового ввода-вывода","Disk I/O error"},                                                                                             // 5610
   {"Образ диска базы данных испорчен","Database disk image corrupted"},                                                                           // 5611
   {"Неизвестный код операции в sqlite3_file_control()","Unknown operation code in sqlite3_file_control()"},                                       // 5612
   {"Ошибка вставки, так как база данных заполнена ","Insertion failed because database is full"},                                                 // 5613
   {"Невозможно открыть файл базы данных","Unable to open the database file"},                                                                     // 5614
   {"Ошибка протокола блокировки базы данных ","Database lock protocol error"},                                                                    // 5615
   {"Только для внутреннего использования","Internal use only"},                                                                                   // 5616
   {"Схема базы данных изменена","Database schema changed"},                                                                                       // 5617
   {"Строка или BLOB превышает ограничение по размеру","String or BLOB exceeds size limit"},                                                       // 5618
   {"Прервано из-за нарушения ограничения","Abort due to constraint violation"},                                                                   // 5619
   {"Несоответствие типов данных","Data type mismatch"},                                                                                           // 5620
   {"Ошибка неправильного использования библиотеки","Library used incorrectly"},                                                                   // 5621
   {"Использование функций операционной системы, не поддерживаемых на хосте","Uses OS features not supported on host"},                            // 5622
   {"Отказано в авторизации","Authorization denied"},                                                                                              // 5623
   {"Не используется ","Not used "},                                                                                                               // 5624
   {"2-й параметр для sqlite3_bind находится вне диапазона","Bind parameter error, incorrect index"},                                              // 5625
   {"Открытый файл не является файлом базы данных","File opened that is not database file"},                                                       // 5626
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages  (5700 - 5706)            |
//| (Matrix and vector methods)                                      |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_matrix_vector[][TOTAL_LANG]=
  {
   {"Внутренняя ошибка исполняющей подсистемы матриц/векторов","Internal error of the matrix/vector executing subsystem"},                         // 5700
   {"Матрица/вектор не инициализирован","Matrix/vector not initialized"},                                                                          // 5701
   {"Несогласованный размер матриц/векторов в операции","Inconsistent size of matrices/vectors in operation"},                                     // 5702
   {"Некорректный размер матрицы/вектора","Invalid matrix/vector size"},                                                                           // 5703
   {"Некорректный тип матрицы/вектора","Invalid matrix/vector type"},                                                                              // 5704
   {"Функция недоступна для данной матрицы/вектора","Function not available for this matrix/vector"},                                              // 5705
   {"Матрица/вектор содержит нечисла (Nan/Inf)","Matrix/vector contains non-numbers (Nan/Inf)"},                                                   // 5706
  };  
//+------------------------------------------------------------------+
//| Array of execution time error messages  (5800 - 5808)            |
//| (ONNX models)                                                    |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_onnx[][TOTAL_LANG]=
  {
   {"Внутренняя ошибка ONNX стандарта","ONNX internal error"},                                                                                     // 5800
   {"Ошибка инициализации ONNX Runtime API","ONNX Runtime API initialization error"},                                                              // 5801
   {"Свойство или значение неподдерживаются языком MQL5","Property or value not supported by MQL5"},                                               // 5802
   {"Ошибка запуска ONNX runtime API","ONNX runtime API run error"},                                                                               // 5803
   {"В OnnxRun передано неверное количество параметров ","Invalid number of parameters passed to OnnxRun"},                                        // 5804
   {"Некорректное значение параметра","Invalid parameter value"},                                                                                  // 5805
   {"Некорректный тип параметра","Invalid parameter type"},                                                                                        // 5806
   {"Некорректный размер параметра","Invalid parameter size"},                                                                                     // 5807
   {"Размерность тензора не задана или указана неверно","Tensor dimension not set or invalid"},                                                    // 5808
  };  
//+------------------------------------------------------------------+
#ifdef __MQL4__


Para que possamos exibir mensagens da biblioteca, cujos textos estão armazenados em arrays de mensagens, temos a classe CMessage. Nela, é necessário adicionar o processamento das novas mensagens de texto adicionadas aos arrays existentes e aos novos arrays criados hoje.

Vamos abrir o arquivo \MQL5\Include\DoEasy\Services\Message.mqh e incluir o processamento dos novos arrays no método GetTextByID(). Esse método atribui à variável m_text o texto da mensagem que está armazenado no índice da mensagem passada como parâmetro para o método.

Para os erros de tempo de execução (0, 4001 — 4019), expandiremos o número de códigos processados de 4019 para 4025. E para os códigos de erro dos métodos de matrizes e vetores, bem como dos modelos ONNX, iremos buscar as mensagens de texto nos novos arrays:

//+------------------------------------------------------------------+
//| Get messages from the text array by an ID                        |
//+------------------------------------------------------------------+
void CMessage::GetTextByID(const int msg_id)
  {
   CMessage::m_text=
     (
      //--- Runtime errors (0, 4001 - 4025)
      msg_id==0                     ?  messages_runtime[msg_id][m_lang_num]                        :
     #ifdef __MQL5__
      msg_id>4000 && msg_id<4026    ?  messages_runtime[msg_id-4000][m_lang_num]                   :
      //--- Runtime errors (Charts 4101 - 4116)
      msg_id>4100 && msg_id<4117    ?  messages_runtime_charts[msg_id-4101][m_lang_num]            :
      //--- Runtime errors (Graphical objects 4201 - 4205)
      msg_id>4200 && msg_id<4206    ?  messages_runtime_graph_obj[msg_id-4201][m_lang_num]         :
      //--- Runtime errors (MarketInfo 4301 - 4305)
      msg_id>4300 && msg_id<4306    ?  messages_runtime_market[msg_id-4301][m_lang_num]            :
      //--- Runtime errors (Access to history 4401 - 4407)
      msg_id>4400 && msg_id<4408    ?  messages_runtime_history[msg_id-4401][m_lang_num]           :
      //--- Runtime errors (Global Variables 4501 - 4524)
      msg_id>4500 && msg_id<4525    ?  messages_runtime_global[msg_id-4501][m_lang_num]            :
      //--- Runtime errors (Custom indicators 4601 - 4603)
      msg_id>4600 && msg_id<4604    ?  messages_runtime_custom_indicator[msg_id-4601][m_lang_num]  :
      //--- Runtime errors (Account 4701 - 4758)
      msg_id>4700 && msg_id<4759    ?  messages_runtime_account[msg_id-4701][m_lang_num]           :
      //--- Runtime errors (Indicators 4801 - 4812)
      msg_id>4800 && msg_id<4813    ?  messages_runtime_indicator[msg_id-4801][m_lang_num]         :
      //--- Runtime errors (Market depth 4901 - 4904)
      msg_id>4900 && msg_id<4905    ?  messages_runtime_books[msg_id-4901][m_lang_num]             :
      //--- Runtime errors (File operations 5001 - 5027)
      msg_id>5000 && msg_id<5028    ?  messages_runtime_files[msg_id-5001][m_lang_num]             :
      //--- Runtime errors (Converting strings 5030 - 5044)
      msg_id>5029 && msg_id<5045    ?  messages_runtime_string[msg_id-5030][m_lang_num]            :
      //--- Runtime errors (Working with arrays 5050 - 5063)
      msg_id>5049 && msg_id<5064    ?  messages_runtime_array[msg_id-5050][m_lang_num]             :
      //--- Runtime errors (Working with OpenCL 5100 - 5114)
      msg_id>5099 && msg_id<5115    ?  messages_runtime_opencl[msg_id-5100][m_lang_num]            :
      //--- Runtime errors (Working with databases 5120 - 5130)
      msg_id>5119 && msg_id<5131    ?  messages_runtime_database[msg_id-5120][m_lang_num]          :
      //--- Runtime errors (Working with WebRequest() 5200 - 5203)
      msg_id>5199 && msg_id<5204    ?  messages_runtime_webrequest[msg_id-5200][m_lang_num]        :
      //--- Runtime errors (Working with network (sockets) 5270 - 5275)
      msg_id>5269 && msg_id<5276    ?  messages_runtime_netsocket[msg_id-5270][m_lang_num]         :
      //--- Runtime errors (Custom symbols 5300 - 5310)
      msg_id>5299 && msg_id<5311    ?  messages_runtime_custom_symbol[msg_id-5300][m_lang_num]     :
      //--- Runtime errors (Economic calendar 5400 - 5402)
      msg_id>5399 && msg_id<5403    ?  messages_runtime_calendar[msg_id-5400][m_lang_num]          :
      //--- Runtime errors (Working with databases 5601 - 5626)
      msg_id>5600 && msg_id<5627    ?  messages_runtime_sqlite[msg_id-5601][m_lang_num]            :
      //--- Runtime errors (Matrix and vector methods 5700 - 5706)
      msg_id>5699 && msg_id<5707    ?  messages_runtime_matrix_vector[msg_id-5700][m_lang_num]     :
      //--- Runtime errors (ONNX models 5800 - 5808)
      msg_id>5799 && msg_id<5809    ?  messages_runtime_onnx[msg_id-5800][m_lang_num]              :
      //--- Trade server return codes (10004 - 10045)
      msg_id>10003 && msg_id<10047  ?  messages_ts_ret_code[msg_id-10004][m_lang_num]              :
     
     #else // MQL4
      msg_id>0    && msg_id<10      ?  messages_ts_ret_code_mql4[msg_id][m_lang_num]               :
      msg_id>63   && msg_id<66      ?  messages_ts_ret_code_mql4[msg_id-54][m_lang_num]            :
      msg_id>127  && msg_id<151     ?  messages_ts_ret_code_mql4[msg_id-116][m_lang_num]           :
      msg_id<4000                   ?  messages_ts_ret_code_mql4[26][m_lang_num]                   :
      //--- MQL4 runtime errors (4000 - 4030)
      msg_id<4031                   ?  messages_runtime_4000_4030[msg_id-4000][m_lang_num]         :
      //--- MQL4 runtime errors (4050 - 4075)
      msg_id>4049 && msg_id<4076    ?  messages_runtime_4050_4075[msg_id-4050][m_lang_num]         :
      //--- MQL4 runtime errors (4099 - 4112)
      msg_id>4098 && msg_id<4113    ?  messages_runtime_4099_4112[msg_id-4099][m_lang_num]         :
      //--- MQL4 runtime errors (4200 - 4220)
      msg_id>4199 && msg_id<4221    ?  messages_runtime_4200_4220[msg_id-4200][m_lang_num]         :
      //--- MQL4 runtime errors (4250 - 4266)
      msg_id>4249 && msg_id<4267    ?  messages_runtime_4250_4266[msg_id-4250][m_lang_num]         :
      //--- MQL4 runtime errors (5001 - 5029)
      msg_id>5000 && msg_id<5030    ?  messages_runtime_5001_5029[msg_id-5001][m_lang_num]         :
      //--- MQL4 runtime errors (5200 - 5203)
      msg_id>5199 && msg_id<5204    ?  messages_runtime_5200_5203[msg_id-5200][m_lang_num]         :
     #endif 
      
      //--- Library messages (ERR_USER_ERROR_FIRST)
      msg_id>ERR_USER_ERROR_FIRST-1 ?  messages_library[msg_id-ERR_USER_ERROR_FIRST][m_lang_num]   : 
      messages_library[MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE-ERR_USER_ERROR_FIRST][m_lang_num]
     );
  }
//+------------------------------------------------------------------+


Algumas funções comuns usadas na biblioteca estão no arquivo \MQL5\Include\DoEasy\Services\DELib.mqh.

Na função que retorna a descrição do modo de preenchimento de ordem, adicionaremos o tratamento para o tipo de preenchimento BoC (Book or Cancel):

//+------------------------------------------------------------------+
//| Return the order filling mode description                        |
//+------------------------------------------------------------------+
string OrderTypeFillingDescription(const ENUM_ORDER_TYPE_FILLING type)
  {
   return
     (
      type==ORDER_FILLING_FOK    ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK)   :
      type==ORDER_FILLING_IOC    ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK)   :
      type==ORDER_FILLING_BOC    ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_BOK)   :
      type==ORDER_FILLING_RETURN ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN): 
      type==WRONG_VALUE          ? "WRONG_VALUE"   :  EnumToString(type)
     );
  }
//+------------------------------------------------------------------+

Agora, se a política de execução for Book or Cancel, a função retornará o texto correspondente ao índice MSG_LIB_TEXT_REQUEST_ORDER_FILLING_BOK no array de mensagens que adicionamos hoje.


Quando o EA é removido do gráfico, uma mensagem de erro de fechamento do livro de ofertas era registrada no log. No entanto, não havia descrição ou código de erro. Considero isso um comportamento inadequado, pois não fica claro o que está acontecendo. Para corrigir essa situação, aprimoraremos o método responsável pelo fechamento do livro de ofertas na classe do objeto-símbolo CSymbol no arquivo \MT5\MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh.

No bloco de código que lida com o erro de fechamento do livro de ofertas, obteremos o código de erro e o registraremos no log após a descrição do erro:

//+------------------------------------------------------------------+
//| Close the market depth                                           |
//+------------------------------------------------------------------+
bool CSymbol::BookClose(void)
  {
//--- If the DOM subscription flag is off, subscription is disabled (or not enabled yet). Return 'true'
   if(!this.m_book_subscribed)
      return true;
//--- Save the result of unsubscribing from the DOM
   bool res=( #ifdef __MQL5__ ::MarketBookRelease(this.m_name) #else true #endif );
//--- If unsubscribed successfully, reset the DOM subscription flag and write the status to the object property
   if(res)
     {
      this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed=false;
      ::Print(CMessage::Text(MSG_SYM_SYMBOLS_BOOK_DEL)+" "+this.m_name);
     }
   else
     {
      this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed=true;
      int err=::GetLastError();
      ::Print(CMessage::Text(MSG_SYM_SYMBOLS_ERR_BOOK_DEL)+": "+CMessage::Text(err)+" (",(string)err,")");
     }
//--- Return the result of unsubscribing from DOM
   return res;
  }
//+------------------------------------------------------------------+

Agora a razão do erro ao tentar fechar o livro de ofertas será clara.


Atualmente, implementamos a funcionalidade de deslocamento de conteúdo do contêiner quando clicamos nas setas do objeto de barra de rolagem. Quando a criamos, decidimos que a quantidade de deslocamento do conteúdo do contêiner seria de dois pixels da tela, o que é mais do que suficiente para posicionar confortavelmente o conteúdo rolado usando as setas de rolagem. No entanto, hoje vamos criar mais duas opções de rolagem: arrastando o controle deslizante da barra de rolagem com o mouse e rolando com a roda do mouse.

Se, ao deslocar o controle deslizante da barra de rolagem, não precisamos de um valor pré-determinado para o deslocamento do conteúdo do contêiner (o deslocamento será determinado pela quantidade em que o controle deslizante foi movido), ao rolar a roda do mouse, precisamos definir quantos pixels o conteúdo do contêiner será deslocado quando o contador da roda do mouse for acionado uma vez. O contador é discreto e envia um evento quando o valor Delta atinge 120 ou -120, dependendo da direção da rolagem da roda do mouse. Para a rolagem da roda do mouse, definiremos um valor de quatro pixels.

Vamos abrir o arquivo \MT5\MQL5\Include\DoEasy\Defines.mqh e corrigir o nome da macro substituição que controla o deslocamento ao clicar na seta (DEF_CONTROL_SCROLL_BAR_SCROLL_STEP). Além disso, adicionaremos uma nova macro substituição que controlará o deslocamento ao rolar a roda do mouse:

#define DEF_CONTROL_SCROLL_BAR_WIDTH                  (11)                 // Default ScrollBar control width
#define DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN         (8)                  // Minimum size of the capture area (slider)
#define DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_CLICK      (2)                  // Shift step in pixels of the container content when scrolling by clicking the button
#define DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_WHELL      (4)                  // Shift step in pixels of the container content when scrolling with the mouse wheel 
#define DEF_CONTROL_CORNER_AREA                       (4)                  // Number of pixels defining the corner area to resize
#define DEF_CONTROL_LIST_MARGIN_X                     (1)                  // Gap between columns in ListBox controls
#define DEF_CONTROL_LIST_MARGIN_Y                     (0)                  // Gap between rows in ListBox controls


Todas as correções preliminares foram feitas; agora, vamos trabalhar na melhoria da funcionalidade da barra de rolagem.

Primeiro, vamos permitir o deslocamento do conteúdo do contêiner arrastando a barra de rolagem com o mouse. A lógica será a seguinte: a barra de rolagem, em uma representação simplificada, exibe o contêiner e seu conteúdo. O controle deslizante, seu tamanho, representa o tamanho da parte visível do contêiner, enquanto a barra de rolagem (entre as setas das setas) exibe o conteúdo do contêiner que está além de seus limites. Temos um ponto de partida, a partir do qual faremos nossos cálculos: é a quantidade pela qual o controle deslizante foi deslocado do lado direito da seta esquerda. Também temos o tamanho do controle deslizante e o tamanho da parte visível do contêiner. Com base nisso, podemos calcular quanto o conteúdo do contêiner deve ser deslocado:

  1. Largura da parte visível do contêiner (W1)
  2. Largura do controle deslizante (W2)
  3. Quanto a parte visível do contêiner é maior do que o controle deslizante (X = W1 / W2)
  4. Quanto o controle deslizante (S1) é deslocado
  5. Quanto deve ser deslocado o conteúdo do contêiner (S1 * X)

Assim, sabendo a proporção entre o tamanho do controle deslizante e a parte visível do contêiner e o quanto o controle deslizante foi deslocado, calculamos o valor necessário para deslocar o conteúdo do contêiner. Se o controle deslizante foi deslocado para a esquerda, o conteúdo do contêiner deve ser movido para a direita, e vice-versa.

Vamos abrir o arquivo da classe da barra de rolagem horizontal \MT5\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarHorisontal.mqh e escrever um bloco de código em seu manipulador de eventos que execute esses cálculos e mova o conteúdo do contêiner:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Adjust subwindow Y shift
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
//--- Get the pointers to control objects of the scrollbar
   CArrowLeftButton  *buttl=this.GetArrowButtonLeft();
   CArrowRightButton *buttr=this.GetArrowButtonRight();
   CScrollBarThumb   *thumb=this.GetThumb();
   if(buttl==NULL || buttr==NULL || thumb==NULL)
      return;
//--- If the event ID is an object movement
   if(id==WF_CONTROL_EVENT_MOVING)
     {
      //--- Move the scrollbar to the foreground
      this.BringToTop();
      //--- Declare the variables for the coordinates of the capture area
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Set the Y coordinate equal to the Y coordinate of the control element
      y=this.CoordY()+this.BorderSizeTop();
      //--- Adjust the X coordinate so that the capture area does not go beyond the control, taking into account the arrow buttons
      if(x<buttl.RightEdge())
        x=buttl.RightEdge();
      if(x>buttr.CoordX()-thumb.Width())
        x=buttr.CoordX()-thumb.Width();
      //--- If the capture area object is shifted by the calculated coordinates
      if(thumb.Move(x,y,true))
        {
         //--- set the object relative coordinates
         thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX());
         thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY());
        }
      //--- Get the pointer to the base object
      CWinFormBase *base=this.GetBase();
      if(base!=NULL)
        {
         //--- Check if the content goes beyond the container
         base.CheckForOversize();

         //--- Calculate the distance the slider is from the left border of the scrollbar (from the right side of the left arrow button)
         int distance=thumb.CoordX()-buttl.RightEdge();
         
         //--- Declare a variable that stores the distance value before the slider shift
         static int distance_last=distance;
         //--- Declare a variable that stores the value in screen pixels the slider was shifted by
         int shift_value=0;
         
         //--- If the values of the past and current distances are not equal (the slider is shifted),
         if(distance!=distance_last)
           {
            //--- calculate the value the slider is shifted by
            shift_value=distance_last-distance;
            //--- and enter the new distance into the value of the previous distance for the next calculation
            distance_last=distance;
           }
         
         //--- Get the largest and smallest coordinates of the right and left sides of the base object content
         int cntt_r=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_RIGHT);
         int cntt_l=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_X);

         //--- Get the coordinate offset of the left side of the base object content 
         //--- relative to the initial coordinate of the base object working area
         int extl=base.CoordXWorkspace()-cntt_l;
         
         //--- Calculate the relative value of the desired coordinate,
         //--- where the contents of the base object, shifted by the slider, should be located
         double x=(double)this.WidthWorkspace()*(double)distance/double(thumb.Width()!=0 ? thumb.Width() : DBL_MIN);
         
         //--- Calculate the required shift value of the base object content along the above calculated coordinate 
         int shift_need=extl-(int)::round(x);
         
         //--- If the slider is shifted to the left (positive shift value)
         if(shift_value>0)
           {
            if(cntt_l+shift_need<=base.CoordXWorkspace())
               base.ShiftDependentObj(shift_need,0);
           }
         //--- If the slider is shifted to the right (negative shift value) 
         if(shift_value<0)
           {
            if(cntt_r-shift_need>=base.RightEdgeWorkspace())
               base.ShiftDependentObj(shift_need,0);
           }
         ::ChartRedraw(this.ChartID());
        }
     }
//--- If any scroll button is clicked
   if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT || id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
     {
      //--- Move the scrollbar to the foreground
      this.BringToTop();
      //--- Get the base object
      CWinFormBase *base=this.GetBase();
      if(base==NULL)
         return;
      //--- Calculate how much each side of the content of the base object goes beyond its borders
      base.CheckForOversize();
      //--- Get the largest and smallest coordinates of the right and left sides of the base object content
      int cntt_r=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_RIGHT);
      int cntt_l=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_X);
      //--- Set the number of pixels, by which the content of the base object should be shifted
      int shift=(sparam!="" ? DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_CLICK : DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_WHELL);
      //--- If the left button is clicked
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT)
        {
         if(cntt_l+shift<=base.CoordXWorkspace())
            base.ShiftDependentObj(shift,0);
        }
      //--- If the right button is clicked
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
        {
         if(cntt_r-shift>=base.RightEdgeWorkspace())
            base.ShiftDependentObj(-shift,0);
        }
      //--- Calculate the width and coordinates of the slider
      this.SetThumbParams();
     }
  }
//+------------------------------------------------------------------+

Praticamente cada linha adicionada no bloco de código é explicada nos comentários. Note que ao mover o controle deslizante para a esquerda, movemos o conteúdo do contêiner para a direita. Além disso, é necessário limitar o deslocamento do conteúdo do contêiner de forma que a borda esquerda do conteúdo não se mova para a direita da borda esquerda da área de trabalho do contêiner - a área dentro da qual o conteúdo do contêiner é visível. Da mesma forma, ao mover o controle deslizante para a direita, movemos o conteúdo do contêiner para a esquerda, e o lado direito do conteúdo não deve sair da borda esquerda da área de trabalho do contêiner. Todas essas verificações são feitas antes de mover o conteúdo do contêiner.

No método que calcula e define os parâmetros da área de captura, faremos alteraremos ligeiramente o cálculo do tamanho relativo da janela da parte visível - abandonaremos os cálculos em porcentagem, calculando a relação entre o tamanho da área de trabalho do contêiner e o tamanho do controle deslizante (quanto um tamanho é maior que o outro):

//+------------------------------------------------------------------+
//| Calculate and set the parameters of the capture area (slider)    |
//+------------------------------------------------------------------+
int CScrollBarHorisontal::SetThumbParams(void)
  {
//--- Get the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return 0;
//--- Get the capture area object (slider)
   CScrollBarThumb *thumb=this.GetThumb();
   if(thumb==NULL)
      return 0;
//--- Get the width size of the visible part inside the container
   int base_w=base.WidthWorkspace();
//--- Calculate the total width of all attached objects
   int objs_w=base_w+base.OversizeLeft()+base.OversizeRight();
//--- Calculate the relative size of the visible part window
   double px=(double)base_w/double(objs_w!=0 ? objs_w : 1);
//--- Calculate and adjust the size of the slider relative to the width of its workspace (not less than the minimum size)
   int thumb_size=(int)::floor(this.BarWorkAreaSize()*px);
   if(thumb_size<DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN)
      thumb_size=DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN;
   if(thumb_size>this.BarWorkAreaSize())
      thumb_size=this.BarWorkAreaSize();
//--- Calculate the coordinate of the slider and change its size to match the previously calculated one
   int thumb_x=this.CalculateThumbAreaDistance(thumb_size);
   if(!thumb.Resize(thumb_size,thumb.Height(),true))
      return 0;
//--- Shift the slider by the calculated X coordinate
   if(thumb.Move(this.BarWorkAreaCoord()+thumb_x,thumb.CoordY()))
     {
      thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX());
      thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY());
     }
//--- Return the calculated slider size
   return thumb_size;
  }
//+------------------------------------------------------------------+

Além de ajustar o tamanho mínimo do controle deslizante, faremos um ajuste no tamanho máximo dele - o valor calculado para a largura do controle deslizante não deve ser maior que o tamanho real da área de trabalho da barra de rolagem (a distância entre os botões das setas esquerda e direita).

Agora, se compilarmos o EA do último artigo com as atuais melhorias na biblioteca, ao mover o controle deslizante da barra de rolagem, moveremos o conteúdo do contêiner na direção oposta:



Agora precisamos possibilitar a rolagem do conteúdo do contêiner usando a roda do mouse - é conveniente e comum. Em muitos programas, a rolagem da roda do mouse causa apenas um deslocamento vertical do conteúdo do contêiner (pense em páginas de navegadores ou editores de texto). Mas faremos um pouco diferente - ao mover o cursor sobre a barra de rolagem horizontal e girar a roda do mouse, moveremos o conteúdo horizontalmente. Parece lógico. Se você precisa de um deslocamento vertical, deve girar a roda do mouse na área da barra de rolagem vertical ou do conteúdo do contêiner. Mas se o cursor estiver na barra de rolagem horizontal, é lógico esperar um deslocamento horizontal do conteúdo do contêiner. Pelo menos é assim que é feito no Adobe Photoshop, o que é lógico e conveniente.

Para implementar essa funcionalidade, surpreendentemente, quase tudo já está pronto. Temos um manipulador que move o conteúdo do contêiner ao pressionar os botões de seta da barra de rolagem. Quando a roda do mouse é girada na área da barra de rolagem, enviaremos um evento de clique nos botões de deslocamento. Dependendo da direção de rotação da roda do mouse, enviaremos um evento de clique no botão esquerdo ou direito.

No manipulador de eventos, juntamente com o identificador do evento, alguns parâmetros são transmitidos - inteiro, decimal e string. Ao clicar em um objeto de botão, o parâmetro de string transmite o nome do objeto de botão. Ao girar a roda do mouse, o parâmetro de string não é usado - é uma string vazia. Usaremos isso no manipulador de eventos do botão para determinar qual evento ocorreu - clique no botão ou rolagem da roda do mouse. Com base no evento identificado, determinaremos o quanto o conteúdo do contêiner deve ser deslocado. Ao clicar no botão da seta - dois pixels, e ao girar a roda do mouse - quatro.

Na seção protegida da classe, declararemos um manipulador de eventos virtual para "Cursor dentro da área ativa, a roda do mouse está rolando":

protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CScrollBarHorisontal(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          CGCnvElement *main_obj,CGCnvElement *base_obj,
                                          const long chart_id,
                                          const int subwindow,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h);

//--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler
   virtual void      MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
public:

Todas as classes de elementos gráficos são derivadas da classe do objeto-forma CForm. Já estão definidos manipuladores de eventos vazios para vários eventos do mouse que precisam ser substituídos nas classes derivadas. Aqui, estamos substituindo o manipulador de eventos para a rotação da roda do mouse dentro da área ativa deste objeto.

Fora do corpo da classe, escreveremos sua implementação:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   ENUM_WF_CONTROL_EVENT evn=(dparam>0 ? WF_CONTROL_EVENT_CLICK_SCROLL_LEFT : dparam<0 ? WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT : WF_CONTROL_EVENT_NO_EVENT);
   this.OnChartEvent(evn,lparam,dparam,sparam);
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+

Aqui é simples: ao girar a roda do mouse, o parâmetro de ponto flutuante dparam recebe o valor do contador Delta da rotação da roda.
Os valores podem ser 120 e -120, dependendo da direção da rotação.
Se o Delta (passado em dparam) for positivo, o evento será um clique no botão da seta para a esquerda.
Com um valor Delta negativo, o evento será um clique no botão da seta para a direita
.
E esse evento é enviado para o manipulador de eventos do objeto atual (barra de rolagem).

Como estamos lidando com dois eventos diferentes (clique no botão e rotação da roda do mouse) com o mesmo bloco de código no manipulador OnChartEvent() do objeto da barra de rolagem, identificamos qual evento ocorreu com base no valor do parâmetro de string sparam - ao girar a roda do mouse, este parâmetro estará vazio, enquanto ao clicar no botão, conterá o nome do botão. No manipulador de eventos, determinamos qual evento ocorreu e definimos o tamanho do deslocamento do conteúdo do contêiner em pixels, dependendo do evento identificado. Para um clique no botão, movemos dois pixels, para a rotação da roda do mouse, movemos quatro.

//--- If any scroll button is clicked
   if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT || id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
     {
      //--- Move the scrollbar to the foreground
      this.BringToTop();
      //--- Get the base object
      CWinFormBase *base=this.GetBase();
      if(base==NULL)
         return;
      //--- Calculate how much each side of the content of the base object goes beyond its borders
      base.CheckForOversize();
      //--- Get the largest and smallest coordinates of the right and left sides of the base object content
      int cntt_r=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_RIGHT);
      int cntt_l=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_X);
      //--- Set the number of pixels, by which the content of the base object should be shifted
      int shift=(sparam!="" ? DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_CLICK : DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_WHELL);
      //--- If the left button is clicked
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT)
        {
         if(cntt_l+shift<=base.CoordXWorkspace())
            base.ShiftDependentObj(shift,0);
        }
      //--- If the right button is clicked
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
        {
         if(cntt_r-shift>=base.RightEdgeWorkspace())
            base.ShiftDependentObj(-shift,0);
        }
      //--- Calculate the width and coordinates of the slider
      this.SetThumbParams();
     }
  }
//+------------------------------------------------------------------+

Agora, se recompilarmos o EA da última postagem e tentarmos girar a roda do mouse enquanto o cursor estiver sobre a barra de rolagem, o conteúdo será movido apenas se o cursor não estiver sobre o controle deslizante:


Se o cursor estiver sobre o controle deslizante, a rolagem não ocorrerá. Por quê? Simplesmente porque o objeto de controle deslizante da barra de rolagem se torna ativo e ainda não possui um manipulador para esse evento. Vamos adicioná-lo.

Vamos abrir o arquivo da classe do controle deslizante da barra de rolagem \MT5\MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarThumb.mqh e declarar dois manipuladores de eventos do mouse na seção protegida:

//+------------------------------------------------------------------+
//| ScrollBarThumb object class of the WForms controls               |
//+------------------------------------------------------------------+
class CScrollBarThumb : public CButton
  {
private:

protected:
//--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
   virtual void      MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler
   virtual void      MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the left mouse button is clicked' event handler
   virtual void      MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler
   virtual void      MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- 'The cursor is inside the form, the mouse wheel is being scrolled' event handler
   virtual void      MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);

//--- Protected constructor with object type, chart ID and subwindow
                     CScrollBarThumb(const ENUM_GRAPH_ELEMENT_TYPE type,
                                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                                     const long chart_id,
                                     const int subwindow,
                                     const string descript,
                                     const int x,
                                     const int y,
                                     const int w,
                                     const int h);
                             
public:

Por que precisamos de dois manipuladores? Cada objeto gráfico na biblioteca possui áreas responsáveis por diferentes estados e envia eventos correspondentes a essas áreas. A zona ativa do objeto de controle deslizante da barra de rolagem é um pixel menor em todos os lados em relação ao tamanho do próprio controle deslizante. Portanto, quando Cursor dentro da zona ativa, o manipulador da zona ativa do objeto é ativado, mas se o cursor estiver bem na borda do controle deslizante, ele entra em outra zona, onde o manipulador para estar dentro da forma do objeto é ativado.

Fora do corpo da classe, escreveremos a implementação desses dois manipuladores.

A implementação do manipulador do evento "Cursor dentro da área ativa, rolagem da roda do mouse" é a seguinte:

//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CScrollBarThumb::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
   base.BringToTop();
   ENUM_WF_CONTROL_EVENT evn=(dparam>0 ? WF_CONTROL_EVENT_CLICK_SCROLL_LEFT : dparam<0 ? WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT : WF_CONTROL_EVENT_NO_EVENT);
   base.OnChartEvent(evn,lparam,dparam,sparam);
   ::ChartRedraw(base.ChartID());
  }
//+------------------------------------------------------------------+

A lógica do manipulador é idêntica à do manipulador do objeto da barra de rolagem que discutimos anteriormente. Mas aqui, primeiro obtemos um ponteiro para o objeto base para o controle deslizante, que é o objeto da barra de rolagem. Em seguida, trazemos a barra de rolagem para a frente (também trazendo o controle deslizante para a frente). Em seguida, determinamos o evento necessário com base na direção da rotação da roda do mouse e enviamos o evento correspondente para o manipulador de eventos do objeto base - a barra de rolagem. Por fim, redesenhamos o gráfico para exibir imediatamente as alterações.


Implementação do manipulador do evento "Cursor dentro da, rolagem da roda do mouse":

Como esse manipulador deve ser completamente idêntico ao manipulador acima, simplesmente chamamos o primeiro manipulador com os parâmetros passados para este:

//+------------------------------------------------------------------+
//| 'The cursor is inside the form,                                  |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CScrollBarThumb::MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   this.MouseActiveAreaWhellHandler(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


Agora, todo o funcional da barra de rolagem horizontal está pronto. Vamos testar o que fizemos.


Teste

Para o teste, usaremos o Expert Advisor do último artigo sem nenhuma alteração. Para o teste, pegaremos o EA da postagem anterior sem quaisquer alterações. Vamos compilá-lo e executá-lo no gráfico, definindo "No" para a opção de redimensionamento automático do contêiner ao iniciar:



Verificaremos o funcionamento de todos os componentes do novo funcional da barra de rolagem horizontal:


Tudo está funcionando como planejado.



O que virá a seguir?

No próximo artigo, transferiremos o funcional criado para o controle "Barra de rolagem vertical".


Voltar ao conteúdo

*Artigos desta série:

 
DoEasy. Controles (Parte 26): Finalizamos o objeto WinForms "ToolTip" e começamos a desenvolver a barra de progresso "ProgressBar"
DoEasy. Controles (Parte 27): Continuamos a trabalhar no objeto WinForms "ProgressBar"
DoEasy. Controles (Parte 28): Estilos de barra no controle ProgressBar
DoEasy. Controles (Parte 29): Controle auxiliar "ScrollBar"
DoEasy. Controles (Parte 30): Animando o controle "ScrollBar"
DoEasy. Controles (Parte 31): Rolando o conteúdo do controle "ScrollBar"

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/12849

Arquivos anexados |
MQL5.zip (4733.14 KB)
Redes neurais de maneira fácil (Parte 47): Espaço contínuo de ações Redes neurais de maneira fácil (Parte 47): Espaço contínuo de ações
Neste artigo, estamos ampliando o escopo das tarefas do nosso agente. No processo de treinamento, incluiremos alguns aspectos de gerenciamento de dinheiro e risco, que são partes integrantes de qualquer estratégia de negociação.
Entendendo a programação orientada a objetos (POO) em MQL5 Entendendo a programação orientada a objetos (POO) em MQL5
Como desenvolvedores, precisamos aprender a criar e desenvolver software que possa ser usado de forma repetida e flexível, sem duplicação de código, especialmente quando lidamos com diferentes objetos que têm comportamentos distintos. Isso pode ser facilmente alcançado usando métodos e princípios de programação orientada a objetos. Neste artigo, apresentaremos os fundamentos da programação orientada a objetos em MQL5.
Iniciando o VPS MetaTrader pela primeira vez - Instruções passo a passo Iniciando o VPS MetaTrader pela primeira vez - Instruções passo a passo
Para todos que usam Expert Advisors ou assinaturas de sinais, mais cedo ou mais tarde, será necessário um serviço de hospedagem confiável 24 horas por dia para a plataforma de negociação. Recomendamos o uso do VPS MetaTrader por vários motivos. Você pode pagar e gerenciar o serviço através da sua conta na MQL5.community.
ChatGPT da OpenAI dentro do framework de desenvolvimento MQL4 e MQL5 ChatGPT da OpenAI dentro do framework de desenvolvimento MQL4 e MQL5
Neste artigo, vamos experimentar e explorar a inteligência artificial ChatGPT da OpenAI, a fim de entender suas capacidades com o objetivo de reduzir o tempo e o esforço de desenvolvimento de seus Expert Advisors, indicadores e scripts. Vou rapidamente abordar essa tecnologia e tentar mostrar como usá-la corretamente para programar nas linguagens MQL4 e MQL5.