Graphical Interfaces VII: The Tabs Control (Chapter 2)
Contents
- Introduction
- Tabs control
- Developing a class for creating the Tabs control
- Testing the Tabs Control
- Conclusion
Introduction
The first article Graphical Interfaces I: Preparation of the Library Structure (Chapter 1) explains in detail what this library is for. You will find a list of articles with links at the end of each chapter. There, you can also download a complete version of the library at the current stage of development. The files must be placed in the same directories as they are located in the archive.
The first chapter of seventh part introduced three classes of controls for creating tables: text label table (CLabelsTable), edit box table (CTable) and rendered table (CCanvasTable). In this article (chapter two) we are going to consider the Tabs control. Two classes will be presented for this control - simple and with extended functionality.
Tabs control
Tabs are used to control the display of predefined sets of graphical interface controls. Often, multi-functional applications require a large number of controls to be fit into a confined space allocated for the graphical interface. Tabs can be used to group the controls by categories and to display only the currently needed group. This makes the interface much more accessible and more intuitive for the end user. On the surface, the tabs look like a group of buttons with labels (name of the controls group). At the same time, only one of them can be selected (active).
Let us enumerate all components of this control.
- Background or the area to fit a group of controls
- Tabs
Fig. 1. Components of the Tabs control.
Let us create four modes to position the tabs relative to the area, where other controls will be placed: top, bottom, left and right.
Developing a class for creating the Tabs control
Create the Tabs.mqh file and include it in the WndContainer.mqh file of the library:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Tabs.mqh"
The CTabs must be created in the Tabs.mqh file. In this class, just like in classes of other controls, it is necessary to create standard methods, as well as methods for storing the pointer to the form, to which this control will be attached.
//+------------------------------------------------------------------+ //| Class for creating tabs | //+------------------------------------------------------------------+ class CTabs : public CElement { private: //--- Pointer to the form to which the element is attached CWindow *m_wnd; //--- public: CTabs(void); ~CTabs(void); //--- (1) Stores the form pointer, (2) returns pointers to scrollbars void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Chart event handler virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Timer virtual void OnEventTimer(void); //--- Moving the element virtual void Moving(const int x,const int y); //--- (1) Show, (2) hide, (3) reset, (4) delete virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Set, (2) reset priorities of the left mouse button press virtual void SetZorders(void); virtual void ResetZorders(void); //--- Reset the color virtual void ResetColors(void) {} }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTabs::CTabs(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTabs::~CTabs(void) { }
Tab properties can be divided into unique and common ones. Let us enumerate both lists.
Unique properties
- Array of pointers to the controls attached to the tab
- Text
- Width
Create the TElements structure for the unique properties and declare a dynamic array with this type:
class CTabs : public CElement { private: //--- Structure of properties and arrays of the controls attached to each tab struct TElements { CElement *elements[]; string m_text; int m_width; }; TElements m_tab[]; };
Common properties
- Tabs positioning mode
- Color of the area background
- Size of the tabs along the Y axis (height)
- Colors of tabs in different states
- Color of tab texts in different states
- Color of tab borders
- Priorities of the left mouse button press
To set the positioning mode the ENUM_TABS_POSITION enumeration must be added to the Enums.mqh file:
//+------------------------------------------------------------------+ //| Enumeration of the tabs positioning | //+------------------------------------------------------------------+ enum ENUM_TABS_POSITION { TABS_TOP =0, TABS_BOTTOM =1, TABS_LEFT =2, TABS_RIGHT =3 };
Declaration of fields and methods for setting the properties are provided in the code listing below:
class CTabs : public CElement { private: //--- Positioning of tabs ENUM_TABS_POSITION m_position_mode; //--- Color of the common area background int m_area_color; //--- Size of the tabs along the Y axis int m_tab_y_size; //--- Colors of tabs in different states color m_tab_color; color m_tab_color_hover; color m_tab_color_selected; color m_tab_color_array[]; //--- Color of tab texts in different states color m_tab_text_color; color m_tab_text_color_selected; //--- Color of tab borders color m_tab_border_color; //--- Priorities of the left mouse button press int m_zorder; int m_tab_zorder; //--- public: //--- (1) Set/set the tab positions (top/bottom/left/right), (2) set the tab size along the Y axis void PositionMode(const ENUM_TABS_POSITION mode) { m_position_mode=mode; } ENUM_TABS_POSITION PositionMode(void) const { return(m_position_mode); } void TabYSize(const int y_size) { m_tab_y_size=y_size; } //--- Color (1) of the common background, (2) colors of tabs in different states, (3) color of tab borders void AreaColor(const color clr) { m_area_color=clr; } void TabBackColor(const color clr) { m_tab_color=clr; } void TabBackColorHover(const color clr) { m_tab_color_hover=clr; } void TabBackColorSelected(const color clr) { m_tab_color_selected=clr; } void TabBorderColor(const color clr) { m_tab_border_color=clr; } //--- Color of tab texts in different states void TabTextColor(const color clr) { m_tab_text_color=clr; } void TabTextColorSelected(const color clr) { m_tab_text_color_selected=clr; } };
Before creating a control, it is necessary to add the required number of tabs with an indication of the displayed text and width. Let us write the CTabs::AddTab() method for that. The default values of the method arguments are «» (empty string) and 50 (width).
class CTabs : public CElement { public: //--- Adds a tab void AddTab(const string tab_text="",const int tab_width=50); }; //+------------------------------------------------------------------+ //| Adds a tab | //+------------------------------------------------------------------+ void CTabs::AddTab(const string tab_text,const int tab_width) { //--- Set the size of tab arrays int array_size=::ArraySize(m_tabs); ::ArrayResize(m_tabs,array_size+1); ::ArrayResize(m_tab,array_size+1); //--- Store the passed properties m_tab[array_size].m_text =tab_text; m_tab[array_size].m_width =tab_width; //--- Store the number of tabs m_tabs_total=array_size+1; }
If a certain tab should be preselected when loading the MQL application on the chart, then, before creating the control, it is necessary to specify its index using the CTabs::SelectedTab() method. It will also require a private CTabs::CheckTabIndex() method to check and adjust the index of the selected tab, in case it exceeds range.
class CTabs : public CElement { private: //--- Index of the selected tab int m_selected_tab; //--- public: //--- (1) Store and (2) return index of the selected tab void SelectedTab(const int index) { m_selected_tab=index; } int SelectedTab(void) const { return(m_selected_tab); } //--- private: //--- Check index of the selected tab void CheckTabIndex(void); }; //+------------------------------------------------------------------+ //| Check index of the selected tab | //+------------------------------------------------------------------+ void CTabs::CheckTabIndex(void) { //--- Checking for exceeding the array range int array_size=::ArraySize(m_tab); if(m_selected_tab<0) m_selected_tab=0; if(m_selected_tab>=array_size) m_selected_tab=array_size-1; }
To create the control, we will need three private methods and one public method:
class CTabs : public CElement { private: //--- Objects for creating the element CRectLabel m_main_area; CRectLabel m_tabs_area; CEdit m_tabs[]; //--- public: //--- Methods for creating the tabs bool CreateTabs(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateMainArea(void); bool CreateTabsArea(void); bool CreateButtons(void); };
If no tab is added before attaching the control, then calling the CTabs::CreateTabs() public method will stop creation of the graphical interface and output this message to the log:
//--- If there is no tab in the group, report if(m_tabs_total<1) { ::Print(__FUNCTION__," > This method is to be called, " "if a group contains at least one tab! Use the CTabs::AddTab() method"); return(false); }
Determination and calculation of coordinates for the component objects of the control will also be different depending on the selected tab positioning mode. These calculations require the CTabs::SumWidthTabs() method, which returns the total width of all tabs. In left (TABS_LEFT) and right (TABS_RIGHT) tab positioning modes, it will return the width of the first tab. For the top (TABS_TOP) and bottom (TABS_BOTTOM) modes, the width of all tabs is summed.
class CTabs : public CElement { private: //--- Positioning of tabs ENUM_TABS_POSITION m_position_mode; //--- private: //--- Width of all tabs int SumWidthTabs(void); }; //+------------------------------------------------------------------+ //| Total width of all tabs | //+------------------------------------------------------------------+ int CTabs::SumWidthTabs(void) { int width=0; //--- If tabs are positioned right or left, return the width of the first tab if(m_position_mode==TABS_LEFT || m_position_mode==TABS_RIGHT) return(m_tab[0].m_width); //--- Sum the width of all tabs for(int i=0; i<m_tabs_total; i++) width=width+m_tab[i].m_width; //--- With consideration of one pixel overlay width=width-(m_tabs_total-1); return(width); }
The CTabs::CreateMainArea() method is designed for creating the area where groups of controls will be located. Calculation of coordinates and object sizes looks like this (shortened version of the method):
//+------------------------------------------------------------------+ //| Create the common area background | //+------------------------------------------------------------------+ bool CTabs::CreateMainArea(void) { //--- Forming the object name string name=CElement::ProgramName()+"_tabs_main_area_"+(string)CElement::Id(); //--- Coordinates int x=0; int y=0; //--- Size int x_size=0; int y_size=0; //--- Calculating coordinates and sizes relative to positioning of tabs switch(m_position_mode) { case TABS_TOP : x =CElement::X(); y =CElement::Y()+m_tab_y_size-1; x_size =CElement::XSize(); y_size =CElement::YSize()-m_tab_y_size; break; case TABS_BOTTOM : x =CElement::X(); y =CElement::Y(); x_size =CElement::XSize(); y_size =CElement::YSize()-m_tab_y_size; break; case TABS_RIGHT : x =CElement::X(); y =CElement::Y(); x_size =CElement::XSize()-SumWidthTabs()+1; y_size =CElement::YSize(); break; case TABS_LEFT : x =CElement::X()+SumWidthTabs()-1; y =CElement::Y(); x_size =CElement::XSize()-SumWidthTabs()+1; y_size =CElement::YSize(); break; } //--- Creating the object if(!m_main_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size)) return(false); //--- Setting the properties //--- Margins from the edge //--- Store the size //--- Store coordinates //--- Store the object pointer //... return(true); }
The following is the calculation of coordinates and background sizes for the tabs depending on the positioning mode specified in the CTabs::CreateTabsArea() method:
//+------------------------------------------------------------------+ //| Create the tab background | //+------------------------------------------------------------------+ bool CTabs::CreateTabsArea(void) { //--- Forming the object name string name=CElement::ProgramName()+"_tabs_area_"+(string)CElement::Id(); //--- Coordinates int x=CElement::X(); int y=CElement::Y(); //--- Size int x_size=SumWidthTabs(); int y_size=0; //--- Calculating sizes relative to positioning of tabs if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM) { y_size=m_tab_y_size; } else { y_size=m_tab_y_size*m_tabs_total-(m_tabs_total-1); } //--- Adjust the coordinates for positioning the tabs at the bottom and on the right if(m_position_mode==TABS_BOTTOM) { y=CElement::Y2()-m_tab_y_size-1; } else if(m_position_mode==TABS_RIGHT) { x=CElement::X2()-x_size; } //--- Creating the object if(!m_tabs_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size)) return(false); //--- Setting the properties //--- Margins from the edge //--- Store the size //--- Store coordinates //--- Store the object pointer //... return(true); }
The CTabs::CreateButtons() method only requires the calculation of coordinates to create tabs. The width is set in the custom class of the application before the control is created. Otherwise, the default value (width) is used. Below is a shortened version of the method:
//+------------------------------------------------------------------+ //| Create tabs | //+------------------------------------------------------------------+ bool CTabs::CreateButtons(void) { //--- Coordinates int x =CElement::X(); int y =CElement::Y(); //--- Calculating coordinates relative to positioning of tabs if(m_position_mode==TABS_BOTTOM) y=CElement::Y2()-m_tab_y_size-1; else if(m_position_mode==TABS_RIGHT) x=CElement::X2()-SumWidthTabs(); //--- Check index of the selected tab CheckTabIndex(); //--- Create tabs for(int i=0; i<m_tabs_total; i++) { //--- Forming the object name string name=CElement::ProgramName()+"_tabs_edit_"+(string)i+"__"+(string)CElement::Id(); //--- Calculating coordinates relative to positioning of tabs for each individual tab if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM) x=(i>0) ? x+m_tab[i-1].m_width-1 : CElement::X(); else y=(i>0) ? y+m_tab_y_size-1 : CElement::Y(); //--- Creating the object if(!m_tabs[i].Create(m_chart_id,name,m_subwin,x,y,m_tab[i].m_width,m_tab_y_size)) return(false); //--- Setting the properties //--- Margins from the edge of the panel //--- Coordinates //--- Size //--- Initializing gradient array //--- Store the object pointer } //--- return(true); }
To attach any control to a specific tab, let us write the CTabs::AddToElementsArray() method. It has two arguments: (1) tab index, to which the control should be attached and (2) reference to the control, a pointer to which should be stored in the array of tab controls.
class CTabs : public CElement { public: //--- Add control to the tab array void AddToElementsArray(const int tab_index,CElement &object); }; //+------------------------------------------------------------------+ //| Add control to the array of the specified tab | //+------------------------------------------------------------------+ void CTabs::AddToElementsArray(const int tab_index,CElement &object) { //--- Checking for exceeding the array range int array_size=::ArraySize(m_tab); if(array_size<1 || tab_index<0 || tab_index>=array_size) return; //--- Add pointer of the passed control to array of the specified tab int size=::ArraySize(m_tab[tab_index].elements); ::ArrayResize(m_tab[tab_index].elements,size+1); m_tab[tab_index].elements[size]=::GetPointer(object); }
When switching tabs, it is necessary to hide the controls of the previous tab and display the controls of the newly selected tab. For this purpose, let us create the CTabs::ShowTabElements() method. A check for the control visibility is at the beginning of the method. If the control is hidden, then the program leaves the method. Next, it checks the index of the active tab and adjusts it, if necessary. Then it checks all tabs in a loop and performs the main task of the method.
class CTabs : public CElement { public: //--- Show controls of the selected tab only void ShowTabElements(void); }; //+------------------------------------------------------------------+ //| Show controls of the selected tab only | //+------------------------------------------------------------------+ void CTabs::ShowTabElements(void) { //--- Leave, if the tabs are hidden if(!CElement::IsVisible()) return; //--- Check index of the selected tab CheckTabIndex(); //--- for(int i=0; i<m_tabs_total; i++) { //--- Get the number of controls attached to the tab int tab_elements_total=::ArraySize(m_tab[i].elements); //--- If this tab is selected if(i==m_selected_tab) { //--- Display the tab controls for(int j=0; j<tab_elements_total; j++) m_tab[i].elements[j].Show(); } //--- Hide the controls of inactive tabs else { for(int j=0; j<tab_elements_total; j++) m_tab[i].elements[j].Hide(); } } }
The CTabs::OnClickTab() method will be used to handle the pressing of a tab. First, the program has to pass two checks: (1) based on the name of the pressed object and (2) based on the identifier of the control, retrieved from the object's name using the CTabs::IdFromObjectName() method. If the checks are passed, then the program (1) finds the pressed tab in a loop, (2) stores its index and (3) sets the corresponding colors. At the very end of the CTabs::ShowTabElements() method, the controls of only the active tab are made visible.
class CTabs : public CElement { private: //--- Handling the pressing on tab bool OnClickTab(const string pressed_object); //--- Get the identifier from the object name int IdFromObjectName(const string object_name); }; //+------------------------------------------------------------------+ //| Pressing a tab in a group | //+------------------------------------------------------------------+ bool CTabs::OnClickTab(const string clicked_object) { //--- Leave, if the pressing was not on the table cell if(::StringFind(clicked_object,CElement::ProgramName()+"_tabs_edit_",0)<0) return(false); //--- Get the identifier from the object name int id=IdFromObjectName(clicked_object); //--- Leave, if the identifier does not match if(id!=CElement::Id()) return(false); //--- for(int i=0; i<m_tabs_total; i++) { //--- If this tab is clicked if(m_tabs[i].Name()==clicked_object) { //--- Store index of the selected tab SelectedTab(i); //--- Set colors m_tabs[i].Color(m_tab_text_color_selected); m_tabs[i].BackColor(m_tab_color_selected); } else { //--- Set the colors for the inactive tabs m_tabs[i].Color(m_tab_text_color); m_tabs[i].BackColor(m_tab_color); } } //--- Show controls of the selected tab only ShowTabElements(); return(true); }
In this case, the code for the CTabs::OnEvent() main event handler is as shown in the listing below:
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CTabs::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Handling of the cursor movement event if(id==CHARTEVENT_MOUSE_MOVE) { //--- Leave, if the element is hidden if(!CElement::IsVisible()) return; //--- Coordinates int x=(int)lparam; int y=(int)dparam; for(int i=0; i<m_tabs_total; i++) m_tabs[i].MouseFocus(x>m_tabs[i].X() && x<m_tabs[i].X2() && y>m_tabs[i].Y() && y<m_tabs[i].Y2()); //--- return; } //--- Handling the left mouse button click on the object if(id==CHARTEVENT_OBJECT_CLICK) { //--- Pressing a tab if(OnClickTab(sparam)) return; } }
All the methods of the CTabs class have been considered. Now, let us test how all this works.
Testing the Tabs Control
For testing, let us use the EA from the previous part of the series and remove everything except the main menu and status bar from its graphical interface. Let us make all tab positioning modes (top/bottom/right/left) easy to test, as well as their size (height) easy to adjust. For this purpose, add external parameters to the EA in the Program.mqh file with the custom class:
//--- External parameters of the Expert Advisor input ENUM_TABS_POSITION TabsPosition =TABS_TOP; // Tabs Position input int TabsHeight =20; // Tabs Height
Next, in the custom class (CProgram) of the application, declare an instance of the CTabs class and method for creating a Tabs control indented from the edge points of the form:
class CProgram : public CWndEvents { private: //--- Tabs CTabs m_tabs; //--- private: //--- Tabs #define TABS1_GAP_X (4) #define TABS1_GAP_Y (45) bool CreateTabs(void); };
There will be four tabs in total. The displayed text and width of tabs can be adjusted by initializing arrays, the values of their elements are then passed in a loop using the CTabs::AddTab() method. Height and positioning of the tabs will be set by the external parameters. The second tab (index 1) will be selected by default (when the program is loaded on chart for the first time). The full code of the CProgram::CreateTabs() method can be seen in the listing below:
//+------------------------------------------------------------------+ //| Create area with tabs | //+------------------------------------------------------------------+ bool CProgram::CreateTabs(void) { #define TABS1_TOTAL 4 //--- Pass the panel object m_tabs.WindowPointer(m_window1); //--- Coordinates int x=m_window1.X()+TABS1_GAP_X; int y=m_window1.Y()+TABS1_GAP_Y; //--- Arrays with text and width for tabs string tabs_text[]={"Tab 1","Tab 2","Tab 3","Tab 4"}; int tabs_width[]={90,90,90,90}; //--- Set properties before creation m_tabs.XSize(596); m_tabs.YSize(243); m_tabs.TabYSize(TabsHeight); m_tabs.PositionMode(TabsPosition); m_tabs.SelectedTab((m_tabs.SelectedTab()==WRONG_VALUE) ? 1 : m_tabs.SelectedTab()); m_tabs.AreaColor(clrWhite); m_tabs.TabBackColor(C'225,225,225'); m_tabs.TabBackColorHover(C'240,240,240'); m_tabs.TabBackColorSelected(clrWhite); m_tabs.TabBorderColor(clrSilver); m_tabs.TabTextColor(clrGray); m_tabs.TabTextColorSelected(clrBlack); //--- Add tabs with the specified properties for(int i=0; i<TABS1_TOTAL; i++) m_tabs.AddTab(tabs_text[i],tabs_width[i]); //--- Create control if(!m_tabs.CreateTabs(m_chart_id,m_subwin,x,y)) return(false); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,m_tabs); return(true); }
The method must be called in the main method for creating the graphical interface of the application (see the shortened version of the method in listing below):
//+------------------------------------------------------------------+ //| Create an expert panel | //+------------------------------------------------------------------+ bool CProgram::CreateExpertPanel(void) { //--- Creating form 1 for controls //--- Creating controls: // Main menu //--- Context menus //--- Tabs if(!CreateTabs()) return(false); //--- Redrawing the chart m_chart.Redraw(); return(true); }
Compile the program and load it on to the chart. The tab positioning modes will be changed successively in the external parameters (see screenshots below):
Fig. 2. Tabs positioning mode — «Top».
Fig. 3. Tabs positioning mode — «Bottom».
Fig. 4. Tabs positioning mode — «Left».
Fig. 5. Tabs positioning mode — «Right».
Now let us test how it works with groups of controls attached to each tab. To do that, create a separate copy of the same EA and delete the external parameters from it. Here, the tabs will be positioned at the top (TABS_TOP) of the workspace.
- A text label table will be attached to the first tab.
- An edit box table will be attached to the second.
- A rendered table to the third.
- The fourth — a group of controls, containing:
- four checkboxes;
- four checkboxes with edit boxes;
- four comboboxes with checkboxes;
- one separation line.
In the custom class (CProgram) of the test application, declare (1) instances of these controls, (2) methods for creating them and (3) margins from the edges of the form (see code listing below):
class CProgram : public CWndEvents { private: //--- Text label table CLabelsTable m_labels_table; //--- Edit box table CTable m_table; //--- Rendered table CCanvasTable m_canvas_table; //--- Checkboxes CCheckBox m_checkbox1; CCheckBox m_checkbox2; CCheckBox m_checkbox3; CCheckBox m_checkbox4; //--- Checkboxes with edit boxes CCheckBoxEdit m_checkboxedit1; CCheckBoxEdit m_checkboxedit2; CCheckBoxEdit m_checkboxedit3; CCheckBoxEdit m_checkboxedit4; //--- Comboboxes with checkboxes CCheckComboBox m_checkcombobox1; CCheckComboBox m_checkcombobox2; CCheckComboBox m_checkcombobox3; CCheckComboBox m_checkcombobox4; //--- Separation line CSeparateLine m_sep_line; //--- private: //--- Text label table #define TABLE1_GAP_X (5) #define TABLE1_GAP_Y (65) bool CreateLabelsTable(void); //--- Edit box table #define TABLE2_GAP_X (5) #define TABLE2_GAP_Y (65) bool CreateTable(void); //--- Rendered table #define TABLE3_GAP_X (5) #define TABLE3_GAP_Y (65) bool CreateCanvasTable(void); //--- Separation line #define SEP_LINE_GAP_X (300) #define SEP_LINE_GAP_Y (70) bool CreateSepLine(void); //--- Checkboxes #define CHECKBOX1_GAP_X (18) #define CHECKBOX1_GAP_Y (75) bool CreateCheckBox1(const string text); #define CHECKBOX2_GAP_X (18) #define CHECKBOX2_GAP_Y (175) bool CreateCheckBox2(const string text); #define CHECKBOX3_GAP_X (315) #define CHECKBOX3_GAP_Y (75) bool CreateCheckBox3(const string text); #define CHECKBOX4_GAP_X (315) #define CHECKBOX4_GAP_Y (175) bool CreateCheckBox4(const string text); //--- Checkboxes with edit boxes #define CHECKBOXEDIT1_GAP_X (40) #define CHECKBOXEDIT1_GAP_Y (105) bool CreateCheckBoxEdit1(const string text); #define CHECKBOXEDIT2_GAP_X (40) #define CHECKBOXEDIT2_GAP_Y (135) bool CreateCheckBoxEdit2(const string text); #define CHECKBOXEDIT3_GAP_X (337) #define CHECKBOXEDIT3_GAP_Y (105) bool CreateCheckBoxEdit3(const string text); #define CHECKBOXEDIT4_GAP_X (337) #define CHECKBOXEDIT4_GAP_Y (135) bool CreateCheckBoxEdit4(const string text); //--- Comboboxes with checkboxes #define CHECKCOMBOBOX1_GAP_X (40) #define CHECKCOMBOBOX1_GAP_Y (205) bool CreateCheckComboBox1(const string text); #define CHECKCOMBOBOX2_GAP_X (40) #define CHECKCOMBOBOX2_GAP_Y (235) bool CreateCheckComboBox2(const string text); #define CHECKCOMBOBOX3_GAP_X (337) #define CHECKCOMBOBOX3_GAP_Y (205) bool CreateCheckComboBox3(const string text); #define CHECKCOMBOBOX4_GAP_X (337) #define CHECKCOMBOBOX4_GAP_Y (235) bool CreateCheckComboBox4(const string text); };
In the previous articles, it had been repeatedly shown how to create controls and attach them to the form. Therefore, the code of only one of these methods will be provided here to show how to attach a control to a tab. The simplest of those controls - separation line - would suffice as an example. In the code listing below, the line that call to the CTabs::AddToElementsArray() method is highlighted in yellow. The first argument is index of the tab, to which a control should be attached. Here the index is 3, that is, the fourth tab. The second argument is the object of the control that must be attached to the specified tab.
//+------------------------------------------------------------------+ //| Creates a separation line | //+------------------------------------------------------------------+ bool CProgram::CreateSepLine(void) { //--- Store the window pointer m_sep_line.WindowPointer(m_window1); //--- Attach to the fourth tab of the first group of tabs m_tabs.AddToElementsArray(3,m_sep_line); //--- Coordinates int x=m_window1.X()+SEP_LINE_GAP_X; int y=m_window1.Y()+SEP_LINE_GAP_Y; //--- Size int x_size=2; int y_size=210; //--- Set properties before creation m_sep_line.DarkColor(C'213,223,229'); m_sep_line.LightColor(clrWhite); m_sep_line.TypeSepLine(V_SEP_LINE); //--- Creating an element if(!m_sep_line.CreateSeparateLine(m_chart_id,m_subwin,0,x,y,x_size,y_size)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,m_sep_line); return(true); }
Once the graphical interface of the application has been created, the CTabs::ShowTabElements() method must be called in order to display the controls of the active tabs only (see the shortened version of the method in listing below). If it is not done, then all controls of all tabs will be displayed.
//+------------------------------------------------------------------+ //| Create an expert panel | //+------------------------------------------------------------------+ bool CProgram::CreateExpertPanel(void) { //--- Creating form 1 for controls //--- Creating controls: //--- Main menu //--- Context menus //--- Status bar //--- Tabs //... //--- Text label table if(!CreateLabelsTable()) return(false); //--- Edit box table if(!CreateTable()) return(false); //--- Create rendered table if(!CreateCanvasTable()) return(false); //--- Separation line if(!CreateSepLine()) return(false); //--- Checkboxes if(!CreateCheckBox1("Checkbox 1")) return(false); if(!CreateCheckBox2("Checkbox 2")) return(false); if(!CreateCheckBox3("Checkbox 3")) return(false); if(!CreateCheckBox4("Checkbox 4")) return(false); //--- Checkboxes with edit boxes if(!CreateCheckBoxEdit1("Checkbox Edit 1:")) return(false); if(!CreateCheckBoxEdit2("Checkbox Edit 2:")) return(false); if(!CreateCheckBoxEdit3("Checkbox Edit 3:")) return(false); if(!CreateCheckBoxEdit4("Checkbox Edit 4:")) return(false); //--- Comboboxes with checkboxes if(!CreateCheckComboBox1("CheckCombobox 1:")) return(false); if(!CreateCheckComboBox2("CheckCombobox 2:")) return(false); if(!CreateCheckComboBox3("CheckCombobox 3:")) return(false); if(!CreateCheckComboBox4("CheckCombobox 4:")) return(false); //--- Display controls of the active tab only m_tabs.ShowTabElements(); //--- Redrawing the chart m_chart.Redraw(); return(true); }
The result should be as in the screenshots below
Fig. 6. Controls of the fist tab.
Fig. 7. Controls of the second tab.
Fig. 8. Controls of the third tab.
Fig. 9. Controls of the fourth tab.
Everything works as intended. The seventh part of the series dedicated to the library for creating graphical interfaces can be now concluded. As a supplement, you can download another class code (CIconTabs) for creating Tabs with extended functionality, which is provided in the article attachments. Unlike the CTabs class, a control of CIconTabs type can have adjustable icons for each tab. This can help to make the graphical interface more user-friendly, if necessary.
The icons and displayed text can be accurately placed relative to the edge of each tab using special methods (see code listing below):
//+------------------------------------------------------------------+ //| Class for creating icon tabs | //+------------------------------------------------------------------+ class CIconTabs : public CElement { private: //--- Label margins int m_icon_x_gap; int m_icon_y_gap; //--- Text label margins int m_label_x_gap; int m_label_y_gap; //--- public: //--- Label margins void IconXGap(const int x_gap) { m_icon_x_gap=x_gap; } void IconYGap(const int y_gap) { m_icon_y_gap=y_gap; } //--- Text label margins void LabelXGap(const int x_gap) { m_label_x_gap=x_gap; } void LabelYGap(const int y_gap) { m_label_y_gap=y_gap; } };
An example of the Icon tabs control appearance is displayed in the screenshot below:
Fig. 10. Icon tabs control.
The code of this expert can also be downloaded from the article attachments.
Conclusion
Currently, the schematic of the library for creating graphical interfaces looks as shown below:
Fig. 11. Structure of the library at the current stage of development.
The tables and tabs controls have been covered in the seventh part of the series about creating graphical interfaces in the MetaTrader trading terminals. Three classes (CLabelsTable, CTable and CCanvasTable) have been provided for creating tables and two classes (CTabs and CIconTabs) for creating tabs.
The next (eighth) part of the series will consider the following controls.
- Static and drop-down calendar.
- Tree view.
- File navigator.
You can find and download the material of the seventh part of the series in the attached files so you can test how it works. If you have questions on how to use the material, presented in these files, you can refer to the detailed description of the library development in one of the articles from the list below or ask your question in the comments below.
List of articles (chapters) of seventh part:
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/2503
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
I am trying to add an input field to a tab but it wont work.
then the function.
this object isn't attached to the Panel but is on thee chart.
and after deleting the chart the object remains
How can I add CLabel to tabs, when I create CLabel item and want add to tabs compiler gives me these error:
Compiler Error:
'WindowPointer' - function not defined MainPanel.mqh 233 27
'm_overview_title_label' - parameter conversion not allowed MainPanel.mqh 235 43
Hi. A really nice article, but at the moment I've some questions:
1.)
I do not find an information, how I can add simple CLabel or CEdit controls to the form. (From objects.mqh)
They do not have a function like WindowPointer().
I can create these objects, but I can't attach them to the window.
The
does not work in this case...
2.)
Is the CComboBox a static object. Is it possible to modify the list of elements in the
ComboBox-Listview after the creation of the ComboBox?
Thank you!
I had same problem, I solve that by create a include file and write my label class and use that. You must create this file in Include\EasyAndFastGUI\Controls\MyLabel.mqh
That code is:
Then create object from MyLabel Class like this and use it:
//---Label MyLabel m_overview_title_label;
Then: