文章 "图形界面 X: 时间控件, 复选框列表控件和表格排序 (集成编译 6)" - 页 2

 

不过,在我看来,最近关于通过从窗体边缘偏移来指定坐标的更改对最终用户并不十分友好。如果说以前用户可以安全地使用两个选项来指定窗口中对象的位置,那么现在其中一个选项变得更加复杂了。我曾遇到过这样的情况,屏幕上到处都是。

  1. 以前,用户可以指定对象的 x、y 坐标,作为从窗口窗体任意边缘的偏移量。为此,只需指定该窗口的初始坐标加上/减去以像素为单位的偏移量即可。而现在,用户只能从窗体的左边缘开始,这就减少了从窗体或对象的任意边缘指定精确坐标或偏移坐标(以像素为单位)的可能性。
  2. 如果以前用户可以不问计算偏移量的对象的大小,从父对象的任意边缘按要求的大小偏移放置一个新对象,那么现在每次改变新对象的大小时,用户都必须重新计算新对象的坐标。
    例如
    按钮 1 - 其 x 坐标距离窗体窗口左边缘4 像素 。设置如下: x=m_window.X()+4; 按钮 1的大小 为 43 像素宽。按钮 1 的大小为 43像素 宽,其右侧边缘可通过以下方式获得: m_butt1.X2();
    Button2 - 其坐标距离按钮 1 的右侧边缘2 像素 。设置如下:x=m_butt1.X2()+2; 按钮 2 的大小为 24 像素宽。按钮 2 的大小为 24像素 宽,其右侧边缘可通过以下方式获得: m_butt2.X2();
    Button3 - 其坐标距离按钮 2 的右侧边缘4 像素 。设置如下: x=m_butt2.X2()+4; 按钮 3 的大小 为 18 像素宽。它的右边缘可以如下获得:m_butt3.X2();
    垂直分隔条 距离按钮 3 的右边缘4 像素x=m_butt3.X2()+4;
    因此,我们可以轻松改变三个按钮中任何一个的大小,它们都将位于我们设置的偏移量处,而分隔条将整齐地放置在我们需要的按钮 3 的偏移量处。
  3. 现在,让我们看看根据新规则构建界面时需要做些什么:
    Button1: x_gap1=4;
    Button2:x_gap2=4+m_butt1.XSize()+2;
    Button3:x_gap3=4+m_butt1.XSize()+2+m_butt2.XSize()+4;
    R-Strip:x_gap4=4+m_butt1.XSize()+2+m_butt2.XSize()+4+m_butt3.XSize()+4;
    这里建议指定它们的 X2() 坐标,而不是指定对象的大小,但事实并非如此--一切都一塌糊涂(例如,我在猜测 Y2() 并未指向正确的 Y 坐标位置时摔坏了脑袋)。

让我们看看:我设置的菜单坐标水平缩进为 1 像素,缩进量与主窗口 "页眉 "的大小相当。菜单高度为 18 像素。

//--- 创建主窗口菜单
   x=m_window_main.X()+1;
   y=m_window_main.Y()+m_window_main.CaptionHeight();
   h=18;

菜单已就位:


现在,我需要将表格偏离菜单的底边。如果表格窗口的 CaptionHeight() 为 18 像素,菜单高度也是 18 像素,那么表格的垂直偏移量应为 36 像素。我们有两种选择:一种是用计算器计算所需的所有偏移量(并在随后调整尺寸时重新计算),另一种是尝试获取这些偏移量。当然,我们选择第二种,并进行尝试:对于表格来说,y_gap 应该等于主窗口标题的高度加上菜单的高度:m_window_main.CaptionHeight()+m_menu_main.YSize();

   //--- 字符表
   x=2;
   y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   w=140;

编译,获取:


标签已就位。但是。在这种情况下,菜单的 Y2() 值不是应该与我们计算的值一致吗?毕竟,不收集彼此相邻的所有对象的尺寸,而只获取它们的坐标会更方便,不是吗?让我们试试看--将坐标计算改为获取坐标:

   //--- 字符表
   x=2;
   //y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   y=m_menu_main.Y2()+1;
   w=140;

编译,获取:


Y2() 不应该返回我们之前计算的 Y2 吗?m_window_main.CaptionHeight()+m_menu_main.YSize()+1;

所以不应该--现在我们不指定从窗口任何边缘偏移的坐标,而只指定从顶部或左侧偏移的坐标。 很好。从得到的 Y2() 坐标中去掉窗体窗口顶部的 Y() 坐标,得到窗体窗口顶部的偏移值:

   //--- 字符表
   x=2;
   //y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   y=m_menu_main.Y2()-m_window_main.Y()+1;
   w=140;

编译并得到我们想要的结果:


那么,对于相距两个像素的 20 个对象,我们是否应该始终将它们的尺寸加上彼此的偏移量,以获得最后一个对象的正确偏移量?毕竟,第 19 个对象的 Y2()(以前只有加上两个点的偏移量才能建立第 20 个对象)现在并没有给我们一个起点,返回的结果也不是我们所期望的....。我们必须将所有对象的尺寸相加才能得到所需的偏移量,或者在创建每个对象之前,根据其 Y2() 坐标和窗体窗口的初始 Y() 坐标计算其偏移量。

我不明白。阿纳托利,请给我一个解决方案。我做错了什么?为什么会这样?为什么说现在更友好了?如果以前您只需指定从窗体窗口任意边缘、该窗口中任意对象、甚至图表边缘的偏移量,就可以创建所需的对象,那么现在您必须跳手鼓舞来计算它的坐标,然后才能创建它?难道编写更多代码和进行不同的计算会更好、更方便吗?

我知道你是作者,你知道得更多。但你仍然让别人使用它。我(仅代表个人观点)认为,这项创新是唯一一个将其作为更新推出的错误决定--它变得更糟了。它不再像以前那样具有简单的界面构建功能。

如果我冒犯了您,我深表歉意(过去,带来坏消息的信使会被砍头)。

 
Artyom Trishkin:

...

如果冒犯了您,我很抱歉。

我稍后再回复你,但在此之前,我想先澄清一下,你说的这句话对我来说是什么意思。什么是 "坏消息",在这里谁是 "信使"?:)

 
Anatoli Kazharski:

我稍后再回答,但在此之前,我想先澄清一下你用这句话说我是什么意思。什么是 "坏消息",在这里谁是 "信使"?:)

:)

坏消息 "是指我对创新的态度,即您不能再将任意坐标作为对象坐标参考点,而只能将窗体的左边缘和上边缘作为对象坐标参考点(我在本地添加了重新计算坐标的功能--不必要的计算)....。当然,送信的人是我,因为我不喜欢并说了出来:)

 
Artyom Trishkin:

:)

坏消息是我对创新的态度,即现在您不能将任意坐标作为对象的坐标参考点,而只能将窗体的左边缘和上边缘作为对象的坐标参考点(我在本地添加了重新计算坐标的功能--不必要的计算)....。当然,送信的人是我,因为我不喜欢并说了出来:)

就这样吧。很好。因为我还以为我理解错了。)

以下是我的回复:

Artyom Trishkin:

不过,我认为最近关于从窗体边缘偏移指定坐标的更改对最终用户不太友好。如果说以前用户可以安全地使用两个选项来指定对象在窗口中的位置,那么现在其中一个选项变得更加复杂了。我曾遇到过这样的情况,屏幕上到处都是裂开的东西。

...

现在,让我们看看根据新规则构建界面时需要做些什么:

按钮 1:x_gap1=4
Button2:x_gap2=4+m_butt1.XSize()+2;
Button3:x_gap3=4+m_butt1.XSize()+2+m_butt2.XSize()+ 4
R Strip:x_gap4=4+m_butt1.XSize()+2+m_butt2.XSize()+ 4+m_butt3.XSize()+4

...

与其指定对象的大小,不如指定它们的 X2() 坐标,但事实并非如此--一切都会一拍即合(例如,在我意识到 Y2() 并未指向 Y 坐标的正确位置之前,我的头都要摔破了)。

其实很简单。以前,创建元素时必须向方法传递绝对 坐标。它们必须相对于连接元素的窗体的最左侧点进行计算。

下面是我们之前必须做的一个示例:

//+------------------------------------------------------------------+
//|| 创建按钮|
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton(const int x_gap,const int y_gap,const string button_text)
  {
//--- 保存指向表单的指针
   m_simple_button.WindowPointer(m_window);
//--- 坐标
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;

//--- 创建前设置属性
   m_simple_button.ButtonXSize(100);
//--- 创建一个按钮
   if(!m_simple_button.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y))
      return(false);
//--- 添加指向基本元素的指针
   CWndContainer::AddToElementsArray(0,m_simple_button);
   return(true);
  }

//---

现在不需要计算任何东西,只要传递相对 坐标就可以了。

现在需要做的示例

//+------------------------------------------------------------------+
//|| 创建按钮|
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton(const int x_gap,const int y_gap,const string button_text)
  {
//--- 传递面板对象
   m_simple_button.WindowPointer(m_window);
//--- 创建前设置属性
   m_simple_button.ButtonXSize(100);
//--- 创建一个按钮
   if(!m_simple_button.CreateSimpleButton(m_chart_id,m_subwin,button_text,x_gap,y_gap))
      return(false);
//--- 添加指向基本元素的指针
   CWndContainer::AddToElementsArray(0,m_simple_button);
   return(true);
  }

//---

在你上面的例子中,你计算的是绝对 坐标。愚蠢的决定,错误的结论,匆忙地公之于众。

CElement::X()、CElement: :X2()、CElement: :Y() 和 CElement:: Y2() 方法返回元素边界的绝对 坐标。如果有任何疑问,只需将这些 输出 终端日志 即可,非常容易理解。

下面是一个计算相对于某个元素的缩进(相对 坐标)的示例。这里有两个按钮。第二个按钮的X(相对)坐标是相对于第一个按钮的右边界计算的。

//+------------------------------------------------------------------+
//|| 创建程序的图形用户界面
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- 创建面板
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
   if(!CreateSimpleButton1(7,25,"BUTTON 1"))
      return(false);
   if(!CreateSimpleButton2(m_simple_button1.X2()-m_window.X()+5,25,"BUTTON 2"))
      return(false);
//---
   return(true);
  }

//---

因此,第二个按钮将从第一个按钮的右边界开始缩进 5 个像素:

//---

通过改变第一个按钮的宽度或 X 坐标,第二个按钮的位置将自动调整。

因此,不必要的计算只出现在上面引用的例子中。

 
Anatoli Kazharski:

//+------------------------------------------------------------------+
//|| 创建程序的图形用户界面
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- 创建面板
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
   if(!CreateSimpleButton1(7,25,"BUTTON 1"))
      return(false);
   if(!CreateSimpleButton2(m_simple_button1.X2()-m_window.X()+5,25,"BUTTON 2"))
      return(false);
//---
   return(true);
  }

//---

因此,第二个按钮将从第一个按钮的右边界开始缩进 5 个像素:

//---

通过改变第一个按钮的宽度或 X 坐标,第二个按钮的位置将自动调整。

因此,额外的计算只出现在上面引用的例子中。

这里变得不是很清楚:

if(!CreateSimpleButton2(m_simple_button1.X2()-m_window.X()+5,25,"BUTTON 2"))

我必须在我的函数中加入这种重新计算,而不是在指定一个新创建对象的坐标时到处都写这种重新计算,而是像以前一样继续写--这样可以更清楚地看到一个对象与另一个对象的关系。当然,这取决于每个人的品味和喜好...

顺便说一句,即使像你说的那样指定坐标,文件导航器也会失灵。但也许只是我的问题。我要去找找原因。

MetaTrader 交易平台截图

EURUSD, D1, 2016.12.12

MetaQuotes Software Corp., MetaTrader 5, 演示版

EURUSD, D1, 2016.12.12, MetaQuotes Software Corp., MetaTrader 5, 演示版


 

阿纳托利,看来你忘了编辑 FileNavigator.mqh 中的

//+------------------------------------------------------------------+
//| 创建树形列表|
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder_w10.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file_w10.bmp"
//---
bool CFileNavigator::CreateTreeView(void)
  {
//--- 保存窗口指针
   m_treeview.WindowPointer(m_wnd);
//--- 设置属性
   m_treeview.Id(CElement::Id());
   m_treeview.ResizeListAreaMode(true);
   m_treeview.TreeViewAreaWidth(m_treeview_area_width);
   m_treeview.ContentAreaWidth(m_content_area_width);
   m_treeview.AutoXResizeMode(CElement::AutoXResizeMode());
   m_treeview.AutoXResizeRightOffset(CElement::AutoXResizeRightOffset());
   m_treeview.AnchorRightWindowSide(m_anchor_right_window_side);
   m_treeview.AnchorBottomWindowSide(m_anchor_bottom_window_side);
//--- 形成树形列表数组
   int items_total=::ArraySize(m_g_item_text);
   for(int i=0; i<items_total; i++)
     {
      //-- 为项目(文件夹/文件)定义图片
      string icon_path=(m_g_is_folder[i])? m_folder_icon : m_file_icon;
      //--- 如果这是一个文件夹,则删除字符串中的最后一个字符('\')。
      if(m_g_is_folder[i])
         m_g_item_text[i]=::StringSubstr(m_g_item_text[i],0,::StringLen(m_g_item_text[i])-1);
      //--- 在树形列表中添加一个项目
      m_treeview.AddItem(i,m_g_prev_node_list_index[i],m_g_item_text[i],icon_path,m_g_item_index[i],
                         m_g_node_level[i],m_g_prev_node_item_index[i],m_g_items_total[i],m_g_folders_total[i],false,m_g_is_folder[i]);
     }
//--- 创建树状列表
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x-1,m_y+m_address_bar_y_size-1))
      return(false);
//-- 保存导航仪的尺寸
   CElement::XSize(m_treeview.XSize());
   CElement::YSize(m_treeview.YSize()+m_address_bar_y_size);
   return(true);
  }
//+------------------------------------------------------------------+

这里您需要添加它:

//--- 创建树状列表
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x-m_wnd.X()-1,m_y-m_wnd.Y()+m_address_bar_y_size-1))
      return(false);
 
Artyom Trishkin:

...

顺便说一下,即使像你说的那样指定了坐标,文件导航器也会崩溃。但也许只是我的问题。我去找了找原因。

我自己也没找到原因:

 
Anatoli Kazharski:

我自己还没复制过:

请检查最新更新中的 FileNavigator.mqh。
 
Artyom Trishkin:
检查最新更新中的 FileNavigator.mqh。
这是最新版本。
 
Anatoli Kazharski:
这是最新的。

我指的是网站上的文章--也许有一个旧的钉在更新里。我从最新更新中提取了文件,从文章底部的网站下载了压缩包。

在这里,我做了上面所写的更正:

MetaTrader 交易平台截图

EURUSD, W1, 2016.12.12

MetaQuotes Software Corp., MetaTrader 5, 演示版

EURUSD, W1, 2016.12.12, MetaQuotes Software Corp., MetaTrader 5, 演示版