2.
关于list
control
的高级应用---
条目编辑
http://www.fa39.com/Article/c/200610/1952.html
论坛中搜索一下,
你会发现不少类似的提问:
我如何编辑list control
的条目?
如何直接编辑list control...
等等;list control
可用来做数据库表的视图,
十分有用.
但报表风格的list control
只能编辑第一列,
其余的该死的微软没为vc
做到.
它怕VB
卖不出.
于是C++
程序员只好DIY.
主要思想是在list control
中动态创建一个控件,
动态移动该控件到相应位置.
这些方法早有人讨论过了,
本文也是基于如上思想的,
但注重于可扩充性与使用的方便.
List control
这头主要是重载OnLButtonDown
方法,
计算出被点中的条目.
这里重要的函数是SubItemHitTest
和GetSubItemRect,
看msdn
上有相关说明.
用户点中后,
就要负责显示控件了:
如果之前选中了其他,
就要验证之前的改动是否成功.
不成功就要回到原来的地方,
成功就应用修改并移到新位置.
看代码:
static const UINT
IDCHAILD="3000";
void CValidateList::OnLButtonDown(UINT nFlags, CPoint
point)
{
CListCtrl::OnLButtonDown(nFlags, point);
LVHITTESTINFO hi;
hi.pt = point;
if(SubItemHitTest(&hi)
!= -1 )//
没有点中条目就不管
{if(m_col==-1||//-1
还没被选过
true==(m_col+m_validate)->Validate (m_row))
{
m_row = hi.iItem, m_col= hi.iSubItem;//m_row,m_col
成
//
员分别跟踪选中的行列
}
((m_col+m_validate))->Move (_GetRect(),m_row);
}
}
WinBlast* CValidateList::SetValidate( WinBlast*in)//
设置验证的
//
控件群,in
对应第一列,in+1
第二列……
{
WinBlast*ret=m_validate;
m_validate=in;
int
counts="GetHeaderCtrl"()->GetItemCount();;
RECT rect;
memset(&rect,0,sizeof(rect));
for(int
i="0";i< span>
(in+i)->Create (this,rect,IDCHAILD+i,i);
m_col=-1;//
没有被选中的
return ret;
}
RECT CValidateList::_GetRect()//
内部使用,
得到相应显示位置
{
CRect ret;
GetSubItemRect(m_row,m_col,LVIR_BOUNDS,ret);
return ret;
}
void CValidateList::NoSelect()//
置未选中状态
{
m_col=-1;//
没有被选中的
}
看到了WinBlast*ret=m_validate
吧.WinBlast
是用来修改和验证数据的控件看它的实现:
class WinBlast
{
int m_col;//
跟踪列,
为什么要这个?
因为你可以让一种控件对
//
不同列用不同的验证策略
CWnd* m_win;//
你的控件窗口
CListCtrl
*m_parent;//
用它获得文本
public:
WinBlast(){m_win=NULL;}
~WinBlast(){m_win->DestroyWindow();delete m_win;}
virtual bool Create( CWnd*
pParentWnd,
const RECT& rect, UINT nID,
int col)
{
m_col=col;m_parent=(CListCtrl *)pParentWnd;
m_win=new CEdit;
return ((CEdit*)m_win)->
Create(ES_NOHIDESEL,rect,pParentWnd,nID);
}
void Move(const RECT
&rect,int row)//
最重要的函数但前面
//
两个动作是必作的,SetText
为虚,
你在那做你喜欢的
; {
m_win->ShowWindow(SW_SHOW);
m_win->MoveWindow(&rect);
SetText(row);
}
virtual bool
Validate(int row)//
验证,
虚函数.
这里永远返回true
{
m_win->ShowWindow(SW_HIDE);
CString set;
m_win->GetWindowText(set);
m_parent->SetItemText(row,m_col,set);
return true;
}
virtual void
SetText(int row)
{
m_win->SetWindowText(m_parent->GetItemText(row,m_col));
((CEdit*)m_win)->SetSel (0,-1);
}
};
实际使用通常是这样的:
WinBlast*p=new
WinBlast[sizeof(col)/sizeof(col[0])];//col
是
//
列名字符数组,sizeof(col)/sizeof(col[0])
计算列数
m_test.SetValidate
(p);//m_test
是CValidateList
类
你可以继承WinBlast,
重载Create
建立一个下拉框,
加入你喜爱的验证方法.
注意我的设计漏洞:CValidateList
应接收WinBlast**,
而不是WinBlast*-----
不理解这个漏洞其实也不要紧:
但要记住,
不改正的话你的WinBlast
后继类就不能加数据成员了.
3.
如何在
VC
MFC
的
List Control
中实现拷贝功能?
Reference:
http://zhidao.baidu.com/question/13639294.html
我在单文档视图中加入一个
List Control
控件
(Report
形式
)
,并关联类
CListCtrl
的一个对象,往里面写数据什么的都行,但在界面上无法实现对报表数据的拷贝(快捷或右键都不行),想请教如何实现拷贝该报表显示的数据?
1
对
CListCtrl
创建
click
事件,记录
item
2
在视图所在的类创建虚函数
PreTranslateMessage
加入代码
if(pMsg->message
== WM_KEYDOWN)
{
if(pMsg->wParam==13)//
这里
13
是表示回车键盘,你也可以改成其他的
{
copy();
}
}
3.
编写
copy
函数,取得
item
处的文本,保存只剪切板
4.
更改列表控件样式
创建列表控件 (List Control) (CListCtrl)
后,可以在任何时间更改它的窗口样式。通过更改窗口样式来更改控件使用的视图类型。例如,为了模拟“资源管理器”,您可以提供菜单项或工具栏按钮以在不同的视图(图标视图、列表视图等)之间切换控件。
例如,用户选择您的菜单项后,您可以调用 GetWindowLong
来检索控件的当前样式,然后调用 SetWindowLong
来重置样式。有关更多信息,请参见 Platform SDK
中的使用列表视图 (ListView)
控件。
可用样式在 Create
中列出。样式 LVS_ICON
、LVS_SMALLICON
、LVS_LIST
和 LVS_REPORT
指定四种列表控件 (List Control)
视图。
扩展样式
除了列表控件 (List Control)
的标准样式之外,还有另一个称为扩展样式的样式集。Platform SDK
中的扩展列表视图样式讨论了这些样式,这些样式可提供自定义列表控件 (List Control)
行为的各种有用功能。若要实现某个样式的行为(如随意选择),请调用 CListCtrl::SetExtendedStyle
传递所需的样式。下面的示例说明该函数调用:
m_myListCtrl.SetExtendedStyle(LVS_EX_TRACKSELECT |
LVS_EX_ONECLICKACTIVATE);
注意
要使随意选择起作用,还必须打开 LVS_EX_ONECLICKACTIVATE
或 LVS_EX_TWOCLICKACTIVATE
。
5.
在列表控件(
List
Control
)中实现工作区
默认情况下,列表控件 (List Control)
按标准网格格式排列所有项。但也支持另一种方法:工作区。工作区将列表项排列到矩形组中。有关实现工作区的列表控件 (List Control)
的图像,请参见 Platform SDK
中的“使用列表-
视图控件”。
注意
工作区只有当列表控件 (List Control)
处于图标或小图标模式时才可见。但是,如果视图切换到报表或列表模式,将维持任何当前工作区。
工作区可用来显示空边框(在项的左侧、顶部和/
或 右侧),或在通常不会有水平滚动条的时候显示水平滚动条。另一个普通用法是创建多个工作区,可以将项移动或放置到这些工作区。使用该方法可以在单一视图中
创建具有不同意义的区域。然后用户可以将项放置到不同的区域以对它们分类。此类示例可以是一个文件系统的视图,它包含一个读/
写文件区,一个只读文件区。如果将文件项移动到只读区,它将自动变为只读。将文件从只读区移动到读/
写区会使文件成为可读/
写的。
CListCtrl
为创建和管理列表控件 (List Control)
工作区提供几个成员函数。GetWorkAreas
和 SetWorkAreas
检索并设置 CRect
对象(或 RECT
结构)数组,该数组存储当前实现的列表控件 (List Control)
工作区。另外,GetNumberOfWorkAreas
检索列表控件 (List Control)
的当前工作区数目(默认值为零)。
项和工作区
工作区创建后,工作区中的项成为其成员。同样,如果将一个项移动到某个工作区,则该项成为它所移动到的工作区的成员。如果某个项不在任何工作区中,则它自动成为第一个工作区(索引 0
)的成员。如果想创建项并将其放置到一个特定的工作区,则需要创建该项,然后调用
SetItemPosition
将项移动到所需工作区。下面的第二个示例说明该技术。
下面的示例在列表控件 (List Control) (
m_listctrl
)
中实现四个大小相等的工作区 (
rcWorkAreas
)
,每个工作区都有 10
像素宽的边框。
CRect curRect;
CSize size;
size= m_listctrl.ApproximateViewRect();
size.cx+= 100;
size.cy+= 100;
CRect rcWorkAreas[4];
rcWorkAreas[0].SetRect(0, 0, (size.cx / 2) - 5, (size.cy
/ 2) - 5);
rcWorkAreas[1].SetRect((size.cx / 2) + 5, 0, size.cx,
(size.cy / 2) - 5);
rcWorkAreas[2].SetRect(0, (size.cy / 2) + 5, (size.cx /
2) - 5, size.cy);
rcWorkAreas[3].SetRect((size.cx / 2) + 5, (size.cy / 2) +
5, size.cx, size.cy);
//set work areas
m_listctrl.SetWorkAreas(4, rcWorkAreas);
调用 ApproximateViewRect
来获取在一个区域显示所有项所需要的总区域大小的估计值。之后,该估计值被分成四个区域并用 5
像素宽的边框填充。
下一个示例将现有列表项分配给每一组 (
rcWorkAreas
)
并刷新控件视图 (
m_listctrl
)
以实现此效果。
// set insertion points for each work area
CPoint rgptWork[4];
for (int i = 0; i < 4; i++)
{
rgptWork[i].x =
rcWorkAreas[i].left + 10;
rgptWork[i].y =
rcWorkAreas[i].top + 10;
}
// now move all the items to the different quadrants
for (i = 0; i < 20; i++)
m_listctrl.SetItemPosition(i,
rgptWork[i % 4]);
// force the control to rearrange the shuffled items
m_listctrl.Arrange(LVA_DEFAULT);
6.
处理列表控件(
List
Control
)中的通知消息
用户单击列标题、拖动图标、编辑标签等时,列表控件 (List Control) (CListCtrl)
将通知消息发送给它的父窗口。如果要进行某种响应,请处理这些消息。例如,用户单击列标题后,可能想基于单击的列的内容对项排序,如在 Microsoft Outlook
中所做的那样。
在视图或对话框类中处理列表控件 (List Control)
的 WM_NOTIFY
消息。基于正在处理的通知消息,用“属性”窗口创建带 switch
语句的 OnChildNotify
处理函数。
有关列表控件 (List Control)
可以发送到其父窗口的通知的列表,请参见 Platform SDK
中的列表视图 (ListView)
控件参考。
7.
虚拟列表控件
虚拟列表控件指具有
LVS_OWNERDATA
样式的列表视图
(ListView)
控件。该样式启用控件来支持项数达到
DWORD
(默认的项数只扩展到
int
)。然而,该样式的最大便利是可以使内存中一次只有一个数据项子集。这使虚拟列表视图
(ListView)
控件可以将自己借给大型信息数据库使用,而在这类数据库中已存在特定的数据访问方法。
注意
MFC
除了在
CListCtrl
中提供虚拟列表功能外,还在
CListView
类中提供相同的功能。
在开发虚拟列表控件时应注意一些兼容性问题。有关更多信息,请参见
Platform SDK
中“
列表
-
视图控件”
主题的“
兼容性问题”
一节。
处理
LVN_GETDISPINFO
通知
虚拟列表控件维护非常少的项信息。除了项选择和焦点信息,所有项信息都由控件的所有者管理。框架通过
LVN_GETDISPINFO
通知消息来请求信息。若要提供请求的信息,虚拟列表控件的所有者(或控件本身)必须处理该通知。使用“
属性”
窗口可以很容易地完成此操作(请参见
将消息映射到函数
)。所得到的代码应类似于下面的示例(其中
CMyListCtrl
是虚拟列表控件对象,控件正在处理通知)。
BEGIN_MESSAGE_MAP(CMyListCtrl,
CListCtrl)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo)
END_MESSAGE_MAP()
在
LVN_GETDISPINFO
通知消息的处理程序中,必须检查正在请求的信息的类型。可能值是:
·
LVIF_TEXT
必须填写
pszText
成员。
·
LVIF_IMAGE
必须填写
iImage
成员。
·
LVIF_INDENT
必须填写
iIndent
成员。
·
LVIF_PARAM
必须填写
lParam
成员。
·
LVIF_STATE
必须填写
state
成员。
然后应将所有请求的信息提供给框架。
下面的示例摘自列表控件
(List Control)
对象的通知处理程序体,它通过为文本缓冲区和项的图像提供信息来说明一种可能的方法:
LV_DISPINFO* pDispInfo =
(LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem=
&(pDispInfo)->item;
int iItemIndx=
pItem->iItem;
if (pItem->mask &
LVIF_TEXT) //valid text buffer?
{
switch(pItem->iSubItem){
case 0: //fill in main text
lstrcpy(pItem->pszText,
m_Items[iItemIndx].m_strItemText);
break;
case 1: //fill in sub item 1 text
lstrcpy(pItem->pszText,
m_Items[iItemIndx].m_strSubItem1Text);
break;
case 2: //fill in sub item 2 text
lstrcpy(pItem->pszText,
m_Items[iItemIndx].m_strSubItem2Text);
break;
}
}
if pItem->mask &
LVIF_IMAGE) //valid image?
pItem->iImage=
m_Items[iItemIndx].m_iImageIndex;
由于这种类型的列表控件
(List Control)
是提供给大的数据集的,因此建议您缓存请求的项数据以提高检索性能。框架提供缓存提示机制,通过发送
LVN_ODCACHEHINT
通知消息来帮助优化缓存。但是,您必须使用一种稍有不同的方法来处理该通知。使用“
属性”
窗口,重写列表控件
(List
Control)
对象的
OnChildNotify
函数。在该示例的情况下为
CMyListCtrl
。
在处理程序体中检查
LVN_ODCACHEHINT