![Das MQL5-Kochbuch: Steuerelemente des Indikatorunterfensters - Die Bildlaufleiste](https://c.mql5.com/i/articles/overlay.png)
Das MQL5-Kochbuch: Steuerelemente des Indikatorunterfensters - Die Bildlaufleiste
Einleitung
Wir setzen das Studium der Steuerelemente fort und kommen diesmal zur Bildlaufleiste, dem Scrollbar. Genauso wie schon in dem vorhergehenden Beitrag „Das MQL5-Kochbuch: Steuerelemente des Indikatorunterfensters - Die Schaltflächen“ arbeiten wir in dem Unterfenster für den Indikator. Den genannten Beitrag sollte man gelesen haben, da in ihm die Arbeit mit Ereignissen in der Funktion OnChartEvent() ausführlich dargelegt wird, während diese hier nur oberflächlich gestreift wird. Als Beispiel wird in diesem Beitrag eine senkrechte Bildlaufleiste für eine lange Aufstellung aller mit den Möglichkeiten der Programmiersprache MQL5 zu beziehenden Kenngrößen eines Finanzinstrumentes angelegt.Funktionen zur Verarbeitung von Ereignissen: OnChartEvent
In den bisherigen Beiträgen zum Programmieren in MQL5 wurde zum Anlegen von Aufstellungen (Listen) das grafische Objekt OBJ_LABEL (Beschriftung) verwendet. Diesmal werden wir uns zur Wiedergabe von Text einer „Leinwand“ bedienen. Der Reiz dieser Herangehensweise besteht darin, dass statt einer Menge von Objekten der Art OBJ_LABEL nur ein einziges Objekt benötigt wird: OBJ_BITMAP_LABEL (grafisches Element). Es können beliebige Elemente der Benutzeroberfläche auf die Leinwand gezeichnet werden, aber wir beschränken uns hier auf die Ausgabe von Text.
Die Bildlaufleiste gestalten wir möglichst schlicht. Für gewöhnlich werden ihr Schaltflächen mit Pfeilen zugeordnet, aber darauf verzichten wir hier. Die Bildlaufleiste besteht lediglich aus einem Hintergrund und einem „Schieber“. Letzterer ändert seine Farbe, wenn der Mauszeiger ihn überfährt. Wird er angeklickt. ändert er seine Farbe ebenfalls, um dem Anwender anzuzeigen, dass er bewegt werden kann. Zum Anlegen der Laufleistenelemente verwenden wir grafische Objekte der Art OBJ_RECTANGLE_LABEL (rechteckige Markierung).
Die Entwicklung des Indikators
Kommen wir zur Programmierung: Legen Sie wie in dem vorhergehenden Beitrag vorgeführt eine Schablone (ein Template) des Indikators an. Ganz zu Anfang müssen wie immer die Variablen und Datenfelder deklariert werden. Für die Arbeit mit der Leinwand beziehen wir die Klasse CCanvas aus der Standardbibliothek ein.
#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" };
Zunächst schreiben wir alle Funktionen, die zur Wiedergabe einer mehrere Punkte umfassenden Aufstellung auf der Leinwand erforderlich sind. Erst danach legen wir die Bildlaufleiste an.
Zur Erstellung der Leinwand schreiben wir die Funktion AddCanvas(), in der wir die 2. Variante der Methode CreateBitmapLabel() der KlasseCCanvas verwenden:
//+------------------------------------------------------------------+ //| 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); }
Außerdem benötigen wir ein Verfahren zur Änderung der Leinwandgröße, um sie an die Abmessungen des Indikatorunterfensters anzupassen. Dazu schreiben wir die Funktion ResizeCanvas(), die sich der Methode Resize() in der Klasse CCanvas bedient:
//+------------------------------------------------------------------+ //| 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); }
Zur Löschung der Leinwand kommt die Methode Destroy() zum Einsatz:
//+------------------------------------------------------------------+ //| Deleting canvas | //+------------------------------------------------------------------+ void DeleteCanvas() { if(ObjectFind(0,canvas_name)>0) canvas.Destroy(); }
Aus der Klasse CCanvas werden in diesem Beitrag zudem die Methoden FontSet() zur Einstellung der Schriftart, TextHeight() zur Bestimmung der Schrifthöhe, TextOut() für die Textausgabe auf der Leinwand, Erase() zur Bereinigung der Leinwand sowie Update() für die aktualisierte Neuzeichnung verwendet. Darüber hinaus zeigt der Beitrag, an welchen Stellen des Programms die aufgeführten Methoden Anwendung finden.
Bei der Bereitstellung der Funktion OnInit() muss das Programm einsatzbereit gemacht werden. Der Code unten zeigt, was getan werden muss. Die Kommentare in jeder Zeile helfen bei der Orientierung. Die Methoden FontSet() und TextHeight() der Klasse CCanvas werden in diesem Programm nur an dieser Stelle verwendet.
//+------------------------------------------------------------------+ //| 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); }
Die Funktion SetSubwindowProperties() wurde unverändert aus dem vorhergehenden Beitrag übernommen: in ihr werden die Werte der Kennziffer und der Größe des Indikatorunterfensters globalen Variablen zugeordnet. Damit kommen wir zu der Funktion 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(); }
Die Funktion ShowSymbolInfo() verfügt lediglich über den Parameter current_thumb_position mit der Voreinstellung „0“ (wenn der voreingestellte Wert verwendet werden soll, muss kein Wert an die Funktion weitergegeben werden). Dieser Parameter bestimmt, ab welcher Zeile die Aufstellung wiedergegeben werden soll, das heißt, der Wert Null gibt an, dass die Aufstellung von Anfang an abgebildet wird.
Gleich zu Anfang wird die Kennziffer der Zeile festgelegt, mit der die Wiedergabe der Aufstellung beginnen soll. Danach werden in der Funktion InitializePropertyArrays() die Datenfelder mit den Werten und den Farben (die im Code oben hervorgehobene Zeile) bereitgestellt. Die Bereitstellung erfolgt ab der in dem vorherigen Arbeitsgang festgelegten Zeile. Anschließend wird die Leinwand mithilfe der Funktion Erase() bereinigt, was in der Praxis bedeutet, dass sie vollständig mit der festgelegten Füllfarbe gefüllt wird. Im letzten Arbeitsgang schließlich erfolgt unter Einsatz der Methode TextOut() die unmittelbare Aufbringung des Textes auf die Leinwand. Abschließend wird die Leinwand mithilfe der Methode Update() aktualisiert.
Es folgt der Code der Funktion 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; } }
In dem Code oben ist z erkennen, dass die Werte der Kenngrößen des Kürzels sowie ihre Farben mithilfe zweier im Grunde gleichartiger Operationen der Funktionen GetStringSymbolInfoByIndex() und GetColorSymbolInfoByIndex() festgelegt werden.
Die Funktion GetStringSymbolInfoByIndex() ist recht einfach, erscheint jedoch in ihrer Darstellung aufgrund der großen Anzahl von Kenngrößen ziemlich umfangreich. Zudem werden zum Bezug einiger Kenngrößen Hilfsfunktionen benötigt (die in dem Code unten hervorgehobenen Zeilen).
//+------------------------------------------------------------------+ //| 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); }
Die oben gekennzeichneten Funktionen TradeCalcModeToString(), TradeModeToString(), TradeExeModeToString(), SwapModeToString() und WeekdayToString() geben die Kenngrößen je nach weitergeleitetem Wert einfach zeilenweise wieder.
//+------------------------------------------------------------------+ //| 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); }
In den Funktionen GetStringExpirationMode() und GetStringFillingMode() wird der jeweilige Zeilenwert in Abhängigkeit davon gebildet, welche Arten der Befristung des Auftrags und der Auffüllung seines Umfangs für das aktuelle Kürzel verfügbar sind.
//+------------------------------------------------------------------+ //| 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); }
Da das Vorhandensein jeder Art einzeln überprüft werden muss, verwenden wir der Einfachheit halber die Hilfsfunktionen IsExpirationTypeAllowed() und IsFillingTypeAllowed() aus den Beispielen in den „Dokumenten“:
//+------------------------------------------------------------------+ //| 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); }
Mit den Zeilenwerten der Kenngrößen des Kürzels wären wir damit fertig. Jetzt sehen wir uns an, wie die Funktion GetColorSymbolInfoByIndex() aussieht. Nicht alle Kenngrößen hängen davon ab, welcher Wert angezeigt wird, deshalb ist der Code dieser Funktion ungleich schlichter:
//+------------------------------------------------------------------+ //| 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); }
Wenn man den Indikator jetzt zusammenstellt und in das Diagramm lädt, erscheint die Aufstellung der Kennziffern zu dem Kürzel im Unterfenster so, wie es in der folgenden Bildschirmaufnahme dargestellt wird:
Abb. 1. In das Diagramm geladener Indikator ohne Bildlaufleiste
Und das alles ist ein einziges Objekt!
Weiter geht es mit der Darstellung der Funktionen Für die Arbeit mit einer senkrechten Bildlaufleiste. Bei zu Beginn dieses Beitrages bereits erwähnt werden zum Anlegender Bildlaufleiste zwei grafische Objekte der Art OBJ_RECTANGLE_LABEL (rechteckige Markierung) verwendet. Eines stellt den Hintergrund dar, das andere den Schieber. Die Bildlaufleiste soll im rechten Teil des Indikatorunterfensters angelegt werden.
Die Funktion CreateRectangleLable() zum Anlegen einer rechteckigen Markierung:
//+------------------------------------------------------------------+ //| 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" } }
Jetzt legen wir zwei Funktionen zur Erstellung und Größenanpassung des Schiebers und des Hintergrundes an: AdjustScrollbarThumb() und 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); } }
Ganz zu Beginn der Funktion AdjustScrollbarThumb() erfolgt in der hervorgehobenen Zeile die Berechnung der Höhe des Schiebers:
//+------------------------------------------------------------------+ //| 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; } }
Dabei dürfen wir nicht vergessen, die grafischen Objekte zu entfernen:
//+------------------------------------------------------------------+ //| 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!"); } }
Jetzt kommen wir zum interessantesten Teil, der Programmierung der Funktionen, mit deren Hilfe es möglich ist den Schieber zu bewegen und dabei gleichzeitig die Aufstellung (Liste) zu verschieben. Außerdem müssen wir dafür sorgen, dass sich die Farbe des Schiebers ändert, wenn der Mauszeiger darüber geführt oder er mit der linken Maustaste angeklickt wird, damit der Anwender weiß, dass der Schieber zur Steuerung verwendet werden kann. Dazu ändern wir die Farbe zusätzlich beim Anklicken.
Die Breite des Schiebers ist recht gering, weshalb es bei seiner Verschiebung noch oben oder unten geschehen kann, dass der Mauszeiger seitlich „ausbricht“. Zur Behebung dieses Mangels verbleibt die Steuerung bis zur nächsten Betätigung der linken Maustaste bei dem Schieber.
Es folgt der Code für die gerade besprochenen Funktionen:
//+------------------------------------------------------------------+ //| 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; }
Dabei handelt es sich noch nicht um alle zur Bewegung des Schiebers erforderlichen Funktionen. Bei der Steuerung der Bewegung beruht eigentlich alles auf Ereignissen. Anders ausgedrückt: Wenn die Maustaste in dem untersuchten Bereich des Diagramms betätigt wurde und danach mit gedrückt gehaltener Maustaste eine Verschiebung um eine vorgegebene Anzahl Bildpunkte (Pixel) erfolgt ist, findet ein bestimmter Vorgang statt. In unserem Fall ist das die Änderung der Schieberposition und dementsprechend der Aufstellung der Kenngrößen zu dem betreffenden Kürzel. Er ist ziemlich simpel.
Unten werden die Funktionen MoveThumb(), UpdateListAndScrollbarThumb() und ThumbYCoordinateToPercent() vorgestellt, mit deren Hilfe die oben beschriebenen Operationen ausgeführt werden:
//+------------------------------------------------------------------+ //| 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); }
Jetzt müssen alle Funktionen so angeordnet werden, dass das Programm wie geplant arbeitet. In der Funktion OnChartEvent() müssen die Ereignisse verarbeitet werden, mit deren Hilfe der Anwender mit dem Unterfenster für den Indikator sowie der in diesem angelegten Aufstellung (Liste) und der Bildlaufleiste interagieren kann:
//+------------------------------------------------------------------+ //| 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; } }
Bei den in dem vorstehenden Code hervorgehobenen handelt es sich um Hilfsfunktionen. Ihre Zweckbestimmung ist den Kommentaren zu entnehmen.
//+-------------------------------------------------------------------+ //| 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; }
Die Bildlaufleiste muss dem Indikatorfenster ebenso wie die Leinwand bei der Bereitstellung hinzugefügt werden.
//+------------------------------------------------------------------+ //| 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); }
Wir dürfen nicht vergessen in der Funktion OnDeinit() „hinter uns aufzuräumen“. Je nach Art der Ursache für die „Bereinigung“ kann das Programm präziser eingestellt werden.
//+------------------------------------------------------------------+ //| 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(); } }
Und schließlich müssen wir die Funktion OnCalculate() um einige Codezeilen erweitern, damit gewisse Kenngrößen des Kürzels in Echtzeit aktualisiert werden:
Fertig! Zur eingehenderen Untersuchung in dem Bearbeitungsprogramm MetaEditor 5 kann der Code im Anhang zu diesem Beitrag heruntergeladen werden. Es folgt ein kurzer Film, in dem die Arbeit der in diesem Beitrag vorgestellten Funktionen veranschaulicht wird.
Fazit
Wir haben gerade die Betrachtung der Steuerung der Bildlaufleiste abgeschlossen. In dem Beitrag wurde gezeigt, wie eine Bildlaufleiste mithilfe einzelner grafischer Objekt angelegt und auf einer Leinwand platziert werden kann. In einem späteren Artikel werden wir uns an der Umsetzung des gesamten Funktionsumfangs mithilfe der Methoden dieser Klasse versuchen.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/751
![Das MQL5-Kochbuch: Entwicklung eines mehrwährungsfähigen Kursschwankungsindikators in MQL5](https://c.mql5.com/2/0/avatar1__1.png)
![Das MQL5-Kochbuch: Steuerelemente des Indikatorunterfensters - Die Schaltflächen](https://c.mql5.com/2/0/buttons-avatar.png)
![Anlegen eines systemübergreifenden mehrwährungsfähigen automatischen Handelssystems](https://c.mql5.com/2/0/MultiCurrency_Expert_Advisor_MQL5.png)
![Grafische Interfaces I: Testen der Bibliothek in unterschiedlichen Programmen und in dem MetaTrader 4 Terminal (Kapitel 5)](https://c.mql5.com/2/21/Graphic-interface__4.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.