王朝网络
分享
 
 
 

VC6.0中基于MSFlexGrid的可编辑表格的制作及其若干问题的解决方案

王朝vc·作者佚名  2006-01-31
宽屏版  字体: |||超大  

VC6.0中基于MSFlexGrid的可编辑表格

的制作及其若干问题的解决方案

一、 概述

在我们制作的用户界面中很多时候会用到表格,当然我们首先想到的是用控件,如MSFlexGrid。我们可以方便的调用控件自身的函数来对表格中的元素进行操作,但是如果要设计一个可以编辑的表格,我们该怎么办呢?事实上这种可编辑表格的应用还真的不少,主要是其用户操作的交互性较好。笔者在前一阶段开发项目时遇到了这个问题,以下介绍笔者的实现方案。

二、 可编辑表格的初步实现

1、 创建新类CCtrlEditGrid

首先创建一个单文档工程EditGrid。

接着在工程中加入MSFlexGrid控件。这是个ActiveX控件,选择AddToProject的Components and Controls Gallery选项可加入该控件。

然后以MSFlexGrid为基类创建新类CCtrlEditGrid,并添加成员函数void InitGrid()(该函数目前只是空的)和成员变量 CEdit* m_pEdit;

CSpinButtonCtrl* m_pSpinButtonCtrl;以后表格的实体类就是该类。

2、 在工程文件的视图类中显示表格

首先在视图类CEditGridView中添加成员变量CCtrlEditGrid* m_pCtrlEditGrid。

接着添加CEditGridView的消息相应函数OnCreate,在其中创建表格

m_pCtrlEditGrid = new CCtrlEditGrid;

m_pCtrlEditGrid->Create(NULL,WS_CHILD|WS_VISIBLE,CRect(0,0,0,0),this,ID_EDITGRID);

m_pCtrlEditGrid->InitGrid ( );

然后为了和视图的大小保持一致在CEditGridView的消息相应函数OnSize中添加代码

if ( m_pCtrlEditGrid != NULL )

m_pCtrlEditGrid->MoveWindow(0,0,cx,cy);

3、 实现CCtrlEditGrid的InitGrid的函数

InitGrid完成表格的属性设置,表格初始内容的填写,可编辑控件的创建。这里的可编辑控件如CEdit,CComboBox,CSpinButtonCtrl,CDateTimeCtrl……。在本例中只使用CEdit和CSinButtonCtrl的结合这一种。如果表格中不同列之间的编辑控件不同,在程序中可以通过检测列号,来决定使用什么控件,事实上在笔者的项目中不同列之间也是使用不同编辑控件的。在此用一种控件来说明表格编辑的实现方法,读者想换其他的控件也很容易了。

void CCtrlEditGrid::InitGrid()

{

//设置行数,列数

SetCols( COL_INITNUMBER );

SetRows( ROW_INITNUMBER );

//设置为无边框

SetBorderStyle(0);

//设置为可以改变行高列宽

SetAllowUserResizing(3);

//设置行宽列宽

CDC* pDC = GetDC();

SetRowHeightMin ( ( long )( ROW_HEIGHT_PIXEL * 1440.0 / pDC->GetDeviceCaps(LOGPIXELSY)) );//坐标单位要转换

for ( int i = 0; i < COL_INITNUMBER; i++ )

SetColWidth ( i,( long ) ( COL_WIDTH_INDEX_PIXEL * 1440.0 / pDC->GetDeviceCaps(LOGPIXELSX) ) );

ReleaseDC(pDC);

//设置列的文字对齐方式

for ( i = 0 ; i < COL_INITNUMBER ; i ++ )

SetColAlignment ( i, 4 );

//设置固定行列的名称

for ( i = 1; i < ROW_INITNUMBER; i ++ )

{

CString strNum;

int nNum = i;

strNum.Format ("%d", i);

SetTextMatrix (i, 0, strNum );

}

for ( i = 1; i < COL_INITNUMBER; i ++ )

{

CString strNum;

int nNum = i;

strNum.Format ("%d", i);

SetTextMatrix (0, i, strNum );

}

//填充Grid中的原始内容

for ( i = 1; i < ROW_INITNUMBER; i ++ )

{

for ( int j = 1; j < COL_INITNUMBER; j ++ )

{

SetTextMatrix (i, j, "1" );

}

}

//创建控件

m_pEdit = new CEdit();

m_pEdit->Create(WS_CHILD|WS_BORDER|ES_AUTOHSCROLL|ES_NUMBER,CRect(0,0,0,0),this,ID_CTRL_EDIT);

m_pSpinButtonCtrl = new CSpinButtonCtrl();

m_pSpinButtonCtrl->Create (UDS_ARROWKEYS | UDS_SETBUDDYINT | UDS_ALIGNRIGHT | WS_BORDER ,

CRect(0,0,0,0),this,ID_CTRL_SPIN);

这里要解释说明的是MSFLEXGRID中的单位是缇(1缇=1/1440in),所以在我们要指定单元格的长宽等时,要将像素单位转换为缇。如ROW_HEIGHT_PIXEL * 1440.0 / pDC->GetDeviceCaps(LOGPIXELSY)。其中ROW_HEIGHT_PIXEL是一个表示行高的像素单位的宏,pDC->GetDeviceCaps(LOGPIXELSY)得到Y轴每inch的像素值。该式计算后的值就是相应的以缇为单位的值。

4、 让表格可以编辑

以上三点只是准备阶段,要想使表格编辑,我们还要响应用户的点击单元格事件和离开单元格事件,以使得当用户点击某一单元格时当前单元格处于编辑状态而离开时又处于非编辑状态。MSFLEXGRID控件提供的OnClick和OnLeaveCell事件正好是我们所需要的。由于CCtrlEditGrid不是MFC类,所以不能用类向导来添加事件。只好用手工添加了。

首先在头文件中添加afx_msg void OnLeaveCell();afx_msg void OnClick();接着在CPP文件中添加事件映射表

BEGIN_EVENTSINK_MAP(CCtrlEditGrid, CMSFlexGrid)

//{{AFX_EVENTSINK_MAP(CEditGrid)

ON_EVENT_REFLECT(CCtrlEditGrid, 72 /* LeaveCell */, OnLeaveCell, VTS_NONE)

ON_EVENT_REFLECT(CCtrlEditGrid, -600 /* Click */, OnClick, VTS_NONE)

//}}AFX_EVENTSINK_MAP

END_EVENTSINK_MAP()

(如果用户觉得手工添加时间映射表有困难,可以先在应用程序中添加一个虚设对话框。接着在对话框中插入MSFlexGrid控件。然后使用ClassWizard将事件处理程序写入对话框,接下来就可以参照着对话框编写事件映射表了。记得最后要删除虚设对话框。)

接着添加OnLeaveCell和OnClick的函数体。

OnLeaveCell函数:如果现在m_pEdit是显示的,则说明单元格是在编辑状态,所以要将数据从m_pEdit框读到表格中,然后将m_pEdit和m_pSpinButtonCtrl隐藏。

void CCtrlEditGrid::OnLeaveCell()

{

if ( m_pEdit->IsWindowVisible() )

{

int nCol;

int nRow;

CString strContent;

nCol = GetCol();

nRow = GetRow();

m_pEdit->GetWindowText(strContent);

SetTextMatrix(nRow, nCol, strContent);

m_pEdit->ShowWindow(SW_HIDE);

m_pSpinButtonCtrl->ShowWindow(SW_HIDE);

}

}

OnClick函数:要在点击的单元格中显示m_pEdit和m_pSpinButtonCtrl,,并使输入焦点在m_pEdit中,这里要说明的一点是在计算编辑控件要显示的位置时,如果FlexGrid控件有边框,就应该考虑边框宽度对位置的影响,在本例中我们在InitGrid中设置为无边框,故不用考虑。

void CCtrlEditGrid::OnClick()

{

CDC* pDC = GetDC();

long x = ( GetCellLeft() * pDC -> GetDeviceCaps ( LOGPIXELSX ) ) / 1440;

long y = ( GetCellTop() * pDC -> GetDeviceCaps ( LOGPIXELSY ) ) / 1440;

long cx = ( GetCellWidth() * pDC -> GetDeviceCaps ( LOGPIXELSX ) ) / 1440;

long cy = ( GetCellHeight() * pDC -> GetDeviceCaps ( LOGPIXELSY ) )/ 1440;

ReleaseDC ( pDC );

CString strContent;

strContent = GetText();

m_pEdit->SetWindowText(strContent);

m_pEdit->MoveWindow(x,y,cx,cy,FALSE);

m_pEdit->ShowWindow(SW_SHOW);

m_pEdit->SetFocus ( );

m_pSpinButtonCtrl->SetBuddy (m_pEdit);

m_pSpinButtonCtrl->SetRange32( 0, 100);

m_pSpinButtonCtrl->MoveWindow ( x + cx – 16, y, 16,cy,FALSE );

m_pSpinButtonCtrl->ShowWindow(SW_SHOW);

}

也许OnEnterCell事件可以替代OnClick,但笔者发现用OnEnterCell实现起来会有一个问题:必须快速的点击,否则编辑框出现之后马上消失。所以笔者使用OnClick事件,该事件是在鼠标Up的时候才响应的。

三、 若干问题的出现及解决方案

通过以上的操作,我们可以在表格中点击某一个单元格进行编辑了,似乎我们已经实现了一个可编辑表格的制作。但在随后的测试过程中发现了如下讨厌的问题:

a在当前某个单元格处于可编辑状态而我们试图改变列宽时,发现在单元格上的编辑控件的大小并没有改变。如下

b在当前某个单元格处于可编辑状态而我们试图移动滚动条时,发现在单元格上的编辑控件的光标随之移动到了别的单元格。而且更加严重的是若点击前单元格部分显示,点击后再移动滚动条发现不只是光标移动还有控件本身也移动到了别的单元格中。如下

c在点击上下控件时会触发垂直滚动条的移动,还间接导致b问题的发生。

1、 a问题的解决

首先想到的是在MSDN中寻找MSFLEXGRID的列宽改变的响应事件,很失望没找到。但发现可以采取以下措施:在CEditGridView中的PreTranslateMessage消息响应函数中捕捉鼠标左键是按下状态并且鼠标移动的消息,在这种状态下若发现编辑控件是显示的,就调用CCtrlEditGrid的Onleave函数(开放为PUBLIC)。这样虽然在改变列宽时原来的编辑状态变为了非编辑状态,但避免了显示上不同步改变大小的问题。

BOOL CEditGridView::PreTranslateMessage(MSG* pMsg)

{

if ( ( pMsg->message == WM_MOUSEMOVE ) && ( pMsg->wParam & MK_LBUTTON ) )

{

CWnd* pWnd = FromHandle ( pMsg->hwnd );

if ( pWnd->GetRuntimeClass ( )->IsDerivedFrom ( RUNTIME_CLASS ( CMSFlexGrid ) ) )

{

if ( m_pCtrlEditGrid->m_pEdit->IsWindowVisible() )

m_pCtrlEditGrid->OnLeaveCell();

}

}

return CView::PreTranslateMessage(pMsg);

}

2、 b问题的解决

首先想到的依然是在MSDN中寻找MSFLEXGRID的列宽改变的响应事件,很幸运找到了。

在CCtrlEditGrid的头文件中添加 afx_msg void OnScroll();

在CCtrlEditGrid的CPP文件的事件映射表中添加ON_EVENT_REFLECT(CTaskEditGrid, 73 /* Scroll */, OnScroll, VTS_NONE),

编写OnScroll函数体:若单元格处于编辑状态调用Onleave函数使得编辑控件不可见。

void CCtrlEditGrid::OnScroll()

{

if ( !m_pEdit->IsWindowVisible ( ) ) return;

else

{

OnLeaveCell();

}

}

3、c问题的解决

该问题的关键是上下控件的VSCROLL事件干扰了FlexGrid的滚动事件响应,只要杜绝上下控件的VSCROLL消息不就行了吗。我们只要改造CSpinButtonCtrl的鼠标左键的响应函数就可以阻止VSCROLL的产生。

首先继承CSpinButtonCtrl产生新类CMySpinButtonCtrl,把原来在CCtrlEditGrid中的成员变量m_pSpinButtonCtrl的类型改为CMySpinButtonCtrl,动态分配时也改为CMySpinButtonCtrl类型。

接着添加CMySpinButtonCtrl的消息响应函数OnLButtonDown,注释掉该函数中默认的一行代码CSpinButtonCtrl::OnLButtonDown(nFlags, point),这样就阻止了VSCROLL消息的传递。

然后判断若点击点在Spin控件的上半部,Edit控件的值就加1,若点击在下半部,Edit控件的值就减1。这样就模拟了原来Spin控件的微调功能。

void CMySpinButtonCtrl::OnLButtonDown(UINT nFlags, CPoint point)

{

CString strNum;

int nNum;

CMySpinButtonCtrl::GetBuddy()->GetWindowText(strNum);

sscanf ( strNum, "%d", &nNum );

CRect Rect;

CMySpinButtonCtrl::GetWindowRect (&Rect);

int nLow,nUpper;

CMySpinButtonCtrl::GetRange(nLow, nUpper);

if(point.y <((Rect.bottom-Rect.top)/2))

{

nNum ++;

if ( nNum > nUpper )

nNum = nUpper;

}

else

{

nNum --;

if ( nNum < nLow )

nNum = nLow;

}

strNum.Format("%d",nNum);

CMySpinButtonCtrl::GetBuddy()->SetWindowText(strNum);

}

四、 总结

经过以上几步我们基本上完成了一个可编辑表格的制作。读者可以尝试着用本文的方法在表格中添加CComboBox,CDateTimeCtrl等不同控件。读者还可以另外增加功能,如在某一单元格处于可编辑状态时双击该单元格使之弹出一个关于本行的信息的属性表,要实现该功能可以参照a问题的解决方案,在CCtrlEditGrid的父窗口CEditGridView的PreTranslateMessage中截获双击事件,识别是否是在单元格中双击,然后弹出一个对话框,最后记住要返回True,表示该消息已被处理过不需再往下传递了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有