Graphical Interfaces V: The List View Element (Chapter 2)
Contents
- Introduction
- The List View Element
- Developing the Class for Creating the Element
- Testing the Setup of the List View
- Methods for Managing the Element
- Fast Forward of the List View
- Conclusion
Introduction
The first article Graphical Interfaces I: Preparation of the Library Structure (Chapter 1) explains in detail what this library is for. You will find a list of articles with links at the end of each chapter. There, you can also download a complete version of the library at the current stage of development. The files must be placed in the same directories as they are located in the archive.
In the previous chapter, we wrote classes for creating vertical and horizontal scrollbars. In this chapter, we will implement them. We will write a class for creating the list view element, a compound part of which will be a vertical scrollbar. We will also demonstrate the implementation of the mechanism for automatic scrolling of the list view when holding the scrollbar button down. At the end, we will test everything using a real example of an MQL application.
The List View Element
The element of the graphical interface list view gives the user a choice of several options. The total number of the list view items and the number of items in its visible part can differ when the total number is too big and does not fit the highlighted working part of the interface. In such cases a scrollbar is used.
We will compose a list view of several primitive objects and an include element. They are:
- List view background.
- Array of the list view items.
- The vertical scrollbar control.
Fig. 1. Compound parts of the list view element.
Below, we are going to consider the development of the class for creating the list view element.
Developing the Class for Creating the Element
To create the element and embed this into the library under development, we need to create a file with the CListView class of the element. In our case it is ListView.mqh. Then we need to include it in the WndContainer.mqh file:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "ListView.mqh"
Similar to the classes of controls that have been considered in the previous articles of this series, the CListView class has a standard set of methods. To use a scrollbar in this element, the Scrolls.mqh file must be included in the ListView.mqh file.
//+------------------------------------------------------------------+ //| ListView.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "Scrolls.mqh" //+------------------------------------------------------------------+ //| Class for creating a list view | //+------------------------------------------------------------------+ class CListView : public CElement { private: //--- Pointer to the form to which the element is attached CWindow *m_wnd; //--- public: CListView(void); ~CListView(void); //--- (1) Stores the form pointer void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Chart event handler virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Timer virtual void OnEventTimer(void); //--- Moving the element virtual void Moving(const int x,const int y); //--- (1) Show, (2) hide, (3) reset, (4) delete virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Set, (2) reset of priorities for the left mouse button virtual void SetZorders(void); virtual void ResetZorders(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CListView::CListView(void) { //--- Store the name of the element class in the base class CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CListView::~CListView(void) { }
Methods for setting properties of primitive objects (compound parts of the list view) will be required before their creation.
- Height of the list view items.
- Color of the frame of the list view background.
- Color of the item background in different states.
- Color of the item text in different states.
Values of the above properties are set in the class constructor.
class CListView : public CElement { private: //--- Properties of the list view background int m_area_zorder; color m_area_border_color; //--- Properties of the list view items int m_item_zorder; int m_item_y_size; color m_item_color; color m_item_color_hover; color m_item_color_selected; color m_item_text_color; color m_item_text_color_hover; color m_item_text_color_selected; //--- public: //--- Height of the item void ItemYSize(const int y_size) { m_item_y_size=y_size; } //--- Color of the background frame void AreaBorderColor(const color clr) { m_area_border_color=clr; } //--- Color of the list view items in different states void ItemColor(const color clr) { m_item_color=clr; } void ItemColorHover(const color clr) { m_item_color_hover=clr; } void ItemColorSelected(const color clr) { m_item_color_selected=clr; } void ItemTextColor(const color clr) { m_item_text_color=clr; } void ItemTextColorHover(const color clr) { m_item_text_color_hover=clr; } void ItemTextColorSelected(const color clr) { m_item_text_color_selected=clr; } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CListView::CListView(void) : m_item_y_size(18), m_area_border_color(C'235,235,235'), m_item_color(clrWhite), m_item_color_hover(C'240,240,240'), m_item_color_selected(C'51,153,255'), m_item_text_color(clrBlack), m_item_text_color_hover(clrBlack), m_item_text_color_selected(clrWhite) { //--- Set priorities of the left mouse button click m_area_zorder =1; m_item_zorder =2; }
There are three private and one public methods for creating the objects of the list view. An array of class instances of the CEdit type has to be declared for the creating list view items. This array can be used for creating graphical objects of the OBJ_EDIT type (entry field).
class CListView : public CElement { private: //--- Objects for creating the list view CRectLabel m_area; CEdit m_items[]; CScrollV m_scrollv; //--- public: //--- Methods for creating the list view bool CreateListView(const long chart_id,const int window,const int x,const int y); //--- private: bool CreateArea(void); bool CreateList(void); bool CreateScrollV(void); };
The default size of the list view and its visible part is equal to two elements as there is no point in creating a list view containing only one item. To set the size of the list view and its visible part, let us create the CListView::ListSize() and CListView::VisibleListSize() methods, checking that the number of items is not less than two.
class CListView : public CElement { private: //--- Array of the list view values string m_value_items[]; //--- Size of the list view and its visible part int m_items_total; int m_visible_items_total; //--- public: //--- Returns the size of (1) the list view and (2) its visible part int ItemsTotal(void) const { return(m_items_total); } int VisibleItemsTotal(void) const { return(m_visible_items_total); } //--- Set the size of (1) the list view and (2) its visible part void ListSize(const int items_total); void VisibleListSize(const int visible_items_total); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CListView::CListView(void) : m_items_total(2), m_visible_items_total(2) { //--- Set the size of the list view and its visible part ListSize(m_items_total); VisibleListSize(m_visible_items_total); } //+------------------------------------------------------------------+ //| Sets the size of the list view | //+------------------------------------------------------------------+ void CListView::ListSize(const int items_total) { //--- No point to make a list view shorter than two items m_items_total=(items_total<2) ? 2 : items_total; ::ArrayResize(m_value_items,m_items_total); } //+------------------------------------------------------------------+ //| Sets the size of the visible part of the list view | //+------------------------------------------------------------------+ void CListView::VisibleListSize(const int visible_items_total) { //--- No point to make a list view shorter than two items m_visible_items_total=(visible_items_total<2) ? 2 : visible_items_total; ::ArrayResize(m_items,m_visible_items_total); }
Relevant methods are required for storing and getting the index and the text of the highlighted item in the list view. The first item of the list view will be highlighted by default. If another item has to be highlighted after the list view has been created, use the CListView::SelectedItemIndex() method. You will need to specify the item index before the creation of the list view and after the number of the elements has been defined.
class CListView : public CElement { private: //--- (1) Index and (2) the text of the highlighted item int m_selected_item_index; string m_selected_item_text; //--- public: //--- Returns/stores (1) the index and (2) the text in the highlighted item in the list view void SelectedItemIndex(const int index); int SelectedItemIndex(void) const { return(m_selected_item_index); } void SelectedItemText(const string text) { m_selected_item_text=text; } string SelectedItemText(void) const { return(m_selected_item_text); } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CListView::CListView(void) : m_selected_item_index(0), m_selected_item_text("") { //--- ... } //+------------------------------------------------------------------+ //| Storing the index | //+------------------------------------------------------------------+ void CListView::SelectedItemIndex(const int index) { //--- Adjustment in case the range has been exceeded m_selected_item_index=(index>=m_items_total)? m_items_total-1 : (index<0)? 0 : index; }
After the list view is created and also at the moment of selecting an item, the item must be highlighted with a different color. Let us write the CListView::HighlightSelectedItem() method for that. At the beginning of this method, there is a check for the current state of the scrollbar. If it is active and the slider can move, then the program leaves the method. If the check has been passed, then get the current position of the slider in the list view. The obtained value will be the starting one for the counter in the loop. With the help of that loop we can identify what element to highlight.
class CListView : public CElement { public: //--- Highlighting of the selected item void HighlightSelectedItem(void); }; //+------------------------------------------------------------------+ //| Highlights selected item | //+------------------------------------------------------------------+ void CListView::HighlightSelectedItem(void) { //--- Leave, if the scrollbar is active if(m_scrollv.ScrollState()) return; //--- Get the current position of the scrollbar slider int v=m_scrollv.CurrentPos(); //--- Iterate over the visible part of the list view for(int r=0; r<m_visible_items_total; r++) { //--- If inside the range of the list view if(v>=0 && v<m_items_total) { //--- Changing the background color and the text color m_items[r].BackColor((m_selected_item_index==v) ? m_item_color_selected : m_item_color); m_items[r].Color((m_selected_item_index==v) ? m_item_text_color_selected : m_item_text_color); //--- Increase the counter v++; } } }
When the list items are created in the CListView::CreateList() method, coordinates and width are calculated so that they do not obstruct the frame of the list view background. The width of the items is calculated considering whether the list view will have a scrollbar. All the items below the first one in the list view are set one on top of the other with the overlapping of one pixel. This is necessary to exclude gaps in two pixels which will be visible when the items are highlighted when the mouse cursor is hovering over them. We need to take it into consideration when calculating the height of the list view background and the scrollbar. After the creation of all the elements, at the end of the method the selected item is highlighted and its text is stored.
//+------------------------------------------------------------------+ //| Creates the list view items | //+------------------------------------------------------------------+ bool CListView::CreateList(void) { //--- Coordinates int x =CElement::X()+1; int y =0; //--- Calculating the width of the list view items int w=(m_items_total>m_visible_items_total) ? CElement::XSize()-m_scrollv.ScrollWidth() : CElement::XSize()-2; //--- for(int i=0; i<m_visible_items_total; i++) { //--- Forming the object name string name=CElement::ProgramName()+"_listview_edit_"+(string)i+"__"+(string)CElement::Id(); //--- Calculating the Y coordinate y=(i>0) ? y+m_item_y_size-1 : CElement::Y()+1; //--- Creating the object if(!m_items[i].Create(m_chart_id,name,m_subwin,x,y,w,m_item_y_size)) return(false); //--- Setting the properties m_items[i].Description(m_value_items[i]); m_items[i].TextAlign(m_align_mode); m_items[i].Font(FONT); m_items[i].FontSize(FONT_SIZE); m_items[i].Color(m_item_text_color); m_items[i].BackColor(m_item_color); m_items[i].BorderColor(m_item_color); m_items[i].Corner(m_corner); m_items[i].Anchor(m_anchor); m_items[i].Selectable(false); m_items[i].Z_Order(m_item_zorder); m_items[i].ReadOnly(true); m_items[i].Tooltip("\n"); //--- Coordinates m_items[i].X(x); m_items[i].Y(y); //--- Size m_items[i].XSize(w); m_items[i].YSize(m_item_y_size); //--- Margins from the edge of the panel m_items[i].XGap(x-m_wnd.X()); m_items[i].YGap(y-m_wnd.Y()); //--- Store the object pointer CElement::AddToArray(m_items[i]); } //--- Highlighting the selected item HighlightSelectedItem(); //--- Store the text of the selected item m_selected_item_text=m_value_items[m_selected_item_index]; return(true); }
When creating a scrollbar in the CListView::CreateScrollV() method, at the very beginning a ratio of the number of items of the whole list view to its visible part is checked. If the total number of items is less or equal to the number of items in its visible part, then there is no point in continuing and the program leaves the method. Then (1) the form pointer is stored, (2) coordinates are calculated and (3) properties are set. The element identifier for the scrollbar must be the same as its parent element. The modes of the drop-down element must be the same too.
//+------------------------------------------------------------------+ //| Creates the vertical scrollbar | //+------------------------------------------------------------------+ bool CListView::CreateScrollV(void) { //--- If the number of items is greater that the size of the list then // set the vertical scrollbar if(m_items_total<=m_visible_items_total) return(true); //--- Store the form pointer m_scrollv.WindowPointer(m_wnd); //--- Coordinates int x=CElement::X()+m_area.X_Size()-m_scrollv.ScrollWidth(); int y=CElement::Y(); //--- Set properties m_scrollv.Id(CElement::Id()); m_scrollv.XSize(m_scrollv.ScrollWidth()); m_scrollv.YSize(CElement::YSize()); m_scrollv.AreaBorderColor(m_area_border_color); m_scrollv.IsDropdown(CElement::IsDropdown()); //--- Creating the scrollbar if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_items_total,m_visible_items_total)) return(false); //--- return(true); }
The size along the Y axis is calculated in the CListView::CreateListView() main method for creating the list view. As mentioned above, when the size is calculated, the one pixel overlap of the list view items must be considered. Please also bear in mind that the array of items must be located strictly inside the list view background so that it does not obstruct its frame.
//+------------------------------------------------------------------+ //| Creates the list view | //+------------------------------------------------------------------+ bool CListView::CreateListView(const long chart_id,const int window,const int x,const int y) { //--- Leave, if there is not a form pointer if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating the list view, the class must be passed " "the form pointer: CListView::WindowPointer(CWindow &object)"); return(false); } //--- Initialization of variables m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =window; m_x =x; m_y =y; m_y_size =m_item_y_size*m_visible_items_total-(m_visible_items_total-1)+2; //--- Margins from the edge CElement::XGap(m_x-m_wnd.X()); CElement::YGap(m_y-m_wnd.Y()); //--- Creating a button if(!CreateArea()) return(false); if(!CreateList()) return(false); if(!CreateScrollV()) return(false); //--- Hide the element if it is a dialog window or it is minimized if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) Hide(); //--- return(true); }
The library user will decide whether he or she needs the items to be highlighted when the mouse cursor is hovering over them. The highlighting will be disabled by default. As an additional adjustable property, let us create a method for setting text alignment to (1) the left margin, (2) to the right margin or (3) to the center. By default the text will be aligned to the left margin.
class CListView : public CElement { private: //--- Mode of highlighting when the cursor is hovering over bool m_lights_hover; //--- Mode of alignment of the text in the list view ENUM_ALIGN_MODE m_align_mode; //--- public: //--- (1) Mode of highlighting items when hovering the cursor, (2) text alignment void LightsHover(const bool state) { m_lights_hover=state; } void TextAlign(const ENUM_ALIGN_MODE align_mode) { m_align_mode=align_mode; } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CListView::CListView(void) : m_lights_hover(false), m_align_mode(ALIGN_LEFT) { //--- ... }
Before creating the list view, the data array must be initialized. For that, create the CListView::ValueToList() method where the check of the array size and adjustment of the index in case of exceeding the array size will take place.
class CListView : public CElement { public: //--- Setting the value in the list view by the specified index of the row void ValueToList(const int item_index,const string value); }; //+------------------------------------------------------------------+ //| Stores the passed value in the list view by the specified index | //+------------------------------------------------------------------+ void CListView::ValueToList(const int item_index,const string value) { int array_size=::ArraySize(m_value_items); //--- If there are no items in the context menu, report if(array_size<1) { ::Print(__FUNCTION__," > This method is to be called, " "when the list view contains at least one item!"); } //--- Adjustment in case the range has been exceeded int i=(item_index>=array_size)? array_size-1 : (item_index <0)? 0 : item_index; //--- Store the value in the list view m_value_items[i]=value; }
Now, we are going to test the setting of the list view with a vertical scrollbar and then will gradually add all required methods for its management.
Testing the Setup of the List View
As the ListView.mqh file is already included in the library, the class of the list view element (CListView) is already available to the user in the custom class. Before we test the embedding of the list view, we need to introduce some additions in the CWndContainer class. A list view is a compound element and therefore the addition of the scrollbar pointer to the element base must be ensured.
For that, let us write the CWndContainer::AddListViewElements() method. A check for the class name will be at the beginning of the method. It the element turns out to be not a list view, the program will leave the method. Then, having increased the size of the common array of pointers and obtained the pointer with the right element type, store it.
class CWndContainer { private: //--- Stores the pointers to the list view objects in the base bool AddListViewElements(const int window_index,CElement &object); }; //+------------------------------------------------------------------+ //| Stores the pointers to the list view objects in the base | //+------------------------------------------------------------------+ bool CWndContainer::AddListViewElements(const int window_index,CElement &object) { //--- Leave, if this is not a list view if(object.ClassName()!="CListView") return(false); //--- Get the list view pointer CListView *lv=::GetPointer(object); //--- Increasing the element array int size=::ArraySize(m_wnd[window_index].m_elements); ::ArrayResize(m_wnd[window_index].m_elements,size+1); //--- Get the scrollbar pointer CScrollV *sv=lv.GetScrollVPointer(); //--- Store the element in the base m_wnd[window_index].m_elements[size]=sv; return(true); }
The CWndContainer::AddListViewElements() method is called in the main public method for adding elements to the base:
//+------------------------------------------------------------------+ //| Adds the pointer to the element array | //+------------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- If the base does not contain forms for controls //--- If the request is for a non-existent form //--- Add to the common array of elements //--- Add element objects to the common array of objects //--- Store the id of the last element in all forms //--- Increase the counter of element identifiers //--- Stores the pointers to the context menu objects in the base //--- Stores the pointers to the main menu objects in the base //--- Stores the pointers to the split button objects in the base //--- Stores the pointers to the tooltip objects in the base //--- Stores the pointers to the list view objects in the base if(AddListViewElements(window_index,object)) return; }
Let us use the EA from the previous part of the series for testing. We will leave only its main menu together with the context menus and the status bar, other elements have to be deleted. Create a class instance in the custom class, declare the method for creating an element and define the margins from the edge of the form:
class CProgram : public CWndEvents { private: //--- List view CListView m_listview1; //--- private: //--- List view #define LISTVIEW1_GAP_X (2) #define LISTVIEW1_GAP_Y (43) bool CreateListView1(void); };
Below is the code of the method for creating a list view. The list view will contain twenty items. Only ten items will be visible. We are going to enable the highlighting of the items when the mouse cursor is hovering over them. Select the sixth (5) item of the list view. As this is only an example, fill the list with the text «SYMBOL» with the item number.
//+------------------------------------------------------------------+ //| Creates list view 1 | //+------------------------------------------------------------------+ bool CProgram::CreateListView1(void) { //--- Size of the list view #define ITEMS_TOTAL1 20 //--- Store the window pointer m_listview1.WindowPointer(m_window1); //--- Coordinates int x=m_window1.X()+LISTVIEW1_GAP_X; int y=m_window1.Y()+LISTVIEW1_GAP_Y; //--- Set properties before creation m_listview1.XSize(100); m_listview1.LightsHover(true); m_listview1.ListSize(ITEMS_TOTAL1); m_listview1.VisibleListSize(10); m_listview1.AreaBorderColor(clrDarkGray); m_listview1.SelectedItemIndex(5); //--- Get the scrollbar pointer CScrollV *sv=m_listview1.GetScrollVPointer(); //--- Properties of the scrollbar sv.ThumbBorderColor(C'190,190,190'); sv.ThumbBorderColorHover(C'180,180,180'); sv.ThumbBorderColorPressed(C'160,160,160'); //--- Filling the list view with data for(int r=0; r<ITEMS_TOTAL1; r++) m_listview1.ValueToList(r,"SYMBOL "+string(r)); //--- Create the list view if(!m_listview1.CreateListView(m_chart_id,m_subwin,x,y)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,m_listview1); return(true); }
The method for creating a list view must be called in the main method for creating the graphical interface:
//+------------------------------------------------------------------+ //| Creates the trading panel | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Creating form 1 for controls //--- Creating controls: // Main menu //--- Context menus //--- Creating the status bar //--- List view if(!CreateListView1()) return(false); //--- Redrawing the chart m_chart.Redraw(); return(true); }
Now, the code can be compiled and the program can be loaded on to the chart. If everything is done correctly, you will see the result as in the screenshot below:
Fig. 2. Test of setting up the list view element.
It looks good but currently this list view cannot be managed and its objects will not react the mouse cursor. In the next part of the article, we will write methods that will allow us to manage the list view.
Methods for Managing the Element
At first, let us create the method that will allow the items to change color when the mouse cursor is hovering over them. Then, we will need a method that will facilitate restoring the default colors.
class CListView : public CElement { public: //--- (1) Resetting the color of the list view items, (2) changing the color of the list view items when the cursor is hovering over them void ResetItemsColor(void); void ChangeItemsColor(const int x,const int y); };
Resetting the colors of all items except the selected one is carried out in the CListView::ResetItemsColor() method. At the beginning of the method, position of the slider is identified. This value is received in the variable which will be used further in the loop as a counter for identifying the selected item.
//+------------------------------------------------------------------+ //| Resetting the color of the list view items | //+------------------------------------------------------------------+ void CListView::ResetItemsColor(void) { //--- Get the current position of the scrollbar slider int v=m_scrollv.CurrentPos(); //--- Iterate over the visible part of the list view for(int i=0; i<m_visible_items_total; i++) { //--- Increase the counter if the list view range has not been exceeded if(v>=0 && v<m_items_total) v++; //--- Skip the selected item if(m_selected_item_index==v-1) continue; //--- Setting the color (background, text) m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } }
Several checks have to be passed at the beginning of the CListView::ChangeItemsColor() method. The program will leave the method in the following cases:
- if highlighting the items when the cursor is hovering over them is not enabled;
- if the scrollbar is under management;
- if it is not a drop-down element and the form is blocked.
Then, similar to many methods of this class, we get the current position of the scrollbar in the variable. This variable will be used for identifying the selected item in the loop so it is skipped as its color does not need changing. The cursor coordinates will be passed to the method. These coordinates will allow to identify in a loop over which item the mouse is placed. More detailed code of the CListView::ChangeItemsColor() method is presented below.
//+------------------------------------------------------------------+ //| Changing color of the list view item when the cursor is hovering | //+------------------------------------------------------------------+ void CListView::ChangeItemsColor(const int x,const int y) { //--- Leave, if the highlighting of the item when the cursor is hovering over it is disabled or the scrollbar is active if(!m_lights_hover || m_scrollv.ScrollState()) return; //--- Leave, if it is not a drop-down element and the form is blocked if(!CElement::IsDropdown() && m_wnd.IsLocked()) return; //--- Get the current position of the scrollbar slider int v=m_scrollv.CurrentPos(); //--- Identify over which item the cursor is over and highlight it for(int i=0; i<m_visible_items_total; i++) { //--- Increase the counter if the list view range has not been exceeded if(v>=0 && v<m_items_total) v++; //--- Skip the selected item if(m_selected_item_index==v-1) continue; //--- If the cursor is over this item, highlight it if(x>m_items[i].X() && x<m_items[i].X2() && y>m_items[i].Y() && y<m_items[i].Y2()) { m_items[i].BackColor(m_item_color_hover); m_items[i].Color(m_item_text_color_hover); } //--- If the cursor is not over this item, assign the color appropriate to its state else { m_items[i].BackColor(m_item_color); m_items[i].Color(m_item_text_color); } } }
Now, we need to use the CListView::ChangeItemsColor() method in the event handler of the CListView class:
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if(id==CHARTEVENT_MOUSE_MOVE) { //--- List hidden if(!CElement::IsVisible()) return; //--- Coordinates int x=(int)lparam; int y=(int)dparam; //--- Changes the color of the list view items when the cursor is hovering over it ChangeItemsColor(x,y); return; } }
If we compile the program for tests, then when we hover the mouse cursor over the list view items, their color will be changed as shown in the screenshot below:
Fig. 3. Test of changing the color of the list view items when the mouse cursor is hovering over them.
To allow the list view to move together with the slider of the scrollbar, let us write the CListView::ShiftList() method. This method also begins with the storing the current position of the slider in the variable. Similar to the previous method, it will be used as a counter in the loop for storing the selected item in the list view and also for moving the data. See the code below.
class CListView : public CElement { public: //--- Scrolling the list view void ShiftList(void); }; //+------------------------------------------------------------------+ //| Moves the list view along the scrollbar | //+------------------------------------------------------------------+ void CListView::ShiftList(void) { //--- Get the current position of the scrollbar slider int v=m_scrollv.CurrentPos(); //--- Iterate over the visible part of the list view for(int i=0; i<m_visible_items_total; i++) { //--- If inside the range of the list view if(v>=0 && v<m_items_total) { //--- Moving the text, the background color and the text color m_items[i].Description(m_value_items[v]); m_items[i].BackColor((m_selected_item_index==v) ? m_item_color_selected : m_item_color); m_items[i].Color((m_selected_item_index==v) ? m_item_text_color_selected : m_item_text_color); //--- Increase the counter v++; } } }
The CListView::ShiftList() method must be called in the CListView::OnEvent() handler provided that the CScrollV::ScrollBarControl() method of the scrollbar will return true. That will mean that the management of the slider is enabled.
class CListView : public CElement { private: //--- State of the left mouse button (pressed down/released) bool m_mouse_state; }; //+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if(id==CHARTEVENT_MOUSE_MOVE) { //--- List hidden if(!CElement::IsVisible()) return; //--- Coordinates and state of the left mouse button int x=(int)lparam; int y=(int)dparam; m_mouse_state=(bool)int(sparam); //--- Checking the focus over the list view CElement::MouseFocus(x>CElement::X() && x<CElement::X2() && y>CElement::Y() && y<CElement::Y2()); //--- Move the list if the management of the slider is enabled if(m_scrollv.ScrollBarControl(x,y,m_mouse_state)) ShiftList(); //--- Changes the color of the list view items when the cursor is hovering over it ChangeItemsColor(x,y); return; } }
After the compilation of the program, there list view can be managed by the slider of the scrollbar as shown in the screenshot below. Everything is implemented in such a way that even if the cursor leaves the boundaries of the slider after the left mouse button was pressed down over it, the management will be passed to the scrollbar and the slider will be moving.
Fig. 4. Managing the list view using the slider of the scrollbar.
Now, we need a method for identifying the pressing on one of the list view items. Let us create a private method and call it CListView::OnClickListItem(). Also, the CListView::IdFromObjectName() private method is required for extracting the element identifier from the object name which has been shown in classes of other elements of our library.
Besides, we will need a unique identifier of clicking on the list view item (ON_CLICK_LIST_ITEM). Add it to the Defines.mqh file:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #define ON_CLICK_LIST_ITEM (16) // Selecting the list view item
At the beginning of the CListView::OnClickListItem() method, there are checks for the name and the identifier of the object that was clicked on. Then, the local variable is assigned the current position of the slider. This variable is used as a counter in the loop to identify the index and the text of the item. At the end of the method, a message is sent containing (1) the identifier of the ON_CLICK_LIST_ITEM event, (2) the element identifier and (3) the text of current selected item.
class CListView : public CElement { private: //--- Handling the pressing on the list view item bool OnClickListItem(const string clicked_object); //--- Getting the identifier from the name of the list view item int IdFromObjectName(const string object_name); }; //+------------------------------------------------------------------+ //| Handling the pressing on the list view item | //+------------------------------------------------------------------+ bool CListView::OnClickListItem(const string clicked_object) { //--- Leave, if the pressing was not on the menu item if(::StringFind(clicked_object,CElement::ProgramName()+"_listview_edit_",0)<0) return(false); //--- Get the identifier and index from the object name int id=IdFromObjectName(clicked_object); //--- Leave, if the identifier does not match if(id!=CElement::Id()) return(false); //--- Get the current position of the scrollbar slider int v=m_scrollv.CurrentPos(); //--- Go over the visible part of the list view for(int i=0; i<m_visible_items_total; i++) { //--- If this list view item was selected if(m_items[i].Name()==clicked_object) { m_selected_item_index =v; m_selected_item_text =m_value_items[v]; } //--- If inside the range of the list view if(v>=0 && v<m_items_total) //--- Increase the counter v++; } //--- Send a message about it ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElement::Id(),0,m_selected_item_text); return(true); }
Now, when we have all required methods for managing the list view, we only have to place the code in the CListView::OnEvent() list view event handler as shown in the code below. Pressing on one of the list view items calls the CListView::HighlightSelectedItem() method. In this method pressing on the buttons of the scrollbar is tracked. If there was pressing on one of its buttons, then the list view is moved in relation to the current position of the slider.
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Handling the pressing on objects if(id==CHARTEVENT_OBJECT_CLICK) { //--- If the pressing was on the list view elements if(OnClickListItem(sparam)) { //--- Highlighting the item HighlightSelectedItem(); return; } //--- If the pressing was on the buttons of the scrollbar if(m_scrollv.OnClickScrollInc(sparam) || m_scrollv.OnClickScrollDec(sparam)) { //--- Moves the list along the scrollbar ShiftList(); return; } } }
Fast Forward of the List View
We have completed the development of the required minimum for managing the list view. We can extend the capacity. Let us create one more private method for a fast forward of the list view by pressing a mouse button and holding it down on one of the buttons of the scrollbar.
We need to ensure that when the left mouse button is held down over the buttons of the scrollbar there is a little delay before the fast forward of the list view. If this is not done, the fast forward will be carried out immediately, which is not suitable for the time when the button is clicked only once for the list view to be moved only by one item. The SPIN_DELAY_MSC identifier has to be added in the Defines.mqh file with the value of -450. That means that the delay will be 450 milliseconds.
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //--- Delay before enabling the fast forward of the counter (milliseconds) #define SPIN_DELAY_MSC (-450)
Below is the code of the CListView::FastSwitching() method. We also need to declare the m_timer_counter field which will be used as the timer counter. At the beginning of the CListView::FastSwitching() method, we check the focus on the list view. If there is no focus, the program leaves the method. Then, if the mouse button is released, the counter is assigned the value of the delay (in our case it is -450 ms). If the mouse button is held down, the value of the counter is increased by the step of the timer set in the library (in this case it is 16 ms). Then there is a condition that prevents the program to work further until the value of the counter is not equal or greater than zero. As soon as this condition is met, the state of the scrollbar buttons is checked. Depending on what button is pressed down, the method for imitating pressing on the button is called. Then the list view is moved according to the current position of the slider of the scrollbar.
class CListView : public CElement { private: //--- Timer counter for fast forwarding the list view int m_timer_counter; private: //--- Fast forward of the list view void FastSwitching(void); }; //+------------------------------------------------------------------+ //| Fast forward of the scrollbar | //+------------------------------------------------------------------+ void CListView::FastSwitching(void) { //--- Leave, if there is no focus on the list view if(!CElement::MouseFocus()) return; //--- Return the counter to the initial value if the mouse button is released if(!m_mouse_state) m_timer_counter=SPIN_DELAY_MSC; //--- If the mouse button is pressed down else { //--- Increase the counter by the set step m_timer_counter+=TIMER_STEP_MSC; //--- Leave, if less than zero if(m_timer_counter<0) return; //--- If scrolling up if(m_scrollv.ScrollIncState()) m_scrollv.OnClickScrollInc(m_scrollv.ScrollIncName()); //--- If scrolling down else if(m_scrollv.ScrollDecState()) m_scrollv.OnClickScrollDec(m_scrollv.ScrollDecName()); //--- Moves the list ShiftList(); } }
The CListView::FastSwitching() method has to be carried out in the CListView::OnEventTimer() timer as shown below. If the list view is a drop-down element, no additional checks are required. Otherwise, we need to check if the form is currently blocked.
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CListView::OnEventTimer(void) { //--- If this is a drop-down element if(CElement::IsDropdown()) //--- Fast forward of the list view FastSwitching(); //--- If this is not a drop-down element, take current availability of the form into consideration else { //--- Track the fast forward of the list view only if the form is not blocked if(!m_wnd.IsLocked()) FastSwitching(); } }
Now, all the methods for managing the list view are ready. We can test them. Let us add two more list views to what we have created earlier:
Fig. 5. Testing three list views in the graphical interface.
We will receive the messages from the list views in the event handler of the custom class (CProgram):
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Event of pressing on the list view item if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { if(lparam==m_listview1.Id()) ::Print(__FUNCTION__," > This message is from the first list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(lparam==m_listview2.Id()) ::Print(__FUNCTION__," > This message is from the second list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); else if(lparam==m_listview3.Id()) ::Print(__FUNCTION__," > This message is from the third list view > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } }
When list view items are pressed on, the messages will be printed in the journal as shown below:
2016.01.16 13:02:00.085 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the first list view > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 11 2016.01.16 13:01:59.056 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the third list view > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 12 2016.01.16 13:01:58.479 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the second list view > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 9 2016.01.16 13:01:57.868 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the third list view > id: 1016; lparam: 9; dparam: 0.0; sparam: SYMBOL 19 2016.01.16 13:01:56.854 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the second list view > id: 1016; lparam: 8; dparam: 0.0; sparam: SYMBOL 4 2016.01.16 13:01:56.136 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the first list view> id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 9 2016.01.16 13:01:55.433 TestLibrary (GBPUSD,D1) CProgram::OnEvent > This message is from the first list view > id: 1016; lparam: 7; dparam: 0.0; sparam: SYMBOL 14
Conclusion
In this article, we have considered the compound control list view. We also demonstrated how a vertical scrollbar can be used. In the next article, we will talk about another compound control - the combobox.
You can download the material of Part V and test how it works. If you have questions on using material from those files, you can refer to the detailed description of the library development in one of the articles from the list below or ask your question in the comments of this article.
List of articles of the fifth part:
- Graphical Interfaces V: The Vertical and Horizontal Scrollbar (Chapter 1)
- Graphical Interfaces V: The List View Element (Chapter 2)
- Graphical Interfaces V: The Combobox Control (Chapter 3)
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/2380
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
New article Graphical Interfaces V: The List View Element (Chapter 2) has been published:
Author: Anatoli Kazharski
Hi,
compiling it with current MT5 Version: 5.00 build 1340 causes compiler Errors:
'return' - cannot convert from const pointer to nonconst pointer SplitButton.mqh 90 65
'return' - cannot convert from const pointer to nonconst pointer ListView.mqh 67 76
Hi,
compiling it with current MT5 Version: 5.00 build 1340 causes compiler Errors:
'return' - cannot convert from const pointer to nonconst pointer SplitButton.mqh 90 65
'return' - cannot convert from const pointer to nonconst pointer ListView.mqh 67 76
Thank.
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
Обсуждение статьи "Графические интерфейсы III: Группы простых и многофункциональных кнопок (Глава 2)"
Anatoli Kazharski, 2016.05.14 18:58
Да, после последнего обновления терминала появилась такая ошибка. Правила "игры" немного изменились. Исправить можно просто удалив спецификатор const.
Перейдите к строке с ошибкой и замените эту строку:
На эту:
//---
Подобные исправления нужно будет внести во всех файлах, где будет встречаться такая ошибка. В следующих статьях серии ошибка будет устранена.
Спасибо за сообщение.
Hello,
I am playing around with the standard library, and I have a really simple question:
I have created a List with CListView. For example, i add elements to it every second. How can i update the list to show the most recent elements automatically without scrolling down with the mouse?
Thank.