DoEasy. Steuerung (Teil 31): Scrollen des Inhalts des ScrollBar-Steuerelements
Inhalt
Konzept
Die Bildlaufleiste kann auf das Drücken von Tasten und das Bewegen von Schiebereglern reagieren. Es gibt jedoch keine weiteren Maßnahmen. Das gewünschte Verhalten ist, dass sich der Inhalt des Containers innerhalb des Containers verschiebt, wenn man auf die Bildlauftasten klickt, wodurch zuvor verborgene Bereiche sichtbar werden und zuvor angezeigte Bereiche auf der gegenüberliegenden Seite des Containers ausgeblendet werden. In diesem Artikel werde ich die Möglichkeit schaffen, den Inhalt des Containers beim Klicken auf die Schaltflächen der horizontalen Bildlaufleiste zu verschieben. In diesem Fall passt der Schieberegler seine Größe und Position automatisch an.
Der Schieberegler für die Bildlaufleiste ist nicht nur ein Teil des Elements, mit dem man die Position des Formularinhalts steuern kann, indem man ihn innerhalb des Containers verschiebt. Sie dient auch als schematische Darstellung der relativen Position des Behälters und seines Inhalts. Die Bildlaufleiste selbst entspricht der Breite des gesamten Inhalts des Containers, während der Schieberegler auf der Bildlaufleiste die Breite des Containers darstellt. Je mehr Inhalt außerhalb des Containers liegt, desto kleiner wird der Schieberegler. Die Schiebereglergröße gibt das Fenster an, in dem der Inhalt sichtbar ist, während die Bildlaufleiste den gesamten Inhalt des Containers anzeigt. Indem wir den Schieberegler entlang der Bildlaufleiste bewegen, zeigen wir dem Programm, welchen Inhalt innerhalb des Containers wir gerade sehen wollen.
Auf die gleiche Weise können wir die Position des Inhalts mit den Pfeilschaltflächen an den Rändern der Bildlaufleiste steuern. Gleichzeitig werden sowohl der Inhalt des Containers selbst als auch der Schieberegler auf der Bildlaufleiste verschoben, was uns zeigt, welcher Teil des gesamten Inhalts gerade angezeigt wird.
In diesem Artikel werde ich die Möglichkeit implementieren, den Inhalt mit den Pfeiltasten der horizontalen Bildlaufleiste zu verschieben. Der Schieberegler bewegt sich dann und hat die richtige relative Größe und Positionskoordinaten auf der Bildlaufleiste. Zunächst werde ich die Funktionalität einer horizontalen Bildlaufleiste entwickeln. Dann übertrage ich sie in einer vorgefertigten Form auf die vertikale Leiste und lasse sie zusammenarbeiten.
Verbesserung der Bibliotheksklassen
Da der Schieberegler automatisch seine Größe anpasst, je nachdem, wie weit der Inhalt des Containers über seine Grenzen hinausgeht, kann der Schieberegler zu klein werden, wenn er stark verkleinert wird. Um diese Situation zu vermeiden, sollten wir die Mindestgröße des Schiebereglers festlegen. Beim Verschieben des Containerinhalts müssen wir die Schrittgröße in Pixeln festlegen, um die sich der Containerinhalt in einem Schritt bewegen kann. In MetaEditor beträgt dieser Schritt zum Beispiel sechs Pixel. Machen wir ihn kleiner - zwei Pixel.
In \MQL5\Include\DoEasy\Defines.mqh erstellen wir zwei neue Makrosubstitutionen, um die oben genannten Parameter anzugeben:
#define DEF_CONTROL_SCROLL_BAR_WIDTH (11) // Default ScrollBar control width #define DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN (8) // Minimum size of the capture area (slider) #define DEF_CONTROL_SCROLL_BAR_SCROLL_STEP (2) // Shift step in pixels of the container content when scrolling #define DEF_CONTROL_CORNER_AREA (4) // Number of pixels defining the corner area to resize #define DEF_CONTROL_LIST_MARGIN_X (1) // Gap between columns in ListBox controls #define DEF_CONTROL_LIST_MARGIN_Y (0) // Gap between rows in ListBox controls
Wenn wir Daten aus den Objekteigenschaften „oberer, unterer, linker und rechter Rand eines grafischen Elements“ abrufen wollen, holen wir sie aus den Eigenschaften des Objekts. Diese Parameter weisen die Werte auf, die bei der erfolgreichen Erstellung des Objekts implementiert wurden. Wenn wir nun die Objektgröße ändern, werden die Daten nicht mehr in den Eigenschaften des grafischen Elements gespeichert. Natürlich erhalten wir Werte, wenn wir sie mit einer der Methoden anfordern, die diese Eigenschaft zurückgeben, wie z. B. BottomEdge(), aber diese Methode gibt einfach den berechneten Wert zurück. Diese Werte ändern sich jedoch nicht in den Objekteigenschaften. Dies sollte behoben werden. In der Datei \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh der Bibliothek grafisches Basiselement, und zwar in allen Methoden, die die Objektgrenzen ändern, implementieren wir das Setzen der neuen Werte für die Objekteigenschaften:
//+-------------------------------------------+ //| Set the new X coordinate | //+-------------------------------------------+ bool CGCnvElement::SetCoordX(const int coord_x) { int x=(int)::ObjectGetInteger(this.ChartID(),this.NameObj(),OBJPROP_XDISTANCE); if(coord_x==x) { if(coord_x==this.GetProperty(CANV_ELEMENT_PROP_COORD_X)) return true; this.SetProperty(CANV_ELEMENT_PROP_COORD_X,coord_x); this.SetRightEdge(); return true; } if(::ObjectSetInteger(this.ChartID(),this.NameObj(),OBJPROP_XDISTANCE,coord_x)) { this.SetProperty(CANV_ELEMENT_PROP_COORD_X,coord_x); this.SetRightEdge(); return true; } return false; } //+-------------------------------------------+ //| Set the new Y coordinate | //+-------------------------------------------+ bool CGCnvElement::SetCoordY(const int coord_y) { int y=(int)::ObjectGetInteger(this.ChartID(),this.NameObj(),OBJPROP_YDISTANCE); if(coord_y==y) { if(coord_y==this.GetProperty(CANV_ELEMENT_PROP_COORD_Y)) return true; this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,coord_y); this.SetBottomEdge(); return true; } if(::ObjectSetInteger(this.ChartID(),this.NameObj(),OBJPROP_YDISTANCE,coord_y)) { this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,coord_y); this.SetBottomEdge(); return true; } return false; } //+-------------------------------------------+ //| Set a new width | //+-------------------------------------------+ bool CGCnvElement::SetWidth(const int width) { if(this.GetProperty(CANV_ELEMENT_PROP_WIDTH)==width) return true; if(!this.m_canvas.Resize(width,this.m_canvas.Height())) { CMessage::ToLog(DFUN+this.TypeElementDescription()+": width="+(string)width+": ",MSG_CANV_ELEMENT_ERR_FAILED_SET_WIDTH); return false; } this.SetProperty(CANV_ELEMENT_PROP_WIDTH,width); this.SetVisibleAreaX(0,true); this.SetVisibleAreaWidth(width,true); this.SetRightEdge(); return true; } //+-------------------------------------------+ //| Set a new height | //+-------------------------------------------+ bool CGCnvElement::SetHeight(const int height) { if(this.GetProperty(CANV_ELEMENT_PROP_HEIGHT)==height) return true; if(!this.m_canvas.Resize(this.m_canvas.Width(),height)) { CMessage::ToLog(DFUN+this.TypeElementDescription()+": height="+(string)height+": ",MSG_CANV_ELEMENT_ERR_FAILED_SET_HEIGHT); return false; } this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,height); this.SetVisibleAreaY(0,true); this.SetVisibleAreaHeight(height,true); this.SetBottomEdge(); return true; } //+------------------------------------------------------------------+
Jetzt werden bei jeder Änderung der Größe oder der Koordinaten eines Objekts die Koordinaten seiner Seiten in die Eigenschaften des grafischen Elements übernommen. Dies ermöglicht es uns, ein gewünschtes Objekt anhand der gegebenen Werte seiner Seiten korrekt zu finden, indem wir nach den Objekteigenschaften sortieren.
Einige der Methoden, die wir zuvor in der Container-Objektklasse erstellt haben, werden nur für Container-Objekte benötigt, z. B. die Methoden, die die Grenzen des Containerbereichs zurückgeben, innerhalb dessen sich angehängte Objekte befinden können. Leider sind diese Methoden (außer bei Containerobjekten) in anderen Bibliotheksobjekten nicht verfügbar. Dies ist ein Problem, da wir auf das Container-Objekt von einer Klasse aus zugreifen, die keinen Zugriff auf ein solches Objekt hat (sie kennt es einfach nicht). Infolgedessen müssen wir auf die Eigenschaften eines solchen Objekts über die Eigenschaften des übergeordneten Objekts zugreifen - dem Basisobjekt aller WinForms-Bibliotheksobjekte. Diese Klasse sieht die Methoden ihres Nachfolgers - der Container-Objektklasse - nicht. So entsteht ein Teufelskreis. Um dieses Problem zu lösen, werde ich alle notwendigen Methoden in die Klasse CWinFormBase verschieben, die eine übergeordnete Klasse für alle WinForms-Bibliotheksobjekte ist, zum leichten Nachteil der Strukturierung der Objekte. Selbst wenn diese Daten zu einem anderen Objekttyp gehören und im aktuellen Objekt nicht verwendet werden, können alle Objekte weiterhin auf die Methoden anderer Objekte zugreifen, die im aktuellen Objekt nicht verwendet werden, aber in den Objekten, auf die zugegriffen wird, Anwendung finden.
Das sieht ziemlich verwirrend aus, aber im Code wird es viel klarer. Wir verschieben einfach einige Methoden aus der Container-Objektklasse in die Basisobjektklasse aller WinForms-Bibliotheksobjekte. Auf diese Weise machen wir die Methoden von jeder Klasse aus sichtbar.
Entfernen wir diese öffentlichen Methoden aus der Datei \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh:
public: //--- Return the size and coordinates of the working area int WidthWorkspace(void) const { return this.Width()-::fmax(this.BorderSizeLeft(),this.PaddingLeft())-::fmax(this.BorderSizeRight(),this.PaddingRight()); } int HeightWorkspace(void) const { return this.Height()-::fmax(this.BorderSizeTop(),this.PaddingTop())-::fmax(this.BorderSizeBottom(),this.PaddingBottom()); } int CoordXWorkspace(void) const { return this.CoordX()+::fmax(this.BorderSizeLeft(),this.PaddingLeft()); } int CoordYWorkspace(void) const { return this.CoordY()+::fmax(this.BorderSizeTop(),this.PaddingTop()); } int RightEdgeWorkspace(void) const { return this.RightEdge()-::fmax(this.BorderSizeRight(),this.PaddingRight()); } int BottomEdgeWorkspace(void) const { return this.BottomEdge()-::fmax(this.BorderSizeBottom(),this.PaddingBottom()); } //--- Return the list of bound WinForms objects with (1) any and (2) specified WinForms object type (from the base one and higher)
und fügen sie in den öffentlichen Abschnitt der Klasse in \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh ein:
//--- Destructor ~CWinFormBase(void) { if(this.m_list_active_elements!=NULL) { this.m_list_active_elements.Clear(); delete this.m_list_active_elements; } } //--- Return the size and coordinates of the working area int WidthWorkspace(void) const { return this.Width()-::fmax(this.BorderSizeLeft(),this.PaddingLeft())-::fmax(this.BorderSizeRight(),this.PaddingRight()); } int HeightWorkspace(void) const { return this.Height()-::fmax(this.BorderSizeTop(),this.PaddingTop())-::fmax(this.BorderSizeBottom(),this.PaddingBottom());} int CoordXWorkspace(void) const { return this.CoordX()+::fmax(this.BorderSizeLeft(),this.PaddingLeft()); } int CoordYWorkspace(void) const { return this.CoordY()+::fmax(this.BorderSizeTop(),this.PaddingTop()); } int RightEdgeWorkspace(void) const { return this.RightEdge()-::fmax(this.BorderSizeRight(),this.PaddingRight()); } int BottomEdgeWorkspace(void) const { return this.BottomEdge()-::fmax(this.BorderSizeBottom(),this.PaddingBottom()); } //--- (1) Set and (2) return the default text color of all panel objects void SetForeColor(const color clr,const bool set_init_color) { if(this.ForeColor()==clr) return; this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr); if(set_init_color) this.SetForeColorInit(clr); }
Jetzt werden diese Methoden von allen WinForms-Objekten der Bibliothek aus sichtbar sein.
Um die Sichtbarkeit der Methoden in allen WinForms-Objektklassen zu gewährleisten, schreiben wir die Methoden, die im Wesentlichen zur Klasse des Containerobjekts gehören sollten, aber die Anfrage an sie kommt von Objekten, die keine Container sind.
Wir müssen wissen, wie viele Pixel die an das Container-Objekt angehängten Objekte über den Container hinausgehen. Diese Werte werden in einer Methode geprüft und in eine Struktur gesetzt, die Felder mit der Anzahl der Pixel oben, unten, links und rechts enthält. Wenn verankerte Objekte über einen oder mehrere Ränder des Containers hinausgehen, speichert die Struktur die Werte der Anzahl der Pixel auf jeder Seite des Objekts, wo der Inhalt über den Container hinausgeht. Aus diesen Daten können wir dann die Größe der Bildlaufleiste im ScrollBar-Steuerelement berechnen.
Im Abschnitt der Klasse deklarieren wir eine solche Struktur und die Variable mit dem Strukturtyp, auf den die erforderlichen Daten gesetzt werden:
protected: CArrayObj *m_list_active_elements; // Pointer to the list of active elements color m_fore_color_init; // Initial color of the control text color m_fore_state_on_color_init; // Initial color of the control text when the control is "ON" private: struct SOversizes // Structure of values for bound objects leaving the container { int top; // top int bottom; // bottom int left; // left int right; // right }; SOversizes m_oversize; // Structure of values for leaving the container //--- Return the font flags uint GetFontFlags(void); public:
Im öffentlichen Abschnitt deklarieren wir die Methode, die prüft, ob die gebundenen Objekte den Container verlassen, und die die oben deklarierte Struktur füllt, die Methode, die alle an den Container gebundenen Objekte verschiebt, und die Methoden, die die Minimal- und Maximalwerte der angegebenen Eigenschaft von allen gebundenen Objekten zum aktuellen Objekt zurückgeben:
//--- Redraw the object virtual void Redraw(bool redraw); //--- Set the new size for the (1) current object and (2) the object specified by index virtual bool Resize(const int w,const int h,const bool redraw); virtual bool Resize(const int index,const int w,const int h,const bool redraw); //--- Return the flag of the container content leaving the container borders bool CheckForOversize(void); //--- Shift all bound objects bool ShiftDependentObj(const int shift_x,const int shift_y); //--- Return the (1) maximum and (2) minimum values of the specified integer property from all attached objects to the current one long GetMaxLongPropFromDependent(const ENUM_CANV_ELEMENT_PROP_INTEGER prop); long GetMinLongPropFromDependent(const ENUM_CANV_ELEMENT_PROP_INTEGER prop); protected: //--- Protected constructor with object type, chart ID and subwindow CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- Constructor
Alle diese Methoden werden benötigt, um mit Container-Objekten zu arbeiten, werden aber von Nicht-Container-Objekten aufgerufen, sodass sie in der gemeinsamen Elternklasse aller WinForms-Objekte zu finden sind.
Da die Struktur und die Variable mit dem Typ der Struktur privat sind, benötigen wir öffentliche Methoden, um die in die Felder dieser Struktur geschriebenen Werte zurückzugeben. Wir implementieren sie im öffentlichen Abschnitt ganz am Ende des Klassenkörpers:
//--- Return the number of pixels, by which attached objects go beyond the container at the (1) top, (2) bottom, (3) left and (4) right int OversizeTop(void) const { return this.m_oversize.top; } int OversizeBottom(void) const { return this.m_oversize.bottom; } int OversizeLeft(void) const { return this.m_oversize.left; } int OversizeRight(void) const { return this.m_oversize.right; } }; //+------------------------------------------------------------------+
In beiden Klassenkonstruktoren initialisieren wir alle Felder der Struktur mit Nullen:
//+-------------------------------------------+ //| Protected constructor with an object type,| //| chart ID and subwindow | //+-------------------------------------------+ CWinFormBase::CWinFormBase(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CForm(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeStateOnColor(this.ForeColor(),true); this.SetForeStateOnColorMouseDown(this.ForeColor()); this.SetForeStateOnColorMouseOver(this.ForeColor()); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; this.m_list_active_elements=new CArrayObj(); ::ZeroMemory(this.m_oversize); } //+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CForm(GRAPH_ELEMENT_TYPE_WF_BASE,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the graphical element and library object types as a base WinForms object this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeStateOnColor(this.ForeColor(),true); this.SetForeStateOnColorMouseDown(this.ForeColor()); this.SetForeStateOnColorMouseOver(this.ForeColor()); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; this.m_list_active_elements=new CArrayObj(); ::ZeroMemory(this.m_oversize); } //+------------------------------------------------------------------+
Die Methode, die das Flag des Containerinhalts zurückgibt, der über seine Grenzen hinausgeht:
//+------------------------------------------------------------------+ //| Return the flag of the container content leaving its borders | //+------------------------------------------------------------------+ bool CWinFormBase::CheckForOversize(void) { //--- Update the structure of values for bound objects leaving the container ::ZeroMemory(this.m_oversize); //--- In the loop by the number of attached objects for(int i=0;i<this.ElementsTotal();i++) { //--- Get the next object and skip scrollbars CWinFormBase *obj=this.GetElement(i); if(obj==NULL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL) continue; //--- Get the value in pixels of the object leaving the form at the right //--- If the value is greater than zero and greater than the one written in the structure field, save it in the structure int r=obj.RightEdge()-this.RightEdgeWorkspace(); if(r>0 && r>this.m_oversize.right) this.m_oversize.right=r; //--- Get the value in pixels of the object leaving the form at the left //--- If the value is greater than zero and greater than the one written in the structure field, save it in the structure int l=this.CoordXWorkspace()-obj.CoordX(); if(l>0 && l>this.m_oversize.left) this.m_oversize.left=l; //--- Get the value in pixels of the object leaving the form at the top //--- If the value is greater than zero and greater than the one written in the structure field, save it in the structure int t=this.CoordYWorkspace()-obj.CoordY(); if(t>0 && t>this.m_oversize.top) this.m_oversize.top=t; //--- Get the value in pixels of the object leaving the form at the bottom //--- If the value is greater than zero and greater than the one written in the structure field, save it in the structure int b=obj.BottomEdge()-this.BottomEdgeWorkspace(); if(b>0 && b>this.m_oversize.bottom) this.m_oversize.bottom=b; } //--- Return the flag indicating that at least one side of the attached object goes beyond the form borders return(m_oversize.top>0 || m_oversize.bottom>0 || m_oversize.left>0 || m_oversize.right>0); } //+------------------------------------------------------------------+
Die Methodenlogik wird in den Codekommentaren beschrieben. Kurz gesagt, wir müssen wissen, dass eines (oder mehrere oder sogar alle) der an den Container gebundenen Objekte dessen Grenzen überschreitet. Um die Maximalwerte zu ermitteln, um die sich die Objekte außerhalb ihres Containers befinden, suchen wir in einer Schleife über alle gebundenen Objekte den Maximalwert auf jeder Seite. Am Ende der Schleife haben wir alle Werte, um die die gebundenen Objekte auf jeder Seite über die Containergrenzen hinausgehen. Anhand dieser Werte können wir später die Größe des Schiebereglers für den Fortschrittsbalken berechnen. Je größer die Überschreitung der Containergröße ist, desto kleiner wird der Schieberegler.
Die Methode, die alle angehängten Objekte verschiebt:
//+-------------------------------------------+ //| Shift all bound objects | //+-------------------------------------------+ bool CWinFormBase::ShiftDependentObj(const int shift_x,const int shift_y) { //--- In the loop by all bound objects, for(int i=0;i<this.ElementsTotal();i++) { //--- get the next object and skip scrollbars CWinFormBase *obj=this.GetElement(i); if(obj==NULL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL ) continue; //--- Set the offset coordinates int x=obj.CoordX()+shift_x; int y=obj.CoordY()+shift_y; if(!obj.Move(x,y,false)) return false; //--- After a successful offset, set relative coordinates and redraw the object obj.SetCoordXRelative(obj.CoordX()-this.CoordX()); obj.SetCoordYRelative(obj.CoordY()-this.CoordY()); obj.Redraw(false); } return true; } //+------------------------------------------------------------------+
Die Logik der Methode ist im Code kommentiert. Wir müssen die an den Container gebundenen Objekte verschieben, wenn die Bildlauftasten angeklickt werden oder wenn der Schieberegler bewegt wird. Die Methode verschiebt alle Objekte in der Liste der mit ihr verbundenen Objekte. Die Verschiebung wird mit der Methode Move() durchgeführt. Bei dieser Methode ist bereits alles so arrangiert, dass sich andere Elemente, die an dem zu verschiebenden Objekt befestigt sind, ebenfalls bewegen. Im Allgemeinen werden hier alle Objekte, die an den Container angehängt sind, mit Ausnahme der Bildlaufleisten, um den angegebenen Betrag verschoben, da sie Steuerelemente des Containerobjekts sind und nicht der an den Container angehängten Objekte (obwohl sie in der allgemeinen Liste enthalten sind).
Die Methode, die den Maximalwert der angegebenen Integer-Eigenschaft aller Objekte zurückgibt, die dem Basisobjekt untergeordnet sind:
//+------------------------------------------------------------------+ //| Return the maximum value of the specified integer | //| property from all objects subordinate to the base one | //+------------------------------------------------------------------+ long CWinFormBase::GetMaxLongPropFromDependent(const ENUM_CANV_ELEMENT_PROP_INTEGER prop) { //--- Initialize 'property' with -1 long property=-LONG_MAX; //--- In the loop through the list of bound objects for(int i=0;i<this.ElementsTotal();i++) { //--- get the next object and skip scrollbars CWinFormBase *obj=this.GetElement(i); if(obj==NULL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL ) continue; //--- If the property value of the received object is greater than the value set in the property, //--- set the current object property value to 'property' if(obj.GetProperty(prop)>property) property=obj.GetProperty(prop); //--- Get the maximum property value from objects bound to the current one long prop_form=obj.GetMaxLongPropFromDependent(prop); //--- If the received value is greater than the 'property' value //--- set the received value to 'property' if(prop_form>property) property=prop_form; } //--- Return the found maximum property value return property; } //+------------------------------------------------------------------+
Auch hier wird die gesamte Logik in den Code-Kommentaren beschrieben. Hier ist alles ganz einfach: Wir durchlaufen eine Schleife durch alle Objekte (außer Bildlaufleisten) und suchen ein Objekt mit dem maximalen Wert der angegebenen Eigenschaft. Der gefundene Höchstwert wird zurückgegeben.
Die Methode, die den Mindestwert der angegebenen Integer-Eigenschaft von allen untergeordneten Basisobjekten zurückgibt:
//+------------------------------------------------------------------+ //| Return the minimum value of the specified integer | //| property from all objects subordinate to the base one | //+------------------------------------------------------------------+ long CWinFormBase::GetMinLongPropFromDependent(const ENUM_CANV_ELEMENT_PROP_INTEGER prop) { //--- Initialize 'property' using the LONG_MAX value long property=LONG_MAX; //--- In the loop through the list of bound objects for(int i=0;i<this.ElementsTotal();i++) { //--- get the next object and skip scrollbars CWinFormBase *obj=this.GetElement(i); if(obj==NULL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL || obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL ) continue; //--- If the value of the obtained object property is less than the value set in 'property', //--- set the current object property value to 'property' if(obj.GetProperty(prop)<property) property=obj.GetProperty(prop); //--- Get the minimum property value from bound objects to the current one long prop_form=obj.GetMinLongPropFromDependent(prop); //--- If the obtained value is less than the property value //--- set the received value to 'property' if(prop_form<property) property=prop_form; } //--- Return the found minimum property value return property; } //+------------------------------------------------------------------+
Die Methode ähnelt der vorhergehenden: Wir durchlaufen eine Schleife durch alle Objekte (außer Bildlaufleisten) und suchen ein Objekt mit dem Mindestwert der angegebenen Eigenschaft. Wir geben den gefundenen Mindestwert zurück.
Wenn der Mauszeiger über dem Schieberegler der Bildlaufleiste schwebt, muss das gesamte ScrollBar-Objekt in den Vordergrund gebracht werden. Wenn wir mit der Maus über den Bereich dieses Objekts fahren, müssen wir es im Allgemeinen über alle Container-Objekte bewegen. Dies ist notwendig, damit der Cursor mit dem obersten Objekt interagieren kann und nicht mit den Objekten, die sich möglicherweise darüber befinden, da sie später erstellt wurden. Im Moment ist die Klasse des Capture-Bereichs-Objekts von der Klasse des Button-Objekts abgeleitet und verwendet die Funktionalität der übergeordneten Klasse, um mit der Maus zu interagieren. Da die Methoden zur Behandlung verschiedener Ereignisse bei der Interaktion mit der Maus virtuell sind, müssen wir sie in der Objektklasse des Erfassungsbereichs außer Kraft setzen.
In \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarThumb.mqh, und zwar im geschützten Abschnitt, deklarieren wir die virtuelle Ereignisbehandlung für Mausereignisse, während wir im öffentlichen Abschnitt die letzte Ereignisbehandlung der Maus deklarieren:
//+-------------------------------------------+ //| Label object class of WForms controls | //+-------------------------------------------+ class CScrollBarThumb : public CButton { private: protected: //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler virtual void MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the left mouse button is clicked' event handler virtual void MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Protected constructor with object type, chart ID and subwindow CScrollBarThumb(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- Last mouse event handler virtual void OnMouseEventPostProcessing(void); //--- Constructor CScrollBarThumb(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
Implementieren wir die angegebenen virtuellen Ereignisbehandlungen.
Der Cursor befindet sich innerhalb des aktiven Bereichs, die Maustasten sind nicht geklickt:
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CScrollBarThumb::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { CWinFormBase *base=this.GetBase(); if(base!=NULL) { base.BringToTop(); } CButton::MouseActiveAreaNotPressedHandler(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Hier erhalten wir den Zeiger auf das Basisobjekt (Schieberegler) und heben ihn in den Vordergrund. Dann rufen wir die Ereignisbehandlung der Mausereignisse des übergeordneten Objekts auf, das der Methode entspricht.
Der Cursor befindet sich innerhalb des aktiven Bereichs, eine beliebige Maustaste wurde angeklickt:
//+-------------------------------------------+ //| 'The cursor is inside the active area, | //| a mouse button is clicked (any) | //+-------------------------------------------+ void CScrollBarThumb::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { CWinFormBase *base=this.GetBase(); if(base!=NULL) { base.BringToTop(); } CButton::MouseActiveAreaPressedHandler(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Hier ist alles genau gleich. Zuerst bringen wir das Basisobjekt in den Vordergrund (es wird eine Bildlaufleiste mit allen Steuerelementen sein), dann rufen wir den Maus-Event-Handler des übergeordneten Objekts auf.
Der Cursor befindet sich im aktiven Bereich, die linke Maustaste wurde geklickt:
//+-------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+-------------------------------------------+ void CScrollBarThumb::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { CWinFormBase *base=this.GetBase(); if(base!=NULL) { base.BringToTop(); } CButton::MouseActiveAreaReleasedHandler(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Alles ist identisch mit den beiden oben genannten Ereignisbehandlungen.
Die letzte Mausereignisbehandlung wird vollständig aus dem übergeordneten Objekt herausgelöst, mit dem Ziel seiner möglichen weiteren Verfeinerung:
//+-------------------------------------------+ //| Last mouse event handler | //+-------------------------------------------+ void CScrollBarThumb::OnMouseEventPostProcessing(void) { if(!this.IsVisible() || !this.Enabled()) return; ENUM_MOUSE_FORM_STATE state=GetMouseState(); switch(state) { //--- The cursor is outside the form, the mouse buttons are not clicked //--- The cursor is outside the form, any mouse button is clicked //--- The cursor is outside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL : if(this.MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED || this.MouseEventLast()==MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED) { this.SetBackgroundColor(this.State() ? this.BackgroundStateOnColor() : this.BackgroundColorInit(),false); this.SetForeColor(this.State() ? this.ForeStateOnColor() : this.ForeColorInit(),false); this.SetBorderColor(this.BorderColorInit(),false); this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT); this.Redraw(false); } break; //--- The cursor is inside the form, the mouse buttons are not clicked //--- The cursor is inside the form, any mouse button is clicked //--- The cursor is inside the form, the mouse wheel is being scrolled //--- The cursor is inside the active area, the mouse buttons are not clicked //--- The cursor is inside the active area, any mouse button is clicked //--- The cursor is inside the active area, the mouse wheel is being scrolled //--- The cursor is inside the active area, left mouse button is released //--- The cursor is within the window scrolling area, the mouse buttons are not clicked //--- The cursor is within the window scrolling area, any mouse button is clicked //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled //--- The cursor is within the window resizing area, the mouse buttons are not clicked //--- The cursor is within the window resizing area, the mouse button (any) is clicked //--- The cursor is within the window resizing area, the mouse wheel is being scrolled //--- The cursor is within the window resizing area, the mouse buttons are not clicked //--- The cursor is within the window resizing area, the mouse button (any) is clicked //--- The cursor is within the window separator area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL : //--- Within the active area case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED : //--- Within the scrolling area at the bottom case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_BOTTOM_WHEEL : //--- Within the scrolling area to the right case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_PRESSED : case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_RIGHT_WHEEL : //--- Within the window resizing area at the top case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_AREA_WHEEL : //--- Within the window resizing area at the bottom case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_AREA_WHEEL : //--- Within the window resizing area to the left case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_LEFT_AREA_WHEEL : //--- Within the window resizing area to the right case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_RIGHT_AREA_WHEEL : //--- Within the window resizing area to the top-left case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_LEFT_AREA_WHEEL : //--- Within the window resizing area to the top-right case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_TOP_RIGHT_AREA_WHEEL : //--- Within the window resizing area at the bottom left case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_LEFT_AREA_WHEEL : //--- Within the window resizing area at the bottom-right case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_RESIZE_BOTTOM_RIGHT_AREA_WHEEL : //--- Within the control area case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_NOT_PRESSED : case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_PRESSED : case MOUSE_FORM_STATE_INSIDE_CONTROL_AREA_WHEEL : break; //--- MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
Im Moment ist die Ereignisbehandlung identisch mit der der übergeordneten Klasse.
In der abstrakten Objektklasse des Schiebereglers in \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBar.mqh, d. h. in der Methode zur Erstellung des Objekts für den Erfassungsbereich (Schieberegler) und der Schaltflächen dafür, stellen wir die korrekte Größe der Schaltflächen ein, die in der Makrosubstitution aufgezeichnet wurde, die speziell für die Speicherung der Größe der Schaltflächen der Schieberegler entwickelt wurde:
//+-------------------------------------------+ //| Create the capture area object | //+-------------------------------------------+ void CScrollBar::CreateThumbArea(void) { this.CreateArrowButtons(DEF_CONTROL_SCROLL_BAR_WIDTH,DEF_CONTROL_SCROLL_BAR_WIDTH); } //+------------------------------------------------------------------+
Geben wir in der Methode, die den Erfassungsbereich berechnet, den Wert zurück , der in der Makrosubstitution angegeben ist, die die standardmäßige Mindestgröße des Schiebereglers speichert, und nicht Null:
//+-------------------------------------------+ //| Calculate the capture area size | //+-------------------------------------------+ int CScrollBar::CalculateThumbAreaSize(void) { return DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN; } //+------------------------------------------------------------------+
Die Objektklasse des Schiebereglers sollte die Größe und Position des Schiebereglers in Abhängigkeit davon berechnen, wie viele Pixel der Inhalt des Containers über ihn hinausragt. Wenn die Größe des Containers oder seines Inhalts geändert wird, sollten die Größe und Position des Schiebereglers neu berechnet werden. Wenn die Bildlauftasten gedrückt werden, sollte sich der Inhalt des Containers (sofern er über den Container hinausgeht) in die entgegengesetzte Richtung des Pfeils der gedrückten Taste bewegen. Der Schieberegler sollte sich in Richtung des Pfeils bewegen.
In diesem Artikel werde ich eine solche Funktionalität für die horizontale Bildlaufleiste erstellen. Danach werde ich die vorgefertigte Funktionalität in die Objektklasse des vertikalen Schiebereglers übertragen. Ihre gemeinsame Arbeit wird anschließend umgesetzt.
Benennen wir in \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ScrollBarHorisontal.mqh die private Methode CalculateThumbAreaSize in CalculateThumbAreaDistance() um. Im öffentlichen Teil deklarieren wir die Methoden zur Berechnung der Größe und der Koordinaten des Schiebereglers sowie die Hauptmethode zur Neuberechnung seiner Parameter:
//+------------------------------------------------------------------+ //| CScrollBarHorisontal object class of WForms controls | //+------------------------------------------------------------------+ class CScrollBarHorisontal : public CScrollBar { private: //--- Create the ArrowButton objects virtual void CreateArrowButtons(const int width,const int height); //--- Calculate the distance of the capture area (slider) int CalculateThumbAreaDistance(const int thumb_size); protected: //--- Protected constructor with object type, chart ID and subwindow CScrollBarHorisontal(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public: //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Return the (1) left and (2) right arrow button CArrowLeftButton *GetArrowButtonLeft(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,0); } CArrowRightButton*GetArrowButtonRight(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,0); } //--- Return the size of the slider working area int BarWorkAreaSize(void); //--- Return the coordinate of the beginning of the slider working area int BarWorkAreaCoord(void); //--- Set the new size virtual bool Resize(const int w,const int h,const bool redraw); //--- Calculate and set the parameters of the capture area (slider) int SetThumbParams(void); //--- Constructor CScrollBarHorisontal(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); //--- Timer virtual void OnTimer(void); //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); }; //+------------------------------------------------------------------+
Betrachten wir nun die deklarierten Methoden.
Die Methode, die die neuen Abmessungen des Objekts festlegt:
//+-------------------------------------------+ //| Set the new size | //+-------------------------------------------+ bool CScrollBarHorisontal::Resize(const int w,const int h,const bool redraw) { //--- If failed to change the object size, return 'false' if(!CWinFormBase::Resize(w,h,redraw)) return false; //--- Get the button object with the right arrow CArrowRightButton *br=this.GetArrowButtonRight(); //--- If the button is not received, return 'false' if(br==NULL) return false; //--- Move the button to the right edge of the scrollbar if(br.Move(this.RightEdge()-this.BorderSizeRight()-br.Width(),br.CoordY())) { //--- Set new relative coordinates for the button br.SetCoordXRelative(br.CoordX()-this.CoordX()); br.SetCoordYRelative(br.CoordY()-this.CoordY()); } //--- Set the slider parameters this.SetThumbParams(); //--- Successful return true; } //+------------------------------------------------------------------+
Legen wir zunächst die neue Größe der Bildlaufleiste mit der Methode der übergeordneten Klasse fest. Als Nächstes müssen wir nach erfolgreicher Größenänderung die Schaltfläche auf der rechten Seite des Objekts so verschieben, dass sie sich am Rand des größenveränderten Objekts befindet (mit der Größenänderung ändert sich auch die Koordinate des rechten Randes). Nach allen Änderungen und Bewegungen berechnen wir die Größe und Position des Schiebereglers mit der Methode SetThumbParams() neu, die ich weiter unten erläutern werde.
Die Methode zur Berechnung und Einstellung der Parameter des Erfassungsbereichs (Schieberegler):
//+------------------------------------------------------------------+ //| Calculate and set the parameters of the capture area (slider) | //+------------------------------------------------------------------+ int CScrollBarHorisontal::SetThumbParams(void) { //--- Get the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return 0; //--- Get the capture area object (slider) CScrollBarThumb *thumb=this.GetThumb(); if(thumb==NULL) return 0; //--- Get the width size of the visible part inside the container int base_w=base.WidthWorkspace(); //--- Calculate the total width of all attached objects and the window size of the visible part in % int objs_w=base_w+base.OversizeLeft()+base.OversizeRight(); double px=base_w*100.0/objs_w; //--- Calculate and adjust the size of the slider in % relative to the width of its workspace (not less than the minimum size) int thumb_size=(int)::ceil(this.BarWorkAreaSize()/100.0*px); if(thumb_size<DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN) thumb_size=DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN; //--- Calculate the coordinate of the slider and change its size to match the previously calculated one int thumb_x=this.CalculateThumbAreaDistance(thumb_size); if(!thumb.Resize(thumb_size,thumb.Height(),true)) return 0; //--- Shift the slider by the calculated X coordinate if(thumb.Move(this.BarWorkAreaCoord()+thumb_x,thumb.CoordY())) { thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX()); thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY()); } //--- Return the calculated slider size return thumb_size; } //+------------------------------------------------------------------+
Jede Zeile der Methode wird ausführlich kommentiert. Die Methode besteht im Wesentlichen darin, die Größe des Schiebereglers in Abhängigkeit von der Größe des Inhalts des Containers zu berechnen, der über seine Grenzen hinausgeht. Je größer der Wert in Pixeln ist, über den der Inhalt des Containers hinausreicht, desto kleiner ist der Schieberegler. Die Koordinate der Schiebereglerposition wird vom linken Rand des Containers aus berechnet, ebenfalls in relativer Größe, sodass sie der Position des sichtbaren Teils des Containerinhalts im Verhältnis zu seinem unsichtbaren Teil entspricht, der über den linken Rand hinausgeht. Je weiter (in Pixeln) der Inhalt des Containers über den linken Rand hinausragt, desto weiter rechts wird der Schieberegler positioniert. Daraus ergibt sich, dass die Bildlaufleiste zusammen mit dem Schieberegler eine verkleinerte Kopie (oder Instanz) des Containers und seines Inhalts ist. Bei der Bildlaufleiste zeigt die Leiste selbst den gesamten Inhalt des Containers an, und der Schieberegler zeigt den Container an - den Teil, in dem der Inhalt sichtbar ist.
Die Methode zur Berechnung der Entfernung des Erfassungsbereichs (Schieberegler):
//+------------------------------------------------------------------+ //| Calculate the distance of the capture area (slider) | //+------------------------------------------------------------------+ int CScrollBarHorisontal::CalculateThumbAreaDistance(const int thumb_size) { CWinFormBase *base=this.GetBase(); if(base==NULL) return 0; double x=(double)thumb_size/(double)base.WidthWorkspace(); return (int)::ceil((double)base.OversizeLeft()*x); } //+------------------------------------------------------------------+
Die Größe des Schiebereglers wird an die Methode übergeben. Als Nächstes wird berechnet, um wie viel die Größe des Schiebereglers kleiner ist als die Größe des Container-Arbeitsbereichs (in dem der Inhalt sichtbar ist). Anhand dieses Verhältnisses wird dann der Abstand des Schiebers berechnet und zurückgegeben. Mit anderen Worten: Der Schieberegler ist kleiner als der Container, und zwar so weit, dass der Abstand des Schiebereglers vom Koordinatenursprung kleiner ist als die Anzahl der Pixel, um die der Inhalt des Containers über seinen linken Rand hinausgeht.
Die Methode, die die Größe des Arbeitsbereichs des Schiebereglers zurückgibt:
//+-------------------------------------------+ //| Return the size of the slider working area| //+-------------------------------------------+ int CScrollBarHorisontal::BarWorkAreaSize(void) { CArrowLeftButton *bl=this.GetArrowButtonLeft(); CArrowRightButton *br=this.GetArrowButtonRight(); int x1=(bl!=NULL ? bl.RightEdge() : this.CoordX()+this.BorderSizeLeft()); int x2=(br!=NULL ? br.CoordX() : this.RightEdge()-this.BorderSizeRight()); return(x2-x1); } //+------------------------------------------------------------------+
Der Arbeitsbereich des Schiebereglers ist der Bereich, in dem er sich in der Bildlaufleiste bewegt. Mit anderen Worten, dies ist der Bereich zwischen den beiden Pfeilschaltflächen der Bildlaufleiste. Um diesen Abstand zu berechnen, müssen wir also die Zeiger auf diese Schaltflächen erhalten und den Abstand zwischen dem linken Rand der rechten Schaltfläche und dem rechten Rand der linken Schaltfläche berechnen.
Die Methode, die die Koordinate des Beginns des Arbeitsbereichs des Schiebereglers zurückgibt:
//+------------------------------------------------------------------+ //| Return the coordinate of the beginning of the slider working area| //+------------------------------------------------------------------+ int CScrollBarHorisontal::BarWorkAreaCoord(void) { CArrowLeftButton *bl=this.GetArrowButtonLeft(); return(bl!=NULL ? bl.RightEdge() : this.CoordX()+this.BorderSizeLeft()); } //+------------------------------------------------------------------+
Die Methode gibt die Koordinate der rechten Kante der linken Bildlaufleisten-Schaltfläche zurück. Diese Koordinate ist die Anfangskoordinate für die Position des Schiebereglers. Von dieser Koordinate aus wird seine Position bei der Berechnung seiner Abmessungen und Koordinaten berechnet.
Die Maus-Ereignisbehandlung ist jetzt neu gestaltet und sieht wie folgt aus:
//+-------------------------------------------+ //| Event handler | //+-------------------------------------------+ void CScrollBarHorisontal::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Adjust subwindow Y shift CGCnvElement::OnChartEvent(id,lparam,dparam,sparam); //--- Get the pointers to control objects of the scrollbar CArrowLeftButton *buttl=this.GetArrowButtonLeft(); CArrowRightButton *buttr=this.GetArrowButtonRight(); CScrollBarThumb *thumb=this.GetThumb(); if(buttl==NULL || buttr==NULL || thumb==NULL) return; //--- If the event ID is an object movement if(id==WF_CONTROL_EVENT_MOVING) { //--- Move the scrollbar to the foreground this.BringToTop(); //--- Declare the variables for the coordinates of the capture area int x=(int)lparam; int y=(int)dparam; //--- Set the Y coordinate equal to the Y coordinate of the control element y=this.CoordY()+this.BorderSizeTop(); //--- Adjust the X coordinate so that the capture area does not go beyond the control, taking into account the arrow buttons if(x<buttl.RightEdge()) x=buttl.RightEdge(); if(x>buttr.CoordX()-thumb.Width()) x=buttr.CoordX()-thumb.Width(); //--- If the capture area object is shifted by the calculated coordinates if(thumb.Move(x,y,true)) { //--- set the object relative coordinates thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX()); thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY()); ::ChartRedraw(this.ChartID()); } //--- Get the pointer to the base object CWinFormBase *base=this.GetBase(); if(base!=NULL) { //--- Check if the content goes beyond the container and recalculate the slider parameters base.CheckForOversize(); this.SetThumbParams(); } } //--- If any scroll button is clicked if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT || id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT) { //--- Move the scrollbar to the foreground this.BringToTop(); //--- Get the base object CWinFormBase *base=this.GetBase(); if(base==NULL) return; //--- Calculate how much each side of the content of the base object goes beyond its borders base.CheckForOversize(); //--- Get the largest and smallest coordinates of the right and left sides of the base object content int cntt_r=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_RIGHT); int cntt_l=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_X); //--- Set the number of pixels, by which the content of the base object should be shifted int shift=DEF_CONTROL_SCROLL_BAR_SCROLL_STEP; //--- If the left button is clicked if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT) { if(cntt_l+shift<=base.CoordXWorkspace()) base.ShiftDependentObj(shift,0); } //--- If the right button is clicked if(id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT) { if(cntt_r-shift>=base.RightEdgeWorkspace()) base.ShiftDependentObj(-shift,0); } //--- Calculate the width and coordinates of the slider this.SetThumbParams(); } } //+------------------------------------------------------------------+
Hier fügen wir die Behandlung von Klicks auf Scrollbar-Schaltflächen hinzu. Wenn die Schaltfläche angeklickt wird, werden die Offset-Koordinaten berechnet und der gesamte Inhalt des Containers in die Richtung verschoben, die der gedrückten Pfeiltaste entgegengesetzt ist. Nach der Verschiebung passen wir die Größe und die Koordinaten des Schiebereglers erneut an.
Bei der Fertigstellung der Klasse des horizontalen Scrollbar-Objekts wurden gleichzeitig ähnliche Änderungen und Verbesserungen an der Klasse des vertikalen Scrollbar-Objekts vorgenommen. Aber nicht alle Änderungen wurden umgesetzt und nicht alle wurden getestet, weil ich zuerst die Funktionsweise der horizontalen Bildlaufleiste implementieren und dann auf die vertikale Leiste übertragen werde. Daher werde ich hier nicht auf die Änderungen eingehen, die am Code der Klasse der vertikalen Bildlaufleiste vorgenommen wurden. Ich werde es nach einer vollständigen Neuerstellung auf der Grundlage des funktionierenden und fehlerbereinigten Codes für die horizontale Bildlaufleiste in Betracht ziehen.
Kommen wir zurück zur Container-Objektklasse in \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh.
Im geschützten Abschnitt deklarieren wir die Methode, mit der die Bildlaufleisten in den Vordergrund gehoben werden. Im öffentlichen Abschnitt implementieren wir die Methoden, die die Zeiger auf die beiden Bildlaufleisten zurückgeben, deklarieren die virtuelle Methode zum Verschieben des Objekts, während die virtuelle Redrawing-Methode ihre Deklaration mit einer Implementierung außerhalb des Klassenkörpers erhält:
//+------------------------------------------------------------------+ //| Class of the base container object of WForms controls | //+------------------------------------------------------------------+ class CContainer : public CWinFormBase { private: //--- Create a new graphical object virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity); //--- Calculate Dock objects' binding coordinates void CalculateCoords(CArrayObj *list); //--- Create the (1) vertical and (2) horizontal ScrollBar CWinFormBase *CreateScrollBarVertical(const int width); CWinFormBase *CreateScrollBarHorisontal(const int width); protected: //--- Adjust the element size to fit its content bool AutoSizeProcess(const bool redraw); //--- Set parameters for the attached object void SetObjParams(CWinFormBase *obj,const color colour); //--- Create vertical and horizontal ScrollBar objects void CreateScrollBars(const int width); //--- Move the scrollbars to the foreground void BringToTopScrollBars(void); public: //--- Return the list of bound WinForms objects with (1) any and (2) specified WinForms object type (from the base one and higher) CArrayObj *GetListWinFormsObj(void); CArrayObj *GetListWinFormsObjByType(const ENUM_GRAPH_ELEMENT_TYPE type); //--- Return the pointer to the specified WinForms object with the specified type by index CWinFormBase *GetWinFormsObj(const ENUM_GRAPH_ELEMENT_TYPE type,const int index); //--- Return the pointer to the (1) vertical and (2) horizontal scrollbar CScrollBarVertical *GetScrollBarVertical(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL,0); } CScrollBarHorisontal *GetScrollBarHorisontal(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0);} //--- Set the (1) X, (2) Y coordinates, (3) element width and (4) height virtual bool SetCoordX(const int coord_x) { return CGCnvElement::SetCoordX(coord_x); } virtual bool SetCoordY(const int coord_y) { return CGCnvElement::SetCoordY(coord_y); } virtual bool SetWidth(const int width) { return CGCnvElement::SetWidth(width); } virtual bool SetHeight(const int height) { return CGCnvElement::SetHeight(height); } //--- Set the new size for the (1) current object and (2) the object specified by index virtual bool Resize(const int w,const int h,const bool redraw); //--- Update the coordinates virtual bool Move(const int x,const int y,const bool redraw=false); //--- Create a new attached element virtual bool CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw); //--- Redraw the object virtual void Redraw(bool redraw); //--- Reset the size of all bound objects to the initial ones bool ResetSizeAllToInit(void); //--- Place bound objects in the order of their Dock binding virtual bool ArrangeObjects(const bool redraw);
In der Methode, die den Modus der automatischen Größenanpassung des Elements an den Inhalt festlegt, fügen wir eine Prüfung hinzu, dass das Flag der automatischen Größenanpassung des Elements in Abhängigkeit vom Inhalt nicht gesetzt ist:
//--- (1) Set and (2) return the mode of the element auto resizing depending on the content void SetAutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode,const bool redraw) { ENUM_CANV_ELEMENT_AUTO_SIZE_MODE prev=this.AutoSizeMode(); if(prev==mode) return; this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,mode); if(!this.AutoSize()) return; if(prev!=this.AutoSizeMode() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); } ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void) const { return (ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE); }
Bei der Einstellung des Modus für die automatische Größenänderung wird die Größe des Containers an seinen Inhalt angepasst, wenn ein Modus eingestellt wird, der nicht mit dem aktuellen Modus übereinstimmt. Wenn das Flag für die automatische Größenanpassung nicht aktiviert ist, muss auch nichts sortiert werden, was der hinzugefügte Code tut. Sie wird von der Methode zurückgegeben, wenn das Flag für die automatische Größenänderung gelöscht wird.
Wenn nach der Erstellung des Container-Objekts das Flag für die automatische Größenanpassung des Containers für den Content nicht gesetzt ist und der Inhalt des Containers über seine Grenzen hinausgeht, sollte die entsprechende Bildlaufleiste angezeigt werden:
//+-------------------------------------------+ //| Create a new attached element | //+-------------------------------------------+ bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- If the object type is less than the base WinForms object if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE) { //--- report the error and return 'false' CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE); return false; } //--- If failed to create a new graphical element, return 'false' CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity); if(obj==NULL) return false; //--- Set parameters for the created object this.SetObjParams(obj,colour); //--- If there are bound objects if(this.ElementsTotal()>0) { //--- If the panel has auto resize enabled, call the auto resize method if(this.AutoSize()) this.AutoSizeProcess(redraw); //--- If auto resize is disabled, determine whether scrollbars should be displayed else { this.CheckForOversize(); //--- If the attached objects go beyond the visibility window to the left or right if(this.OversizeLeft()>0 || this.OversizeRight()>0) { CScrollBarHorisontal *sbh=this.GetScrollBarHorisontal(); if(sbh!=NULL) { sbh.SetThumbParams(); sbh.SetDisplayed(true); sbh.Show(); } } } } //--- Crop the created object along the edges of the visible part of the container obj.Crop(); //--- return 'true' return true; } //+------------------------------------------------------------------+
In diesem Fall werden zunächst die an den Container angehängten Objekte überprüft, um festzustellen, ob sie sich außerhalb des linken oder rechten Rahmens befinden, und dann wird die horizontale Bildlaufleiste angezeigt. Aber zuerst werden die Parameter des Schiebereglers berechnet und eingestellt, und danach wird das Scrollbar-Objekt selbst angezeigt.
Da nun das Scrollbar-Objekt selbst die Größenänderung vornimmt, hat sich die Methode zur Größenänderung des aktuellen Objekts geändert:
//+-------------------------------------------+ //| Set the new size for the current object | //+-------------------------------------------+ bool CContainer::Resize(const int w,const int h,const bool redraw) { //--- If it was not possible to change the size of the container, return 'false' if(!CWinFormBase::Resize(w,h,redraw)) return false; //--- Get the vertical scrollbar and CScrollBarVertical *scroll_v=this.GetScrollBarVertical(); if(scroll_v!=NULL) { //--- If the vertical size of the scrollbar is changed to fit the size of the container workspace if(scroll_v.Resize(scroll_v.Width(),this.HeightWorkspace(),false)) { //--- Move the vertical scrollbar to new coordinates if(scroll_v.Move(this.RightEdgeWorkspace()-scroll_v.Width(),this.CoordYWorkspace())) { scroll_v.SetCoordXRelative(scroll_v.CoordX()-this.CoordX()); scroll_v.SetCoordYRelative(scroll_v.CoordY()-this.CoordY()); } } scroll_v.BringToTop(); } //--- Get the horizontal scrollbar and CScrollBarHorisontal *scroll_h=this.GetScrollBarHorisontal();//this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,0); if(scroll_h!=NULL) { //--- If the horizontal size of the scrollbar is changed to fit the size of the container workspace if(scroll_h.Resize(this.WidthWorkspace(),scroll_h.Height(),false)) { //--- Move the horizontal scrollbar to new coordinates if(scroll_h.Move(this.CoordXWorkspace(),this.BottomEdgeWorkspace()-scroll_h.Height())) { scroll_h.SetCoordXRelative(scroll_h.CoordX()-this.CoordX()); scroll_h.SetCoordYRelative(scroll_h.CoordY()-this.CoordY()); } } scroll_h.BringToTop(); } return true; } //+------------------------------------------------------------------+
Da es nun nicht mehr notwendig ist, die Größe der Bildlaufleisten zu ändern, um ihre Schaltflächen an neue Stellen zu verschieben, ist die Methode kürzer und übersichtlicher geworden. Jede Bildlaufleiste wird nach der Größenänderung in den Vordergrund gebracht.
Die Methode zum Neuzeichnen eines Objekts:
//+-------------------------------------------+ //| Redraw the object | //+-------------------------------------------+ void CContainer::Redraw(bool redraw) { CWinFormBase::Redraw(redraw); this.BringToTopScrollBars(); } //+------------------------------------------------------------------+
Zunächst zeichnen wir das Objekt mit der Methode der übergeordneten Klasse neu und rufen dann die Methode auf, um die Bildlaufleisten in den Vordergrund zu bringen.
Die Methode, die Bildlaufleisten in den Vordergrund bringt:
//+-------------------------------------------+ //| Bring scrollbars to the foreground | //+-------------------------------------------+ void CContainer::BringToTopScrollBars(void) { CScrollBarHorisontal *sbh=this.GetScrollBarHorisontal(); CScrollBarVertical *sbv=this.GetScrollBarVertical(); if(sbh!=NULL) sbh.BringToTop(); if(sbv!=NULL) sbv.BringToTop(); } //+------------------------------------------------------------------+
Holen wir uns die Zeiger auf die horizontalen und vertikalen Bildlaufleisten-Objekte und verschieben bei erfolgreichem Erhalt des Zeigers jedes Objekt in den Vordergrund.
Die Methode zur Aktualisierung der Koordinaten:
//+-------------------------------------------+ //| Update the coordinates | //+-------------------------------------------+ bool CContainer::Move(const int x,const int y,const bool redraw=false) { if(!CForm::Move(x,y,redraw)) return false; this.BringToTopScrollBars(); return true; } //+------------------------------------------------------------------+
Zunächst rufen wir die Methode der übergeordneten Klasse auf, um das Objekt zu verschieben. Als Nächstes, nach einer erfolgreichen Verschiebung, verschieben wir die Bildlaufleisten in den Vordergrund.
Nun müssen wir die Ereignisse der Scrollbar-Objekte in die Verarbeitung in der Kollektionsklasse der grafischen Elemente aufnehmen.
In der Datei \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh, und zwar in ihrer Ereignisbehandlung, fügen wir den folgenden Codeblock hinzu, um den Handler für Mausereignisse des Scrollbar-Objekts im Objekt-Click-Handler aufzurufen:
//+-------------------------------------------+ //| Clicking the control | //+-------------------------------------------+ if(idx==WF_CONTROL_EVENT_CLICK) { //--- If TabControl type is set in dparam if((ENUM_GRAPH_ELEMENT_TYPE)dparam==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL) { //--- Set the event type depending on the element type that generated the event int event_id= (object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT ? WF_CONTROL_EVENT_CLICK_SCROLL_LEFT : object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT ? WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT : object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP ? WF_CONTROL_EVENT_CLICK_SCROLL_UP : object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN ? WF_CONTROL_EVENT_CLICK_SCROLL_DOWN : WF_CONTROL_EVENT_NO_EVENT); //--- If the base control is received, call its event handler if(base_elm!=NULL) base_elm.OnChartEvent(event_id,lparam,dparam,sparam); } //--- If the base object is a horizontal or vertical scrollbar if(base!=NULL && (base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL || base.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL)) { //--- Set the event type depending on the element type that generated the event int event_id= (object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT ? WF_CONTROL_EVENT_CLICK_SCROLL_LEFT : object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT ? WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT : object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP ? WF_CONTROL_EVENT_CLICK_SCROLL_UP : object.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN ? WF_CONTROL_EVENT_CLICK_SCROLL_DOWN : WF_CONTROL_EVENT_NO_EVENT); //--- Call the event handler of the base element base.OnChartEvent(event_id,lparam,dparam,sparam); } } //+-------------------------------------------+ //| Selecting the TabControl tab | //+-------------------------------------------+ if(idx==WF_CONTROL_EVENT_TAB_SELECT) { if(base!=NULL) base.OnChartEvent(idx,lparam,dparam,sparam); }
Alles ist bereit für einen Test.
Test
Um den Test durchzuführen, verwende ich den EA aus dem vorherigen Artikel und speichere ihn in \MQL5\Experts\TestDoEasy\Part131\ als TestDoEasy131.mq5.
Beim letzten Mal habe ich das große Schaltflächenobjekt auf dem Bedienfeld erstellt. Nun ist es an der Zeit, einen Text hinzuzufügen, damit wir die horizontale Verschiebung des Objekts sehen können, wenn wir auf die Bildlauftasten klicken. Die Schaltfläche selbst sollte das Textbeschriftungsobjekt haben, das anzeigt, dass alle angehängten Objekte korrekt verschoben sind:
//--- Create the required number of WinForms Panel objects CPanel *pnl=NULL; for(int i=0;i<FORMS_TOTAL;i++) { pnl=engine.CreateWFPanel("WinForms Panel"+(string)i,(i==0 ? 50 : 70),(i==0 ? 50 : 70),410,200,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false); if(pnl!=NULL) { pnl.Hide(); //--- Set Padding to 4 pnl.SetPaddingAll(3); //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs pnl.SetMovable(InpMovable); pnl.SetAutoSize(InpAutoSize,false); pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false); //--- pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,-40,10,pnl.WidthWorkspace()+80,pnl.HeightWorkspace()-30,clrNONE,255,true,false); CButton *btn=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,0); btn.SetText("123456789012345678901234567890"); pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,40,20,60,20,clrDodgerBlue,255,false,false); CLabel *lbl=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,0); lbl.SetText("LABEL"); /* //--- Create TabControl pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,InpTabControlX,InpTabControlY,pnl.Width()-30,pnl.Height()-40,clrNONE,255,true,false); CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0); if(tc!=NULL) {
Kompilieren Sie den EA und starten Sie ihn auf einem Chart:
Wie Sie sehen können, funktioniert der Bildlauf mit den Pfeiltasten gut. Wenn wir versuchen, den Schieberegler mit der Maus zu bewegen, „widersteht“ er, was natürlich ist - wir haben noch keine Verarbeitung der Schiebereglerverschiebung, aber wir haben bereits eine Neuberechnung seiner Größe und Koordinaten. Wenn wir also versuchen, den Schieberegler mit der Maus zu verschieben, wird er durch die Methode zum Festlegen seiner Koordinaten an die Position zurückgebracht, die der Position des Containerinhalts in seinem sichtbaren Bereich entspricht. Dieses Verhalten wird in den folgenden Artikeln näher erläutert.
Was kommt als Nächstes?
Im nächsten Artikel werde ich die Entwicklung des ScrollBar-Steuerelements fortsetzen.
*Vorherige Artikel in dieser Reihe:
DoEasy. Steuerung (Teil 26): Fertigstellung des ToolTip WinForms-Objekts und Weiterführung der ProgressBar-Entwicklung
DoEasy. Steuerung (Teil 27): Arbeiten am WinForms Objekt der ProgressBar
DoEasy. Steuerung (Teil 28): Balkenstile im ProgressBar-Steuerelement
DoEasy. Steuerung (Teil 29): ScrollBar-Hilfssteuerelement
DoEasy. Steuerung (Teil 30): Animieren des ScrollBar-Steuerelements
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/11926
- 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.