目录
- 1。介绍
- 2。目标
- 三。CBOX
3.1类。布局样式3.2。计算控制3.3之间的间隔。对齐3.4。组件渲染3.5.部件尺寸调整3.6.递归呈现 - 4。在对话框窗口中实现
- 5。例行程序5.1。程序1:简单点值计算5.2。例程2:控制重新配置示例
- 6。优缺点
- 7。结论
1。介绍
在大多数应用程序中,GUI是通过使用对话框窗口中控件绝对定位的直接方法创建的。然而,在某些情况下,这种图形用户界面(GUI)设计是不方便甚至不实用的。本文介绍了一种使用布局管理器cbox类基于布局和容器创建GUI(图形用户界面)的替代方法。
本文中实现和使用的布局管理器大致相当于一些主流编程语言(如Box Dead(Java)和几何管理器包(Python/Tkter))。
2。目标
查看MetaTrader 5中提供的SimplePanel和控制例程,我们可以看到这些面板中的控件是由像素(绝对定位)定位的。创建的每个控件都在工作区中指定了一个特定的位置,并且每个控件都依赖于它之前创建的控件,并附加了一些偏移量。虽然这是一种自然的方法,但在大多数情况下,虽然不需要高精度,但这种方法的使用在许多方面是不利的。
在设计图形用户界面时,任何有经验的程序员都可以使用图形控件的精确像素定位。但是,这有以下缺点:
- 通常,当修改组件的大小或位置时,它不能阻止其他组件受到影响。
- 大多数代码是不可重用的-接口中的微小更改可能导致代码发生重大更改。
- 它可能很耗时,尤其是在设计更复杂的接口时。
这促使我们创建具有以下目标的布局系统:
- 代码应该是可重用的。
- 修改接口的一部分应该尽量减少对其他组件的影响。
- 应自动计算接口中组件的位置。
本文介绍了使用container-cbox类实现这类系统的方法。
三。CBOX
级
类cbox的一个实例用作容器或框控件-可以将控件添加到框中,cbox自动计算控件在其分配的空间中的位置。典型的cbox类实例应该具有以下布局:
传奇1。CBox布局
外箱代表集装箱的整体尺寸,内箱代表班轮边界。蓝色区域表示填充空间。剩余的空白是控件可以放置在容器中的空间。
根据面板的复杂性,cbox类可以以不同的方式使用。例如,它可以用作容器(cbox),用一组控件存储其他容器。或者容器包含控件和其他容器。但是,对于给定的父容器,强烈建议使用对等容器。
我们通过扩展cwndclient(不带滚动条)来构建cbox,如下节所示:
#include <Controls/WndClient.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CBox : public CWndClient { public: CBox(); ~CBox(); virtual bool Create(const long chart,const string name,const int subwin, const int x1,const int y1,const int x2,const int y2); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CBox::CBox() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CBox::~CBox() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CBox::Create(const long chart,const string name,const int subwin, const int x1,const int y1,const int x2,const int y2) { if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); if(!CreateBack()) return(false); if(!ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG)) return(false); if(!ColorBorder(clrNONE)) return(false); return(true); } //+------------------------------------------------------------------+
cBox类也可以直接从cwndcontainer继承。但是,这样做将丢失此类中一些有用的函数,如背景和边框。相反,可以直接从扩展的cwndobj实现更简单的版本,但您需要添加carrayobj实例作为其私有成员或受保护成员,并重新创建相关对象的类方法以保存该实例。
3.1。版式风格
cBox有两种布局样式:垂直和水平。
水平样式具有以下基本布局:
图利2。水平样式(中心)
垂直样式具有以下基本布局:
传奇3。垂直样式(中心)
cBox默认为水平样式。
使用这两种布局的组合(可能带有多个容器),您可以重建几乎任何类型的GUI面板设计。此外,在容器中放置控件允许分段设计。也就是说,它允许您自定义给定容器中控件的大小和位置,而不影响其他容器的位置。
为了在cbox中实现水平和垂直样式,我们需要声明一个枚举,然后将其保存为所述类中的成员:
enum LAYOUT_STYLE
{
LAYOUT_STYLE_VERTICAL,
LAYOUT_STYLE_HORIZONTAL
};
3.2。计算控件之间的间隔
cBox最大化分配给它的可用空间,并均匀地定位控件,如上图所示。
总的来说,我们可以使用以下伪代码导出计算给定cbox容器中控件之间间隔的公式:
对于水平布局: x 间隔 = ((可用空间 x)-(所有控件的总计 x 大小))/(控件总数 + 1) y 间隔 = ((可用空间 y)-(y 控件大小))/2 对于垂直布局: x 间隔 = ((可用空间 x)-(x 控件大小))/2 y 间隔 = ((可用空间 y)-(所有控件的总计 y 大小))/(控件总数 + 1)
3.3。对准
如前一节所述,控件之间的间隔计算仅用于中心对齐。我们希望cbox类能够容纳更多的对齐方式,因此我们需要在计算中做一些小的修改。
对于水平对齐,可用选项为左、右和中心(无边界),但容器居中除外,如下图所示:
传奇4。水平样式(左对齐)
传奇5。水平样式(右对齐)
传奇6。水平样式(集中,无限)
对于水平对齐,除了容器居中之外,可用的选项是顶部、底部、中心和中心(无限),如下图所示:
传奇7。垂直对齐样式:(左)上,(中)中-无边,(右)下
应该注意的是,cBox类应该根据这些对齐设置自动计算控件之间的x和y间隔。所以,最好使用除数。
(控件总数 + 1)
为了得到控制间隔,我们使用控制的总数作为除数,并且(控制的总数-1)作为中心控制,在边界处没有边界。
与布局样式类似,实现CBox类的对齐特性将需要枚举。我们要为每个对齐样式声明一个枚举,如下所示:
enum VERTICAL_ALIGN { VERTICAL_ALIGN_CENTER, VERTICAL_ALIGN_CENTER_NOSIDES, VERTICAL_ALIGN_TOP, VERTICAL_ALIGN_BOTTOM }; enum HORIZONTAL_ALIGN { HORIZONTAL_ALIGN_CENTER, HORIZONTAL_ALIGN_CENTER_NOSIDES, HORIZONTAL_ALIGN_LEFT, HORIZONTAL_ALIGN_RIGHT };
3.4。组件呈现
通常,我们通过指定x1、y1、x2和y2参数来创建控件,例如创建按钮剪辑,如下所示:
CButton m_button; int x1 = currentX; int y1 = currentY; int x2 = currentX+BUTTON_WIDTH; int y2 = currentY+BUTTON_HEIGHT if(!m_button.Create(m_chart_id,m_name+"Button",m_subwin,x1,y1,x2,y2)) return(false);
这里x2减去x1和y2减去y1分别等于控件的宽度和高度。如果没有,我们可以使用cbox以一种更简单的方式创建相同的按钮,如下节所示:
if(!m_button.Create(m_chart_id,m_name+"Button",m_subwin,0,0,BUTTON_WIDTH,BUTTON_HEIGHT)) return(false);
类cbox将自动重新定位稍后创建的面板窗口中的小部件。调用方法包()用于重新定位控件和容器,并再次调用render()方法。
bool CBox::Pack(void) { GetTotalControlsSize(); return(Render()); }
方法包()只获取容器的组合大小,然后调用render()方法,在该方法中执行更多操作。以下代码段说明了在实际容器中呈现控件的render()方法:
bool CBox::Render(void) { int x_space=0,y_space=0; if(!GetSpace(x_space,y_space)) return(false); int x=Left()+m_padding_left+ ((m_horizontal_align==HORIZONTAL_ALIGN_LEFT||m_horizontal_align==HORIZONTAL_ALIGN_CENTER_NOSIDES)?0:x_space); int y=Top()+m_padding_top+ ((m_vertical_align==VERTICAL_ALIGN_TOP||m_vertical_align==VERTICAL_ALIGN_CENTER_NOSIDES)?0:y_space); for(int j=0;j<ControlsTotal();j++) { CWnd *control=Control(j); if(control==NULL) continue; if(control==GetPointer(m_background)) continue; control.Move(x,y); if (j<ControlsTotal()-1) Shift(GetPointer(control),x,y,x_space,y_space); } return(true); }
3.5。部件尺寸调整
当控件大小大于其容器的可用空间时,应调整控件大小以适应可用空间。否则,控件将溢出容器,导致整个面板出现外观问题。当您希望控件最大限度地占用整个客户或容器空间时,这种方法也很方便。如果给定控件的宽度或高度超过容器的宽度或高度减去填充(双边),则控件大小将调整为最大可用宽度或高度。
应注意,当cBox中包含的所有控件的总大小超过可用空间时,将不会调整容器大小。在这种情况下,需要手动调整主对话框窗口(CDialog或CappDialog)或单个控件的大小。
3.6。递归呈现
对于cbox的简单使用,只需调用pack()方法即可。但是,对于嵌套容器,需要调用相同的方法,以便所有容器都可以找到自己的单独控件或容器。我们可以通过向函数中添加方法来防止这种情况,并且我们可以为我们自己的控件实现相同的方法,前提是并且仅当所讨论的控件是cbox类或任何布局类的实例时。为此,我们首先定义一个宏并为其指定一个唯一值:
#define CLASS_LAYOUT 999
然后,我们重写cobject类的type()方法,以便它返回我们刚才准备的宏值:
virtual int Type() const {return CLASS_LAYOUT;}
最后,在cbox类的pack()方法中,我们将为其子容器执行呈现方法,这些方法是布局类的示例:
for(int j=0;j<ControlsTotal();j++) { CWnd *control=Control(j); if(control==NULL) continue; if(control==GetPointer(m_background)) continue; control.Move(x,y); //如果它是布局类, 调用控件 Pack() 方法 if(control.Type()==CLASS_LAYOUT) { CBox *container=control; container.Pack(); } if (j<ControlsTotal()-1) Shift(GetPointer(control),x,y,x_space,y_space); }
呈现方法在开始时计算容器中控件的可用空间。这些值分别以m_total_x和m_total_y存储。The next task is to calculate the interval between controls based on layout style and alignment. 最后一步是在容器中实现真正的控制重定位。
cBox维护重新定位控件的记录,因为容器中的某些对象不需要重新定位,例如cwndclient本地后台对象或其他可能的cBox扩展控件。
cBox还保留容器中控件的最小大小(背景除外),由m_m in_大小(cSize结构)定义。目标是保持控件在容器中均匀地堆叠,无论是水平还是垂直。它的定义非常违反直觉,因为它实际上是最大的控制大小。但是,这里我们将其定义为最小的,因为cBox假定大小最小,并根据该大小计算可用空间。
注意,shift()方法遵循类似于常规控制定位实现(绝对定位)的路径。渲染方法保留对x和y坐标的引用,并记住在更新cbox时重新定位每个控件。但是,对于cbox,这可以自动完成,让面板开发人员只需设置所使用的每个控件的实际大小。
4。在对话窗口中实现
在使用cbox时,我们几乎要替换cdialog或cappdialog本地客户机区域m_客户机区域的功能,这是cwndclient的一个例子。所以我们现在至少有三个选择:
- 扩展/重写cappdialog或cdialog以用cbox替换客户区域。
- 使用容器并将其添加到客户区域。
- 使用主cbox容器保存其余较小的容器。
使用第一个选项可能需要很多工作,我们需要重写对话框对象以使用新的客户机区域对象。相反,可扩展的对话对象使用自定义容器类,但是给我们留下了一个无用的cwndclient(m_client_area)实例,它占用了不必要的内存空间。
第二种选择也是可行的。我们只需将控件放在cbox容器中,然后使用像素定位将它们添加到客户区域。但是这个选项并没有充分利用cbox类的潜力,也就是说,面板的设计不必被独立控件和容器的位置混淆。
建议使用第三个选项。也就是说,我们创建一个body cbox容器来容纳所有其他较小的容器和控件。主容器将占据整个本地客户区,并且是它添加的唯一子容器。这将使本地客户区有点多余,但至少它仍然可用。此外,我们可以避免使用此选项进行大量编码/重新编码。
5。常规
5.1。程序1:简单点值计算
现在,我们使用cbox类来实现一个简单的面板:点值计算器。“点值计算器”对话框将包含sange cedit类型的字段,称为:
- 品种名称或金融工具;
- 输入品种或金融工具的每个点的大小;
- 输入品种或金融工具的每一点的价值;
这给我们带来了总共七个不同的控件,包括每个字段的标签(clabel)和执行计算的按钮(cButton)。计算器屏幕截图如下图所示:
0
图8。点值计算器
通过查看计算器面板,我们可以使用cbox将其减少到五个不同的容器。每个字段有三个水平容器,另一个作为按钮位于右侧。所有这些容器将封装在一个垂直样式的主容器中。最后,主体容器将附加到cappdialog实例的客户机区域。下图显示了容器的布局。紫色框表示水平线。白色框表示基本控件,包含所有较小框的最大灰色框是主框窗口。
1
图9。点值计算器布局
注意,当使用cbox容器时,我们不能为间隙和缩进声明任何宏。我们只需声明控件大小的宏,配置每个cbox实例,并将它们分配给控件。
为了构建这个面板,我们首先创建一个头文件“pipValueCalculator”。mqh’应该与我们稍后准备的源代码体文件(pipValueCalculator)放在同一个文件夹中。MQ5)。我们在这个文件中包含cbox类的头文件,以及面板所需的其他包含文件。我们还需要csymbolinfo类,我们将实际计算所有给定品种的点值:
#include <Trade/SymbolInfo.mqh> #include <Layouts/Box.mqh> #include <Controls/Dialog.mqh> #include <Controls/Label.mqh> #include <Controls/Button.mqh>
最后一步是指定要使用的控件的宽度和高度。您可以为每个控件指定特定的大小,但对于此面板,我们将使用通用空间大小。也就是说,所有基本控件的宽度和高度都相同:
#define CONTROL_WIDTH (100) #define CONTROL_HEIGHT (20)
现在,让我们继续创建真正的面板类对象。这可以通过从cappdialog类继承一个新类来实现,方法通常是:
class CPipValueCalculatorDialog : public CAppDialog
类的初始结构如下所示:
class CPipValueCalculatorDialog : public CAppDialog { protected: //此处是类的保护成员 public: CPipValueCalculatorDialog(); ~CPipValueCalculatorDialog(); protected: //此处是类的保护方法 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CPipValueCalculatorDialog::CPipValueCalculatorDialog(void) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CPipValueCalculatorDialog::~CPipValueCalculatorDialog(void) { }
从上面的代码片段中,我们现在有了点计算器面板类的起始模板(事实上,在创建类似面板时,它可以重用)。现在,让我们继续创建一个主容器类的成员,它将是面板上显示的所有其他cbox容器的父容器:
class CPipValueCalculatorDialog : public CAppDialog { protected: CBox m_main; //此处更多代码...
我们已经为面板定义了body cbox容器,但是还没有为它创建实际的函数。为此,我们向panel类添加其他类方法,如下所示:
// 开始类定义 // ... public: CPipValueCalculatorDialog(); ~CPipValueCalculatorDialog(); protected: virtual bool CreateMain(const long chart,const string name,const int subwin); // 其余的定义 // ...
稍后,在类外部,我们定义类方法的实体(类似于如何定义类的构造函数和析构函数):
bool CPipValueCalculatorDialog::CreateMain(const long chart,const string name,const int subwin) { //创建主体 CBox 容器 if(!m_main.Create(chart,name+"main",subwin,0,0,CDialog::m_client_area.Width(),CDialog::m_client_area.Height())) return(false); //应用垂直布局 m_main.LayoutStyle(LAYOUT_STYLE_VERTICAL); //设置所有边框衬垫 10 个像素 m_main.Padding(10); return(true); }
我们使用cdialog::m_客户机_区域。宽度()和cdialog::m_客户机区域。height()指定容器的宽度和高度。也就是说,它占据了面板工作区的整个空间。我们还对容器做了一些修改:应用垂直样式,并将所有边框的填充设置为10像素。这些函数由cbox类提供。
既然我们已经定义了body容器类成员,并且应该创建它,那么我们将为图例9中的每一行创建成员。对于最上面的一行(即变体),我们首先创建容器来声明它们,然后创建包含它们的基本控件,就在上面代码段中显示的主容器类成员的下面:
CBox m_main; CBox m_symbol_row; //行容器 CLabel m_symbol_label; //标签控件 CEdit m_symbol_edit; //编辑控件
与body容器类似,我们还定义了一个函数来创建实际的行容器:
bool CPipValueCalculatorDialog::CreateSymbolRow(const long chart,const string name,const int subwin) { //为行创建 CBox 容器 (品种行) if(!m_symbol_row.Create(chart,name+"symbol_row",subwin,0,0,CDialog::m_client_area.Width(),CONTROL_HEIGHT*1.5)) return(false); //创建标签控件 if(!m_symbol_label.Create(chart,name+"symbol_label",subwin,0,0,CONTROL_WIDTH,CONTROL_HEIGHT)) return(false); m_symbol_label.Text("品种"); //创建编辑控件 if(!m_symbol_edit.Create(chart,name+"symbol_edit",subwin,0,0,CONTROL_WIDTH,CONTROL_HEIGHT)) return(false); m_symbol_edit.Text(m_symbol.Name()); //添加基本控件至它们的父控件 (行) if(!m_symbol_row.Add(m_symbol_label)) return(false); if(!m_symbol_row.Add(m_symbol_edit)) return(false); return(true); }
在这个函数中,我们首先创建一个变化行容器。请注意,我们使用工作区的整个宽度作为宽度,同时使其比前面定义的控件的高度高50%。
创建行后,我们创建单独的控件。这一次,它们使用我们前面定义的宽度和高度宏。还要注意我们是如何创建这些控件的:
Create(chart,name+"symbol_edit",subwin,0,0,CONTROL_WIDTH,CONTROL_HEIGHT))
红色值是x1和y1坐标。这意味着创建时,所有控件都放置在图表的左上角。在调用cbox类的pack()方法后,这些方法会立即重新排列。
我们已经创建了行容器。我们还创建了容器中的基本控件。下一步是将刚刚创建的控件添加到行容器中,该容器在函数的最后几行中表示:
if(!m_symbol_row.Add(m_symbol_label)) return(false); if(!m_symbol_row.Add(m_symbol_edit)) return(false);
对于其他行(点大小、点值和按钮行),我们的实现与品种行中的实现大致相同。
当使用cbox类时,需要创建body容器和其他子程序。现在,我们进入了一个熟悉的情况,即创建面板对象本身。这可以通过重写cappDialog类的方法create()来实现(无论是否使用cbox)。我们前面定义的两个方法最终是有意义的,因为我们将在这里称之为:
bool CPipValueCalculatorDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { //创建 CAppDialog 面板 if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //使用我们之前定义的函数创建主体 CBox 容器 if(!CreateMain(chart,name,subwin)) return(false); //使用我们之前定义的函数创建品种行 CBox 容器 if(!CreateSymbolRow(chart,name,subwin)) return(false); //添加品种行作为主体 CBox 容器的子 CBox 容器 if(!m_main.Add(m_symbol_row)) return(false); //渲染主体 CBox 容器以及它的所有子容器 (递归) if (!m_main.Pack()) return(false); //添加主体 CBox 容器作为面板客户区域的仅有子容器 if (!Add(m_main)) return(false); return(true); }
Don’t forget to declare the overridden Create () method in the CPipValueCalculatorDialog class as follows:
public: CPipValueCalculatorDialog(); ~CPipValueCalculatorDialog(); virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
如上所示,它应该是类的公共方法,因为我们将从类外部调用它。在主源代码文件pipValueCalculator.mq5中需要更多详细信息:
#include "PipValueCalculator.mqh" CPipValueCalculatorDialog ExtDialog; //+------------------------------------------------------------------+ //| 初始函数 | //+------------------------------------------------------------------+ int OnInit() { //--- //--- 创建应用对话框 if(!ExtDialog.Create(0,"点值计算器",0,50,50,279,250)) return(INIT_FAILED); //--- 运行应用 if(!ExtDialog.Run()) return(INIT_FAILED); //--- ok return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 逆初函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- ExtDialog.Destroy(reason); } //+------------------------------------------------------------------+ //| 报价处理函数 | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //评论至此, 将在章节稍后继续讨论 //ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
除了以下三点外,此代码与我们在面板主体源代码文件中经常看到的非常相似:
- 我们添加了“PipValueCalculator”。mqh’头文件,而不是包含来自cappdalog的文件。’PipValueCalculator。mqh’已经包含在头文件中,因此不再需要将其包含在主源代码中。’PipValueCalculator。mqh’还负责包含cbox类的头文件。
- 我们将extdialog声明为在“pipValueCalculator”中定义的类的实例。mqh'(PipValueCalculator class).
- 我们为在extdialog中定义的面板指定了一个自定义大小。创建()。
仅编译品种线时,面板看起来类似于以下屏幕截图:
2
图10。点值计算器面板
,只有一行
主容器垂直对齐并居中,各种行水平对齐(也水平和垂直对齐)。为了将面板组装成一个图例。8,我们需要添加另外三行,基本上与创建变体行的方式相同。一个例外是按钮行,它只包含一个基本控件(按钮),并且对它是正确的:
m_button_row.HorizontalAlign(HORIZONTAL_ALIGN_RIGHT);
事件处理超出了本文的范围,但是为了这个例程的完整性,我们将简要介绍它。首先,我们为PipValueCalculator类声明一个新的类成员m_符号。我们还包括另外两个成员,m_digits_adjust和m_points_adjust,稍后将用于将点转换为点值。
CSymbolInfo *m_symbol; int m_digits_adjust; double m_points_adjust;
使用以下代码在类的构造函数或create()方法中初始化m_符号:
if (m_symbol==NULL) m_symbol=new CSymbolInfo(); if(m_symbol!=NULL) { if (!m_symbol.Name(_Symbol)) return(false); }
如果品种号为空,我们将创建一个新的csymbolinfo实例。如果它不是空的,我们为它指定的名称就是图表的名称。
下一步是为按钮定义Click事件处理程序。这可以通过onclickButton()类方法实现。我们将其实体定义如下:
void CPipValueCalculatorDialog::OnClickButton() { string symbol=m_symbol_edit.Text(); StringToUpper(symbol); if(m_symbol.Name(symbol)) { m_symbol.RefreshRates(); m_digits_adjust=(m_symbol.Digits()==3 || m_symbol.Digits()==5)?10:1; m_points_adjust=m_symbol.Point()*m_digits_adjust; m_pip_size_edit.Text((string)m_points_adjust); m_pip_value_edit.Text(DoubleToString(m_symbol.TickValue()*(StringToDouble(m_pip_size_edit.Text()))/m_symbol.TickSize(),2)); } else Print("无效输入"); }
class方法首先获取m_符号编辑控件的值以计算点值。然后将产品名称传递给CSymbolinfo类的实例。类获取所选品种的报告值,然后乘以某个乘数以调整和计算点值。
最后一步是启用类(也在PipValueCalculator类中)中定义的事件处理程序。在类的公共方法下插入这些代码行:
virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
然后,我们定义类外部的类的方法实体,如下所示:
EVENT_MAP_BEGIN(CPipValueCalculatorDialog) ON_EVENT(ON_CLICK,m_button,OnClickButton) EVENT_MAP_END(CAppDialog)
5.2。例程2:控制重构示例
在安装新的metatrader之后,控制面板的例程将自动安装。在导航窗口中,您可以在专家顾问/示例/控件下找到它。此面板的屏幕截图如下图所示:
3
传奇11。控件对话框(原始)
上面显示的对话框窗口的布局如下图所示。要使用cbox实例重建面板,我们将看到四条主要的水平线(紫色),形成以下一组控件:
- 编辑控件;
- 三按钮控制;
- spinedit和日期选择器;
- 组合框、RadioGroup和CheckGroup(第1列)和ListView(第2列)。
最后一个水平容器是一个特殊情况,因为它是一个嵌套的水平容器,可以容纳其他两个容器(绿色的第1列和第2列)。这些容器应垂直排列。
4
传奇12。控件对话框布局
重建对话框控件时,应删除调用add()方法的所有代码,但主容器除外,它将是客户端区域的唯一子角色。同时,其他控件和容器应添加到其设计的父容器中,从最深的容器到主容器,最终添加到本地客户区域。
安装完成后,编译并执行。除了日期选择器的递增、递减和列表按钮将拒绝工作外,其他一切都应该正常。这是由于CDatePicker类的下拉列表是在其他容器的上下文中设置的。要解决此问题,请搜索位于%data folder%/mql5/include/controls/datepicker.mqh中的cdatpicker类文件。找到listshow()方法并在函数开头插入以下代码行:
BringToTop();
重新编译和测试。这会将日期选择器的下拉列表放在前景中,并在显示单击事件时优先处理该事件。以下是完整的函数片段:
bool CDatePicker::ListShow(void) { BringToTop(); //--- 设置数值 m_list.Value(m_value); //--- 显示列表 return(m_list.Show()); }
控制对话框重建的屏幕截图如下图所示:
5
传奇13。控件对话框(使用cbox)
宏观图形与原始图形几乎相同。但是这里也有一个显著的区别,顺便说一下,第1列和第2列是完全对齐的。在原始图像中,我们可以看到checkgroup和listview在底部均匀地堆叠。但是,在顶部,组合框与ListView不对齐。当然,坐标可以在原始面板中重新定位,但这不仅需要调整组合框的像素,还需要调整RadioGroup和三个控件之间的间距。另一方面,使用cbox容器,只需将顶部和底部衬垫设置为零,并使用正确的对齐方式。
然而,这并不意味着使用cbox或布局在细度上更优越。尽管不可否认的是,控制坐标的精度比强制编码要低,但是使用容器和布局仍然可以提供正确的精度水平,同时使GUI设计变得更加容易。
6。优缺点
优势:
- 代码可重用-您可以在不同的应用程序和对话框中交织和重用CBOX或任何布局类。
- 可伸缩性-尽管在小型应用程序中使用它可以使源代码更长,但它的好处可以在更复杂的面板和对话框中体现出来。
- 控件集合分组-它允许您修改一组控件,而不影响其他空间的位置。
- 自动定位-缩进、间隙和间隔由布局类自动计算,无需手动编码。
缺点:
- 需要为容器创建其他控件,以及操作它们的其他函数。
- 低精度-位置受可用布局和对齐选项的限制。
- 当包含的控件大小不同时,它们可能会变得有问题或复杂——在这种情况下,它们可能被保持在最小值,或者它们可能使用嵌套容器。
7。结论
在本文中,我们研究了在图形面板设计中使用布局和容器的可能性。此方法使我们能够使用布局和对齐样式自动处理不同控件的位置。它可以使图形面板的设计更简单,在某些情况下,减少编码时间。
类cBox类是一个辅助控件,用作GUI面板中基本控件的容器。在本文中,我们已经演示了它的操作以及如何在实际应用中使用它。虽然精度低于绝对定位,但其精度水平在许多应用中仍能得到提高。
由MetaQuotes Software Corp.从英文翻译为
原文。https://www.mql5.com/en/articles/1867
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。