English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 Coobook: 지표 하위 창 컨트롤 - 스크롤바

MQL5 Coobook: 지표 하위 창 컨트롤 - 스크롤바

MetaTrader 5 | 3 9월 2021, 11:12
68 0
Anatoli Kazharski
Anatoli Kazharski

소개

계속해서 다양한 컨트롤을 탐색하고 이번에는 스크롤바에 주의를 기울이겠습니다. 이전 글 "MQL5 Cookbook: Indicator Subwindow Controls - Buttons"에서와 같이 지표 하위 창에서 작업할 것입니다. OnChartEvent() 함수의 이벤트 작업에 대한 자세한 설명을 제공하는 위에서 언급한 글을 잠시 읽어보십시오. 이 점은 이 글에서 가볍게 다룰 것입니다. 설명을 위해 이번에는 MQL5 리소스를 사용하여 얻을 수 있는 모든 금융 상품 속성의 큰 목록에 대한 세로 스크롤 바를 만듭니다.

MQL5 프로그래밍에 대한 이전 글에서는 그래픽 개체 OBJ_LABEL(텍스트 레이블)를 사용하여 목록을 생성했습니다. 이 글에서는 캔버스를 사용하여 텍스트를 표시합니다. 이러한 접근 방식의 편리함은 많은 수의 OBJ_LABEL 개체 대신 OBJ_BITMAP_LABEL(비트맵 레이블) 하나만 사용한다는 것입니다. 캔버스에 모든 인터페이스 요소를 그릴 수 있지만 이번에는 텍스트로만 제한하겠습니다.

스크롤바는 매우 간단합니다. 일반적으로 화살표 버튼이 있지만 우리의 경우에는 기능이 아닙니다. 스크롤바는 배경과 스크롤 상자로만 구성됩니다. 커서가 스크롤 상자 위로 이동하면 스크롤 상자의 색상이 변경됩니다. 클릭하면 색상이 변경되어 사용자에게 이제 스크롤 상자가 선택되었으며 끌 수 있음을 알립니다. 스크롤 개체를 만들 때 OBJ_RECTANGLE_LABEL(사각형 레이블) 유형의 그래픽 개체를 사용합니다.

 

지표 개발

프로그래밍을 시작합시다. 이전 글에서 했던 것처럼 지표의 템플릿을 만듭니다. 맨 처음에는 평소와 같이 변수와 배열을 선언해야 합니다. 캔버스로 작업할 수 있도록 표준 라이브러리의 CCanvas 클래스를 포함합니다.

#define LIST_SIZE 71                // Number of strings in the list of financial instrument properties
//--- Include the class for working with the canvas
#include <Canvas\Canvas.mqh>
CCanvas canvas;
//+------------------------------------------------------------------+
//|   Global variables                                               |
//+------------------------------------------------------------------+
//--- Indicator subwindow properties
int               subwindow_number              =WRONG_VALUE;                    // Subwindow number
int               subwindow_height              =0;                              // Subwindow height
string            subwindow_shortname           ="TestScrollbar";                // Short name of the indicator
string            prefix                        =subwindow_shortname+"_";        // Prefix for objects
int               chart_width                   =0;                              // Chart width
int               chart_height                  =0;                              // Chart height
int               chart_y_offset                =0;                              // Distance from the chart top to the subwindow
//--- Canvas properties
string            canvas_name                   =prefix+"canvas";                // Canvas name
color             canvas_background_color       =C'20,20,20';                    // Canvas background color
ENUM_COLOR_FORMAT color_format                  =COLOR_FORMAT_XRGB_NOALPHA;      // Alpha channel component is ignored
//--- List properties
int               list_height                   =0;                              // List height
int               text_height                   =0;                              // Text height
int               font_size                     =15;                             // Font size
string            font_name                     ="Calibri";                      // Font
double            line_size                     =100/LIST_SIZE;                  // Size of a single string on the list, expressed as percentage
//--- Scrollbar properties: scroll box
string            scrollbar_thumb_name          =prefix+"scrollbar_thumb";       // Scroll box object name
int               scrollbar_thumb_x1            =0;                              // x1 coordinate
int               scrollbar_thumb_y1            =0;                              // y1 coordinate
int               scrollbar_thumb_x2            =0;                              // x2 coordinate
int               scrollbar_thumb_y2            =0;                              // y2 coordinate
double            scrollbar_thumb_y_percent     =0.0;                            // Y-coordinate expressed as percentage
int               scrollbar_thumb_width         =9;                              // Width
int               scrollbar_thumb_height        =0;                              // Height
bool              scrollbar_thumb_clicked       =false;                          // State (whether or not the scroll box is clicked)
color             scrollbar_thumb_color         =clrSilver;                      // Scroll box color
color             scrollbar_thumb_color_on_hover=clrDimGray;                     // Scroll box color when the cursor hovers over it
color             scrollbar_thumb_color_on_click=clrSlateGray;                   // Scroll box color when clicked
//--- Scrollbar properties: background
string            scrollbar_background_name     =prefix+"scrollbar_background";  // Background object name
int               scrollbar_background_width    =9;                              // Background width
color             scrollbar_background_color    =C'50,50,50';                    // Background color
//--- Scrollbar properties: other
int               scrollbar_fix_point           =0;                              // Y-coordinate of the fix point upon clicking
int               scrollbar_fix_point_y_offest  =0;                              // Distance along the Y-axis from the scrollbar top to the fix point
//--- Mouse button state (pressed/released)
bool              mouse_button_state=false;
//--- Arrays for financial instrument properties
color             symbol_property_colors[];                                      // Colors of values
string            symbol_property_values[];                                      // Values
//--- Financial instrument property names
string symbol_propety_names[LIST_SIZE]=
  {
   "Number of deals in the current session",
   "Total number of Buy orders at the moment",
   "Total number of Sell orders at the moment",
   "Volume of the last deal",
   "Maximum daily volume",
   "Minimum daily volume",
   "Time of the last quote",
   "Number of decimal places",
   "Spread in points",
   "Floating spread indication",
   "Maximum number of requests displayed in the Depth of Market",
   "Contract price calculation mode",
   "Order execution type",
   "Trading start date for an instrument (usually used for futures)",
   "Trading end date for an instrument (usually used for futures)",
   "Minimum distance in points from the current closing price for the purpose of setting Stop orders",
   "Freeze distance for trading operations (in points)",
   "Deal execution mode",
   "Swap calculation model",
   "Day of the week when triple swap is charged",
   "Flags of allowed order expiration modes",
   "Flags of allowed order filling modes",
//---
   "Bid - best price at which an instrument can be sold",
   "Maximum Bid of the day",
   "Minimum Bid of the day",
   "Ask - best price at which an instrument can be bought",
   "Maximum Ask of the day",
   "Minimum Ask of the day",
   "Last - last deal price",
   "Maximum Last of the day",
   "Minimum Last of the day",
   "Point value",
   "Calculated tick value for a winning position",
   "Calculated tick value for a losing position",
   "Minimum price change",
   "Trade contract size",
   "Minimum volume for deal execution",
   "Maximum volume for deal execution",
   "Minimum step of volume change for deal execution",
   "Maximum allowable total volume of an open position and pending orders in the same direction",
   "Long swap value",
   "Short swap value",
   "Initial margin - amount in the margin currency required for opening a position (1 lot)",
   "Maintenance margin for an instrument",
   "Margin requirement applicable to long positions",
   "Margin requirement applicable to short positions",
   "Margin requirement applicable to Limit orders",
   "Margin requirement applicable to Stop orders",
   "Margin requirement applicable to Stop Limit orders",
   "Total volume of deals in the current session",
   "Total turnover in the current session",
   "Total volume of open positions",
   "Total volume of buy orders at the moment",
   "Total volume of sell orders at the moment",
   "Open price of the session",
   "Close price of the session",
   "Average weighted price of the session",
   "Settlement price of the current session",
   "Minimum allowable price value for the session",
   "Maximum allowable price value for the session",
//---
   "Base currency of an instrument",
   "Profit currency",
   "Margin currency",
   "Current quote source",
   "String description of a symbol",
   "Name of a trading symbol in the international system of securities identification numbers (ISIN)",
   "Location in the symbol tree",
//---
   "Current number of bars for a symbol on a selected time frame",
   "The very first date for a symbol on a selected time frame",
   "The very first date in the history for a symbol on a selected time frame",
   "Symbol data synchronized"
  };

먼저 캔버스에 속성 목록을 표시하는 데 필요한 모든 기능을 작성해 보겠습니다. 이 작업이 완료되면 스크롤바 생성을 진행합니다.

캔버스를 생성하기 위해 AddCanvas() 함수를 작성하고 CCanvas 클래스의 CreateBitmapLabel() 메소드의 두 번째 변형을 사용합니다.

//+------------------------------------------------------------------+
//| Adding canvas                                                    |
//+------------------------------------------------------------------+
void AddCanvas()
  {
//--- If there is no canvas, add it
   if(ObjectFind(0,canvas_name)<0)
      canvas.CreateBitmapLabel(0,subwindow_number,canvas_name,0,0,chart_width,subwindow_height,color_format);
  }

이를 위해 CCanvas 클래스에서 Resize() 를 사용하는 ResizeCanvas() 함수를 작성합니다. 이를 위해 CCanvas 클래스에서 Resize() 를 사용하는 ResizeCanvas() 함수를 작성합니다.

//+------------------------------------------------------------------+
//| Resizing canvas                                                  |
//+------------------------------------------------------------------+
void ResizeCanvas()
  {
//--- If the canvas has already been added to the indicator subwindow, set the new size
   if(ObjectFind(0,canvas_name)==subwindow_number)
      canvas.Resize(chart_width,subwindow_height);
//--- If there is no canvas, add it
   else
      canvas.CreateBitmapLabel(0,subwindow_number,canvas_name,0,0,chart_width,subwindow_height,color_format);
  }

캔버스를 삭제하려면 Destroy() 를 사용합니다.

//+------------------------------------------------------------------+
//| Deleting canvas                                                  |
//+------------------------------------------------------------------+
void DeleteCanvas()
  {
   if(ObjectFind(0,canvas_name)>0)
      canvas.Destroy();
  }

이 글에서 우리는 또한 글꼴 설정을 위한 FontSet(), 글자 높이를 결정하기 위한TextHeight(), 캔버스에 텍스트를 프린트하기 위한 TextOut(), 캔버스를 지우기 위한 Erase() 그리고 다시 그리기를 위한 Update()와 같은 CCanvas 클래스의 다른 메소드를 사용할 것입니다. 더 아래에서 위의 방법이 프로그램에서 어디에 사용되는지 볼 것입니다.

OnInit() 함수에서 초기화하는 동안 프로그램 작업을 위한 길을 열어야 합니다. 아래 코드는 수행해야 할 작업을 보여줍니다. 각 문자열에 제공된 주석은 작업을 더 잘 이해하는 데 도움이 됩니다. CCanvas 클래스의 FontSet()TextHeight() 는 프로그램의 이 부분에서만 사용됩니다.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Enable tracking of mouse events
   ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
//--- Set the short name
   IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname);
//--- Set sizes of arrays of symbol properties and their colors
   ArrayResize(symbol_property_colors,LIST_SIZE);
   ArrayResize(symbol_property_values,LIST_SIZE);
//--- Set subwindow properties
   SetSubwindowProperties();
//--- Set the font for displaying on the canvas
   canvas.FontSet(font_name,font_size,FW_NORMAL);
//--- Save the text size (height) for calculations
   text_height=canvas.TextHeight("A")-1;
//--- Calculate the height of the entire list
   list_height=text_height*LIST_SIZE;
//--- Add the canvas to the chart
   AddCanvas();
//--- Display the list of symbol properties
   ShowSymbolInfo();
//--- Refresh the chart
   ChartRedraw();
//--- Everything completed successfully
   return(INIT_SUCCEEDED);
  }

SetSubwindowProperties() 함수는 이전 문서에서 가져온 것입니다. 이 함수는 지표 하위 창 번호와 크기를 전역 변수에 할당합니다. ShowSymbolInfo() 함수로 바로 진행해 보겠습니다.

//+------------------------------------------------------------------+
//| Displaying current symbol information                            |
//+------------------------------------------------------------------+
void ShowSymbolInfo(double current_thumb_position=0.0)
  {
   int    list_lines       =0;   // Counter of strings displayed in the canvas
   double thumb_position   =0.0; // Position of the scroll box expressed as percentage for determining the first displayed string
   int    y_distance       =0;   // For determining the coordinate of the next string in the list
   int    line_number      =0;   // Number of the string starting from which the list will be displayed
//--- Determine the string starting from which the list will be displayed
   for(int i=0; i<LIST_SIZE; i++)
     {
      //--- Count strings until you reach the one starting from which the list will be displayed
      if(thumb_position>=current_thumb_position)
         break;
      //---
      thumb_position+=line_size;
      line_number++;
     }
//--- Initialize list arrays from the specified string
   InitializePropertyArrays(line_number);
//--- Clear the canvas
   canvas.Erase(canvas_background_color);
//--- Show the list on the canvas
   for(int i=line_number; i<LIST_SIZE; i++)
     {
      //--- Property name
      canvas.TextOut(655,y_distance,symbol_propety_names[i]+" :",ColorToARGB(clrWhite),TA_RIGHT|TA_TOP);
      //--- Property value
      canvas.TextOut(665,y_distance,symbol_property_values[i],ColorToARGB(symbol_property_colors[i]),TA_LEFT|TA_TOP);
      //--- Calculate the coordinate for the next string
      y_distance+=text_height;
      //--- Count the number of displayed strings
      list_lines++;
      //--- If you go beyond the subwindow boundaries, terminate the loop
      if(list_lines*text_height>subwindow_height)
         break;
     }
//--- Refresh the canvas
   canvas.Update();
  }

ShowSymbolInfo() 함수에는 기본적으로 0과 같은 current_thumb_position 매개변수가 하나 있습니다(기본값을 사용해야 하는 경우 함수에 대한 값). 이 매개변수는 목록이 표시되어야 하는 문자열을 결정합니다. 즉, 0 값은 목록이 맨 처음부터 표시되어야 함을 의미합니다.

맨 처음에 목록이 표시되어야 하는 문자열의 번호를 결정합니다. 그런 다음 값 및 색상 배열(위 코드에서 강조 표시된 문자열)이 InitializePropertyArrays() 함수에서 초기화됩니다. 초기화는 이전 루프에서 결정된 문자열부터 수행됩니다. 그런 다음 Erase() 를 사용하여 캔버스를 지운 후 실제로 전체 캔버스를 지정된 색상으로 채웁니다. 마지막 루프에서 텍스트는 TextOut()를 사용하여 캔버스에 프린트됩니다. 마지막에 Update() 를 사용하여 캔버스를 새로 고칩니다.

InitializePropertyArrays() 함수 코드는 다음과 같습니다.

//+-----------------------------------------------------------------------+
//| Initializing arrays of values and their colors for the current symbol |
//+-----------------------------------------------------------------------+
void InitializePropertyArrays(int line_number)
  {
   int lines_count=0;
//---
   for(int i=line_number; i<LIST_SIZE; i++)
     {
      //--- Determine the value and color of the symbol property
      symbol_property_values[i]=GetStringSymbolInfoByIndex(i);
      symbol_property_colors[i]=GetColorSymbolInfoByIndex(i);
      //--- Increase the counter
      lines_count++;
      //--- If the number of strings exceeds the subwindow height, exit
      if(lines_count*text_height>subwindow_height)
         break;
     }
  }

위의 코드는 GetStringSymbolInfoByIndex()GetColorSymbolInfoByIndex()라는 유사한 원칙에 따라 작동하는 두 가지 함수를 사용하여 기호 속성의 값과 해당 색상을 결정한다고 제안합니다.

GetStringSymbolInfoByIndex() 함수는 단순하지만 많은 속성으로 인해 상당히 방대합니다. 게다가 일부 속성을 얻으려면 보조 기능이 필요합니다(아래 코드에서 강조 표시된 문자열).

//+------------------------------------------------------------------+
//| Returning the string value of the symbol property by index       |
//+------------------------------------------------------------------+
string GetStringSymbolInfoByIndex(int index)
  {
   string str           ="-";
   long   l_check_value =0;
   double d_check_value =0.0;
   string s_check_value ="";
//---
   switch(index)
     {
      case 0  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_SESSION_DEALS);
         str=(l_check_value==0) ? "-" : IntegerToString(l_check_value);                                  break;
      case 1  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_SESSION_BUY_ORDERS);
         str=(l_check_value==0) ? "-" : IntegerToString(l_check_value);                                  break;
      case 2  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_SESSION_SELL_ORDERS);
         str=(l_check_value==0) ? "-" : IntegerToString(l_check_value);                                  break;
      case 3  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_VOLUME);
         str=(l_check_value==0) ? "-" : IntegerToString(l_check_value);                                  break;
      case 4  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_VOLUMEHIGH);
         str=(l_check_value==0) ? "-" : IntegerToString(l_check_value);                                  break;
      case 5  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_VOLUMELOW);
         str=(l_check_value==0) ? "-" : IntegerToString(l_check_value);                                  break;
      case 6  :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TIME);
         str=(l_check_value==0) ? "-" : TimeToString(l_check_value);                                     break;
      case 7  : str=IntegerToString(SymbolInfoInteger(_Symbol,SYMBOL_DIGITS));                           break;
      case 8  : str=IntegerToString(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));                           break;
      case 9  : str=(!SymbolInfoInteger(_Symbol,SYMBOL_SPREAD_FLOAT)) ? "false" : "true";                break;
      case 10 :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TICKS_BOOKDEPTH);
         str=(l_check_value==0) ? "-" : DoubleToString(l_check_value,_Digits);                           break;
      case 11 : str=TradeCalcModeToString(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_CALC_MODE));            break;
      case 12 : str=TradeModeToString(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_MODE));                     break;
      case 13 :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_START_TIME);
         str=(l_check_value==0) ? "-" : TimeToString(l_check_value);                                     break;
      case 14 :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME);
         str=(l_check_value==0) ? "-" : TimeToString(l_check_value);                                     break;
      case 15 :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         str=(l_check_value==0) ? "false" : IntegerToString(l_check_value);                              break;
      case 16 :
         l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL);
         str=(l_check_value==0) ? "false" : IntegerToString(l_check_value);                              break;
      case 17 : str=TradeExeModeToString(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE));               break;
      case 18 : str=SwapModeToString(SymbolInfoInteger(_Symbol,SYMBOL_SWAP_MODE));                       break;
      case 19 : str=WeekdayToString(SymbolInfoInteger(_Symbol,SYMBOL_SWAP_ROLLOVER3DAYS));               break;
      case 20 : str=ExpirationModeToString();                                                            break;
      case 21 : str=FillingModeToString();                                                               break;
      //---
      case 22 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_BID);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 23 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_BIDHIGH);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 24 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_BIDLOW);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 25 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 26 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_ASKHIGH);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 27 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_ASKLOW);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 28 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_LAST);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 29 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_LASTHIGH);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 30 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_LASTLOW);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits);                           break;
      case 31 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_POINT),_Digits);                      break;
      case 32 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE_PROFIT),2);          break;
      case 33 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE_LOSS),2);            break;
      case 34 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE),_Digits);            break;
      case 35 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_CONTRACT_SIZE),2);              break;
      case 36 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN),2);                       break;
      case 37 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX),2);                       break;
      case 38 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP),2);                      break;
      case 39 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         str=(d_check_value==0) ? "Unlimited" : DoubleToString(d_check_value,2);                         break;
      case 40 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SWAP_LONG);
         str=(d_check_value==0) ? "false" : DoubleToString(d_check_value,2);                             break;
      case 41 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SWAP_SHORT);
         str=(d_check_value==0) ? "false" : DoubleToString(d_check_value,2);                             break;
      case 42 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_INITIAL);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 43 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_MAINTENANCE);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 44 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_LONG);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 45 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_SHORT);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 46 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_LIMIT);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 47 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_STOP);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 48 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_STOPLIMIT);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 49 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_VOLUME);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 50 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_TURNOVER);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 51 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_INTEREST);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 52 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 53 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 54 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_OPEN);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 55 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_CLOSE);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 56 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_AW);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 57 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_PRICE_SETTLEMENT);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 58 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_PRICE_LIMIT_MIN);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
      case 59 :
         d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_PRICE_LIMIT_MAX);
         str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2);                                 break;
         //---
      case 60 : str=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);                                      break;
      case 61 : str=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_PROFIT);                                    break;
      case 62 : str=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_MARGIN);                                    break;
      case 63 :
         s_check_value=SymbolInfoString(_Symbol,SYMBOL_BANK);
         str=(s_check_value!="") ? s_check_value : "-";                                                  break;
      case 64 : str=SymbolInfoString(_Symbol,SYMBOL_DESCRIPTION);                                        break;
      case 65 :
         s_check_value=SymbolInfoString(_Symbol,SYMBOL_ISIN);
         str=(s_check_value!="") ? s_check_value : "-";                                                  break;
      case 66 : str=SymbolInfoString(_Symbol,SYMBOL_PATH);                                               break;
      //---
      case 67 : str=IntegerToString(SeriesInfoInteger(_Symbol,_Period,SERIES_BARS_COUNT));               break;
      case 68 : str=TimeToString((datetime)SeriesInfoInteger(_Symbol,_Period,SERIES_FIRSTDATE));         break;
      case 69 : str=TimeToString((datetime)SeriesInfoInteger(_Symbol,_Period,SERIES_SERVER_FIRSTDATE));  break;
      case 70 : str=(!(bool)SeriesInfoInteger(_Symbol,_Period,SERIES_SYNCHRONIZED)) ? "false" : "true";  break;
     }
//---
   return(str);
  }

위에서 강조 표시된 기능: TradeCalcModeToString(), TradeModeToString(), TradeExeModeToString(), SwapModeToString()WeekdayToString()은 전달된 값에 따라 속성의 문자열 표현을 반환합니다.

//+------------------------------------------------------------------+
//| Returning string representation of the margin calculation        |
//| method for an instrument                                         |
//+------------------------------------------------------------------+
string TradeCalcModeToString(long mode)
  {
   string str="?";
//---
   switch((ENUM_SYMBOL_CALC_MODE)mode)
     {
      case SYMBOL_CALC_MODE_FOREX               :
         str="Forex mode";          break;
      case SYMBOL_CALC_MODE_FUTURES             :
         str="Futures mode";        break;
      case SYMBOL_CALC_MODE_CFD                 :
         str="CFD mode";            break;
      case SYMBOL_CALC_MODE_CFDINDEX            :
         str="CFD index mode";      break;
      case SYMBOL_CALC_MODE_CFDLEVERAGE         :
         str="CFD Leverage mode";   break;
      case SYMBOL_CALC_MODE_EXCH_STOCKS         :
         str="Exchange mode";       break;
      case SYMBOL_CALC_MODE_EXCH_FUTURES        :
         str="Futures mode";        break;
      case SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS  :
         str="FORTS Futures mode";  break;
     }
//---
   return(str);
  }
//+------------------------------------------------------------------+
//| Returning string representation of the trade mode                |
//+------------------------------------------------------------------+
string TradeModeToString(long mode)
  {
   string str="-";
//---
   switch((ENUM_SYMBOL_TRADE_MODE)mode)
     {
      case SYMBOL_TRADE_MODE_DISABLED  :
         str="Trade is disabled for a given symbol";               break;
      case SYMBOL_TRADE_MODE_LONGONLY  :
         str="Only long positions are allowed";                    break;
      case SYMBOL_TRADE_MODE_SHORTONLY :
         str="Only short positions are allowed";                   break;
      case SYMBOL_TRADE_MODE_CLOSEONLY :
         str="Only position closing operations are allowed";       break;
      case SYMBOL_TRADE_MODE_FULL      :
         str="No trade restrictions";                              break;
     }
//---
   return(str);
  }
//+------------------------------------------------------------------+
//| Returning string representation of the deal execution mode       |
//+------------------------------------------------------------------+
string TradeExeModeToString(long mode)
  {
   string str="-";
//---
   switch((ENUM_SYMBOL_TRADE_EXECUTION)mode)
     {
      case SYMBOL_TRADE_EXECUTION_REQUEST  :
         str="Request execution";         break;
      case SYMBOL_TRADE_EXECUTION_INSTANT  :
         str="Instant execution";         break;
      case SYMBOL_TRADE_EXECUTION_MARKET   :
         str="Market execution";          break;
      case SYMBOL_TRADE_EXECUTION_EXCHANGE :
         str="Exchange execution";        break;
     }
//---
   return(str);
  }
//+------------------------------------------------------------------+
//| Returning string representation of the swap calculation model    |
//+------------------------------------------------------------------+
string SwapModeToString(long mode)
  {
   string str="-";
//---
   switch((ENUM_SYMBOL_SWAP_MODE)mode)
     {
      case SYMBOL_SWAP_MODE_DISABLED         :
         str="No swaps";                                                             break;
      case SYMBOL_SWAP_MODE_POINTS           :
         str="Swaps calculated in points";                                           break;
      case SYMBOL_SWAP_MODE_CURRENCY_SYMBOL  :
         str="Swaps calculated in base currency of the symbol";                      break;
      case SYMBOL_SWAP_MODE_CURRENCY_MARGIN  :
         str="Swaps calculated in margin currency of the symbol";                    break;
      case SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT :
         str="Swaps calculated in the client's deposit currency";                    break;
      case SYMBOL_SWAP_MODE_INTEREST_CURRENT :
         str="Swaps expressed as a percent per annum of the instrument price";       break;
      case SYMBOL_SWAP_MODE_INTEREST_OPEN    :
         str="Swaps expressed as a percent per annum of the position opening price"; break;
      case SYMBOL_SWAP_MODE_REOPEN_CURRENT   :
         str="Swaps based on position reopening (close price +/-)";                  break;
      case SYMBOL_SWAP_MODE_REOPEN_BID       :
         str="Swaps based on position reopening (bid price +/-)";                    break;
     }
//---
   return(str);
  }
//+------------------------------------------------------------------+
//| Returning string representation of the day of the week           |
//+------------------------------------------------------------------+
string WeekdayToString(long day)
  {
   string str="-";
//---
   switch((ENUM_DAY_OF_WEEK)day)
     {
      case SUNDAY    :
         str="Sunday";            break;
      case MONDAY    :
         str="Monday";            break;
      case TUESDAY   :
         str="Tuesday";           break;
      case WEDNESDAY :
         str="Wednesday";         break;
      case THURSDAY  :
         str="Thursday";          break;
      case FRIDAY    :
         str="Friday";            break;
      case SATURDAY  :
         str="Saturday";          break;
     }
//---
   return(str);
  }

GetStringExpirationMode()GetStringFillingMode() 함수에서 문자열 표현은 현재 기호에 사용할 수 있는 주문 만료 및 볼륨 채우기 모드에 따라 생성됩니다.

//+------------------------------------------------------------------+
//| Returning string representation of the order expiration modes    |
//+------------------------------------------------------------------+
string ExpirationModeToString()
  {
   string str="";    // For string generation
//--- Variables for checking the modes
   bool   gtc           =false; // The order is valid for an unlimited time until explicitly canceled
   bool   day           =false; // The order is valid until the end of the day
   bool   specified     =false; // The expiration time is specified in the order
   bool   specified_day =false; // The expiration date is specified in the order
//--- Check the modes
   gtc           =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_GTC);
   day           =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_DAY);
   specified     =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_SPECIFIED);
   specified_day =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_SPECIFIED_DAY);
//--- Generate a string of the modes available
   if(gtc)
     {
      StringAdd(str,"GTC");
      if(day || specified || specified_day)
         StringAdd(str," / ");
     }
//---
   if(day)
     {
      StringAdd(str,"Day");
      if(specified || specified_day)
         StringAdd(str," / ");
     }
//---
   if(specified)
     {
      StringAdd(str,"Specified");
      if(specified_day)
         StringAdd(str," / ");
     }
//---
   if(specified_day)
      StringAdd(str,"Specified Day");
//---
   return(str);
  }
//+------------------------------------------------------------------+
//| Returning string representation of the volume filling modes      |
//+------------------------------------------------------------------+
string FillingModeToString()
  {
//--- Variable for string generation
   string str="";

//--- Variables for checking the modes:

//    "Fill or Kill"   -  if the required order volume cannot be filled at the specified price,
//                      the order is canceled and the deal is not executed
   bool   fok=false;

//    "Immediate or Cancel" -  if the deal volume can only be partially filled at the price specified in the order, 
//                      the deal is executed to the extent of the volume available. The remaining volume of the order is canceled 
//                      and the new order is not placed
   bool   ioc=false;

//    "Return"       -  The deal is executed to the extent of the volume available at the price specified in the order.
//                      A new order is placed for the remaining volume at the same price
   bool   return_remainder=false;

//--- Check the modes
   fok   =IsFillingTypeAllowed(_Symbol,SYMBOL_FILLING_FOK);
   ioc   =IsFillingTypeAllowed(_Symbol,SYMBOL_FILLING_IOC);
//--- For "Market execution" and "Exchange execution" modes
   ENUM_SYMBOL_TRADE_EXECUTION symbol_trade_exemode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);
   return_remainder=(symbol_trade_exemode==SYMBOL_TRADE_EXECUTION_MARKET || 
                     symbol_trade_exemode==SYMBOL_TRADE_EXECUTION_EXCHANGE) ? true : false;
//--- Generate a string of the modes available
   if(fok)
     {
      StringAdd(str,"Fill or Kill");
      if(ioc || return_remainder)
         StringAdd(str," / ");
     }
//---
   if(ioc)
     {
      StringAdd(str,"Immediate or Cancel");
      if(return_remainder)
         StringAdd(str," / ");
     }
//---
   if(return_remainder)
      StringAdd(str,"Return");
//---
   return(str);
  }

각 모드의 가용성을 개별적으로 확인해야 하므로 편의를 위해 문서 예제에 제공된 IsExpirationTypeAllowed()IsFillingTypeAllowed() 보조 함수를 사용합니다.

//+------------------------------------------------------------------+
//| Checking if a given expiration mode is allowed                   |
//+------------------------------------------------------------------+
bool IsExpirationTypeAllowed(string symbol, int exp_type)
  {
//--- Get the value of the property describing the allowable expiration modes
   int expiration=(int)SymbolInfoInteger(symbol,SYMBOL_EXPIRATION_MODE);
//--- Return true if the exp_type mode is allowed
   return((expiration&exp_type)==exp_type);
  }
//+------------------------------------------------------------------+
//| Checking if a given filling mode is allowed                      |
//+------------------------------------------------------------------+
bool IsFillingTypeAllowed(string symbol, int fill_type)
  {
//--- Get the value of the property describing the filling mode
   int filling=(int)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);
//--- Return true if the fill_type mode is allowed
   return((filling&fill_type)==fill_type);
  }

그래서 기호 속성의 문자열 값을 검토했습니다. 이제 GetColorSymbolInfoByIndex() 함수를 살펴보겠습니다. 모든 속성이 표시된 값에 의존하지 않는다는 사실 때문에 이 함수의 코드는 훨씬 간단합니다.

//+------------------------------------------------------------------+
//| Returning the symbol property color by index                     |
//+------------------------------------------------------------------+
color GetColorSymbolInfoByIndex(int index)
  {
   double check_value =0.0;
   color  clr         =clrWhiteSmoke;
//---
   switch(index)
     {
      case 6  :
         clr=(SymbolInfoInteger(_Symbol,SYMBOL_TIME)>0) ? clrCornflowerBlue : clrWhiteSmoke;                      break;
         //---
      case 9  : clr=(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD_FLOAT)>0) ? clrGold : clrRed;                        break;
      //---
      case 13 :
         clr=(SymbolInfoInteger(_Symbol,SYMBOL_START_TIME)>0) ? clrCornflowerBlue : clrWhiteSmoke;                break;
      case 14 :
         clr=(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME)>0) ? clrCornflowerBlue : clrWhiteSmoke;           break;
         //---
      case 15 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)>0) ? clrWhiteSmoke : clrRed;             break;
      case 16 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL)>0) ? clrWhiteSmoke : clrRed;            break;
      //---
      case 20 : clr=clrGold;                                                                                      break;
      case 21 : clr=clrGold;                                                                                      break;
      //---
      case 39 : clr=(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT)>0) ? clrWhiteSmoke : clrGold;                  break;
      case 40 : clr=(SymbolInfoDouble(_Symbol,SYMBOL_SWAP_LONG)>0) ? clrLime : clrRed;                            break;
      case 41 : clr=(SymbolInfoDouble(_Symbol,SYMBOL_SWAP_SHORT)>0) ? clrLime : clrRed;                           break;
      //---
      case 60 : clr=clrGold;                                                                                      break;
      case 61 : clr=clrGold;                                                                                      break;
      case 62 : clr=clrGold;                                                                                      break;
      //---
      case 68 :
         clr=(SeriesInfoInteger(_Symbol,_Period,SERIES_FIRSTDATE)>0) ? clrCornflowerBlue : clrWhiteSmoke;         break;
      case 69 :
         clr=(SeriesInfoInteger(_Symbol,_Period,SERIES_SERVER_FIRSTDATE)>0) ? clrCornflowerBlue : clrWhiteSmoke;  break;
      case 70 : clr=(!(bool)SeriesInfoInteger(_Symbol,_Period,SERIES_SYNCHRONIZED)) ? clrRed : clrGold;           break;
     }
//---
   return(clr);
  }

이제 지표를 컴파일하여 차트에 추가하면 아래 스크린샷과 같이 하위 창에서 기호 속성 목록을 볼 수 있습니다.

그림 1. 스크롤바 없이 차트에 부착된 지표

그림 1. 스크롤바 없이 차트에 부착된 지표

이 모든 것이 하나의 개체입니다!

또한 세로 스크롤 바로 작업하기 위한 함수를 작성합니다. 글 시작 부분에서 이미 언급했듯이 스크롤 바는 두 개의 그래픽 개체 OBJ_RECTANGLE_LABEL(사각형 레이블)를 사용하여 생성됩니다. 하나는 배경으로 사용하고 다른 하나는 스크롤 상자로 사용합니다. 스크롤바는 지표 하위 창의 오른쪽에 위치합니다.

CreateRectangleLable() - 직사각형 레이블을 만드는 함수:

//+------------------------------------------------------------------+
//| Creating a rectangle                                             |
//+------------------------------------------------------------------+
void CreateRectangleLable(long              chart_id,          // chart id
                          int               sub_window,        // window number
                          string            object_name,       // object name
                          int               x_distance,        // X-coordinate
                          int               y_distance,        // Y-coordinate
                          int               x_size,            // width
                          int               y_size,            // height
                          ENUM_BASE_CORNER  corner,            // chart corner
                          color             border_color,      // border color
                          color             background_color,  // background color
                          bool              selectable,        // cannot select the object if FALSE
                          bool              is_on_background)  // background position
  {
//--- If the object has been created successfully
   if(ObjectCreate(chart_id,object_name,OBJ_RECTANGLE_LABEL,sub_window,0,0))
     {
      // set its properties
      ObjectSetInteger(chart_id,object_name,OBJPROP_XDISTANCE,x_distance);
      ObjectSetInteger(chart_id,object_name,OBJPROP_YDISTANCE,y_distance);
      ObjectSetInteger(chart_id,object_name,OBJPROP_XSIZE,x_size);
      ObjectSetInteger(chart_id,object_name,OBJPROP_YSIZE,y_size);
      ObjectSetInteger(chart_id,object_name,OBJPROP_BORDER_TYPE,BORDER_FLAT);    // set the flat border style
      ObjectSetInteger(chart_id,object_name,OBJPROP_COLOR,border_color);
      ObjectSetInteger(chart_id,object_name,OBJPROP_CORNER,corner);
      ObjectSetInteger(chart_id,object_name,OBJPROP_BGCOLOR,background_color);
      ObjectSetInteger(chart_id,object_name,OBJPROP_SELECTABLE,selectable);
      ObjectSetInteger(chart_id,object_name,OBJPROP_BACK,is_on_background);      // it will be used as a background if true
      ObjectSetString(chart_id,object_name,OBJPROP_TOOLTIP,"\n");                // no tooltip if "\n"
     }
  }

스크롤 상자와 스크롤바 배경 크기를 생성하고 수정하는 함수를 작성해 보겠습니다. AdjustScrollbarThumb()AdjustScrollbarBackground():

//+------------------------------------------------------------------+
//| Adding scroll box or adjusting its size                          |
//+------------------------------------------------------------------+
void AdjustScrollbarThumb()
  {
//--- Calculate the scroll box size relative to the subwindow height
   CalculateScrollbarThumbHeight();
//--- If the scroll box is already available in the chart, adjust its properties
   if(ObjectFind(0,scrollbar_thumb_name)>0)
     {
      //--- Set the height and X-coordinate
      ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_YSIZE,scrollbar_thumb_height);
      ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_XDISTANCE,chart_width-scrollbar_thumb_width);
      //--- Adjust the scroll box position along the Y-axis if you go below the subwindow bottom boundary
      if(scrollbar_thumb_y1+scrollbar_thumb_height>subwindow_height)
         ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE,subwindow_height-scrollbar_thumb_height);
     }
//--- Create the scroll box if it does not exist
   else
     {
      CreateRectangleLable(0,subwindow_number,scrollbar_thumb_name,
                           chart_width-scrollbar_thumb_width,0,scrollbar_thumb_width,scrollbar_thumb_height,
                           CORNER_LEFT_UPPER,clrSilver,clrSilver,false,false);
     }
  }
//+------------------------------------------------------------------+
//| Adding the scrollbar background or adjusting its size            |
//+------------------------------------------------------------------+
void AdjustScrollbarBackground()
  {
//--- If the scrollbar background is already available in the chart, adjust its properties
   if(ObjectFind(0,scrollbar_background_name)>0)
     {
      //--- Set the background size
      ObjectSetInteger(0,scrollbar_background_name,OBJPROP_YDISTANCE,0);
      ObjectSetInteger(0,scrollbar_background_name,OBJPROP_XDISTANCE,chart_width-scrollbar_background_width);
      ObjectSetInteger(0,scrollbar_background_name,OBJPROP_YSIZE,subwindow_height);
     }
//--- If there is no background, create it
   else
     {
      CreateRectangleLable(0,subwindow_number,scrollbar_background_name,
                           chart_width-scrollbar_background_width,0,scrollbar_background_width,subwindow_height,
                           CORNER_LEFT_UPPER,scrollbar_background_color,scrollbar_background_color,false,false);
     }
  }

스크롤 상자 높이는 강조 표시된 문자열에서 AdjustScrollbarThumb() 함수의 맨 처음에 계산됩니다.

//+------------------------------------------------------------------+
//| Calculating the scroll box size relative to the subwindow height |
//+------------------------------------------------------------------+
void CalculateScrollbarThumbHeight()
  {
//--- If the subwindow height is greater than the list size, save the subwindow size
   if(subwindow_height>=list_height)
      scrollbar_thumb_height=subwindow_height-1;
//--- Otherwise calculate the scroll box size
   else
     {
      double height_temp=0.0;
      //--- Calculate the scroll box size relative to the subwindow height
      height_temp=subwindow_height-(((double)subwindow_height/100)*(100-((double)subwindow_height/list_height)*100));
      //--- Set the minimum size at 25% of the subwindow height
      if(height_temp/subwindow_height<0.25)
         height_temp=subwindow_height/4;
      //--- Save to the global variable
      scrollbar_thumb_height=(int)height_temp;
     }
  }

그래픽 개체를 삭제하는 것을 잊지 마십시오.

//+------------------------------------------------------------------+
//| Deleting the scrollbar                                           |
//+------------------------------------------------------------------+
void DeleteScrollbar()
  {
   DeleteObjectByName(scrollbar_thumb_name);
   DeleteObjectByName(scrollbar_background_name);
  }
//+------------------------------------------------------------------+
//| Deleting the object by name                                      |
//+------------------------------------------------------------------+
void DeleteObjectByName(string object_name)
  {
//--- If such object exists
   if(ObjectFind(0,object_name)>=0)
     {
      //--- If an error occurred when deleting, print the relevant message
      if(!ObjectDelete(0,object_name))
         Print("Error ("+IntegerToString(GetLastError())+") when deleting the object!");
     }
  }

이제 가장 흥미로운 부분으로 넘어가겠습니다. 스크롤 상자를 끌어 목록도 이동하도록 하는 함수를 작성해야 합니다. 또한 커서가 스크롤 상자 위에 놓일 때와 스크롤 상자를 클릭하여 컨트롤이 스크롤 상자로 전달되었으며 이제 스크롤 상자를 끌 수 있음을 나타내기 위해 스크롤 상자의 색상 변경을 구현해야 합니다. 이를 위해 클릭 시 스크롤 상자 색상이 추가로 변경됩니다.

스크롤 상자의 너비가 상당히 좁기 때문에 위/아래로 이동할 때 커서가 옆으로 이동하는 것을 경험할 수 있습니다. 이 문제를 해결하기 위해 왼쪽 마우스 버튼을 누르고 있는 동안 스크롤 상자에 컨트롤을 전달합니다.

아래는 위에서 설명한 기능의 코드입니다.

//+------------------------------------------------------------------+
//| Setting the scroll box color                                     |
//+------------------------------------------------------------------+
void SetScrollbarThumbColor(color thumb_color)
  {
   ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_COLOR,thumb_color);
   ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_BGCOLOR,thumb_color);
  }
//+------------------------------------------------------------------+
//| Setting the scroll box boundaries                                |
//+------------------------------------------------------------------+
void SetScrollbarThumbBoundaries()
  {
   scrollbar_thumb_x1=(int)ObjectGetInteger(0,scrollbar_thumb_name,OBJPROP_XDISTANCE);
   scrollbar_thumb_y1=(int)ObjectGetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE);
   scrollbar_thumb_x2=scrollbar_thumb_x1+scrollbar_thumb_width;
   scrollbar_thumb_y2=scrollbar_thumb_y1+scrollbar_thumb_height;
  }
//+------------------------------------------------------------------+
//| Changing the color of the scroll box when the cursor hovers over |
//+------------------------------------------------------------------+
void ChangeScrollbarThumbColorOnHover(int x,int y)
  {
//--- If the cursor is within the scroll box area, make the color darker
   if(x>scrollbar_thumb_x1 && x<scrollbar_thumb_x2 && y>scrollbar_thumb_y1 && y<scrollbar_thumb_x2)
      SetScrollbarThumbColor(scrollbar_thumb_color_on_hover);
//--- If the cursor is outside the scroll box boundaries
   else
     {
      //--- If the mouse button is released, set the standard scroll box color
      if(!mouse_button_state)
         SetScrollbarThumbColor(scrollbar_thumb_color);
     }
  }
//+------------------------------------------------------------------+
//| Determining the scroll box state                                 |
//+------------------------------------------------------------------+
void SetScrollbarThumbState(int x,int y)
  {
//--- If the mouse cursor is within the scroll box boundaries
   if(x>scrollbar_thumb_x1 && x<scrollbar_thumb_x2 && y>scrollbar_thumb_y1 && y<scrollbar_thumb_x2)
     {
      //--- If the mouse button is pressed, save it
      if(mouse_button_state)
         scrollbar_thumb_clicked=true;
     }
//--- If the cursor is outside the scroll box boundaries
   else
     {
      //--- If the mouse button is released, disable the scroll box control
      if(!mouse_button_state)
         ZeroScrollbarThumbVariables();
     }
  }
//+------------------------------------------------------------------+
//| Zeroing out variables related to scroll box movement             |
//+------------------------------------------------------------------+
void ZeroScrollbarThumbVariables()
  {
   scrollbar_thumb_clicked       =false;
   scrollbar_fix_point           =0;
   scrollbar_fix_point_y_offest  =0;
  }

스크롤 상자를 이동하는 데 필요한 모든 기능은 아닙니다. 사실 스크롤 상자의 이동성은 이벤트를 기반으로 합니다. 즉, 추적 차트 영역에 커서가 있는 상태에서 마우스 버튼을 누른 다음 마우스 버튼을 계속 누르고 있는 상태에서 지정된 픽셀 수만큼 커서를 이동하면 특정 동작이 트리거됩니다. 이 경우 스크롤 상자 위치가 변경되고 결과적으로 기호 속성 목록이 변경됩니다. 그건 아주 간단합니다.

아래에서 위 작업을 구현하는 데 사용되는 MoveThumb(), UpdateListAndScrollbarThumb()ThumbYCoordinateToPercent() 함수를 볼 수 있습니다.

//+------------------------------------------------------------------+
//| Moving the scroll box vertically to the specified coordinate     |
//+------------------------------------------------------------------+
void MoveThumb(int y)
  {
   int  threshold   =1; // Threshold in pixels for recalculation
   int  new_y_point =0; // New Y-coordinate
//--- If the mouse button is pressed
   if(mouse_button_state)
     {
      //--- Set the clicked scroll box color
      SetScrollbarThumbColor(scrollbar_thumb_color_on_click);
      //--- Save the current Y-coordinate of the cursor
      if(scrollbar_fix_point==0)
         scrollbar_fix_point=y;
      //--- Save the distance from the scroll box top to the cursor
      if(scrollbar_fix_point_y_offest==0)
         scrollbar_fix_point_y_offest=scrollbar_thumb_y1-scrollbar_fix_point;
     }
//--- If you scrolled down below the threshold value, while keeping the button pressed
   if(y-scrollbar_fix_point>=threshold)
     {
      //--- If you are still within the subwindow boundaries
      if(scrollbar_thumb_y1+scrollbar_thumb_height+threshold<subwindow_height)
         new_y_point=y+scrollbar_fix_point_y_offest;
      else
        {
         scrollbar_fix_point_y_offest=0;
         new_y_point=int(subwindow_height-scrollbar_thumb_height)-1;
        }
      //--- Refresh the list and the scroll box
      UpdateListAndScrollbarThumb(new_y_point);
      return;
     }
//--- If you scrolled up above the threshold value, while keeping the button pressed
   if(y-scrollbar_fix_point<=-(threshold))
     {
      //--- If you are still within the subwindow boundaries
      if(y-fabs(scrollbar_fix_point_y_offest)>=0)
         new_y_point=y-fabs(scrollbar_fix_point_y_offest);
      else
        {
         new_y_point=0;
         scrollbar_fix_point_y_offest=0;
        }
      //--- Refresh the list and the scroll box
      UpdateListAndScrollbarThumb(new_y_point);
      return;
     }
  }
//+------------------------------------------------------------------+
//| Refreshing the list and the scroll box position                  |
//+------------------------------------------------------------------+
void UpdateListAndScrollbarThumb(int new_point)
  {
//--- Set the new Y-coordinate for the scroll box
   ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE,new_point);
//--- Refresh the list of the symbol properties relative to the current scroll box position
   ShowSymbolInfo(ThumbYCoordinateToPercent(new_point));
//--- Zero out the fix point
   scrollbar_fix_point=0;
  }
//+------------------------------------------------------------------+
//| Converting the Y-coordinate of the scroll box to percentage      |
//+------------------------------------------------------------------+
double ThumbYCoordinateToPercent(long y)
  {
   if(subwindow_height<=0)
      subwindow_height=1;
//---
   return(((double)y/subwindow_height)*100);
  }

이제 프로그램이 처음에 생각한 대로 작동할 수 있도록 모든 기능을 특정 순서로 배치해야 합니다. OnChartEvent() 함수에서 사용자가 지표 하위 창과 상호 작용하는 데 도움이 되는 이벤트와 하위 창에 있는 목록 및 스크롤 바를 처리해야 합니다.

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Track mouse clicks on the chart
   if(id==CHARTEVENT_CLICK)
     {
      //--- Zero out variables related to the scroll box movement
      ZeroScrollbarThumbVariables();
      //--- Refresh the chart
      ChartRedraw();
      return;
     }
//--- Track the cursor movement and the state of the left mouse button
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      int      x      =(int)lparam; // X-coordinate
      int      y      =(int)dparam; // Y-coordinate
      int      window =WRONG_VALUE; // Number of the window where the cursor is located
      datetime time   =NULL;        // Time corresponding to the X-coordinate
      double   price  =0.0;         // Price corresponding to the Y-coordinate
 
      //--- Set subwindow properties
      SetSubwindowProperties();
      //--- Check and save the state of the mouse button
      CheckMouseButtonState(sparam);
      //--- Get the position of the cursor
      if(ChartXYToTimePrice(0,x,y,window,time,price))
        {
         //--- If the cursor is within the subwindow boundaries
         if(window==subwindow_number)
           {
            //--- Disable scrolling of the price chart
            ChartSetInteger(0,CHART_MOUSE_SCROLL,false);
            //--- Recalculate the Y coordinate relative to the indicator subwindow
            ChartYToSubwindowY(y);
            //--- Determine the scroll box boundaries
            SetScrollbarThumbBoundaries();
            //--- Change the scroll box color when the cursor moves over it
            ChangeScrollbarThumbColorOnHover(x,y);
            //--- Determine the scroll box state
            SetScrollbarThumbState(x,y);
            //--- If the control is passed to the scroll box, drag it and refresh the list
            if(scrollbar_thumb_clicked)
               MoveThumb(y);
           }
         //--- If the cursor is outside the subwindow boundaries
         else
           {
            //--- Enable scrolling of the price chart
            ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
            //--- If the mouse button is released, set the standard scroll box color
            if(!scrollbar_thumb_clicked)
               SetScrollbarThumbColor(scrollbar_thumb_color);
           }
        }
      //--- If the position of the cursor could not be determined
      else
        {
         //--- If the control has not been passed to the scroll box, set the standard scroll box color
         if(!scrollbar_thumb_clicked)
            SetScrollbarThumbColor(scrollbar_thumb_color);
        }
      //--- Refresh the chart
      ChartRedraw();
      return;
     }
//--- Track the change of properties and size of the chart
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Set subwindow properties
      SetSubwindowProperties();
      //--- Get the Y-coordinate of the scroll box
      scrollbar_thumb_y1=(int)ObjectGetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE);
      //--- If the subwindow size is zero, exit
      if(subwindow_height<=0)
         return;
      //--- Set the new canvas size
      ResizeCanvas();
      //--- Refresh the scrollbar background
      AdjustScrollbarBackground();
      //--- Refresh the scroll box
      AdjustScrollbarThumb();
      //--- Refresh the data on the canvas
      ShowSymbolInfo(ThumbYCoordinateToPercent(scrollbar_thumb_y1));
      //---
      return;
     }
  }

위 코드에서 강조 표시된 기능은 보조 기능입니다. 제공된 주석에서 목적을 쉽게 이해할 수 있습니다.

//+-------------------------------------------------------------------+
//| Checking the mouse button state                                   |
//+-------------------------------------------------------------------+
void CheckMouseButtonState(string state)
  {
//--- Left mouse button is pressed
   if(state=="1")
      mouse_button_state=true;
//--- Left mouse button is released
   if(state=="0")
     {
      //--- Zero out variables
      ZeroScrollbarThumbVariables();
      mouse_button_state=false;
     }
  }
//+-------------------------------------------------------------------+
//| Recalculating the Y-coordinate relative to the indicator subwindow|
//+-------------------------------------------------------------------+
void ChartYToSubwindowY(int &y)
  {
//--- Get the distance from the chart top to the indicator subwindow
   chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number);
//--- Recalculate the Y-coordinate relative to the indicator subwindow
   y-=chart_y_offset;
  }

캔버스와 마찬가지로 초기화 중에 지표 하위 창에 스크롤 바를 추가해야 합니다.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Enable tracking of mouse events
   ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
//--- Set the short name
   IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname);
//--- Set sizes of arrays of symbol properties and their colors
   ArrayResize(symbol_property_colors,LIST_SIZE);
   ArrayResize(symbol_property_values,LIST_SIZE);
//--- Set subwindow properties
   SetSubwindowProperties();
//--- Set the font for displaying on the canvas
   canvas.FontSet(font_name,font_size,FW_NORMAL);
//--- Save the text size (height) for calculations
   text_height=canvas.TextHeight("A")-1;
//--- Calculate the height of the entire list
   list_height=text_height*LIST_SIZE;
//--- Add the canvas to the chart
   AddCanvas();
//--- Add the scrollbar: background and scroll box
   AdjustScrollbarBackground();
   AdjustScrollbarThumb();
//--- Display the list of symbol properties
   ShowSymbolInfo();
//--- Refresh the chart
   ChartRedraw();
//--- Everything completed successfully
   return(INIT_SUCCEEDED);
  }

OnDeinit() 함수에서 "정리"하는 것을 잊지 마십시오. 초기화 해제 사유에 따라 프로그램을 보다 정확하게 설정할 수 있습니다.

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(reason==REASON_REMOVE      || // If the indicator has been removed from the chart or
      reason==REASON_CHARTCHANGE || // the symbol or time frame has been modified or
      reason==REASON_RECOMPILE   || // the program has been recompiled or
      reason==REASON_CHARTCLOSE  || // the chart has been closed or
      reason==REASON_CLOSE)         // the terminal has been closed
     {
      //--- Delete the scrollbar
      DeleteScrollbar();
      //--- Delete the canvas
      DeleteCanvas();
      //--- Enable scrolling of the price chart
      ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
      //--- Disable cursor tracking
      ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,false);
      //--- Refresh the chart
      ChartRedraw();
     }
  }

마지막으로 실시간 모드에서 일부 기호 속성을 새로 고치려면 OnCalculate() 함수에 몇 가지 코드 문자열을 추가해야 합니다.

이제 모든 것이 준비되었습니다. 소스 코드는 MetaEditor 5에서 고려할 수 있도록 글에 첨부되어 있으며 다운로드할 수 있습니다. 이 글에서 다루는 기능의 작동은 아래 제공된 비디오에 나와 있습니다.

 

결론

스크롤바 컨트롤에 대한 검토를 완료했습니다. 이 글에서는 캔버스에 있는 별도의 그래픽 개체로 스크롤바를 만드는 방법을 보여주었습니다. 향후 글 중 하나에서 이 클래스의 를 사용하여 전체 기능을 구현하려고 합니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/751

파일 첨부됨 |
testscrollbar.mq5 (57.23 KB)
MQL5 Coobook: MQL5에서 다중 기호 변동성 지표 개발 MQL5 Coobook: MQL5에서 다중 기호 변동성 지표 개발
이 글에서는 다중 기호 변동성 지표의 개발을 고려해볼 것입니다. 다중 기호 지표의 개발은 이 글에서 명확히 하는 데 도움이 되는 초보 MQL5 개발자에게 몇 가지 어려움을 줄 수 있습니다. 다중 기호 지표 개발 과정에서 발생하는 주요 문제는 현재 기호에 대한 다른 기호 데이터의 동기화, 일부 지표 데이터의 부족 및 주어진 시간 프레임의 '참' 바에 대한 시작 식별과 관련이 있습니다. 이 모든 문제는 글에서 면밀히 고려해보겠습니다.
MQL5 Coobook: 지표 하위 창 컨트롤 - 버튼 MQL5 Coobook: 지표 하위 창 컨트롤 - 버튼
이 글에서는 버튼 컨트롤이 있는 사용자 인터페이스를 개발하는 예를 고려할 것입니다. 사용자에게 상호 작용에 대한 아이디어를 전달하기 위해 커서가 버튼 위에 있을 때 버튼의 색상이 변경됩니다. 버튼 위에 커서가 있으면 버튼 색상이 약간 어두워지고 버튼을 클릭하면 훨씬 더 어두워집니다. 또한 각 버튼에 툴팁을 추가하여 직관적인 인터페이스를 만듭니다.
다중 통화 다중 시스템 Expert Advisor 만들기 다중 통화 다중 시스템 Expert Advisor 만들기
이 글에서는 여러 기호를 거래하고 여러 거래 시스템을 동시에 사용하는 Expert Advisor의 구조를 소개합니다. 모든 EA에 대한 최적의 입력 매개변수를 이미 식별하고 각각에 대해 개별적으로 좋은 백테스팅 결과를 얻었다면 모든 전략을 함께 사용하여 모든 EA를 동시에 테스트하면 어떤 결과를 얻을 수 있는지 자문해 보십시오.
MQL5 Coobook: 단일 창에서 여러 시간 프레임 모니터링 MQL5 Coobook: 단일 창에서 여러 시간 프레임 모니터링
MetaTrader 5에는 분석을 위해 21개의 시간 프레임이 있습니다. 기존 차트에 배치하고 기호, 시간 프레임 및 기타 속성을 바로 설정할 수 있는 특수 차트 개체를 활용할 수 있습니다. 이 글에서는 이러한 차트 그래픽 개체에 대한 자세한 정보를 제공합니다. 하위 창에서 동시에 여러 차트 개체를 설정할 수 있는 컨트롤(버튼)이 있는 지표를 만듭니다. 또한 차트 개체는 하위 창에 정확하게 맞고 기본 차트 또는 터미널 창의 크기가 조정될 때 자동으로 조정됩니다.