基于CDialogBar的IE多标签栏的实现
2009-02-17 20:00:16 来源:WEB开发网本文源代码下载地址:
http://flashview.ddvip.com/2009_02/TabBarDemo.rar
IE浏览器的多标签模式已日趋占据浏览器市场的主流模式。回忆IE6.0时代的单文档多实例年代,那浏览多网页是何等的痛苦。原本有限的空间就要被那些烦琐的网页所占据,要从这些烦琐的网页中切换到自己目的网页更是何等的不便。现在很多第三方IE浏览器对IE浏览器所显示出来的弊病虎视眈眈许久,多标签浏览器也应运而生。遨游、世界之窗、TT等如今都是拜多标签浏览器之福,早早占领了市场,占据了一席之地。而如今微软也知道自己浏览器帝国的根基也岌岌可危,其怎可示弱,IE7.0也就相继问世。
IE多标签栏的主要特点是:单实例多文档模式,文档间的切换是通过标签实现。一个实例就可以包容多个文档,察看网页是何其的方便。
构成IE多标签栏的界面要素包括工具栏和标签。工具栏采用CDialogBar做为标签的容器,标签采用自绘按钮来实现,CtabCtrl做标签不容易实现自绘(自绘的时候有灰色的border出现)。
在CDialogBar的宽度发生改变的时候,其上面的按钮标签应做适当的调整。当然在足够容纳按钮标签的时,可以给一个设定值。若空间有限的话,则就适当缩小标签的大小,其上的内容通过提示框提示。所以只要响应CdialogBar的WM_SIZE来调整标签的摆放方式。至于标签栏的自绘通过响应WM_PAINT消息就可以做到。
void CTabBar::OnSize(UINT nType, int cx, int cy)
当选中标签后,鼠标移动到标签右边的时候,则会出现一个关闭按钮,用来关闭文档。所以这个里面涉及到按钮的自绘原理,这样的文章比较多,这里就不多做说明。不过俺想提个问题,对于按钮的0nDrawItem与DrawItem,是否有什么区别,如果将下面换成OnDrawItem消息响应函数是否可以?实在不懂得的话可以去跟踪CButton的源代码,就知道了。
{
CDialogBar::OnSize(nType, cx, cy);
CRect rcClient;
GetClientRect(rcClient);
int nBarWidth = rcClient.Width();
int nTabWidth = nBarWidth-120;
int nCount = m_ptrArray.GetCount();
if(nCount*m_nWidth>nTabWidth)
{
//平均分配位置
int nAveWidth = nTabWidth*1.0/nCount;
for(int i=0; i<nCount; i++)
{
CRect rcBtn;
TABINFO* pTabInfo = (TABINFO*)m_ptrArray.GetAt(i);
pTabInfo->pTabButton->GetClientRect(&rcBtn);
pTabInfo->pTabButton->MoveWindow(nAveWidth*i,
(rcClient.Height()-rcBtn.Height())/2,
nAveWidth,
m_nHeight,
FALSE);
}
}
else
{
//固定大小
for(int i=0; i<nCount; i++)
{
CRect rcBtn;
TABINFO* pTabInfo = (TABINFO*)m_ptrArray.GetAt(i);
pTabInfo->pTabButton->GetClientRect(&rcBtn);
pTabInfo->pTabButton->MoveWindow(m_nWidth*i,
(rcClient.Height()-rcBtn.Height())/2,
m_nWidth, m_nHeight, FALSE);
}
}
Invalidate();
}void CTabButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
值得注意的是,CdialogBar的按钮命令消息是先发给主框架窗口处理,如果主框架窗口没有为其提供相应的响应函数,则命令的消息路由就中断,按钮则会出现disabled状态。或者你也可以获取到这个命令消息后,重载OnCmdMsg再将这个命令消息发往其他窗口。然后就可以通过按钮的消息反射机制,使按钮能够自己处理自己的事件,比较贴近面向对象的设计。由于命令消息响应函数里面没有将发命令消息的对象传到CtabBar中,这样CtabBar如果靠按钮ID来设置主键唯一标志一个标签按钮的话,那那就会占用很多的ID。本设计并没有这么做,在给ctabbar提供相的相关通知,是在里面发送一个通知消息到ctabbar的,其中LPARAM就可以保存按钮标签对象。这样标签栏就可以实现对底下标签的操作。以下就是按钮发送通知消息给标签栏的实现细节。
{
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
CRect rcItem = lpDrawItemStruct->rcItem;
HWND hWnd = lpDrawItemStruct->hwndItem;
UINT nState = lpDrawItemStruct->itemState;
HDC hDC = lpDrawItemStruct->hDC;
CDC dc;
dc.Attach(hDC);
dc.SetBkMode(TRANSPARENT);
CString strTitle;
GetWindowText(strTitle);
if (m_bSel)
{
if(!m_bmpBmpBkgnd.IsNull())
{
m_bmpBmpBkgnd.Draw(dc.m_hDC, CRect(0,0,10, m_bmpBmpBkgnd.GetHeight()),
CRect(m_bmpBmpBkgnd.GetWidth()/4*2, 0, m_bmpBmpBkgnd.GetWidth()/4*2+10, m_bmpBmpBkgnd.GetHeight()));
m_bmpBmpBkgnd.Draw(dc.m_hDC, CRect(4,0,rcItem.right-10, m_bmpBmpBkgnd.GetHeight()),
CRect(m_bmpBmpBkgnd.GetWidth()/4*2+10, 0, m_bmpBmpBkgnd.GetWidth()/4*3-10, m_bmpBmpBkgnd.GetHeight()));
m_bmpBmpBkgnd.Draw(dc.m_hDC, CRect(rcItem.right-10,0,rcItem.right, m_bmpBmpBkgnd.GetHeight()),
CRect(m_bmpBmpBkgnd.GetWidth()/4*3-10, 0, m_bmpBmpBkgnd.GetWidth()/4*3, m_bmpBmpBkgnd.GetHeight()));
}
if(!m_bmpIcon.IsNull())
{
m_bmpIcon.Draw(dc.m_hDC, CRect(4, 4, 4+m_bmpIcon.GetWidth(), 4+m_bmpIcon.GetHeight()),
CRect(0,0,m_bmpIcon.GetWidth(), m_bmpIcon.GetHeight()));
}
CRect rcLabel;
rcLabel.left = rcItem.left+4*2+16;
rcLabel.top = rcItem.top;
rcLabel.right = rcItem.right - (4*2+16);
rcLabel.bottom = rcItem.bottom;
dc.SetTextColor(RGB(0xff,0xff,0xff));
dc.DrawText(strTitle, &rcLabel, DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS);
if(m_bOverCloseButton)
{
if(!m_bmpClose.IsNull())
{
m_bmpClose.Draw(dc.m_hDC,
CRect(rcItem.Width()-m_bmpClose.GetWidth()/3-5,
(rcItem.Height()-m_bmpClose.GetHeight())/2,rcItem.Width()-2,
(rcItem.Height()-m_bmpClose.GetHeight())/2+m_bmpClose.GetHeight()),
CRect(m_bmpClose.GetWidth()/3, 0, m_bmpClose.GetWidth()/3*2, m_bmpClose.GetHeight()));
}
}
else
{
if(!m_bmpClose.IsNull())
{
m_bmpClose.Draw(dc.m_hDC, CRect(rcItem.Width()-m_bmpClose.GetWidth()/3-5,
(rcItem.Height()-m_bmpClose.GetHeight())/2,rcItem.Width()-2,
(rcItem.Height()-m_bmpClose.GetHeight())/2+m_bmpClose.GetHeight()),
CRect(0, 0, m_bmpClose.GetWidth()/3, m_bmpClose.GetHeight()));
}
}
}
else
{
if(!m_bmpBmpBkgnd.IsNull())
{
m_bmpBmpBkgnd.Draw(dc.m_hDC, CRect(0,0,10, m_bmpBmpBkgnd.GetHeight()),
CRect(0, 0, 10, m_bmpBmpBkgnd.GetHeight()));
m_bmpBmpBkgnd.Draw(dc.m_hDC, CRect(10,0,rcItem.right-10, m_bmpBmpBkgnd.GetHeight()),
CRect(10, 0, m_bmpBmpBkgnd.GetWidth()/4-10, m_bmpBmpBkgnd.GetHeight()));
m_bmpBmpBkgnd.Draw(dc.m_hDC, CRect(rcItem.right-10,0,rcItem.right, m_bmpBmpBkgnd.GetHeight()),
CRect(m_bmpBmpBkgnd.GetWidth()/4-10, 0, m_bmpBmpBkgnd.GetWidth()/4, m_bmpBmpBkgnd.GetHeight()));
}
if(!m_bmpIcon.IsNull())
{
m_bmpIcon.Draw(dc.m_hDC, CRect(4, 4, 4+m_bmpIcon.GetWidth(), 4+m_bmpIcon.GetHeight()),
CRect(0,0,m_bmpIcon.GetWidth(), m_bmpIcon.GetHeight()));
}
CRect rcLabel;
rcLabel.left = rcItem.left + 4*2+16;
rcLabel.top = rcItem.top;
rcLabel.right = rcItem.right - (4*2+16);
rcLabel.bottom = rcItem.bottom;
dc.SetTextColor(RGB(0xff,0xff,0xff));
dc.DrawText(strTitle, &rcLabel, DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS);
}
dc.Detach();
}#define NM_TBSEL WM_USER+1
总体的设计思想是:标签栏应该用一个数组来存放标签,但由于一个标签又是跟一个CchildFrame一一对应起来,两者应该可以相互访问,效率比较高的话可以用cmap映射来实现。本设计采用一个结构体来存储这两个关键要素。
void CTabButton::OnBnClicked()
{
CTabBar* pTabBar = (CTabBar*)GetParent();
pTabBar->SendMessage(NM_TBSEL, 0, (LPARAM)this);
}
HRESULT CTabBar::OnNmTbSel(WPARAM wParam, LPARAM lParam)
{
CTabButton* pTabButton = (CTabButton*)lParam;
SetCurSel(pTabButton);
CChildFrame* pChildFrame = GetChildFrameByTabButton(pTabButton);
pChildFrame->MDIActivate();
pChildFrame->MDIMaximize();
return TRUE;
}typedef struct _TABINFO
这个标签栏就可以通过一个数组维护这样的结构。至于CchildFrame和CtabButton的对应关系可以通过下面来实现,不过是个轮询过程,的确很浪费时间。
{
CChildFrame* pChildFrame;
CTabButton* pTabButton;
}TABINFO, *PTABINFO;CTabButton* CTabBar::GetTabButtonByChildFrame(CChildFrame* pChildFrame)
以下是我实现的浏览器的一个截图,并附有一个vs2003的demo,具体细节可以察看这个demo。
{
int nCount = m_ptrArray.GetCount();
for(int i=0; i<nCount; i++)
{
TABINFO* pTabInfo = (TABINFO*)m_ptrArray.GetAt(i);
if(pTabInfo->pChildFrame==pChildFrame)
{
return pTabInfo->pTabButton;
}
else
{
continue;
}
}
return NULL;
}
CChildFrame* CTabBar::GetChildFrameByTabButton(CTabButton* pTabButton)
{
int nCount = m_ptrArray.GetCount();
for(int i=0; i<nCount; i++)
{
TABINFO* pTabInfo = (TABINFO*)m_ptrArray.GetAt(i);
if(pTabInfo->pTabButton==pTabButton)
{
return pTabInfo->pChildFrame;
}
else
{
continue;
}
}
return NULL;
}
Tags:基于 CDialogBar IE
编辑录入:爽爽 [复制链接] [打 印]更多精彩
赞助商链接