專題1: 如何使CListCtrl完全可編輯?
1. 背景 : 我們知道如果CListCtrl是報表樣式,那麼CListCtrl所提供的編輯功能只局限於第一列.也就是說只有第一列可編輯.這樣顯然無法滿足一般資料庫的要求.我們想要每個子項都能編輯.
2. 思路 : CEdit是一個很好的可控制編輯控制項.如何把CEdit和我們的CListCtrl聯絡起來?一種很好的想法是------一般我們如果想編輯某一項,那麼就應該去雙擊.雙擊以後就讓CEdit在那裡顯示,當然要把大小調整和子項表格一樣.如果CEdit失去了焦點,表示修改完畢,那麼立即更改子項的資料,同時讓CEdit隱藏.因為每次只能編輯一項,所以只需要一個CEdit就夠了.
3. 方法:
(1) 首先從CListCtrl派生一個類,其他已經有的變數或者函數設定我已經介紹,如果不清楚的讀者,可以去參考”基礎篇”.
(2) 有一點可以肯定,我們必須響應雙擊事件:
void Cmylist::OnLButtonDblClk(UINT nFlags, CPoint point)
{
int index;//行號
int colnum;//列號
GetWindowRect(r);//稍後說明
GetParent()->ScreenToClient(r);//稍後說明
if((index=HitTestEx(point,&colnum))!=-1)
EditSubItem(index,colnum);
CListCtrl::OnLButtonDblClk(nFlags, point);
}
其中HitTestEx是用來求出雙擊點所在的行列號,如果行號不為-1,那麼就調用函數EditSubItem. 這個函數會根據行列號求出該子項具體座標,方便CEdit調整位置.
(3) 如何求出行列號?行號是很好求出來的 ,但是列號就不是很簡單了,必須詳細判斷.
int Cmylist::HitTestEx(CPoint &point, int *pcolumn)
{
int columnNum=0;
//擷取頁面內首行索引號,不一定是0,要考慮捲軸的情況
int row=GetTopIndex();
// GetCountPerPage()擷取在頁面內行的總數
int bottom=row+this->GetCountPerPage();
// 防止超出範圍
if(bottom>this->GetItemCount())
bottom=GetItemCount();
//擷取列的總數
int ncolumncount=this->GetHeaderCtrl()->GetItemCount();
//可以肯定雙擊點肯定在頁面內,因此從頁面首行索引號開始判斷
for(;row<=bottom;++row)
{
CRect rect;
//求出行的rect
GetItemRect(row,&rect,LVIR_BOUNDS);
//點是否在行的矩形內
if(rect.PtInRect(point))
//如果點在行的矩形內,求出點在哪一列
for(columnNum=0;columnNum<ncolumncount;columnNum++)
{
//求出列的寬度
int colwidth=this->GetColumnWidth(columnNum);
if(point.x>=rect.left&&point.x<=(rect.left+colwidth))
{
*pcolumn=columnNum;
return row;
}
rect.left+=colwidth;
}
}
return -1;
}
當然上面那種方法有點複雜,是完全從頭開始判斷.其實我們可以先利用CListCtrl提供的函數求出行號,再求列號,這樣稍微簡單點
int Cmylist::HitTestEx(CPoint &point, int *pcolumn)
{
int columnNum=0;
int row=HitTest(point);//求出行號
int ncolumncount=this->GetHeaderCtrl()->GetItemCount();
LVHITTESTINFO Info;
Info.pt=point;
this->SubItemHitTest(&Info);
*pcolumn=Info.iSubItem;
if(*pcolumn>=0&&*pcolumn<ncolumncount)
return row;
else
return -1;
/* int ncolumncount=this->GetHeaderCtrl()->GetItemCount();
CRect rect;
GetItemRect(row,&rect,LVIR_BOUNDS);
if(rect.PtInRect(point))
for(columnNum=0;columnNum<ncolumncount;columnNum++)
{
int colwidth=this->GetColumnWidth(columnNum);
if(point.x>=rect.left&&point.x<=(rect.left+colwidth))
{
*pcolumn=columnNum;
return row;
}
rect.left+=colwidth;
}*/
}
(4) 求出具體CEdit移動座標
int Cmylist::Item_X(int row, int column,CRect& rect_X)
{
int offset=0;
for(int i=0;i<column;i++)
offset+=GetColumnWidth(i);
CRect rect;
GetItemRect(row,rect,LVIR_BOUNDS);
//注意水平捲軸的影響,如果已經移動了水平捲軸,可能left為0,或者超出總大小
if(offset+rect.left<0||offset+rect.left>client_rect.right)
{
CSize size;
//offset肯定為正,如果出現了rect.left為負
if(offset+rect.left>0)
size.cx=- (offset+rect.left);
else
size.cx=offset+rect.left;
size.cy=0;//垂直不用管
//如果某一列的一半在捲軸左邊,一半在右邊,就再次調整捲軸的位置.
Scroll(size);
rect.left - =size.cx;
}
rect.left+=offset+2;
rect.right=rect.left+GetColumnWidth(column)-2;
//bottom和top不用管
rect_X=rect;
return rect.right;
}
(5) 移動CEdit
void Cmylist::EditSubItem(int Item, int Column)
{
CRect rect;
//求出行列所在rect
this->Item_X(Item,Column,rect);
EditCellShow(rect,Item,Column,r);
}
void Cmylist::EditCellShow(CRect rect, int Item, int Column,CRect r)
{
//還記得r嗎?在開始的雙擊函數OnLButtonDblClk中,它是CListCtrl在父視窗中的位置
rect.left+=r.left;
rect.top+=r.top+2;
rect.right+=r.left;
rect.bottom+=r.top+2;
//pedit是CEdit對象的指標,提供介面,只要在程式中讓pedit指向一個對象即可
pedit->MoveWindow(rect,TRUE);
pedit->ShowWindow(TRUE);
pedit->SetFocus();
}
^_^!這樣就完成了.效果還可以.當然你還要去響應CEdit失去焦點和得到焦點的事件.這個就不是我的工作了,因為每個人的要求不一樣啊!
看看我的效果!