![DoEasy. Controles (Parte 12): Objeto base lista, objetos WinForms ListBox e ButtonListBox](https://c.mql5.com/2/49/doeasy_012_600x314.jpg)
DoEasy. Controles (Parte 12): Objeto base lista, objetos WinForms ListBox e ButtonListBox
Conteúdo
- Ideia
- Modificando as classes da biblioteca
- Classe base para listas de objetos WinForms
- Classes para objetos WinForms ListBox e ButtonListBox
- Teste
- O que virá a seguir?
Ideia
Continuamos a trabalhar na criação de objetos WinForms na biblioteca. No último artigo, criamos um objeto CheckedListBox, que é essencialmente uma lista de objetos CheckBox. Mas já que vamos criar mais várias listas de objetos WinForms, é lógico agora criar uma classe para a lista de objetos base de objetos WinForms e, com base nela, criar todos os vários outros. A classe conterá a principal funcionalidade para trabalhar com listas de objetos WinForms e, mais tarde, possuirá vários dados vinculados ao controle (DataBindings no MS Visual Studio), cujos dados são usados para exibir listas de elementos em linhas, o que permitirá exibir dados completamente diferentes do ambiente tanto provenientes da própria biblioteca quanto do terminal e seus bancos de dados, e, claro, isso facilitará ter acesso a eles por meio dessas listas.
Com base nessa classe base, hoje vamos criar outro objeto WinForms ListBox, que é uma lista simples que exibe uma determinada coleção de dados. Por enquanto, a lista só exibirá os nomes dos itens criados quando ela é construída. Os nomes dos itens permitirão interagir com o mouse (alterando a cor de fundo ao passar o mouse e selecionar). Mas o objeto ainda não terá nenhum uso prático (será apenas uma prévia da futura funcionalidade do objeto), pois para criar a funcionalidade de evento de objetos WinForms, ainda precisamos criar um certo número deles, para determinar os dados que precisam ser passados para manipuladores de eventos. E, quanto mais objetos diferentes forem criados, mais teremos uma ideia sobre os dados necessários e sua estrutura, e serão necessários para a correta criação da funcionalidade de eventos dos objetos WinForms.
Também hoje vamos criar um objeto-lista de botões. Como o ListBox, suas linhas são criadas apenas com base na classe do objeto CButton, seria bastante lógico criar um objeto adicional que exibisse um conjunto de botões em sua lista (semelhante ao objeto CheckedListBox que exibe objetos CheckBox em sua lista ). Sim, tal objeto não está na lista de controles padrão no MS Visual Studio, mas o que nos impede de experimentar?
Modificando as classes da biblioteca
Após as últimas atualizações do terminal do cliente, a classe de negociação CTrading da biblioteca parou de funcionar, uma vez que o acesso aos seus métodos privados OpenPosition() e PlaceOrder() das classes herdadas foi interrompido.
Por esse motivo, no arquivo \MQL5\Include\DoEasy\Trading.mqh, vamos definir a seção protegida da classe para estes métodos:
//--- 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); //--- (1) Open a position, (2) place a pending order protected: 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:
Aqui criamos uma seção protegida dentro da seção privada da classe e, em seguida, retornamos a seção privada novamente.
No arquivo \MQL5\Include\DoEasy\Defines.mqh, adicionamos três novos tipos à enumeração de tipos de elementos gráficos:
//+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_STANDARD, // Standard graphical object GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED, // Extended standard graphical object GRAPH_ELEMENT_TYPE_SHADOW_OBJ, // Shadow object GRAPH_ELEMENT_TYPE_ELEMENT, // Element GRAPH_ELEMENT_TYPE_FORM, // Form GRAPH_ELEMENT_TYPE_WINDOW, // Window //--- WinForms GRAPH_ELEMENT_TYPE_WF_UNDERLAY, // Panel object underlay GRAPH_ELEMENT_TYPE_WF_BASE, // Windows Forms Base //--- 'Container' object types are to be set below GRAPH_ELEMENT_TYPE_WF_CONTAINER, // Windows Forms container base object GRAPH_ELEMENT_TYPE_WF_PANEL, // Windows Forms Panel GRAPH_ELEMENT_TYPE_WF_GROUPBOX, // Windows Forms GroupBox //--- 'Standard control' object types are to be set below GRAPH_ELEMENT_TYPE_WF_COMMON_BASE, // Windows Forms base standard control GRAPH_ELEMENT_TYPE_WF_LABEL, // Windows Forms Label GRAPH_ELEMENT_TYPE_WF_BUTTON, // Windows Forms Button GRAPH_ELEMENT_TYPE_WF_CHECKBOX, // Windows Forms CheckBox GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON, // Windows Forms RadioButton GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX, // Base list object of Windows Forms elements GRAPH_ELEMENT_TYPE_WF_LIST_BOX, // Windows Forms ListBox GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // Windows Forms CheckedListBox GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // Windows Forms ButtonListBox }; //+------------------------------------------------------------------+
No final da lista de propriedades inteiras do item gráfico na tela, adicionamos duas novas propriedades e aumentamos o número de propriedades inteiras de 83 para 85:
//+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Element ID CANV_ELEMENT_PROP_TYPE, // Graphical element type //---... //---... CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Color of control checkbox when clicking on the control CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Color of control checkbox when hovering the mouse over the control CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN, // Horizontal display of columns in the ListBox control CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH, // Width of each ListBox control column }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (85) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
Adicionamos novas propriedades à lista de critérios para ordenar os elementos gráficos na tela:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type //---... //---... SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN, // Sort by color of control checkbox when clicking on the control SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER, // Sort by color of control checkbox when hovering the mouse over the control SORT_BY_CANV_ELEMENT_LIST_BOX_MULTI_COLUMN, // Sort by horizontal column display flag in the ListBox control SORT_BY_CANV_ELEMENT_LIST_BOX_COLUMN_WIDTH, // Sort by the width of each ListBox control column //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name SORT_BY_CANV_ELEMENT_TEXT, // Sort by graphical element text }; //+------------------------------------------------------------------+
Agora poderemos classificar e filtrar listas e selecionar objetos por essas novas propriedades. Em princípio, não há necessidade particular de trabalhar com objetos gráficos para construir uma GUI (não vejo outras maneiras de usar a pesquisa com base nessas propriedades), mas inserimos absolutamente todas as propriedades dos objetos GUI nestas listas para poder construir então uma GUI em nosso programa para gerar um shell diretamente na janela do gráfico e trabalhar com ele - aqui é onde absolutamente todas as propriedades dos elementos gráficos precisarão ser obtidas e alteradas.
No arquivo \MQL5\Include\DoEasy\Data.mqh, escrevemos os índices das novas mensagens da biblioteca , renomeamos o índice MSG_CHECKED_LIST_ERR_FAILED_CREATE_CHECK_BOX_OBJ para um novo e excluímos o índice MSG_CHECKED_LIST_ERR_FAILED_GET_CHECK_BOX_OBJ:
//--- WinForms standard MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE, // WinForms base standard control MSG_GRAPH_ELEMENT_TYPE_WF_LABEL, // Label control MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX, // CheckBox control MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON, // RadioButton control MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON, // Button control MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX, // Base list object of Windows Forms elements MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX, // ListBox control MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // CheckedListBox control MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // ButtonListBox control MSG_GRAPH_OBJ_BELONG_PROGRAM, // Graphical object belongs to a program MSG_GRAPH_OBJ_BELONG_NO_PROGRAM, // Graphical object does not belong to a program
...
//--- CPanel MSG_PANEL_OBJECT_ERR_FAILED_CREATE_UNDERLAY_OBJ, // Failed to create the underlay object MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE, // Error. The created object should be of WinForms Base type or be derived from it //--- ElementsListBox MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ, // Failed to get a graphical element //--- CButtonListBox MSG_BUTT_LIST_ERR_FAILED_SET_GROUP_BUTTON, // Failed to set the group for the button with the index MSG_BUTT_LIST_ERR_FAILED_SET_TOGGLE_BUTTON, // Failed to set the Toggle flag to the button with the index //--- Integer properties of graphical elements
...
MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Color of control checkbox when clicking on the control MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Color of control checkbox when hovering the mouse over the control MSG_CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN, // Horizontal display of columns in the ListBox control MSG_CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH, // Width of each ListBox control column //--- Real properties of graphical elements //--- String properties of graphical elements MSG_CANV_ELEMENT_PROP_NAME_OBJ, // Graphical element object name MSG_CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name MSG_CANV_ELEMENT_PROP_TEXT, // Graphical element text }; //+------------------------------------------------------------------+
e mensagens de texto correspondentes aos índices recém-adicionados:
//--- WinForms standard {"Базовый стандартный элемент управления WinForms","Basic Standard WinForms Control"}, {"Элемент управления \"Label\"","Control element \"Label\""}, {"Элемент управления \"CheckBox\"","Control element \"CheckBox\""}, {"Элемент управления \"RadioButton\"","Control element \"RadioButton\""}, {"Элемент управления \"Button\"","Control element \"Button\""}, {"Базовый объект-список Windows Forms элементов","Basic Windows Forms List Object"}, {"Элемент управления \"ListBox\"","Control element \"ListBox\""}, {"Элемент управления \"CheckedListBox\"","Control element \"CheckedListBox\""}, {"Элемент управления \"ButtonListBox\"","Control element \"ButtonListBox\""}, {"Графический объект принадлежит программе","The graphic object belongs to the program"}, {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},
...
//--- CPanel {"Не удалось создать объект-подложку","Failed to create underlay object"}, {"Ошибка. Создаваемый объект должен иметь тип WinForms Base или быть его наследником","Error. The object being created must be of type WinForms Base or be derived from it"}, //--- ElementsListBox {"Не удалось получить графический элемент ","Failed to get graphic element "}, //--- CButtonListBox {"Не удалось установить группу кнопке с индексом ","Failed to set group for button with index "}, {"Не удалось установить флаг \"Переключатель\" кнопке с индексом ","Failed to set the \"Toggle\" flag on the button with index "}, //--- Integer properties of graphical elements
...
{"Цвет флажка проверки элемента управления при нажатии мышки на элемент управления","Control Checkbox Colorl when the mouse is pressed on the control"}, {"Цвет флажка проверки элемента управления при наведении мышки на элемент управления","Control Checkbox Colorl when hovering the mouse over the control"}, {"Горизонтальное отображение столбцов в элементе управления ListBox","Display columns horizontally in a ListBox control"}, {"Ширина каждого столбца элемента управления ListBox","The width of each column of the ListBox control"}, //--- String properties of graphical elements {"Имя объекта-графического элемента","The name of the graphic element object"}, {"Имя графического ресурса","Image resource name"}, {"Текст графического элемента","Text of the graphic element"}, }; //+---------------------------------------------------------------------+
Precisamos retornar uma descrição do tipo de elemento gráfico especificado. No momento, a classe do objeto gráfico base da biblioteca possui o método TypeElementDescription(), que retorna uma descrição do tipo de objeto gráfico atual, isto é, uma descrição do seu próprio tipo. Mas precisaremos retornar a descrição do elemento gráfico especificado nos parâmetros de entrada do método. Portanto, vamos adicionar um parâmetro formal ao método existente, no qual passaremos o tipo do objeto, e vamos escrever um método sobrecarregado que retorne o tipo do objeto atual.
Vamos fazer as alterações necessárias no arquivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh.
Em uma seção pública, vamos declarar um novo método sobrecarregado, no qual passaremos o tipo do objeto, enquanto o método anterior passará o tipo do objeto atual para o novo método:
//--- Return the graphical object type (ENUM_OBJECT) calculated from the object type (ENUM_OBJECT_DE_TYPE) passed to the method ENUM_OBJECT GraphObjectType(const ENUM_OBJECT_DE_TYPE obj_type) const { return ENUM_OBJECT(obj_type-OBJECT_DE_TYPE_GSTD_OBJ-1); } //--- Return the description of the type of the graphical object (1) type, (2, 3) element, (4) affiliation and (5) species string TypeGraphObjectDescription(void); string TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type); string TypeElementDescription(void); string BelongDescription(void); string SpeciesDescription(void);
e fora do corpo da classe vamos finalizar os dois métodos.
No método com parâmetro formal, vamos adicionar o retorno da descrição de novos tipos de elementos gráficos da biblioteca de acordo com o tipo especificado:
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type) { return ( type==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : type==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : type==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : type==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : //--- WinForms type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY) : type==GRAPH_ELEMENT_TYPE_WF_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE) : //--- Containers type==GRAPH_ELEMENT_TYPE_WF_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER) : type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX) : type==GRAPH_ELEMENT_TYPE_WF_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL) : //--- Standard controls type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE) : type==GRAPH_ELEMENT_TYPE_WF_LABEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL) : type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX) : type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX) : "Unknown" ); } //+------------------------------------------------------------------+
E o método anterior, que retornou o tipo do objeto atual, agora retornará o resultado da chamada a seu método sobrecarregado, para o qual é passado o tipo do objeto atual:
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(void) { return this.TypeElementDescription(this.TypeGraphElement()); } //+------------------------------------------------------------------+
Como a operação dos botões dentro do grupo depende diretamente de a qual grupo o botão pertence, para controlar a seleção correta do grupo de botões, escreveremos a exibição do número do grupo em uma mensagem de teste no log.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh, no manipulador de eventos "Cursor dentro da área ativa, botão do mouse (esquerdo) liberado", adicionamos o processamento do botão que opera dentro do grupo e introduzimos os números do grupo de botões na linha de registro no log:
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+------------------------------------------------------------------+ void CButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- The mouse button released outside the element means refusal to interact with the element if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge()) { //--- If this is a simple button, set the initial background color if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorInit(),false); //--- If this is the toggle button, set the initial color depending on whether the button is pressed or not else this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundColorToggleONInit(),false); //--- Set the initial frame color this.SetBorderColor(this.BorderColorInit(),false); //--- Send the test message to the journal Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel")); } //--- The mouse button released within the element means a click on the control else { //--- If this is a simple button, set the color for "The cursor is over the active area" status if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorMouseOver(),false); //--- If this is the toggle button, else { //--- if the button does not work in the group, set its state to the opposite, if(!this.GroupButtonFlag()) this.SetState(!this.State()); //--- if the button is not pressed yet, set it to the pressed state else if(!this.State()) this.SetState(true); //--- set the background color for "The cursor is over the active area" status depending on whether the button is clicked or not this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false); } //--- Send the test message to the journal Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click"),", this.State()=",this.State(),", ID=",this.ID(),", Group=",this.Group()); //--- Set the frame color for "The cursor is over the active area" status this.SetBorderColor(this.BorderColorMouseOver(),false); } //--- Redraw the object this.Redraw(false); } //+------------------------------------------------------------------+
Para botões de alternância, temos vários modos de operação. O botão de alternância simples pode ser pressionado ou não. Se houver vários botões de alternância localizados no mesmo contêiner e com o mesmo grupo, esses botões funcionam de tal forma que pressionar um provoca a liberação do restante dos botões desse grupo. Neste caso, se você pressionar novamente o botão já pressionado, ele será liberado.
Se adicionarmos um sinalizador de botão de grupo para cada grupo de botões, e cada um deles tiver o mesmo grupo, esses botões funcionarão de maneira um pouco diferente. Da mesma forma, pressionar um faz com que o resto seja liberado, mas pressionar um botão já pressionado não o liberará. Ou seja, neste caso, um dos botões estará sempre pressionado.
O registro do número do grupo é uma mensagem de depuração, e após a depuração ela será excluída, mas para entender o funcionamento correto, às vezes precisamos ver a qual grupo pertence ao botão que foi pressionado.
Vamos renomear o método GroupButton() que retorna o sinalizador do botão de grupo para deixar claro que o método retorna exatamente o sinalizador, não o número do grupo:
bool State(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE); } //--- (1) Set and (2) return the group flag void SetGroupButtonFlag(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,flag); } bool GroupButtonFlag(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP); } //--- (1,2) Set and (3) return the main background color for the 'enabled' status void SetBackgroundColorToggleON(const color colour,const bool set_init_color)
Classe base para listas de objetos WinForms
Os objetos-lista de objetos WinForms têm funcionalidade semelhante, por isso é aconselhável criar um objeto base para eles que tenha funcionalidade comum para seus descendentes e do qual os outros serão herdados, e estes últimos que já venham a criar sua própria funcionalidade única inerente à classe filha.
No diretório de biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo Label.mqh da classe CLabel.
A classe deve ser herdada da classe contêiner base e um arquivo desta classe deve ser incluído nela:
//+------------------------------------------------------------------+ //| ElementsListBox.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Containers\Container.mqh" //+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CElementsListBox : public CContainer { }
Na seção privada da classe, vamos declarar um método que retorna as coordenadas do próximo objeto criado na lista. Na seção protegida, declararemos um método que cria o número especificado de objetos WinForms definidos. Na seção pública da classe, vamos declarar um construtor paramétrico e métodos para definir e retornar permissão para colocar objetos na lista em várias colunas e um método que define a largura de cada coluna:
//+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CElementsListBox : public CContainer { private: //--- Return the coordinates of the next object placed in the list void GetCoordsObj(CWinFormBase *obj,int &x,int &y); protected: //--- Create the specified number of specified WinForms objects void CreateElements(ENUM_GRAPH_ELEMENT_TYPE element_type, const int count, const int x, const int y, const int w, const int h, uint new_column_width=0, const bool autosize=true); public: //--- Constructor CElementsListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); //--- (1) Set and (2) return the flag of horizontal display of columns void SetMultiColumn(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,flag); } bool MultiColumn(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN); } //--- (1) Set and (2) return the width of each column void SetColumnWidth(const uint value) { this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,value); } uint ColumnWidth(void) const { return (uint)this.GetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH); } }; //+------------------------------------------------------------------+
Vamos dar uma olhada nos métodos declarados.
No construtor da classe, especificamos o tipo do elemento gráfico e o tipo do objeto gráfico da biblioteca, especificamos o tamanho de uma moldura simples de um pixel, definimos as cores padrão para a moldura e os textos dentro do objeto, definimos o proibição do exibição da lista por colunas e também definimos a largura da coluna como zero:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CElementsListBox::CElementsListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CContainer(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBorderColor(CLR_DEF_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetMultiColumn(false); this.SetColumnWidth(0); } //+------------------------------------------------------------------+
Método que cria o número de objetos WinForms especificados:
//+------------------------------------------------------------------+ //| Create the specified number of certain WinForms objects | //+------------------------------------------------------------------+ void CElementsListBox::CreateElements(ENUM_GRAPH_ELEMENT_TYPE element_type, const int count, const int x, const int y, const int w, const int h, uint new_column_width=0, const bool autosize=true) { //--- Set the width of columns if the value greater than zero has been passed if(new_column_width>0) { if(this.ColumnWidth()!=new_column_width) this.SetColumnWidth(new_column_width); } //--- Create a pointer to the created WinFormBase object CWinFormBase *obj=NULL; //--- In the loop through the specified number of objects for(int i=0;i<count;i++) { //--- Get the coordinates of the created object int coord_x=x, coord_y=y; this.GetCoordsObj(obj,coord_x,coord_y); //--- If the object could not be created, send the appropriate message to the log and move on to the next one if(!this.CreateNewElement(element_type,coord_x,coord_y,w,h,clrNONE,255,true,false)) { ::Print(DFUN,MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ,this.TypeElementDescription(element_type)); continue; } //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(element_type)); continue; } //--- Set the frame size of the created object to zero obj.SetBorderSizeAll(0); //--- Set the opacity of the base object and the default background color obj.SetOpacity(this.Opacity()); obj.SetBackgroundColor(this.BackgroundColor(),true); obj.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_STD_MOUSE_OVER); obj.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_STD_MOUSE_DOWN); } //--- If the flag of auto resizing the base object is passed to the method, //--- set the auto resize mode to "increase and decrease" if(autosize) this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK,false); } //+------------------------------------------------------------------+
A lógica do método está descrita nos comentários ao código e, espero, tudo está claro lá. O redimensionamento automático do painel, no qual são construídos os objetos criados, é necessário para alinhar as dimensões do painel com os elementos criados. Algumas classes não precisarão fazer isso, por isso foi introduzido um sinalizador para indicar a necessidade de alinhar as dimensões do painel ao seu conteúdo.
Método que retorna as coordenadas do próximo objeto colocado na lista:
//+------------------------------------------------------------------+ //| Return the coordinates of the next object placed in the list | //+------------------------------------------------------------------+ void CElementsListBox::GetCoordsObj(CWinFormBase *obj,int &x,int &y) { //--- Save the coordinates passed to the method in the variables int coord_x=x; int coord_y=y; //--- If the flag of using multiple columns is not set, if(!this.MultiColumn()) { //--- set the X coordinate the same as the one passed to the method, //--- set the Y coordinate for the first object in the list to be equal to the one passed to the method, //--- set the rest 4 pixels lower than the bottom edge of the previous object located above. //--- After setting the coordinates to the variables, leave the method x=coord_x; y=(obj==NULL ? coord_y : obj.BottomEdgeRelative()+4); return; } //--- If multiple columns can be used //--- If this is the first object in the list, if(obj==NULL) { //--- set the coordinates the same as those passed to the method and leave x=coord_x; y=coord_y; return; } //--- If this is not the first object in the list //--- If (the bottom border of the previous object + 4 pixels) is below the bottom border of the ListBox panel (the next object will go beyond the borders), if(obj.BottomEdge()+4>this.BottomEdge()) { //--- If the columns width is zero, then the X coordinate of the created object will be the right border of the previous object + 6 pixels //--- Otherwise, if the width of the columns is greater than zero, then the X coordinate of the created object will be the X coordinate of the previous one + the column width //--- The Y coordinate will be the value passed to the method (start placing objects in a new column) x=(this.ColumnWidth()==0 ? obj.RightEdgeRelative()+6 : int(obj.CoordXRelative()+this.ColumnWidth())); y=coord_y; } //--- If the created object is placed within the ListBox panel, else { //--- the X coordinate of the created object will be the offset of the previous one from the panel edge minus the width of its frame, //--- the Y coordinate will be the lower border of the previous object located above plus 4 pixels x=obj.CoordXRelative()-this.BorderSizeLeft(); y=obj.BottomEdgeRelative()+4; } } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. O método calcula a coordenada do próximo objeto, com base nas coordenadas iniciais onde o primeiro objeto da lista deve estar localizado e a partir das coordenadas dos objetos já localizados na lista (anterior). Além disso, se for definido um sinalizador que permita que os objetos sejam organizados em várias colunas, o método calcula se o próximo objeto criado caberá na área de seu painel (somente sua coordenada de localização é considerada, não o objeto inteiro) . Se um objeto (sua coordenada Y) se encaixa no painel, ele é construído sob o anterior. Se a coordenada for além do painel, o objeto será construído à direita em relação à borda direita do objeto anterior na coordenada Y inicial - isso significa o início da construção de uma nova coluna. Depois que os objetos são posicionados dentro do painel, suas dimensões são ajustadas ao conteúdo no método a partir do qual este é chamado. Assim, todas as imprecisões na localização dos objetos mais baixos serão corrigidas. Imprecisões (coordenada Y do objeto inferior dentro do painel e seu resto fora dos limites) podem ser formadas devido ao fato de que a altura de um objeto na lista pode diferir do outro, pois mais tarde poderemos colocar objetos diferentes nas listas. Portanto, em vez de calcular as dimensões do objeto futuro e levar em consideração se ele calhará inteiramente na área do painel e se a distância da borda inferior à borda inferior do painel estará correta, é muito mais fácil para nós basta ajustar as dimensões do painel ao conteúdo já criado dentro dele.
Como agora temos um objeto base para objetos WinForms lista, precisamos modificar o objeto-lista CheckedListBox já criado.
Vamos fazer correções na classe deste objeto no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckedListBox.mqh.
Em vez da inclusão no arquivo desta classe do arquivo da classe do objeto-painel
#include "..\Containers\Panel.mqh"
incluímos o arquivo da classe recém-criada. Assim, agora herdaremos dela:
//+------------------------------------------------------------------+ //| CheckedListBox.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ElementsListBox.mqh" //+------------------------------------------------------------------+ //| CheckedListBox object class of the WForms controls | //+------------------------------------------------------------------+ class CCheckedListBox : public CElementsListBox {
Na declaração do método que cria o número especificado de objetos CheckBox, adicionamos parâmetros formais para especificar a largura do objeto que está sendo criado e o novo valor da largura da coluna:
public: //--- Create the specified number of CheckBox objects void CreateCheckBox(const int count,const int width,const int new_column_width=0); //--- Constructor
No construtor da classe, em sua lista de inicialização, passamos agora parâmetros para a nova classe pai:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CCheckedListBox::CCheckedListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CElementsListBox(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBorderColor(CLR_DEF_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_FORE_COLOR,true); } //+------------------------------------------------------------------+
O método que cria o número especificado de objetos CheckBox agora foi redesenhado, pois a nova classe pai tem um método para criar o número especificado de objetos do tipo especificado:
//+------------------------------------------------------------------+ //| Create the specified number of CheckBox objects | //+------------------------------------------------------------------+ void CCheckedListBox::CreateCheckBox(const int count,const int width,const int new_column_width=0) { //--- Create a pointer to the CheckBox object CCheckBox *obj=NULL; //--- Create the specified number of CheckBox objects CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,count,2,2,width,DEF_CHECK_SIZE+1,new_column_width); //--- In the loop by the created number of objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_CHECKBOX)); continue; } //--- Set the left center alignment of the checkbox and the text obj.SetCheckAlign(ANCHOR_LEFT); obj.SetTextAlign(ANCHOR_LEFT); //--- Set the object text obj.SetText("CheckBox"+string(i+1)); } } //+------------------------------------------------------------------+
Sendo assim, agora criamos primeiro uma lista de objetos e, em seguida, em um loop pelo número de objetos criados, definimos os parâmetros necessários para cada um.
O método tornou-se mais curto e mais legível.
Classes para objetos WinForms ListBox e ButtonListBox
Vamos começar criando uma nova classe do objeto Winforms ListBox.
Este objeto é uma lista de texto simples na qual você pode selecionar qualquer item da lista. Como precisamos das linhas da lista para poder interagir com o mouse, e o objeto rótulo de texto (classe de biblioteca CLabel) não possui essa funcionalidade, é lógico usar a classe de objeto-botão para exibir a lista. Eles podem reagir ao passar o mouse sobre eles e serem selecionados (botão pressionado).
Simplesmente, para fazer com que os botões pareçam itens de lista de texto, precisamos tornar a cor da borda igual à cor do plano de fundo. Nesse caso, o botão se misturará ao plano de fundo e apenas o texto nele ficará visível. Quando passarmos o mouse sobre a área do botão (sobre o texto), a cor de fundo do texto muda. E quando você clicar no texto (botão), ele será selecionado (o botão é pressionado).
Para que a lista criada a partir de botões se comporte como o ListBox no MS Visual Studio, precisamos fazer de todos os botões de lista um botão de grupo (definimos um sinalizador de grupo para eles) e torná-los botões de alternância (com a capacidade de ter dois estados - on/off). A cada botão (linha da lista) será atribuído um número de grupo correspondente ao número do grupo do painel, e o sinalizador de grupo definido para cada um dos botões da lista não permitirá desmarcar qualquer item da lista já selecionado.
No diretório de biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo Label.mqh da classe CLabel.
A classe deve ser herdada da classe CCheckBox, e seu arquivo deve estar anexo ao arquivo da classe que está sendo criada:
//+------------------------------------------------------------------+ //| ListBox.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ElementsListBox.mqh" //+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CListBox : public CElementsListBox { }
Na seção privada da classe, vamos declarar um método virtual para criar um novo objeto gráfico, e na seção pública, um método para criar uma lista e um construtor paramétrico:
//+------------------------------------------------------------------+ //| Class of the base object of the WForms control list | //+------------------------------------------------------------------+ class CListBox : public CElementsListBox { private: //--- Create a new graphical object virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity); public: //--- Create a list from the specified number of rows (Label objects) void CreateList(const int line_count); //--- Constructor CListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
No construtor paramétrico, especificamos o tipo do elemento gráfico e o tipo do objeto da biblioteca, e definimos os valores padrão para a moldura do objeto, cor da moldura e texto, e especificamos que não é permitida mais de uma coluna e que a largura da coluna é zero:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CListBox::CListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CElementsListBox(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_LIST_BOX); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBorderColor(CLR_DEF_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetMultiColumn(false); this.SetColumnWidth(0); } //+------------------------------------------------------------------+
Método que cria uma lista a partir do número especificado de linhas:
//+--------------------------------------------------------------------+ //| Create the list from the specified number of rows (Button objects) | //+--------------------------------------------------------------------+ void CListBox::CreateList(const int count) { //--- Create the pointer to the Button object CButton *obj=NULL; //--- Create the specified number of Button objects CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_BUTTON,count,2,2,this.Width()-4,12,0,false); //--- In the loop by the created number of objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON)); continue; } //--- Set left center text alignment obj.SetTextAlign(ANCHOR_LEFT); //--- Set the object text obj.SetFontSize(8); obj.SetText("ListBoxItem"+string(i+1)); //--- Set the frame colors equal to the background colors and the flag of the toggle button obj.SetBorderColor(obj.BackgroundColor(),true); obj.SetBorderColorMouseDown(obj.BackgroundColorMouseDown()); obj.SetBorderColorMouseOver(obj.BackgroundColorMouseOver()); obj.SetToggleFlag(true); obj.SetGroupButtonFlag(true); } } //+------------------------------------------------------------------+
O método é idêntico ao método CheckedListBox revisado acima. Ele cria objetos-botões como strings e define as configurações de cor da borda para eles para que se misturem com a cor de fundo. Para cada botão criado, são definidos um sinalizador de botão de alternância e um sinalizador de botão que funciona em um grupo. O número do grupo para cada botão é herdado do painel em que eles são colocados.
Método virtual que cria um novo objeto gráfico:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CListBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string obj_name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { string name=this.CreateNameDependentObject(obj_name); //--- create the Button object CGCnvElement *element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h); if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name); //--- set the object relocation flag and relative coordinates element.SetMovable(movable); element.SetCoordXRelative(element.CoordX()-this.CoordX()); element.SetCoordYRelative(element.CoordY()-this.CoordY()); return element; } //+------------------------------------------------------------------+
O objeto-botão é criado no método e os parâmetros mínimos são definidos para ele - o sinalizador de realocação e as coordenadas relativas do objeto.
Nesta fase, isso é tudo o que é necessário para esta classe funcionar. Naturalmente, estenderemos ainda mais as classes de nossos objetos-listas com a funcionalidade necessária para receber objetos selecionados e enviar mensagens, mas faremos isso um pouco mais tarde.
Agora vamos criar uma classe de objeto-lista de objetos-botões. Tal objeto combinará os botões criados no painel. Os botões podem ser atribuídos a diferentes grupos, eles podem definir sinalizadores de botão de grupo e outros parâmetros. Assim, será possível criar diferentes grupos de botões em um objeto (em um painel).
No diretório de biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo Label.mqh da classe CLabel.
A classe deve ser herdada da classe CCheckBox, e seu arquivo deve estar anexo ao arquivo da classe que está sendo criada:
//+------------------------------------------------------------------+ //| ButtonListBox.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ElementsListBox.mqh" //+------------------------------------------------------------------+ //| ButtonListBox object class of WForms controls | //+------------------------------------------------------------------+ class CButtonListBox : public CElementsListBox { }
Na seção privada da classe, declararemos um método para criar um novo objeto gráfico e, na seção pública, declararemos um método para criar o número especificado de botões, um construtor paramétrico e métodos para trabalhar com os botões criados no painel:
//+------------------------------------------------------------------+ //| ButtonListBox object class of WForms controls | //+------------------------------------------------------------------+ class CButtonListBox : public CElementsListBox { private: //--- Create a new graphical object virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity); public: //--- Create the specified number of CheckBox objects void CreateButton(const int count,const int width,const int height,const int new_column_width=0); //--- Constructor CButtonListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); //--- (1) Set and (2) return the group of the button specified by index void SetButtonGroup(const int index,const int group); int ButtonGroup(const int index); //--- (1) Set and (2) return the flag of the group button specified by the button index void SetButtonGroupFlag(const int index,const bool flag); bool ButtonGroupFlag(const int index); //--- Sets the specified button "multiselect" mode void SetMultiSelect(const bool flag); //--- (1) Set and (2) return the "Toggle button" flag of the button specified by index void SetButtonToggle(const int index,const bool flag); bool ButtonToggle(const int index); //--- Set the "Toggle button" flag for all buttons of the object void SetToggle(const bool flag); }; //+------------------------------------------------------------------+
Vamos dar uma olhada nos métodos declarados.
No construtor paramétrico, especificamos o tipo do elemento gráfico, o tipo do objeto da biblioteca, e definimos os valores padrão para a moldura do painel, sua cor e estilo, bem como a cor para os textos dos objetos no painel:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CButtonListBox::CButtonListBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CElementsListBox(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBorderColor(CLR_DEF_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_FORE_COLOR,true); } //+------------------------------------------------------------------+
Método que cria o número especificado de objetos Button:
//+------------------------------------------------------------------+ //| Create the specified number of Button objects | //+------------------------------------------------------------------+ void CButtonListBox::CreateButton(const int count,const int width,const int height,const int new_column_width=0) { //--- Create the pointer to the Button object CButton *obj=NULL; //--- Create the specified number of Button objects CElementsListBox::CreateElements(GRAPH_ELEMENT_TYPE_WF_BUTTON,count,2,2,width,height,new_column_width); //--- In the loop by the created number of objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the created object from the list by the loop index obj=this.GetElement(i); //--- If the object could not be obtained, send the appropriate message to the log and move on to the next one if(obj==NULL) { ::Print(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ,this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_BUTTON)); continue; } //--- Set left center text alignment obj.SetTextAlign(ANCHOR_CENTER); //--- Set the object text obj.SetText("Button"+string(i+1)); } } //+------------------------------------------------------------------+
O método é idêntico aos métodos semelhantes dos objetos-listas vistos acima. Primeiro, o número especificado de objetos-botões é criado e, em seguida, em um loop sobre o número de objetos criados, eles são definidos com seus valores padrão.
Método virtual que cria um novo objeto gráfico:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CButtonListBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string obj_name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { string name=this.CreateNameDependentObject(obj_name); //--- create the CButton object CGCnvElement *element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h); if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name); //--- set the object relocation flag and relative coordinates element.SetMovable(movable); element.SetCoordXRelative(element.CoordX()-this.CoordX()); element.SetCoordYRelative(element.CoordY()-this.CoordY()); return element; } //+------------------------------------------------------------------+
E aqui tudo é exatamente o mesmo que em métodos semelhantes de classes de objetos-listas.
Método que define o grupo do botão especificado pelo índice:
//+------------------------------------------------------------------+ //| Set the group of the button specified by index | //+------------------------------------------------------------------+ void CButtonListBox::SetButtonGroup(const int index,const int group) { CButton *butt=this.GetElement(index); if(butt==NULL) { ::Print(DFUN,CMessage::Text(MSG_BUTT_LIST_ERR_FAILED_SET_GROUP_BUTTON),(string)index); return; } butt.SetGroup(group); } //+------------------------------------------------------------------+
Obtemos um objeto a partir da lista pelo índice especificado. Se o objeto não puder ser obtido, reportamos isso ao log e saímos do método.
O objeto é definido com o número do grupo passado para o método.
Método que retorna o grupo do botão especificado pelo índice:
//+------------------------------------------------------------------+ //| Return the group of the button specified by index | //+------------------------------------------------------------------+ int CButtonListBox::ButtonGroup(const int index) { CButton *butt=this.GetElement(index); return(butt!=NULL ? butt.Group() : WRONG_VALUE); } //+------------------------------------------------------------------+
Obtemos o objeto pelo índice da lista. Se o objeto for recebido, retornamos seu grupo, caso contrário, retornamos -1.
Método que define o sinalizador do botão de grupo do botão especificado pelo índice:
//+------------------------------------------------------------------+ //| Set the flag of the group button specified by the button index | //+------------------------------------------------------------------+ void CButtonListBox::SetButtonGroupFlag(const int index,const bool flag) { CButton *butt=this.GetElement(index); if(butt==NULL) { ::Print(DFUN,CMessage::Text(MSG_BUTT_LIST_ERR_FAILED_SET_GROUP_BUTTON),(string)index); return; } butt.SetGroupButtonFlag(flag); } //+------------------------------------------------------------------+
Obtemos um objeto a partir da lista pelo índice especificado. Se o objeto não puder ser obtido, reportamos isso ao log e saímos do método.
Definimos o sinalizador passado para o método para o objeto recebido.
Método que retorna o sinalizador do botão de grupo especificado pelo índice do botão:
//+------------------------------------------------------------------+ //| Return the flag of the group button specified by the button index| //+------------------------------------------------------------------+ bool CButtonListBox::ButtonGroupFlag(const int index) { CButton *butt=this.GetElement(index); return(butt!=NULL ? butt.GroupButtonFlag() : false); } //+------------------------------------------------------------------+
Obtemos um objeto a partir da lista pelo índice especificado. Se o objeto for recebido, retornamos o sinalizador do botão de grupo, caso contrário, retornamos false.
Método que define o modo "multisseleção" dos botões:
//+------------------------------------------------------------------+ //| Set the "multiselect" mode of the buttons | //+------------------------------------------------------------------+ void CButtonListBox::SetMultiSelect(const bool flag) { int group=this.Group()+(flag ? 1 : 0); for(int i=0;i<this.ElementsTotal();i++) this.SetButtonGroup(i,group+(flag ? i : 0)); } //+------------------------------------------------------------------+
O método permite que você faça com que os botões colocados no painel funcionem independentemente, para que cada um possa ser pressionado/liberado independentemente dos outros botões do painel. Para fazer isso, cada botão deve ser atribuído ao seu próprio grupo.
Primeiro, definimos o valor inicial para o grupo do primeiro botão como o grupo do painel mais 1 - se você deseja permitir a seleção múltipla de botões, ou 0 - se os botões devem ser dependentes um do outro. Além disso, em um loop sobre todos os botões criados, definimos quer seja um novo grupo para cada botão subsequente, calculado como o número do grupo do painel mais o índice do loop, o que significa que cada botão terá um grupo igual à posição do botão na lista+1, ou adicionamos zero ao número do grupo de painéis, o que significa que todos os botões terão um grupo igual ao grupo de painéis.
Método que define o sinalizador "Botão de alternância" do botão especificado pelo índice:
//+------------------------------------------------------------------+ //| Set the "Toggle button" flag | //| button specified by index | //+------------------------------------------------------------------+ void CButtonListBox::SetButtonToggle(const int index,const bool flag) { CButton *butt=this.GetElement(index); if(butt==NULL) { ::Print(DFUN,CMessage::Text(MSG_BUTT_LIST_ERR_FAILED_SET_TOGGLE_BUTTON),(string)index); return; } butt.SetToggleFlag(flag); } //+------------------------------------------------------------------+
Obtemos o botão pelo índice especificado a partir da lista. Se o botão não puder ser obtido, reportamos isso no log e saímos do método.
Definimos o sinalizador passado para o método para o botão recebido.
Método que retorna o sinalizador "Botão de alternância" do botão especificado pelo índice:
//| Return the "Toggle button" flag | //| button specified by index | //+------------------------------------------------------------------+ bool CButtonListBox::ButtonToggle(const int index) { CButton *butt=this.GetElement(index); return(butt!=NULL ? butt.Toggle() : false); } //+------------------------------------------------------------------+
Obtemos o botão pelo índice especificado a partir da lista. Se o botão for recebido, retornamos seu sinalizador de botão de alternância, caso contrário, retornamos false.
Método que define o sinalizador "Botão de alternância" para todos os botões do objeto:
//+------------------------------------------------------------------+ //| Set the "Toggle button" flag to all buttons of the object | //+------------------------------------------------------------------+ void CButtonListBox::SetToggle(const bool flag) { for(int i=0;i<this.ElementsTotal();i++) this.SetButtonToggle(i,flag); } //+------------------------------------------------------------------+
Em um loop sobre todos os objetos na lista, definimos o sinalizador especificado para cada botão subsequente usando o método SetButtonToggle() discutido acima.
Criamos todos os objetos planejados para hoje.
Agora precisamos ter certeza de que a biblioteca "sabe" sobre eles e podemos criá-los a partir de nossos programas.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh do objeto-contêner base, escreveremos pequenas modificações: não precisaremos criar novos objetos diretamente dentro do contêiner, mas adicionar os criados anteriormente a ele. Para simplificar essa ação, precisamos dividir o método que cria o novo objeto anexado em dois - um método criará um novo objeto e o outro definirá algumas propriedades padrão para o novo objeto. Ao adicionar um objeto, não criaremos um novo, mas adicionaremos o especificado à lista e, se necessário, alteraremos seus parâmetros.
Na seção protegida da classe, vamos declarar um novo método para definir parâmetros para o objeto criado:
protected: //--- Adjust the element size to fit its content bool AutoSizeProcess(const bool redraw); //--- Set parameters for the attached object void SetObjParams(CWinFormBase *obj,const color colour); public:
Vamos criá-lo fora do corpo da classe.
//+------------------------------------------------------------------+ //| Set parameters for the attached object | //+------------------------------------------------------------------+ void CContainer::SetObjParams(CWinFormBase *obj,const color colour) { //--- Set the text color of the object to be the same as that of the base container obj.SetForeColor(this.ForeColor(),true); //--- If the created object is not a container, set the same group for it as the one for its base object if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_GROUPBOX) obj.SetGroup(this.Group()); //--- Depending on the object type switch(obj.TypeGraphElement()) { //--- For the Container, Panel and GroupBox WinForms objects case GRAPH_ELEMENT_TYPE_WF_CONTAINER : case GRAPH_ELEMENT_TYPE_WF_PANEL : case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : //--- set the frame color equal to the background color obj.SetBorderColor(obj.BackgroundColor(),true); break; //--- For "Label", "CheckBox" and "RadioButton" WinForms objects case GRAPH_ELEMENT_TYPE_WF_LABEL : case GRAPH_ELEMENT_TYPE_WF_CHECKBOX : case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON : //--- set the object text color depending on the one passed to the method: //--- either the container text color, or the one passed to the method. //--- The frame color is set equal to the text color //--- Set the background color to transparent obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour,true); obj.SetBorderColor(obj.ForeColor(),true); obj.SetBackgroundColor(CLR_CANV_NULL,true); obj.SetOpacity(0,false); break; //--- For the Button WinForms object case GRAPH_ELEMENT_TYPE_WF_BUTTON : //--- set the object text color as a container text color depending on the one passed to the method: //--- set the background color depending on the one passed to the method: //--- either the default standard control background color, or the one passed to the method. //--- The frame color is set equal to the text color obj.SetForeColor(this.ForeColor(),true); obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true); obj.SetBorderColor(obj.ForeColor(),true); obj.SetBorderStyle(FRAME_STYLE_SIMPLE); break; //--- For "ListBox", "CheckedListBox" and "ButtonListBox" WinForms object case GRAPH_ELEMENT_TYPE_WF_LIST_BOX : case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX : case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : //--- set the object text color as a container text color depending on the one passed to the method: //--- set the background color depending on the one passed to the method: //--- either the default standard control background color, or the one passed to the method. //--- The frame color is set equal to the text color obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true); obj.SetBorderColor(CLR_DEF_BORDER_COLOR,true); obj.SetForeColor(CLR_DEF_FORE_COLOR,true); break; default: break; } } //+------------------------------------------------------------------+
Aqui nós simplesmente movemos o bloco de código que define os parâmetros do objeto a partir do método CreateNewElement(). Ao método passamos o ponteiro para o objeto criado, bem como a cor passada para o método CreateNewElement(). Também adicionamos manipulação de novos objetos aqui, e isso coincide com o processamento do objeto CheckedListBox criado anteriormente. Por isso, não tivemos que escrever nada extra, apenas indicamos que esses objetos são processados de forma semelhante ao objeto CheckedListBox em um case de botão de opção switch.
Vamos reescrever o método que cria um novo elemento anexado:
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- If the object type is less than the base WinForms object if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE) { //--- report the error and return 'false' CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE); return false; } //--- If failed to create a new graphical element, return 'false' CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity); if(obj==NULL) return false; //--- Set parameters for the created object this.SetObjParams(obj,colour); //--- If the panel has auto resize enabled and features bound objects, call the resize method if(this.AutoSize() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); //--- Redraw the panel and all added objects, and return 'true' this.Redraw(redraw); return true; } //+------------------------------------------------------------------+
Agora, em vez do bloco de código transferido para o novo método, temos uma chamada para esse novo método. Assim, o código ficou mais curto, simples e claro, e agora podemos usar essa divisão de um método em dois para anexar objetos já criados à lista.
Modificamos a classe de objeto-painel no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh.
Adicionamos arquivos de novos objetos criados hoje à lista de arquivos incluídos:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Container.mqh" #include "GroupBox.mqh" #include "..\..\WForms\Common Controls\ListBox.mqh" #include "..\..\WForms\Common Controls\CheckedListBox.mqh" #include "..\..\WForms\Common Controls\ButtonListBox.mqh" //+------------------------------------------------------------------+
No método que cria um novo objeto gráfico, adicionamos um bloco de código para criar novos objetos:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string obj_name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { string name=this.CreateNameDependentObject(obj_name); CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CONTAINER : element=new CContainer(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : element=new CGroupBox(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_PANEL : element=new CPanel(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LABEL : element=new CLabel(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKBOX : element=new CCheckBox(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON : element=new CRadioButton(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON : element=new CButton(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : element=new CButtonListBox(this.ChartID(),this.SubWindow(),name,x,y,w,h); break; default: break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name); return element; } //+------------------------------------------------------------------+
Alterações idênticas para criar novos objetos foram adicionadas ao mesmo método CreateNewGObject() da classe de objeto-contêiner GroupBox
no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh. Não as consideraremos aqui, pois essas alterações podem ser encontradas nos arquivos anexados ao artigo.
Incluímos o arquivo de classe do objeto ButtonListBox ao arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh da classe-coleção de elementos gráficos:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Graph\WForms\Containers\GroupBox.mqh" #include "..\Objects\Graph\WForms\Containers\Panel.mqh" #include "..\Objects\Graph\WForms\Common Controls\CheckedListBox.mqh" #include "..\Objects\Graph\WForms\Common Controls\ButtonListBox.mqh" #include "..\Objects\Graph\Standard\GStdVLineObj.mqh"
Depois disso, todos os outros objetos criados hoje ficarão visíveis em programas criados com base na biblioteca.
Ficamos por aqui, fizemos todos os objetos e melhorias planejados para hoje.
Teste
Para testar, vamos pegar o Expert Advisor do artigo anterior e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part112\ com o novo nome TstDE112.mq5.
O que vamos testar? No segundo grupo de objetos GroupBox2, criamos novos objetos de lista ButtonListBox e ListBox. O último objeto, suas coordenadas de localização no contêiner, dependerá da aparência dos objetos CheckedListBox e ButtonListBox. Se o sinalizador para a possibilidade de construir listas em várias colunas estiver habilitado, então o objeto ListBox estará localizado abaixo, caso contrário, à direita das duas primeiras.
Também verificaremos o funcionamento dos botões de grupo, especificamente a possibilidade de funcionarem em diferentes grupos e a capacidade do botão ser liberado quando pressionado novamente.
Vamos adicionar dois novos parâmetros às variáveis de entrada do EA:
//--- input parameters sinput bool InpMovable = true; // Panel Movable flag sinput ENUM_INPUT_YES_NO InpAutoSize = INPUT_YES; // Panel Autosize sinput ENUM_AUTO_SIZE_MODE InpAutoSizeMode = AUTO_SIZE_MODE_GROW; // Panel Autosize mode sinput ENUM_BORDER_STYLE InpFrameStyle = BORDER_STYLE_SIMPLE; // Label border style sinput ENUM_ANCHOR_POINT InpTextAlign = ANCHOR_CENTER; // Label text align sinput ENUM_INPUT_YES_NO InpTextAutoSize = INPUT_NO; // Label autosize sinput ENUM_ANCHOR_POINT InpCheckAlign = ANCHOR_LEFT; // Check flag align sinput ENUM_ANCHOR_POINT InpCheckTextAlign = ANCHOR_LEFT; // Check label text align sinput ENUM_CHEK_STATE InpCheckState = CHEK_STATE_UNCHECKED; // Check flag state sinput ENUM_INPUT_YES_NO InpCheckAutoSize = INPUT_YES; // CheckBox autosize sinput ENUM_BORDER_STYLE InpCheckFrameStyle = BORDER_STYLE_NONE; // CheckBox border style sinput ENUM_ANCHOR_POINT InpButtonTextAlign = ANCHOR_CENTER; // Button text align sinput ENUM_INPUT_YES_NO InpButtonAutoSize = INPUT_YES; // Button autosize sinput ENUM_AUTO_SIZE_MODE InpButtonAutoSizeMode= AUTO_SIZE_MODE_GROW; // Button Autosize mode sinput ENUM_BORDER_STYLE InpButtonFrameStyle = BORDER_STYLE_NONE; // Button border style sinput bool InpButtonToggle = false; // Button toggle flag sinput bool InpListBoxMColumn = false; // ListBox MultiColumn flag sinput bool InpButtListMSelect = false; // ButtonListBox Button MultiSelect flag //--- global variables CEngine engine; color array_clr[]; //+------------------------------------------------------------------+
O primeiro parâmetro indicará a possibilidade de criar uma lista em várias colunas (se todos os objetos não couberem na altura do painel), o segundo parâmetro definirá a possibilidade de seleção múltipla de botões no grupo.
Vamos adicionar um bloco de código para criar novos objetos ao manipulador OnInit() do Expert Advisor (apenas parte do código é mostrada):
if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,x,2,w,h,C'0x91,0xAA,0xAE',0,true,false)) { //--- get the pointer to the GroupBox object by its index in the list of bound GroupBox type objects gbox2=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,1); if(gbox2!=NULL) { //--- set the "indented frame" type, the frame color matches the main panel background color, //--- while the text color is the background color of the last attached panel darkened by 1 gbox2.SetBorderStyle(FRAME_STYLE_STAMP); gbox2.SetBorderColor(pnl.BackgroundColor(),true); gbox2.SetForeColor(gbox2.ChangeColorLightness(obj.BackgroundColor(),-1),true); gbox2.SetText("GroupBox2"); //--- Create the CheckedListBox object gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,4,12,160,20,clrNONE,255,true,false); //--- get the pointer to the CheckedListBox object by its index in the list of bound objects of the CheckBox type CCheckedListBox *clbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,0); //--- If CheckedListBox is created and the pointer to it is received if(clbox!=NULL) { clbox.SetMultiColumn(InpListBoxMColumn); clbox.SetColumnWidth(0); clbox.CreateCheckBox(4,66); } //--- Create the ButtonListBox object gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,4,clbox.BottomEdgeRelative()+6,160,30,clrNONE,255,true,false); //--- get the pointer to the ButtonListBox object by its index in the list of attached objects of the Button type CButtonListBox *blbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,0); //--- If ButtonListBox is created and the pointer to it is received if(blbox!=NULL) { blbox.SetMultiColumn(InpListBoxMColumn); blbox.SetColumnWidth(0); blbox.CreateButton(4,66,16); blbox.SetMultiSelect(InpButtListMSelect); blbox.SetToggle(InpButtonToggle); for(int i=0;i<blbox.ElementsTotal();i++) { blbox.SetButtonGroup(i,(i % 2==0 ? blbox.Group()+1 : blbox.Group()+2)); blbox.SetButtonGroupFlag(i,(i % 2==0 ? true : false)); } } //--- Create the ListBox object int lbx=4; int lby=blbox.BottomEdgeRelative()+6; int lbw=146; if(!InpListBoxMColumn) { lbx=blbox.RightEdgeRelative()+6; lby=14; lbw=100; } gbox2.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LIST_BOX,lbx,lby,lbw,70,clrNONE,255,true,false); //--- get the pointer to the ListBox object by its index in the list of attached objects of Button type CListBox *lbox=gbox2.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LIST_BOX,0); //--- If ListBox has been created and the pointer to it has been received if(lbox!=NULL) { lbox.CreateList(4); } } } //--- Redraw all objects according to their hierarchy pnl.Redraw(true); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Aqui criamos dois novos objetos de lista no objeto GroupBox2, e se eles forem criados com sucesso, então criamos quatro objetos em cada um deles.
O código completo do manipulador OnInit() do Expert Advisor pode ser encontrado nos arquivos anexados ao artigo.
Compilamos o Expert Advisor e o iniciamos no gráfico:
Aqui podemos ver que os dois primeiros botões ButtonListBox funcionam de forma um pouco diferente dos dois inferiores. Depende dos sinalizadores definidos. No primeiro caso, os botões são proibidos de serem desativados quando pressionados novamente. Podemos desativar apenas um botão pressionando o segundo. No segundo caso, o botão pode ser desabilitado clicando no segundo e pressionando novamente o já habilitado. Isso é afetado pelo sinalizador do botão de grupo. Se estiver definido, os botões são completamente dependentes um do outro, porque funcionam em grupo.
O objeto-lista funciona corretamente. Mas a aparência deixa muito a desejar, porque, no original em MS Visual Studio, a lista é mais compactada, pois os objetos estão mais próximos uns dos outros. Mas o que nos impede de fazê-lo aqui é o fato de que se colocarmos objetos mais juntos, a cor de fundo do objeto nem sempre funcionará corretamente quando ele interage com o mouse. Assim que encontrarmos e corrigirmos esse "mau funcionamento", poderemos corrigir a aparência dos objetos criados.
O que virá a seguir?
No próximo artigo, continuaremos trabalhando nos elementos gráficos da GUI de programas criados com base na biblioteca.
*Artigos desta série:
DoEasy. Controles (Parte 1): Primeiros passos
DoEasy. Controles (Parte 2): Continuamos trabalhando na classe CPanel
DoEasy. Controles (Parte 3): Criando controles vinculados
DoEasy. Controles (Parte 4): Controle "Painel", parâmetros Padding e Dock
DoEasy. Controles (Parte 5): Objeto base WinForms, controle Painel, parâmetro AutoSize
DoEasy. Controles (Parte 6): Controle "Painel", redimensionamento automático do contêiner para adequá-lo ao seu conteúdo
DoEasy. Controles (Parte 7): Controle "Rótulo de texto"
DoEasy. Controles (Parte 8): Objetos básicos do WinForms por categoria, controles GroupBox e CheckBox
DoEasy. Controles (Parte 9): Reorganizando métodos de objetos WinForms, controles "RadioButton" e "Button"
DoEasy. Controles (Parte 10): Objetos WinForms, dando vida à interface
DoEasy. Controles (Parte 11): Objetos WinForms - grupos, objeto WinForms CheckedListBox
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/11228
![DoEasy. Controles (Parte 13): Otimizando a interação de objetos WinForms com o mouse, dando início ao desenvolvimento do objeto WinForms TabControl](https://c.mql5.com/2/48/MQL5-avatar-doeasy-library-2__1.png)
![Ciência de Dados e Aprendizado de Máquina — Redes Neurais (Parte 01): Entendendo as Redes Neurais Feed Forward](https://c.mql5.com/2/48/forward_neural_network.png)
![Experiências com redes neurais (Parte 2): Otimização inteligente de redes neurais](https://c.mql5.com/2/51/neural_network_experiments_p2.png)
![Aprendendo a construindo um EA que opera de forma automática (Parte 05): Gatilhos manuais (II)](https://c.mql5.com/2/50/Aprendendo_construindo_005_avatar.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso