Русский 中文 Español Deutsch 日本語 Português
Graphical Interfaces I: Testing Library in Programs of Different Types and in the MetaTrader 4 Terminal (Chapter 5)

Graphical Interfaces I: Testing Library in Programs of Different Types and in the MetaTrader 4 Terminal (Chapter 5)

MetaTrader 5Examples | 25 February 2016, 09:00
9 461 0
Anatoli Kazharski
Anatoli Kazharski

Content

 

Introduction

This article is the continuation of the first part of the series about graphical interfaces. The first article Graphical Interfaces I: Preparation of the Library Structure (Chapter 1) considers in detail what this library is for. A full list of the links to the articles of the first part is at the end of each chapter. There you can also find and download a complete version of the library at the current stage of development. The files must be located in the same directories as in the archive.

In the previous chapter of the first part of the series about graphical interfaces, the form class was enriched by methods, which allowed managing the form by pressing its controls. Tests were previously conducted for the program of the "Expert Advisor" type and only in the MetaTrader 5 terminal. In this article, we will test our work in different types of MQL program such as indicators and scripts. As the library was designed to be cross-platform so it could be used in all MetaTrader platforms, we will also test it in MetaTrader 4.

 

Using the Form in Indicators

Create a folder for a new indicator in the MetaTrader 5 terminal directory of indicators <data_directory>\MQL5\Indicators). Like with the test we carried out earlier for the EA, create in this folder the main program file and the Program.mqh file that will contain the CProgram class. You can just copy the Program.mqh file from the folder created for the EA to the folder with the indicator. At this stage this code is sufficient to test the form for controls on the indicator. Enter the code in the main file of the indicator as shown below.

The line highlighted in yellow means that the indicator will be located in the main chart window. The number of indicator buffers can be set as zero as now we are only going to test elements of the graphical interface on the indicator and skip the calculations with price arrays.

//+------------------------------------------------------------------+
//|                                                  ChartWindow.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0
//--- Including the trading panel class
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   program.OnInitEvent();  
//--- Set up the trading panel
   if(!program.CreateTradePanel())
     {
      ::Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
     }
//--- Initialization successful
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   program.OnDeinitEvent(reason);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int    rates_total,     // size of the price[] array
                 const int    prev_calculated, // bars processed on the previous call
                 const int    begin,           // where significant data start
                 const double &price[])        // array for calculation
  {
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer(void)
  {
   program.OnTimerEvent();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   program.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Compile the indicator and load it on to the chart. The result should be as in the screenshot below. As with the EA, the form can be minimized, maximized and moved over the chart. Its controls react to the cursor movement and if the "Close" button is pressed (the cross in the top right corner), the indicator will be deleted from the chart.

Fig. 1. Test of the form on the indicator in the main chart window.

Fig. 1. Test of the form on the indicator in the main chart window

Please note the form icon in its top left corner. The program automatically identified the program type and set up the default icon. As you may remember, at this stage of development of the CWindow class, there is a possibility to alter this icon.

The form for controls works on an indicator too without any significant changes of the code. However, if we try to create an indicator with the same form in a window other than the main one, not all the features will work as expected. Namely, (1) the form will be set up in the main chart window instead of the indicator sub-window as required, (2) pressing the "Close" button will not delete the indicator from the chart. Why is that? We are going to see how this can be changed.

Actually, nothing needs changing from what we have done earlier. All we need to do is to create in the CWndEvents class a method that will automatically identify the window number in the program depending on its type and the number of the indicators located in sub-windows on the chart. Let us name this method DetermineSubwindow(). At the beginning of this method there must be a check if the program is an indicator. If this is not an indicator, there is no point in proceeding because from all types of MQL programs only indicators are located in sub-windows.

Then, get the number of the indicator window using the ChartWindowFind() function. If this is unsuccessful and the function returns -1, then a corresponding message is printed in the journal and the method finishes work. If the window number is greater than zero, it means that this is not the main window of the chart. A further check is required if this sub-window contains any other indicators. If there are other indicators in this sub-window, then our indicator will be deleted from the chart, having reported about the reason for the deletion to the journal. For that, get the total number of indicators in the specified sub-window using the ChartIndicatorsTotal() function. Then, get the short name of the last indicator in the list and if the number of indicators is not equal to 1, delete the program from the chart. Getting the short name of the indicator is compulsory as the deletion of the indicator from the chart is possible only if its short name is specified.

Add declaration and implementation of the DetermineSubwindow() method in the CWndEvents class as shown in the code below:

class CWndEvents : public CWndContainer
  {
private:
   //--- Identifying the sub-window number
   void              DetermineSubwindow(void);
  };
//+------------------------------------------------------------------+
//| Identifying the sub-window number                                |
//+------------------------------------------------------------------+
void CWndEvents::DetermineSubwindow(void)
  {
//--- If program type is not an indicator, leave
   if(PROGRAM_TYPE!=PROGRAM_INDICATOR)
      return;
//--- Reset the last error
   ::ResetLastError();
//--- Identifying the number of the indicator window
   m_subwin=::ChartWindowFind();
//--- If identification of the number failed, leave
   if(m_subwin<0)
     {
      ::Print(__FUNCTION__," > Error when identifying the sub-window number: ",::GetLastError());
      return;
     }
//--- If this is not the main window of the chart
   if(m_subwin>0)
     {
      //--- Get the total number of indicators in the specified sub-window
      int total=::ChartIndicatorsTotal(m_chart_id,m_subwin);
      //--- Get the short name of the last indicator in the list
      string indicator_name=::ChartIndicatorName(m_chart_id,m_subwin,total-1);
      //--- If the sub-window already contains an indicator, remove the program from the chart
      if(total!=1)
        {
         ::Print(__FUNCTION__," > This sub-window already contains an indicator.");
         ::ChartIndicatorDelete(m_chart_id,m_subwin,indicator_name);
         return;
        }
     }
  }

This method must be called in the constructor of the CWndEvents class:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWndEvents::CWndEvents(void)
  {
//--- Identifying the sub-window number
   DetermineSubwindow();
  }

Now, the library is ready for the indicators, which are not in the main window of the chart. We are going to test how this works and correct errors and flaws if any are discovered.

The library will have three main modes for the graphical interfaces, which will be located in sub-windows:

  1. Free mode. In this mode, the indicator sub-window height is not fixed and can be changed with no limitations by the user. When the form for controls is minimized, the height of the indicator sub-window is not changed and remains free to be modified.
  2. Fixed mode. The indicator sub-window height can be fixed to the size of the set height of the form for controls. When the form is minimized, the sub-window height stays the same in this mode too.
  3. Fixed mode with a possibility of the sub-window minimization. This means that when the indicator is loaded on to the chart, the sub-window size is fixed to the size of the set height of the form for controls, but when the sub-window is minimized, it takes the size of the form header.

For convenient setting of any of the modes described above, the RollUpSubwindowMode() method was created earlier in the CWindow class. The way to use this will be shown later.

Let us create three indicators for demonstrating each mode. To make things more interesting, enumeration (a drop-down list) will play a role of the external parameter for these indicators. This drop-down list gives a choice of three options for setting up a form:

  1. in the left hand side of the chart window;
  2. in the right hand side of the chart window;
  3. across the whole width of the chart.

If the third options is selected, the form width must change when the chart width is changed. The CWindow class currently does not contain a method that would allow us to do this. Let us create such a method and name this ChangeWindowWidth(). At the beginning of the method, the current width will be compared to the value that was passed to the method. If the passed value is different, then the width of the form objects must be changed and the button coordinates must be updated.

Declaration and implementation of the CWindow::ChangeWindowWidth() method:

class CWindow : public CElement
  {
public:
   //--- Changes the width of the window
   void              ChangeWindowWidth(const int width);
  };
//+------------------------------------------------------------------+
//| Changes the width of the window                                  |
//+------------------------------------------------------------------+
void CWindow::ChangeWindowWidth(const int width)
  {
//--- If the width has not changed, leave
   if(width==m_bg.XSize())
      return;
//--- Update the width for the background and the header
   CElement::XSize(width);
   m_bg.XSize(width);
   m_bg.X_Size(width);
   m_caption_bg.XSize(width);
   m_caption_bg.X_Size(width);
//--- Update coordinates and margins for all buttons:
//--- Closing button
   int x=CElement::X2()-CLOSE_BUTTON_OFFSET;
   m_button_close.X(x);
   m_button_close.XGap(x-m_x);
   m_button_close.X_Distance(x);
//--- Maximizing button
   x=CElement::X2()-ROLL_BUTTON_OFFSET;
   m_button_unroll.X(x);
   m_button_unroll.XGap(x-m_x);
   m_button_unroll.X_Distance(x);
//--- Minimizing button
   m_button_rollup.X(x);
   m_button_rollup.XGap(x-m_x);
   m_button_rollup.X_Distance(x);
//--- Tooltip button (if enabled)
   if(m_tooltips_button)
     {
      x=CElement::X2()-TOOLTIP_BUTTON_OFFSET;
      m_button_tooltip.X(x);
      m_button_tooltip.XGap(x-m_x);
      m_button_tooltip.X_Distance(x);
     }
  }

Create three copies of the indicator, which was created earlier for the tests in the main window of the chart. Each copy will be for one of the modes listed above. Give each of them a unique name.

For example:

  • FreeHeight — for the free mode.
  • RollUp — for the fixed mode.
  • DownFall — for the fixed mode with a possibility of the sub-window minimization.

One line has to be changed in the code of the main file of all these indicators. Instead of the instruction that the indicator is to be located in the main window, it has to be specified that the indicator is to be created in the sub-window:

//+------------------------------------------------------------------+
//|                                                   FreeHeight.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_separate_window
#property indicator_buffers 0
#property indicator_plots   0

Then, at the beginning of every Program.mqh include file add the enumeration (enum) and the external parameter (input) for selecting the form setting mode:

//--- Enumeration of window setting modes
enum ENUM_WINDOW_MODE
  {
   LEFT  =0,
   RIGHT =1,
   FULL  =2
  };
//--- External parameters
input ENUM_WINDOW_MODE WindowMode=LEFT;

If the RIGHT or the FULL option is selected in the external settings, when the size of the chart window changes, the program will adjust either the form coordinates (in the RIGHT mode) or its size (in the FULL mode) so its right edge does not exceed the chart border.

I think there is no point in making the form movable in such confined space as an indicator sub-window. In the current form implementation, adjustment is not carried out when the chart size is changed if it is specified in its properties that it is not movable. That is why the adjustment for non-movable forms has to be performed individually in the internal event handler of the chart of the MQL application under development. Add the code below to the CProgram::OnEvent() handler in all Program.mqh files of created indicators:

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- If the mode of the form for the whole width of the chart is selected
      if(WindowMode==FULL)
         m_window.ChangeWindowWidth(m_chart.WidthInPixels()-2);
      //--- If the mode of setting the form on the right is selected
      else if(WindowMode==RIGHT)
         m_window.X(m_chart.WidthInPixels()-(m_window.XSize()+1));
     }
  }

As you can see, currently the content of all the files of all indicators being created is absolutely identical. Finally, we have approached the answer to the question of how to use the CWindow::RollUpSubwindowMode() method for setting the mode of the indicator sub-window in which the form for controls is located.

By default, values of the CWindow class variables, which the program uses for identifying the indicator sub-window mode, are initialized in the class constructor for using the mode when the indicator sub-window height can be changed. Therefore, this method can be skipped completely for the FreeHeight indicator. The code for setting up a form in the sub-window in the CProgram class is as shown below. Bear in mind that the width of the form and its coordinates are defined depending on the selected option in the external settings. The place where the CWindow::RollUpSubwindowMode() method is to be used in the indicators with different modes is highlighted in yellow.

//+------------------------------------------------------------------+
//| Creates a form for controls                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_window);
//--- Sizes
   int x_size=(WindowMode!=FULL)? 205 : m_chart.WidthInPixels()-2;
   int y_size=243;
//--- Coordinates
   int x=(WindowMode!=RIGHT)? 1 : m_chart.WidthInPixels()-(x_size+1);
   int y=1;
//--- Properties
   m_window.XSize(x_size);
   m_window.YSize(y_size);
// ...
//--- Creating a form
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   return(true);
  }

The RollUp indicator will be used in the mode when the indicator sub-window has a fixed height and does not change when the form is minimized. That is why the line of code below must be added to the place highlighted in the code above:

m_window.RollUpSubwindowMode(false,true);

The value of the first parameter means that the sub-window does not have to be minimized when the form is minimized. The value of the second parameter indicates that the sub-window must have a fixed height equal to the form height. Parameters in the CWindow::RollUpSubwindowMode() method in the DownFall indicator must be specified as shown below:

m_window.RollUpSubwindowMode(true,true);

The value of the first parameter sets the mode when at the form minimization the sub-window height will be set equal to the height of the form header.

Compile files in which you introduced changes and the indicator files. Test each of them on the chart. Everything is working correctly according to the set modes. You will notice that when the chart window size is changed, coordinates (in the RIGHT mode) and the form width (in the FULL mode) are not adjusted. This is not surprising! Now, the stream of events is not going to the OnEvent() chart event handler in the CProgram class. When we were building the main structure of the library, we mentioned that the stream of chart events can be forwarded from the CWndEvents class in the ChartEvent() method to the OnEvent() local handler, which is located in the CProgram class. The latter is the class of the application that we are developing.

Let us place the event forwarding straight after the iteration over all control event handlers in the CWndEvents::CheckElementsEvents() method:

//+------------------------------------------------------------------+
//| Checking control events                                          |
//+------------------------------------------------------------------+
void CWndEvents::CheckElementsEvents(void)
  {
   int elements_total=CWndContainer::ElementsTotal(0);
   for(int e=0; e<elements_total; e++)
      m_wnd[0].m_elements[e].OnEvent(m_id,m_lparam,m_dparam,m_sparam);
//--- Forwarding the event to the application file
   OnEvent(m_id,m_lparam,m_dparam,m_sparam);
  }

The adjustment of the form coordinates in the RIGHT mode will be incorrect even now. The reason being that the program will not update the form coordinates if it calls the handler of the CWindow class with the CHARTEVENT_CHART_CHANGE event, because in the UpdateWindowXY() method the coordinates get updated only if the form is movable. As you remember, it was agreed earlier that the form will be fixed on the indicators. Having finished the iteration over all controls, the program will call the chart event handler in the class of the CProgram application as it was shown in the code above and will update the X coordinate according to the current width of the chart window.

Then the program will leave the CheckElementsEvents() method in the CWndEvents class and will call the CWndEvents::ChartEventMouseMove() method and leave it straight away as the event is not a mouse event. Currently, updating the form location according to its actual coordinates in the form properties is carried out only in the CWndEvents::ChartEventMouseMove() method. Now, it is time to add handling of the CHARTEVENT_CHART_CHANGE event to the CWndEvents::ChartEvent() method as highlighted in yellow below.

//+------------------------------------------------------------------+
//| Program event handling                                           |
//+------------------------------------------------------------------+
void CWndEvents::ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- If the array is empty, leave
   if(CWndContainer::WindowsTotal()<1)
      return;
//--- Initialization of event parameter fields
   InitChartEventsParams(id,lparam,dparam,sparam);
//--- Verification of interface control events
   CheckElementsEvents();
//--- Event of mouse movement
   ChartEventMouseMove();
//--- Event of changing the chart properties
   ChartEventChartChange();
  }

Add calling of the window move function by the actual coordinates and redrawing of the chart to the body of the CWndEvents::ChartEventChartChange() method:

//+------------------------------------------------------------------+
//| CHARTEVENT CHART CHANGE event                                    |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventChartChange(void)
  {
//--- Event of changing the chart properties
   if(m_id!=CHARTEVENT_CHART_CHANGE)
      return;
//--- Moving the window
   MovingWindow();
//--- Redraw chart
   m_chart.Redraw();
  }

Compile files of the library and indicators. Try testing them by loading on to the chart. Now, the form coordinates in the RIGHT mode are updated correctly.

There is another detail that has not been considered before. In the current implementation when one of the created indicators together with the form for controls are loaded on to the chart, the sub-window number is defined by the program automatically in the CWndEvents::DetermineSubwindow() method in the class constructor. If one of those indicators is loaded on to the chart, which already contains another indicator also situated in a sub-window, everything will work smoothly while this indicator is on the chart. When this is deleted, the sub-window of our indicator will receive a new number in the list of chart windows. As the library requires the window number in which it is working (for example, for identifying a relative coordinate) then some functionality will work incorrectly. For that reason, the number of windows in the chart has to be tracked. As soon as their number changes, the value has to be declared in the fields of the classes where this is required

When programs are loaded and deleted from the chart, the CHARTEVENT_CHART_CHANGE event is generated. That is why a check has to be placed in the CWndEvents::ChartEventChartChange() method. Let us create a new method dedicated to such a check and call this CWndEvents::CheckSubwindowNumber(). Its declaration and implementation are presented in the code below. At first there is a check for matching of the program window and the number, which was stored when loading the program on to the chart. When they do not match, the current window number where the program is located has to be defined and then this number has to be updated in all the form controls.

class CWndEvents : public CWndContainer
  {
private:
   //--- Verification of the control events
   void              CheckSubwindowNumber(void);
   //---
  };
//+------------------------------------------------------------------+
//| Checking and updating the program window number                  |
//+------------------------------------------------------------------+
void CWndEvents::CheckSubwindowNumber(void)
  {
//--- If the program in the sub-window and the numbers do not match
   if(m_subwin!=0 && m_subwin!=::ChartWindowFind())
     {
      //--- Identify the sub-window number
      DetermineSubwindow();
      //--- Store in all controls
      int windows_total=CWndContainer::WindowsTotal();
      for(int w=0; w<windows_total; w++)
        {
         int elements_total=CWndContainer::ElementsTotal(w);
         for(int e=0; e<elements_total; e++)
            m_wnd[w].m_elements[e].SubwindowNumber(m_subwin);
        }
     }
  }

Calling of this method has to be performed in the CWndEvents::ChartEventChartChange() method:

//+------------------------------------------------------------------+
//| CHARTEVENT CHART CHANGE event                                    |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventChartChange(void)
  {
//--- Event of changing the chart properties
   if(m_id!=CHARTEVENT_CHART_CHANGE)
      return;
//--- Checking and updating the program window number
   CheckSubwindowNumber();
//--- Moving the window
   MovingWindow();
//--- Redraw chart
   m_chart.Redraw();
  }

Now, everything is going to work as it was designed. All indicators for testing of the modes discussed above can be downloaded at the end of the article.

Fig. 2. Testing of the form on the indicators in the chart sub-windows.

Fig. 2. Testing of the form on the indicators in the chart sub-windows

In the current implementation to work correctly on one chart it is recommended to use no more than one application for development of which this library was used. The screenshot above shows several indicators on one chart only for a compact demonstration of all the modes and saving space in the article. At the moment of enabling/disabling of the chart scrolling, there will be a conflict between the applications where the library is used. There are several ideas about eliminating such a conflict between the applications. If the test results are satisfactory and the solution is viable, then one of the future articles of this series will be dedicated to that.

 

Using the Form in Scripts

We have already considered using the form on an EA and an indicator. Can the form be used in scripts? The answer is yes. In scripts the work of the form will be very limited. Scripts do not have event handlers. That is why (1) the form will not be movable, (2) there is no point in setting the controls dedicated for some actions on the form and (3) graphical objects will not react to mouse movements.

However, if you need to quickly create an information panel, which works during the executions of the script and shows the user some statistical data, why not use the form as a base for this? That said, all your applications will have one design no matter what type of MQL program they belong to because you will use one library.

Create a folder to put the main file of the script and the Program.mqh file. The CProgram class in this file will be derived from the CWndEvents class as was considered above for the EA and indicators. In general, everything is the same. The only exception is that here there will be one OnEvent() handler and the event for it will be artificially generated in the main file of the script in a perpetual loop. This method will have only one parameter, which is the number of milliseconds for a pause before leaving the method.

//+------------------------------------------------------------------+
//| Class for creating an application                                |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
protected:
   //--- Window
   CWindow           m_window;
   //---
public:
                     CProgram(void);
                    ~CProgram(void);
   //--- Event handler
   virtual void      OnEvent(const int milliseconds);
   //--- Creates the information panel
   bool              CreateInfoPanel(void);
   //---
protected:
   //--- Creates a form
   bool              CreateWindow(const string text);
  };

To imitate some process, we will change the text of the form header in the CProgram::OnEvent() method with the intervals equal to the number of passed milliseconds. Let us create an ellipsis process indicator. The code below shows how this is implemented:

//+------------------------------------------------------------------+
//| Events                                                           |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int milliseconds)
  {
   static int count =0;  // Counter
   string     str   =""; // Line for the header
//--- Formation of the header with the process progress
   switch(count)
     {
      case 0 : str="SCRIPT PANEL";     break;
      case 1 : str="SCRIPT PANEL .";   break;
      case 2 : str="SCRIPT PANEL ..";  break;
      case 3 : str="SCRIPT PANEL ..."; break;
     }
//--- Update the header line
   m_window.CaptionText(str);
//--- Redrawing of the chart
   m_chart.Redraw();
//--- Increase the counter
   count++;
//--- If greater than three, zero
   if(count>3)
      count=0;
//--- Pause
   ::Sleep(milliseconds);
  }

Now, we only need to create a form and organize a constant call of the CProgram::OnEvent() method in the main file of the script in the OnStart() function. The code below demonstrates that this method will be called every 250 milliseconds. This will be working while the IsStopped() function returns the false value. To stop this perpetual loop, the script has to be deleted from the chart manually. When the program is deleted by the user, the IsStopped() function will return the true value, the loop will stop and the script will stop its work.

//+------------------------------------------------------------------+
//|                                                    InfoPanel.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.0"
//--- Including the trading panel class
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart(void)
  {
//--- Set up the trading panel
   if(!program.CreateInfoPanel())
     {
      ::Print(__FUNCTION__," > Failed to create graphical interface!");
      return;
     }
//--- The script will be working until deleted
   while(!::IsStopped())
     {
      //--- Generate an event every 250 milliseconds
      program.OnEvent(250);
     }
  }
//+------------------------------------------------------------------+

Compile the files and load the script on to the chart:

Fig. 3. Test of the form in the script.

Fig. 3. Test of the form in the script.

You can see in the screenshot above that the library identified the program type and set up a default icon as a form label. This script can be downloaded at the end of this article. This will be enriched with elements that belong to the information group for other examples later. This script, as well as the EA and indicators considered in this article, can be used as a template for developing your programs.

 

Using the Library in MetaTrader 4

The library for creating graphical interfaces can be used as it is in the MetaTrader 4 trading platform. Copy all the files of the library and program which were created in this article from the directories of the MetaTrader 5 terminal to the directories of the MetaTrader 4 terminal. The library is nearly ready to use.

The only thing to be added to some library files to avoid compilation errors is an additional specific parameter that will be an instruction to the compiler to use a special strict mode for checking for errors. For that add the following line of code at the beginning of the header files of the library:

#property strict

Do this in the WndContainer.mqh, Element.mqh files and in the main files of the programs. This will prevent errors when compiling code in these files.

If everything is correct and all files are compiled, test all programs from this article in the MetaTrader 4 terminal. Everything should work the same as in the MetaTrader 5 terminal.

Fig. 4. Library test in the Metatrader 4 terminal.

Fig. 4. Library test in the Metatrader 4 terminal

 

Conclusion

We can evaluate where we are in the development of the library for creating graphical interfaces. At this stage the library structure can be presented as shown below. If you have read the article thoroughly, this schematic should be logical and easy to understand.

Fig. 5. Library structure at the current stage of development

Fig. 5. Library structure at the current stage of development

I will briefly remind you what elements of this structure mean:

  • The blue arrows mean interconnections between classes like: base -> derived 1 -> derived 2 -> derived N.
  • The yellow arrows mean including a file in another file. However, if an include file contains a class, then this is not a base one for the class in which it is included. It can be used as an object inside a class or classes of one chain (base -> derived).
  • Light brown rectangles with a semi-bold font denote classes from the standard library of MQL and the rest belong to the custom library.
  • White rectangles with a gray frame are either classes in a separate file or additions belonging to a certain group such as macros (define) or enumerations (enum).
  • If a gray rectangle with a thick blue frame has a header with a file name, this means that all classes, depicted inside with white rectangles with a blue frame, are contained in the file specified in the header.
  • If a gray rectangle with a thick blue frame does not have a header, then all the classes that are inside it, are located in different files but have one base class. That is if a gray rectangle with a thick blue frame is connected to a class with a blue arrow, this means that this class is a base one for all classes which are inside the gray one.
  • A light brown rectangle with the text CChartObject… in the schematic connected to a gray rectangle with a thick blue frame means that one of the classes of the standard library, which is a primitive object, is a base class for one of those classes inside the gray rectangle.

The first part of this series considered in detail the process of preparation of the main library structure for creating graphical interfaces and the creation of the main interface element - form or window. Other controls will be added to this form. Their creation will be described in the following articles of this series. The continuation of this series will be useful as it will discuss in detail the creation of your own custom control and the way to add it to the library so it becomes a well-integrated part and does not conflict with other controls.

The archives below with library files at the current stage of development, pictures and files of the programs considered in the article (the EA, indicators and the script) can be downloaded for tests in the MetaTrader 4 and MetaTrader 5 terminals. If you have questions on using material from those 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 of this article.

List of articles (chapters) of the first series:

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/2129

Attached files |
Adding a control panel to an indicator or an Expert Advisor in no time Adding a control panel to an indicator or an Expert Advisor in no time
Have you ever felt the need to add a graphical panel to your indicator or Expert Advisor for greater speed and convenience? In this article, you will find out how to implement the dialog panel with the input parameters into your MQL4/MQL5 program step by step.
Trading signals module using the system by Bill Williams Trading signals module using the system by Bill Williams
The article describes the rules of the trading system by Bill Williams, the procedure of application for a developed MQL5 module to search and mark patterns of this system on the chart, automated trading with found patterns, and also presents the results of testing on various trading instruments.
Graphical Interfaces II: the Menu Item Element (Chapter 1) Graphical Interfaces II: the Menu Item Element (Chapter 1)
In the second part of the series, we will show in detail the development of such interface elements as main menu and context menu. We will also mention drawing elements and create a special class for it. We will discuss in depth such question as managing program events including custom ones.
Graphical Interfaces I: Functions for the Form Buttons and Deleting Interface Elements (Chapter 4) Graphical Interfaces I: Functions for the Form Buttons and Deleting Interface Elements (Chapter 4)
In this article, we are going to continue developing the CWindow class by adding methods, which will allow managing the form by clicking on its controls. We will enable the program to be closed by a form button as well as implement a minimizing and maximizing feature for the form.