CListCtrl要实现report风格可编辑,这个,刚开始弄得时候,是纠结比较久的,因为网上能找到的实现,一般都是自己贴个编辑框到CListCtrl上面,
这个要说难,真的是也不难,但是比较麻烦,因为要自己修正编辑框的大小,显示隐藏编辑框等.
但是,又有一个事实是,CListCtrl有个可编辑的属性,让你可以编辑它的第一列的,但是不能编辑其他列. 不知道设计的人为什么要这样实现.
既然它已经可以编辑第一列了,为什么不能让它编辑所有的呢.
网上搜了下,还是有人实现了的,按照人家提供的方法,我也实现了,这里上下代码.
实现的原理及步骤:
1.先继承控件 CEdit -----. CItemEdit;
class CItemEdit :
public CEdit
{
public:
CItemEdit(void);
~CItemEdit(void);
DECLARE_MESSAGE_MAP()
afx_msg void OnKillFocus(CWnd* pNewWnd);
afx_msg void OnSetFocus(CWnd* pOldWnd);
public:
BOOL m_bExchange;
afx_msg void OnEnChange();
afx_msg void OnEnterIdle(UINT nWhy, CWnd* pWho);
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos);
void SetWindowRect( CRect &rect );
CRect m_WindowRect;
};
CItemEdit::CItemEdit(void)
{
m_bExchange = FALSE;
}
CItemEdit::~CItemEdit(void)
{
}
BEGIN_MESSAGE_MAP(CItemEdit, CEdit)
// ON_WM_KILLFOCUS()
// ON_WM_SETFOCUS()
// ON_CONTROL_REFLECT(EN_CHANGE, &CItemEdit::OnEnChange)
// ON_WM_ENTERIDLE()
ON_WM_WINDOWPOSCHANGING()
END_MESSAGE_MAP()
void CItemEdit::OnKillFocus(CWnd* pNewWnd)
{
CEdit::OnKillFocus(pNewWnd);
// TODO: 在此处添加消息处理程序代码
CWnd* pParent = this->GetParent();
::PostMessage(pParent->GetSafeHwnd(),WM_USER_EDIT_END,m_bExchange,0);
// TODO: 在此处添加消息处理程序代码
}
void CItemEdit::OnSetFocus(CWnd* pOldWnd)
{
CEdit::OnSetFocus(pOldWnd);
// TODO: 在此处添加消息处理程序代码
}
void CItemEdit::OnEnChange()
{
// TODO: 如果该控件是 RICHEDIT 控件,则它将不会
// 发送该通知,除非重写 CEdit::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
// TODO: 在此添加控件通知处理程序代码
//1.编辑框中文字改变时给父窗口或者你期望接受编辑框中数据改变的窗口发送编辑框中数据更新的消息,接受到消息的窗口负责将数据反映到CListCtrl对应的项中
CWnd* pParent = this->GetParent();
::PostMessage(pParent->GetSafeHwnd(),WM_USER_EDIT_END,m_bExchange,0);
}
void CItemEdit::OnEnterIdle(UINT nWhy, CWnd* pWho)
{
CEdit::OnEnterIdle(nWhy, pWho);
//2.用户没有编辑时隐藏编辑框窗口
ShowWindow( SW_HIDE );
// TODO: 在此处添加消息处理程序代码
}
BOOL CItemEdit::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
//3.编辑过程中如果用户按下回车或者esc则结束编辑,并给父窗口或者你期望接受编辑框中数据改变的窗口发送编辑框中数据更新的消息,接受到消息的窗口负责将数据反映到CListCtrl对应的项中
if(pMsg->message == WM_KEYDOWN)
{
if(pMsg->wParam == VK_RETURN)
{
CWnd* pParent = this->GetParent();
m_bExchange = TRUE;
::PostMessage(pParent->GetSafeHwnd(),WM_USER_EDIT_END,m_bExchange,0);
ShowWindow( SW_HIDE );
return TRUE;
}
else if(pMsg->wParam == VK_ESCAPE)
{
CWnd* pParent = this->GetParent();
m_bExchange = FALSE;
::PostMessage(pParent->GetSafeHwnd(),WM_USER_EDIT_END,m_bExchange,0);
ShowWindow( SW_HIDE );
return TRUE;
}
}
return CEdit::PreTranslateMessage(pMsg);
}
void CItemEdit::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
//4.窗口位置改变时,重新设置窗口的位置为我们保存的窗口的位置 !!!重要,这里是关键,如果没有这个,那么,你会发现,不过在外面怎么
//SetWindowRect,编辑框总是在第一列!!!!!!!!!!!!!!!!!!!!!!!!!
lpwndpos->cx = m_WindowRect.Width();
lpwndpos->cy = m_WindowRect.Height();
lpwndpos->x = m_WindowRect.left;
lpwndpos->y = m_WindowRect.top;
CEdit::OnWindowPosChanging(lpwndpos);
// TODO: 在此处添加消息处理程序代码
}
void CItemEdit::SetWindowRect( CRect &rect )
{
//保存我们希望编辑框出现的位置
m_WindowRect = rect;
}
2.在使用CListCtrl的容器类中定义变量 CItemEdit m_Edit; 因为编辑框要一直随listctrl一直存在,因此定义成成员变量或者指针亦可,保证其生命周期就行.
3.一般是双击列表时编辑一项,响应CListCtrl的 dbclick,在其中添加如下代码
LPNMITEMACTIVATE pNMListView = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
CRect rc;
CListCtrl &m_list = GetListCtrl();
CPoint pt( pNMListView->ptAction );
m_row = HitTextEx( pt , &m_column ); //这里是根据点击位置取得双击的行,列索引
m_list.GetSubItemRect(m_row, m_column,LVIR_BOUNDS,rc);//取得子项的矩形
rc.left+=3;
rc.top+=2;
rc.right+=3;
rc.bottom+=2;
m_pEdit = m_list.EditLabel( m_row ); //取得CListCtrl自带的编辑框
m_edit.SubclassWindow( m_pEdit->GetSafeHwnd() ); //用我们子类化的类,子类华这个自带的编辑框类
m_edit.SetWindowRect( rc ); //将窗口移至对应项的窗口位置
// m_pEdit->MoveWindow( rc );
CString strInfo = m_list.GetItemText( m_row , m_column ); //取得对应项的内容设置到编辑框中.,
m_pEdit->SetWindowText( strInfo );
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
4. HitTestEx的实现
int HitTextEx( CPoint &point , int *col )const
{
int colnum = 0;
CListCtrl &listCtrl = GetListCtrl();
int row = listCtrl.HitTest( point, NULL );
if( col ) *col = 0;
// Make sure that the CListCtrlis in LVS_REPORT
if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
return row;
// Get the top and bottom row visible
row = listCtrl.GetTopIndex();
int bottom = row + listCtrl.GetCountPerPage();
if( bottom > listCtrl.GetItemCount() )
bottom = listCtrl.GetItemCount();
// Get the number of columns
CHeaderCtrl* pHeader = (CHeaderCtrl*)listCtrl.GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// Loop through the visible rows
for( ;row <=bottom;row++)
{
// Get bounding rect of item and check whether point falls in it.
CRect rect;
listCtrl.GetItemRect( row, &rect, LVIR_BOUNDS );
if( rect.PtInRect(point) )
{
// Now find the column
for( colnum = 0; colnum < nColumnCount; colnum++ )
{
int colwidth = listCtrl.GetColumnWidth(colnum);
if( point.x >= rect.left
&& point.x <= (rect.left + colwidth ) )
{
if( col ) *col = colnum;
return row;
}
rect.left += colwidth;
}
}
}
return -1;
}
5.接收处理CItemEdit发送回来的 Edit_end消息
:OnLvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
CListCtrl &listCtrl = GetListCtrl();
listCtrl.SetItemText( m_row , m_column , pDispInfo->item.pszText );
if( ::IsWindow( m_edit.GetSafeHwnd() ) )
m_edit.UnsubclassWindow();
m_bEditing = FALSE;
m_pEdit = NULL;
*pResult = 0;
}
6. 相信你以大功告成了,不用谢我.我也只是参照别人的基础上,把我的代码贴上来