图形界面 VII: 表格控件 (第一章)·进阶篇
(2/3)· 文本标签、编辑框与绘制型表格三套类如何各司其职,多数自定义面板卡在滚动与锁定表头
◍ 表格控件的偏移与冻结接口
在 MT5 自定义表格控件里,先要把绘制偏移和首行首列冻结这两个基础能力摸透。XOffset 与 ColumnXOffset 分别接管整体横向偏移和列区域横向偏移,直接写私有成员 m_x_offset、m_column_x_offset,调用一次即生效。 首行首列锁定用重载方法实现读写分离:FixFirstRow() 无参版本返回 m_fix_first_row 布尔状态,带 bool 参数的版本改写该标志。FixFirstColumn 同理,两套接口让面板在滚动时可以选择钉住表头或首列。 CLabelsTable 继承 CElement 后,用 LTLabels 结构以 m_rows[] 嵌套存可见单元格对象,LTOptions 则单独存 m_vrows[] 文本与 m_colors[] 颜色,二者按下标对应。 行数列数有四档查询:RowsTotal / ColumnsTotal 拿逻辑总规模,VisibleRowsTotal / VisibleColumnsTotal 只返回当前可视区数量。实盘面板若只渲染可视区,这两组值差可能达到数十倍,务必分开用。 别把偏移当摆设 把 XOffset 设成图表宽度的 1/10(例如 1920 宽屏设 192),再开 FixFirstColumn(true),左列品种名在横向滚动报价表时就不会被挤出视野。外汇与贵金属波动剧烈,这类界面辅助仅降低盯盘失误率,不预示任何方向。
class="type">void XOffset(const class="type">int x_offset) { m_x_offset=x_offset; } class="type">void ColumnXOffset(const class="type">int x_offset) { m_column_x_offset=x_offset; } class=class="str">"cmt">//--- (class="num">1) 读取 和 (class="num">2) 设置第一行的锁定模式 class="type">bool FixFirstRow(class="type">void) const { class="kw">return(m_fix_first_row); } class="type">void FixFirstRow(const class="type">bool flag) { m_fix_first_row=flag; } class=class="str">"cmt">//--- (class="num">1) 读取 和 (class="num">2) 设置第一列的锁定模式 class="type">bool FixFirstColumn(class="type">void) const { class="kw">return(m_fix_first_column); } class="type">void FixFirstColumn(const class="type">bool flag) { m_fix_first_column=flag; } }; class CLabelsTable : class="kw">public CElement { class="kw">private: class=class="str">"cmt">//--- 表格可见部分的对象数组 class="kw">struct LTLabels { CLabel m_rows[]; }; LTLabels m_columns[]; class=class="str">"cmt">//--- 表格数值和属性的数组 class="kw">struct LTOptions { class="type">class="kw">string m_vrows[]; class="type">color m_colors[]; }; LTOptions m_vcolumns[]; class=class="str">"cmt">//--- class="kw">public: class=class="str">"cmt">//--- 返回 (class="num">1) 行 和(class="num">2) 列 的总数 class="type">int RowsTotal(class="type">void) const { class="kw">return(m_rows_total); } class="type">int ColumnsTotal(class="type">void) const { class="kw">return(m_columns_total); } class=class="str">"cmt">//--- 返回表格中可见部份的 (class="num">1) 行 和 (class="num">2) 列 的总数 class="type">int VisibleRowsTotal(class="type">void) const { class="kw">return(m_visible_rows_total); } class="type">int VisibleColumnsTotal(class="type">void) const { class="kw">return(m_visible_columns_total); } class=class="str">"cmt">//--- 设置 (class="num">1) 表格的大小 和 (class="num">2) 可见部份的大小
表格尺寸与可见区的数组初始化逻辑
在 MT5 里用 C++ 类封装报价面板时,表格的物理尺寸和可见尺寸要分开管。CLabelsTable 提供了两个 void 方法:TableSize 管后台全量行列数组,VisibleTableSize 管前台实际绘制的行列数,两者下限都被硬性锁死——列最少 1、行最少 2。 TableSize 先把传入值做三元判断兜底,再对 m_vcolumns 做 ArrayResize,然后按列数循环给每列的 m_vrows、m_colors 扩到对应行数,并用 ArrayInitialize 把颜色数组刷成默认文字色。VisibleTableSize 逻辑类似,但只扩 m_columns[i].m_rows,不碰颜色数组。 构造函数里顺手给了几个易忽略的默认参数:行高 m_row_y_size=18、左边距 m_x_offset=30、列间距 m_column_x_offset=60,背景色默认 clrWhiteSmoke。外汇与贵金属行情跳动快,把这些值直接写死可能在高分屏上挤压报价,建议开 MT5 把 18 和 60 改成变量传参验证显示效果。
class="type">void TableSize(const class="type">int columns_total,const class="type">int rows_total); class="type">void VisibleTableSize(const class="type">int visible_columns_total,const class="type">int visible_rows_total); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 设置表格的大小 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CLabelsTable::TableSize(const class="type">int columns_total,const class="type">int rows_total) { class=class="str">"cmt">//--- 必须至少有一列 m_columns_total=(columns_total<class="num">1) ? class="num">1 : columns_total; class=class="str">"cmt">//--- 必须至少有两行 m_rows_total=(rows_total<class="num">2) ? class="num">2 : rows_total; class=class="str">"cmt">//--- 设置列数组的大小 ::ArrayResize(m_vcolumns,m_columns_total); class=class="str">"cmt">//--- 设置行数组的大小 for(class="type">int i=class="num">0; i<m_columns_total; i++) { ::ArrayResize(m_vcolumns[i].m_vrows,m_rows_total); ::ArrayResize(m_vcolumns[i].m_colors,m_rows_total); class=class="str">"cmt">//--- 使用默认值初始化文字颜色数组 ::ArrayInitialize(m_vcolumns[i].m_colors,m_text_color); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 设置表格可见部份的大小 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CLabelsTable::VisibleTableSize(const class="type">int visible_columns_total,const class="type">int visible_rows_total) { class=class="str">"cmt">//--- 必须至少有一列 m_visible_columns_total=(visible_columns_total<class="num">1) ? class="num">1 : visible_columns_total; class=class="str">"cmt">//--- 必须至少有两行 m_visible_rows_total=(visible_rows_total<class="num">2) ? class="num">2 : visible_rows_total; class=class="str">"cmt">//--- 设置列数组的大小 ::ArrayResize(m_columns,m_visible_columns_total); class=class="str">"cmt">//--- 设置行数组的大小 for(class="type">int i=class="num">0; i<m_visible_columns_total; i++) ::ArrayResize(m_columns[i].m_rows,m_visible_rows_total); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 构造函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ CLabelsTable::CLabelsTable(class="type">void) : m_fix_first_row(class="kw">false), m_fix_first_column(class="kw">false), m_row_y_size(class="num">18), m_x_offset(class="num">30), m_column_x_offset(class="num">60), m_area_color(clrWhiteSmoke),
「标签表格类的私有结构与构造初始化」
在 MT5 自定义控件库里,CLabelsTable 继承自 CElement,用私有成员把表格拆成背景区、纵向/横向滚动条和可见标签数组三块。m_area 是 CRectLabel 背景,m_scrollv / m_scrollh 分别管垂直和水平滚动,m_columns[] 里套了 LTLabels 结构,每个结构挂一个 CLabel 类型的 m_rows[] 动态数组,这样就能按列→行二级索引填文字。 构造函数里先把文本色锁成 clrBlack,总行/列和可见行/列都设成 2 和 1(m_rows_total(2)、m_columns_total(1)、m_visible_rows_total(2)、m_visible_columns_total(1)),意味着这个实例默认就是 1 列 2 行的窄表。随后调 CElement::ClassName 存类标识,m_zorder=0 和 m_area_zorder=1 把背景压在鼠标响应层下面,再用 TableSize 与 VisibleTableSize 把逻辑尺寸和可视尺寸落地。 CreateLabels 方法从 CElement::X() 取基准 x,y 初始为 0,offset 按是否首列在 m_x_offset 与 m_column_x_offset 之间切。循环只跑可见列数(m_visible_columns_total),c>0 时才换用列间偏移,说明首列左缘和后续列左缘是分开算的——改这两个偏移量能直接调表格左边留白和列间距。外汇与贵金属图表挂这类自定义控件时波动跳变频繁,实盘前应在 MT5 策略测试器用历史 tick 验证重绘不会错位。
m_text_color(clrBlack), m_rows_total(class="num">2), m_columns_total(class="num">1), m_visible_rows_total(class="num">2), m_visible_columns_total(class="num">1) { class=class="str">"cmt">//--- 在基类中保存元件类的名称 CElement::ClassName(CLASS_NAME); class=class="str">"cmt">//--- 设置鼠标左键点击的优先级 m_zorder =class="num">0; m_area_zorder =class="num">1; class=class="str">"cmt">//--- 设置表格和它可见部分的大小 TableSize(m_columns_total,m_rows_total); VisibleTableSize(m_visible_columns_total,m_visible_rows_total); } class CLabelsTable : class="kw">public CElement { class="kw">private: class=class="str">"cmt">//--- 创建表格的对象 CRectLabel m_area; CScrollV m_scrollv; CScrollH m_scrollh; class=class="str">"cmt">//--- 表格可见部分的对象数组 class="kw">struct LTLabels { CLabel m_rows[]; }; LTLabels m_columns[]; class=class="str">"cmt">//--- class="kw">public: class=class="str">"cmt">//--- 返回滚动条的指针 CScrollV *GetScrollVPointer(class="type">void) const { class="kw">return(::GetPointer(m_scrollv)); } CScrollH *GetScrollHPointer(class="type">void) const { class="kw">return(::GetPointer(m_scrollh)); } class=class="str">"cmt">//--- 用于创建表格的方法 class="type">bool CreateLabelsTable(const class="type">long chart_id,const class="type">int subwin,const class="type">int x,const class="type">int y); class=class="str">"cmt">//--- class="kw">private: class="type">bool CreateArea(class="type">void); class="type">bool CreateLabels(class="type">void); class="type">bool CreateScrollV(class="type">void); class="type">bool CreateScrollH(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 创建文字标签的数组 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CLabelsTable::CreateLabels(class="type">void) { class=class="str">"cmt">//--- 坐标和偏移 class="type">int x =CElement::X(); class="type">int y =class="num">0; class="type">int offset =class="num">0; class=class="str">"cmt">//--- 列 for(class="type">int c=class="num">0; c<m_visible_columns_total; c++) { class=class="str">"cmt">//--- 计算表格偏移 offset=(c>class="num">0) ? m_column_x_offset : m_x_offset;
◍ 逐行铺标签的坐标与属性写入
在 MT5 面板里画一个多列表格,核心就是给每个单元格算好 X/Y 然后建对象。下面这段循环先把列偏移累加进 x,再按可见行数 m_visible_rows_total 逐个处理;r=0 时 Y 从表单顶向下 10 像素起,之后每行叠 m_row_y_size-1,避免行距过大。 对象名用程序名+列号 c+行号 r+元素 ID 拼出来,保证同图表多实例不重名。Create 失败直接 return(false),否则继续写 Description、字体、字号、颜色、锚点居中、不可选中、Z 序和空 tooltip。XGap/YGap 存的是相对表单左上角的偏移,方便后续整体移动时同步。
class=class="str">"cmt">//--- 计算X坐标 x=x+offset; class=class="str">"cmt">//--- 行 for(class="type">int r=class="num">0;r<m_visible_rows_total; r++) { class=class="str">"cmt">//--- 构建对象名称 class="type">class="kw">string name=CElement::ProgramName()+"_labelstable_label_"+(class="type">class="kw">string)c+"_"+(class="type">class="kw">string)r+"__"+(class="type">class="kw">string)CElement::Id(); class=class="str">"cmt">//--- 计算 Y 坐标 y=(r>class="num">0) ? y+m_row_y_size-class="num">1 : CElement::Y()+class="num">10; class=class="str">"cmt">//--- 创建对象 if(!m_columns[c].m_rows[r].Create(m_chart_id,name,m_subwin,x,y)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 设置属性 m_columns[c].m_rows[r].Description(m_vcolumns[c].m_vrows[r]); m_columns[c].m_rows[r].Font(FONT); m_columns[c].m_rows[r].FontSize(FONT_SIZE); m_columns[c].m_rows[r].Color(m_text_color); m_columns[c].m_rows[r].Corner(m_corner); m_columns[c].m_rows[r].Anchor(ANCHOR_CENTER); m_columns[c].m_rows[r].Selectable(class="kw">false); m_columns[c].m_rows[r].Z_Order(m_zorder); m_columns[c].m_rows[r].Tooltip("\n"); class=class="str">"cmt">//--- 与表单边缘的距离 m_columns[c].m_rows[r].XGap(x-m_wnd.X()); m_columns[c].m_rows[r].YGap(y-m_wnd.Y()); class=class="str">"cmt">//--- 坐标 m_columns[c].m_rows[r].X(x); m_columns[c].m_rows[r].Y(y); class=class="str">"cmt">//--- 保存对象指针 CElement::AddToArray(m_columns[c].m_rows[r]); } } class=class="str">"cmt">//--- class="kw">return(true); } class CLabelsTable : class="kw">public CElement { class="kw">public: class=class="str">"cmt">//--- 设置指定表格单元的值 class="type">void SetValue(const class="type">int column_index,const class="type">int row_index,const class="type">class="kw">string value); class=class="str">"cmt">//--- 取得指定表格单元的值 class="type">class="kw">string GetValue(const class="type">int column_index,const class="type">int row_index); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 设置指定索引的值 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CLabelsTable::SetValue(const class="type">int column_index,const class="type">int row_index,const class="type">class="kw">string value) { class=class="str">"cmt">//--- 检查是否超出了列的范围 class="type">int csize=::ArraySize(m_vcolumns); if(csize<class="num">1 || column_index<class="num">0 || column_index>=csize) class="kw">return; class=class="str">"cmt">//--- 检查是否超出了行的范围 class="type">int rsize=::ArraySize(m_vcolumns[column_index].m_vrows); if(rsize<class="num">1 || row_index<class="num">0 || row_index>=rsize)
class=class="str">"cmt">//--- 计算X坐标 x=x+offset; class=class="str">"cmt">//--- 行 for(class="type">int r=class="num">0;r<m_visible_rows_total; r++) { class=class="str">"cmt">//--- 构建对象名称 class="type">class="kw">string name=CElement::ProgramName()+"_labelstable_label_"+(class="type">class="kw">string)c+"_"+(class="type">class="kw">string)r+"__"+(class="type">class="kw">string)CElement::Id(); class=class="str">"cmt">//--- 计算 Y 坐标 y=(r>class="num">0) ? y+m_row_y_size-class="num">1 : CElement::Y()+class="num">10; class=class="str">"cmt">//--- 创建对象 if(!m_columns[c].m_rows[r].Create(m_chart_id,name,m_subwin,x,y)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 设置属性 m_columns[c].m_rows[r].Description(m_vcolumns[c].m_vrows[r]); m_columns[c].m_rows[r].Font(FONT); m_columns[c].m_rows[r].FontSize(FONT_SIZE); m_columns[c].m_rows[r].Color(m_text_color); m_columns[c].m_rows[r].Corner(m_corner); m_columns[c].m_rows[r].Anchor(ANCHOR_CENTER); m_columns[c].m_rows[r].Selectable(class="kw">false); m_columns[c].m_rows[r].Z_Order(m_zorder); m_columns[c].m_rows[r].Tooltip("\n"); class=class="str">"cmt">//--- 与表单边缘的距离 m_columns[c].m_rows[r].XGap(x-m_wnd.X()); m_columns[c].m_rows[r].YGap(y-m_wnd.Y()); class=class="str">"cmt">//--- 坐标 m_columns[c].m_rows[r].X(x); m_columns[c].m_rows[r].Y(y); class=class="str">"cmt">//--- 保存对象指针 CElement::AddToArray(m_columns[c].m_rows[r]); } } class=class="str">"cmt">//--- class="kw">return(true); } class CLabelsTable : class="kw">public CElement { class="kw">public: class=class="str">"cmt">//--- 设置指定表格单元的值 class="type">void SetValue(const class="type">int column_index,const class="type">int row_index,const class="type">class="kw">string value); class=class="str">"cmt">//--- 取得指定表格单元的值 class="type">class="kw">string GetValue(const class="type">int column_index,const class="type">int row_index); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 设置指定索引的值 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CLabelsTable::SetValue(const class="type">int column_index,const class="type">int row_index,const class="type">class="kw">string value) { class=class="str">"cmt">//--- 检查是否超出了列的范围 class="type">int csize=::ArraySize(m_vcolumns); if(csize<class="num">1 || column_index<class="num">0 || column_index>=csize) class="kw">return; class=class="str">"cmt">//--- 检查是否超出了行的范围 class="type">int rsize=::ArraySize(m_vcolumns[column_index].m_vrows); if(rsize<class="num">1 || row_index<class="num">0 || row_index>=rsize)
表格单元的读取与刷新逻辑
在 CLabelsTable 里,GetValue 先做边界检查再返回值:用 ArraySize 拿到列数与行数,若 column_index 或 row_index 越界直接返回空串。这套防御写法能避免 MT5 面板在数据源抖动时抛数组越界错误。 TextColor 同理,先核列再核行,通过才写 m_colors[row_index]。想让某行报价变红提示,就在这里传 clrRed,不用重绘整个表格。 UpdateTable 是刷新入口:固定表头时 t、l 偏移 1,再取双向滚动条 CurrentPos 算可视起点。下例里 h、v 就是横向、纵向实际读的单元索引,m_fix_first_column 为真时还会单独同步第 0 列第 0 格的 Description。 外汇与贵金属标的波动快,面板刷新频率高,这段逻辑若越界保护漏写,可能在跳空时让 EA 界面卡死,属高风险操作环境,参数需实盘前在策略测试器验证。
class="type">class="kw">string CLabelsTable::GetValue(const class="type">int column_index,const class="type">int row_index) { class=class="str">"cmt">//--- 检查是否超出了列的范围 class="type">int csize=::ArraySize(m_vcolumns); if(csize<class="num">1 || column_index<class="num">0 || column_index>=csize) class="kw">return(""); class=class="str">"cmt">//--- 检查是否超出了行的范围 class="type">int rsize=::ArraySize(m_vcolumns[column_index].m_vrows); if(rsize<class="num">1 || row_index<class="num">0 || row_index>=rsize) class="kw">return(""); class=class="str">"cmt">//--- 返回值 class="kw">return(m_vcolumns[column_index].m_vrows[row_index]); } class="type">void CLabelsTable::TextColor(const class="type">int column_index,const class="type">int row_index,const class="type">color clr) { class=class="str">"cmt">//--- 检查是否超出了列的范围 class="type">int csize=::ArraySize(m_vcolumns); if(csize<class="num">1 || column_index<class="num">0 || column_index>=csize) class="kw">return; class=class="str">"cmt">//--- 检查是否超出了行的范围 class="type">int rsize=::ArraySize(m_vcolumns[column_index].m_vrows); if(rsize<class="num">1 || row_index<class="num">0 || row_index>=rsize) class="kw">return; class=class="str">"cmt">//--- 设置颜色 m_vcolumns[column_index].m_colors[row_index]=clr; } class="type">void CLabelsTable::UpdateTable(class="type">void) { class=class="str">"cmt">//--- 如果启用了固定表头模式,偏移一个索引 class="type">int t=(m_fix_first_row) ? class="num">1 : class="num">0; class="type">int l=(m_fix_first_column) ? class="num">1 : class="num">0; class=class="str">"cmt">//--- 取得垂直和水平滚动条滑块的当前位置 class="type">int h=m_scrollh.CurrentPos()+l; class="type">int v=m_scrollv.CurrentPos()+t; class=class="str">"cmt">//--- 在左侧列中移动表头 if(m_fix_first_column) { m_columns[class="num">0].m_rows[class="num">0].Description(m_vcolumns[class="num">0].m_vrows[class="num">0]); class=class="str">"cmt">//--- 行 for(class="type">int r=t; r<m_visible_rows_total; r++)
「滚动偏移下的单元格重绘逻辑」
表格在响应鼠标移动事件时,先通过 CHARTEVENT_MOUSE_MOVE 拿到光标的 x、y 以及左键状态,再交给 ScrollBarControl 判断是否触动了垂直或水平滑轨;任一滑轨生效就调用 UpdateTable() 重算视口。 重绘的核心是把滚动条滑块位置换算成虚拟行列下标:水平方向 h = m_scrollh.CurrentPos() + l,垂直方向 v = m_scrollv.CurrentPos() + t。这两个偏移量直接决定了可视区里每一个 m_columns[c].m_rows[r] 该绑定哪一条 m_vcolumns[h].m_vrows[v] 数据。 如果开启了首行固定(m_fix_first_row),会在双重循环之前单独把第 0 行表头按当前水平偏移 h 重写一次,避免滚动时列标题错乱。外层循环跑列、内层循环跑行,当 v 与 h 都落在有效范围内才执行颜色与描述的同步,否则该格保持空白。 在 MT5 里把这段接进自己的 CLabelsTable 派生类,拖动滚动条时应能看到数据随滑块位置无缝平移;若发现首行表头被覆盖,优先检查 m_fix_first_row 分支里的 h++ 是否漏了边界判断。
{
if(r>=t && r<m_rows_total)
m_columns[class="num">0].m_rows[r].Description(m_vcolumns[class="num">0].m_vrows[v]);
class=class="str">"cmt">//---
v++;
}
}
class=class="str">"cmt">//--- 在顶部行中移动表头
if(m_fix_first_row)
{
m_columns[class="num">0].m_rows[class="num">0].Description(m_vcolumns[class="num">0].m_vrows[class="num">0]);
class=class="str">"cmt">//--- 列
for(class="type">int c=l; c<m_visible_columns_total; c++)
{
if(h>=l && h<m_columns_total)
m_columns[c].m_rows[class="num">0].Description(m_vcolumns[h].m_vrows[class="num">0]);
class=class="str">"cmt">//---
h++;
}
}
class=class="str">"cmt">//--- 取得水平滚动条滑块的当前位置
h=m_scrollh.CurrentPos()+l;
class=class="str">"cmt">//--- 列
for(class="type">int c=l; c<m_visible_columns_total; c++)
{
class=class="str">"cmt">//--- 取得垂直滚动条滑块的当前位置
v=m_scrollv.CurrentPos()+t;
class=class="str">"cmt">//--- 行
for(class="type">int r=t; r<m_visible_rows_total; r++)
{
class=class="str">"cmt">//--- 移动表格数据
if(v>=t && v<m_rows_total && h>=l && h<m_columns_total)
{
class=class="str">"cmt">//--- 颜色的调整
m_columns[c].m_rows[r].Color(m_vcolumns[h].m_colors[v]);
class=class="str">"cmt">//--- 值的调整
m_columns[c].m_rows[r].Description(m_vcolumns[h].m_vrows[v]);
v++;
}
}
class=class="str">"cmt">//---
h++;
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 事件处理函数 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CLabelsTable::OnEvent(const class="type">int id,const class="type">long &lparam,const class="type">class="kw">double &dparam,const class="type">class="kw">string &sparam)
{
class=class="str">"cmt">//--- 处理鼠标光标移动事件
if(id==CHARTEVENT_MOUSE_MOVE)
{
class=class="str">"cmt">//--- 如果元件是隐藏的就退出
if(!CElement::IsVisible())
class="kw">return;
class=class="str">"cmt">//--- 鼠标左键的坐标和状态
class="type">int x=(class="type">int)lparam;
class="type">int y=(class="type">int)dparam;
m_mouse_state=(class="type">bool)class="type">int(sparam);
class=class="str">"cmt">//--- 检查表格的焦点
CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2());
class=class="str">"cmt">//--- 如果滑轨管理被启用,移动列表
if(m_scrollv.ScrollBarControl(x,y,m_mouse_state) || m_scrollh.ScrollBarControl(x,y,m_mouse_state))
UpdateTable();
class=class="str">"cmt">//---
class="kw">return;
}
class=class="str">"cmt">//--- 处理对象的点击