一个朋友让我看一个关于DialogBar的程序,问题是在一个DialogBar中添加一个Tree Control控件,在初始化函数OnInitDialog中使用InsertItem添加一项,结果运行后,始终在DialogBar中显示不出来,后来我又加入了一个Button测试,结果Button是灰色的,根本不能点击。在OnInitDialog中加入断点,程序根本执行不到这个函数。按理说,界面已经显示出来了,早应该调用OnInitDialog函数了。当时百思不得其解。
后来在网上查了一下,才知道CDialogBar根本没有重载OnInitDialog这个函数(你查一下MSDN,就知道CDialog是重载了这个函数的),所以导致我虽然有这个函数,但是始终不调用。这就需要我手动添加这个消息映射。我通过ON_MESSAGE宏,映射了这个消息响应函数,运行结果没问题,Tree Control中有我添加的项了。
剩下的就是Button不能用的问题了。通过网上和自己试验,知道在DialogBar中,Button发出的消息根本就不通过DialogBar,而是直接发到MainFrame中,所以只要在MainFrame中进行响应或者把消息再转发到DialogBar中就可以了。我在CMainFrame类中重载了OnCmdMsg这个函数,在这个函数中,先用DialogBar去拦截消息,这样,只需要在DialogBar中取相应Button的消息就可以了。
下面是我做试验的大致过程:
1、 创建一个单文档的MFC工程,名为MyDialogBar。
2、 要使用DialogBar,当然首先要在ResourceView中添加一个DialogBar了。添加好了DialogBar,ID采用默认的IDD_DIALOGBAR,至于它的properties,就采用默认的好了。
3、 添加一个Tree Control,ID为IDC_TREE,在它的properties对话框Styles选项卡中选择Has buttons、Has lines、Lines at root三个选项,看起来就比较正常了。
4、 然后为这个DialogBar添加一个类,类名为CDlgBar,继承自CDialog,因为在向导中没有CDialogBar这个基类。然后把它的WM_INITDIALOG消息响应函数加上,记住这时我们的DialogBar还是一个CDialog的子类呢。然后给Tree Control映射变量m_tree。
5、 下面就轮到对我们自己的DialogBar动手脚了。
(1)首先中把CDlgBar的基类改成CDialogBar,然后在.cpp文件中,把CDialogBar的构造函数的对CDialog的构造函数去掉,即:
将CDlgBar::CDlgBar(CWnd* pParent/*=NULL*/):CDialog(CDlgBar::IDD, pParent)修改为
CDlgBar::CDlgBar(CWnd* pParent /*=NULL*/)
(2)然后把DoDataExchange函数中的CDialog::DoDataExchange(pDX);一句改成CDialogBar::DoDataExchange(pDX);。
(3)把BEGIN_MESSAGE_MAP(CDlgBar,CDialog)中的CDialog改成CDialogBar。
(4)把OnInitDialog函数中的CDialog::OnInitDialog();一句去掉。
6、 在OnInitDialog中添加向TreeControl中插入数据的语句:
BOOLCDlgBar::OnInitDialog()
{
HTREEITEM hParent =m_tree.InsertItem("parent");
m_tree.InsertItem("boy",hParent);
m_tree.InsertItem("girl",hParent);
return TRUE; // return TRUE unless you setthe focus to a control
// EXCEPTION: OCX Property Pages should returnFALSE
}
7、 在CMainFrame中添加一个变量:CDlgBar m_wndDlgBar;,当然不能忘了include进头文件DlgBar.h。然后在它的OnCreate函数中创建DialogBar。添加如下语句:
if(!m_wnd DlgBar.Create(this, IDD_DIALOGBAR, WS_CHILD |WS_VISIBLE |
CBRS_LEFT | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC, IDD_DIALOGBAR))
{
TRACE0("Failed to create dialogbar\n");
return -1; // fail to create
}
m_wndDlgBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndDlgBar);
这是你可以运行一下,就会看到DialogBar虽然已经加入了,但是里面的tree control中什么都没有。在CDlgBar的OnInitDialog函数中设置断点,调试,也进入不到这个函数中。
8、 这时,你可以在CDlgBar中为WM_INITDIALOG消息映射我们的OnInitDialog函数。这样,当WM_INITDIALOG消息发送到CDlgBar中就可以响应了。
BEGIN_MESSAGE_MAP(CDlgBar,CDialogBar)
//{{AFX_MSG_MAP(CDlgBar)
ON_MESSAGE(WM_INITDIALOG, OnInitDialog)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
这时,你运行一下,就会看到一个断言错误。
在OnInitDialog函数中一开始添加UpdateData(FALSE);,这时你再运行,就会看到我们的tree control中有东西了。
那么这个UpdateData(FALSE);到底干了什么呢?MSDN上说当参数为FALSE时,会调用DoDataExchange函数,对控件进行初始化,当参数为TRUE时,从控件中获取值。
9、 在ResourceView中DialogBar中添加一个按钮,ID为IDC_BTN_TEST,并添加单击响应函数OnBtnTest。里面使用MessageBox(“OK”)来表示响应单击消息。
运行一下,可以看到,按钮是灰色的。
10、 原因是Button的消息发送到了MainFrame,到底有些什么消息,我还不是很清楚。这个问题的解决方法就是在CMainFrame中重载一个函数OnCmdMsg,并在这里面拦截消息发送到我们的CDlgBar中。
BOOLCMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
if(m_wndDlgBar.OnCmdMsg(nID, nCode, pExtra,pHandlerInfo))
{
return TRUE;
}
return CFrameWnd::OnCmdMsg(nID, nCode,pExtra, pHandlerInfo);
}
运行一下我们的程序,就可以看到没有问题了。
11、顺便添加一个tree control的选择消息TVN_SELCHANGED响应函数。在向导里面,选择IDC_TREE,添加它的TVN_SELCHANGED响应函数OnSelchangedTree。如下代码:
voidCDlgBar::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
HTREEITEM hSelItem = pNMTreeView->itemNew.hItem;// 选择的项
CStringstrSelText = m_tree.GetItemText(hSelItem);
MessageBox(strSelText);
*pResult = 0;
}
图标创建
CImagelist*m_imagelist;
m_imagelist=newCImageList();
m_imagelist->Create(16,16,TRUE,2,2);
// BOOLCreate(intcx,intcy,UINTnFlags,intnInitial,intnGrow);
nInitial 创建的图标个数
nGrow 一般等于nIntial
HICON hIcon;
HTREEITEM htem;
TV_INSERTSTRUCT Instru;
hIcon=AfxGetApp()->LoadIcon(IDI_ICON2);
m_imagelist->Add(hIcon);
hIcon=AfxGetApp()->LoadIcon(IDI_ICON1);
m_imagelist->Add(hIcon);
m_treeList.ShowWindow(SW_HIDE);
m_treeList.SetImageList(m_imagelist,TVSIL_NORMAL);
htem=m_treeList.InsertItem("北京太格服务器",0,0);
m_treeList.InsertItem("北京太格通道一",1,1,htem);//参数2.图标索引.参数3.TreeList项目被选中是显示的图标索引,参数4.父节点
m_treeList.InsertItem("北京太格通道二",1,1,htem);
// HTREEITEMInsertItem(LPTVINSERTSTRUCTlpInsertStruct);
//HTREEITEM InsertItem(LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT,HTREEITEM hInsertAfter = TVI_LAST );
//HTREEITEM InsertItem(LPCTSTR lpszItem, int nImage, int nSelectedImage,HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter= TVI_LAST);
// HTREEITEMInsertItem(LPTVINSERTSTRUCTlpInsertStruct);
Instru.hParent=NULL;
Instru.item.iImage=0;
Instru.item.iSelectedImage=0;
Instru.item.mask=TVIF_CHILDREN|TVIF_IMAGE|TVIF_TEXT;
Instru.item.pszText="测试服务器";
m_treeList.InsertItem(&Instru);
m_treeList.SetBkColor(RGB(193,255,255));
m_treeList.SetTextColor(RGB(255,0,0));
主要用到的类有:CListCtrl,CTreeCtrl,CImageList,CFileFind 和函数SHGetFileInfo()
简述步骤如下:
1、增加 TreeCtrl 的TVS_HASBUTTONS,TVS_HASLINES、TVS_LINESATROOT Style,代码如下:
DWORD dwStyle =GetWindowLong(m_tree.m_hWnd,GWL_STYLE);
dwStyle |= TVS_HASBUTTONS | TVS_HASLINES |TVS_LINESATROOT;
SetWindowLong(m_tree.m_hWnd,GWL_STYLE,dwStyle);
2、为TreeCtrl添加Root项:
m_hRoot = m_tree.InsertItem("我的电脑");
InsertItem()的函数原形为
HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEMhParent = TVI_ROOT,
HTREEITEM hInsertAfter = TVI_LAST );
3、获取本地逻辑驱动器,并添加:
void CTreeViewDlg::GetLogicalDrives(HTREEITEM hParent)
{
size_t szAllDriveStrings =GetLogicalDriveStrings(0,NULL);
char *pDriveStrings = new char[szAllDriveStrings +sizeof(_T(""))];
GetLogicalDriveStrings(szAllDriveStrings,pDriveStrings);
size_t szDriveString = strlen(pDriveStrings);
while(szDriveString 0)
{
m_tree.InsertItem(pDriveStrings,hParent);
pDriveStrings += szDriveString + 1;
szDriveString = strlen(pDriveStrings);
}
}
4、添加TVN_EXPANDED消息处理函数,当一项展开时,为其子项添加下一级目录:
voidCTreeViewDlg::OnItemexpandedTree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler codehere
TVITEM item = pNMTreeView-itemNew;
if(item.hItem == m_hRoot)
return;
HTREEITEM hChild = m_tree.GetChildItem(item.hItem);
while(hChild)
{
AddSubDir(hChild);
hChild = m_tree.GetNextItem(hChild,TVGN_NEXT);
}
*pResult = 0;
}
AddSubDir函数功能添加子项,具体代码见示例。
5、添加TVN_SELCHANGED消息处理函数,在这个函数里,用GetFullPath()取得选中项的绝对路径(GetFullPath()具体代码看示例),在ListCtrl中添加文件而非文件夹的图标:
voidCTreeViewDlg::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult)
{
m_list.DeleteAllItems();
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
TVITEM item = pNMTreeView-itemNew;
if(item.hItem == m_hRoot)
return;
CString str = GetFullPath(item.hItem);
if(str.Right(1) != "\\")
str += "\\";
str += "*.*";
CFileFind file;
BOOL bContinue = file.FindFile(str);
while(bContinue)
{
bContinue = file.FindNextFile();
if(!file.IsDirectory() && !file.IsDots())
{
SHFILEINFO info;
CString temp = str;
int index = temp.Find("*.*");
temp.Delete(index,3);
SHGetFileInfo(temp + file.GetFileName(),
0,
&info,sizeof(&info),
SHGFI_DISPLAYNAME | SHGFI_ICON);
int i = m_ImageList.Add(info.hIcon);
m_list.InsertItem(i,info.szDisplayName,i);
}
}
*pResult = 0;
}
这只是一个简单的例子,你可以在 ListCtrl 中添加鼠标双击消息的处理函数,用 Process 打开该选中的文件; 该示例在VC6,xp下编译通过。
原文地址:http://wenku.baidu.com/view/8848d40a79563c1ec5da71f6.html
?