原文:http://blog.csdn.net/rabbitguiming/archive/2009/05/03/4144278.aspx
QQ的上下拉的抽屉式控件估计已经深入民心了。课程设计作业需要,花了半天时间写了一个类似的控件,可以实现对每个抽屉按钮设置不同的视图控件,不过没有美化,只是有骨没有肉的控件。
//QQGroupCtrl.h文件
view plaincopy to clipboardprint?
#pragma once
#include <vector>
using namespace std;
// QQGroupCtrl
#define GROUPCTRL_HIGHT 22
#define GROUPCTRL_INITBUTTONID 90000//分组按钮的id号,自增
//QQ分组按钮
class QQGroupButton : public CButton
{
DECLARE_DYNAMIC(QQGroupButton)
public:
QQGroupButton();
virtual ~QQGroupButton();
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};
//分组按钮信息,关联按钮和窗口
class TGroupInfo
{
public:
TGroupInfo():m_szName(_T("")), m_pWnd(NULL){}
virtual ~TGroupInfo(){}
public:
CString m_szName; // 组名称
CWnd *m_pWnd; // 组窗口指针
QQGroupButton *Button; //自己
};
//分组控件,包含N个分组
class QQGroupCtrl : public CWnd
{
DECLARE_DYNAMIC(QQGroupCtrl)
public:
QQGroupCtrl();
virtual ~QQGroupCtrl();
protected:
DECLARE_MESSAGE_MAP()
public:
virtual BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect,
CWnd *pParentWnd, UINT uID);
bool AddOneGroup(TCHAR* szName, CWnd *pWnd = NULL);
void ChangeView(QQGroupButton* selectButton);
private:
vector<TGroupInfo*> m_vecGroups;// 组信息列表
int m_iDown; // 当前鼠标按下的索引
CRect m_rtClient; // 客户区大小
int m_initBUttonID; //初始化按钮id
};
#pragma once
#include <vector>
using namespace std;
// QQGroupCtrl
#define GROUPCTRL_HIGHT 22
#define GROUPCTRL_INITBUTTONID 90000//分组按钮的id号,自增
//QQ分组按钮
class QQGroupButton : public CButton
{
DECLARE_DYNAMIC(QQGroupButton)
public:
QQGroupButton();
virtual ~QQGroupButton();
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};
//分组按钮信息,关联按钮和窗口
class TGroupInfo
{
public:
TGroupInfo():m_szName(_T("")), m_pWnd(NULL){}
virtual ~TGroupInfo(){}
public:
CString m_szName; // 组名称
CWnd *m_pWnd; // 组窗口指针
QQGroupButton *Button; //自己
};
//分组控件,包含N个分组
class QQGroupCtrl : public CWnd
{
DECLARE_DYNAMIC(QQGroupCtrl)
public:
QQGroupCtrl();
virtual ~QQGroupCtrl();
protected:
DECLARE_MESSAGE_MAP()
public:
virtual BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect,
CWnd *pParentWnd, UINT uID);
bool AddOneGroup(TCHAR* szName, CWnd *pWnd = NULL);
void ChangeView(QQGroupButton* selectButton);
private:
vector<TGroupInfo*> m_vecGroups;// 组信息列表
int m_iDown; // 当前鼠标按下的索引
CRect m_rtClient; // 客户区大小
int m_initBUttonID; //初始化按钮id
};
//QQGroupCtrl.cpp文件
view plaincopy to clipboardprint?
// QQGroupCtrl.cpp : 实现文件
//
#include "stdafx.h"
#include "CustomQQ.h"
#include "QQGroupCtrl.h"
#include "Mmsystem.h"
// QQGroupButton
IMPLEMENT_DYNAMIC(QQGroupButton, CButton)
QQGroupButton::QQGroupButton()
{
}
QQGroupButton::~QQGroupButton()
{
}
BEGIN_MESSAGE_MAP(QQGroupButton, CButton)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void QQGroupButton::OnLButtonDown(UINT nFlags, CPoint point)
{
QQGroupCtrl* parent=(QQGroupCtrl*)this->GetParent();
parent->ChangeView(this);//分组按钮
CButton::OnLButtonDown(nFlags,point);
}
//
// QQGroupCtrl
IMPLEMENT_DYNAMIC(QQGroupCtrl, CWnd)
QQGroupCtrl::QQGroupCtrl()
{
m_initBUttonID=GROUPCTRL_INITBUTTONID;
m_iDown=0;//选中的分组索引
}
//析构
QQGroupCtrl::~QQGroupCtrl()
{
vector <TGroupInfo*>::iterator Iter=m_vecGroups.begin( ) ;
while (Iter != m_vecGroups.end( ))
{
TGroupInfo* groupinfo=*Iter;
if (groupinfo->Button)
{
delete groupinfo->Button;
}
if (groupinfo->m_pWnd)
{
delete groupinfo->m_pWnd;
}
Iter++ ;
}
}
BEGIN_MESSAGE_MAP(QQGroupCtrl, CWnd)
END_MESSAGE_MAP()
BOOL QQGroupCtrl::Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect,
CWnd *pParentWnd, UINT uID)
{
CWnd* pWnd = this;
return pWnd->Create(NULL, lpszWindowName, dwStyle, rect, pParentWnd, uID);
}
bool QQGroupCtrl::AddOneGroup(TCHAR* szName, CWnd *pWnd)
{
TGroupInfo* ginfo=new TGroupInfo;
ginfo->m_szName=CString(szName);
ginfo->m_pWnd=pWnd;
QQGroupButton* b=new QQGroupButton;
CRect rect(0,0,0,0);
b->Create(ginfo->m_szName,WS_CHILD|WS_VISIBLE,rect,this,m_initBUttonID);
ginfo->Button=b;
m_vecGroups.push_back(ginfo);
//每加进一个组,重新计算面板的大小
CRect groupClient;
GetClientRect(&groupClient);
int index=0;
int count=m_vecGroups.size();//总数
vector <TGroupInfo*>::iterator Iter;
for ( Iter = m_vecGroups.begin( ) ; Iter != m_vecGroups.end( ) ; Iter++ )
{
TGroupInfo* groupinfo=*Iter;
//计算按钮的坐标
if (index==0)
{
CPoint leftPoint(0,0);
CSize bsize(groupClient.Width(),GROUPCTRL_HIGHT);
CRect rect(leftPoint,bsize);
groupinfo->Button->MoveWindow(rect);
}
else
{
//从下往上数的高度是bottom-(count-index)*buttonHight
CSize bsize(groupClient.Width(),GROUPCTRL_HIGHT);
CPoint leftPoint(0,groupClient.bottom-(count-index)*GROUPCTRL_HIGHT);
CRect rect(leftPoint,bsize);
groupinfo->Button->MoveWindow(rect);
}
//计算按钮的所对应窗口的坐标,即传来的pwnd
if (groupinfo->m_pWnd)//移动到按钮的下面
{
CPoint leftPoint(0,index*GROUPCTRL_HIGHT+GROUPCTRL_HIGHT);
CSize bsize(groupClient.Width(),groupClient.Height()-count*GROUPCTRL_HIGHT);
CRect rect(leftPoint,bsize);
groupinfo->m_pWnd->MoveWindow(rect);
if (index==0)
{
groupinfo->m_pWnd->ShowWindow(SW_SHOW);
}
else
{
groupinfo->m_pWnd->ShowWindow(SW_HIDE);
}
}
index++;
m_initBUttonID++;
}
GetClientRect(&m_rtClient);
return true;
}
void QQGroupCtrl::ChangeView(QQGroupButton* selectButton)
{
vector <TGroupInfo*>::iterator Iter;
int selectIndex=0;
BOOL bPtIn=FALSE;//鼠标是否点中按钮
int count=m_vecGroups.size();
//得到上一次按钮,隐藏上次的pwnd
TGroupInfo* &lastinfo=m_vecGroups.at(m_iDown);
if (lastinfo->m_pWnd)
{
lastinfo->m_pWnd->ShowWindow(SW_HIDE);
}
//得到选择索引
for ( Iter = m_vecGroups.begin( ) ; Iter != m_vecGroups.end( ) ; Iter++ )
{
TGroupInfo* groupinfo=*Iter;
if (selectButton==groupinfo->Button)
{
bPtIn=TRUE;
break;
}
selectIndex++;
}
if (!bPtIn)//没有点中
{
return ;
}
int i=0;
for ( Iter = m_vecGroups.begin( ) ; Iter != m_vecGroups.end( ) ; Iter++ )
{
TGroupInfo* groupinfo=*Iter;
//所选择的分组以及其以上的所有分组,均上移
if (i<=selectIndex)
{
groupinfo->Button->MoveWindow(0,i*GROUPCTRL_HIGHT,m_rtClient.Width(),GROUPCTRL_HIGHT,TRUE);
}
//所选择的分组以下的分组下移,不包括自己
else
{
if (selectIndex+1<count)
groupinfo->Button->MoveWindow(0,m_rtClient.bottom-(count-i)*GROUPCTRL_HIGHT,m_rtClient.Width(),GROUPCTRL_HIGHT,TRUE);
}
i++;//索引值
}
//显示所选择的分组窗口
TGroupInfo* ¤tInfo=m_vecGroups.at(selectIndex);
if (currentInfo->m_pWnd)
{
currentInfo->m_pWnd->ShowWindow(SW_SHOW);//显示本次的pwnd
}
//保存选中的按钮索引
m_iDown=selectIndex;
//UpdateWindow和Invalidate都不行,只能这样刷新了
ShowWindow(SW_HIDE);
ShowWindow(SW_SHOW);
}
// QQGroupCtrl.cpp : 实现文件
//
#include "stdafx.h"
#include "CustomQQ.h"
#include "QQGroupCtrl.h"
#include "Mmsystem.h"
// QQGroupButton
IMPLEMENT_DYNAMIC(QQGroupButton, CButton)
QQGroupButton::QQGroupButton()
{
}
QQGroupButton::~QQGroupButton()
{
}
BEGIN_MESSAGE_MAP(QQGroupButton, CButton)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void QQGroupButton::OnLButtonDown(UINT nFlags, CPoint point)
{
QQGroupCtrl* parent=(QQGroupCtrl*)this->GetParent();
parent->ChangeView(this);//分组按钮
CButton::OnLButtonDown(nFlags,point);
}
//
// QQGroupCtrl
IMPLEMENT_DYNAMIC(QQGroupCtrl, CWnd)
QQGroupCtrl::QQGroupCtrl()
{
m_initBUttonID=GROUPCTRL_INITBUTTONID;
m_iDown=0;//选中的分组索引
}
//析构
QQGroupCtrl::~QQGroupCtrl()
{
vector <TGroupInfo*>::iterator Iter=m_vecGroups.begin( ) ;
while (Iter != m_vecGroups.end( ))
{
TGroupInfo* groupinfo=*Iter;
if (groupinfo->Button)
{
delete groupinfo->Button;
}
if (groupinfo->m_pWnd)
{
delete groupinfo->m_pWnd;
}
Iter++ ;
}
}
BEGIN_MESSAGE_MAP(QQGroupCtrl, CWnd)
END_MESSAGE_MAP()
BOOL QQGroupCtrl::Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect,
CWnd *pParentWnd, UINT uID)
{
CWnd* pWnd = this;
return pWnd->Create(NULL, lpszWindowName, dwStyle, rect, pParentWnd, uID);
}
bool QQGroupCtrl::AddOneGroup(TCHAR* szName, CWnd *pWnd)
{
TGroupInfo* ginfo=new TGroupInfo;
ginfo->m_szName=CString(szName);
ginfo->m_pWnd=pWnd;
QQGroupButton* b=new QQGroupButton;
CRect rect(0,0,0,0);
b->Create(ginfo->m_szName,WS_CHILD|WS_VISIBLE,rect,this,m_initBUttonID);
ginfo->Button=b;
m_vecGroups.push_back(ginfo);
//每加进一个组,重新计算面板的大小
CRect groupClient;
GetClientRect(&groupClient);
int index=0;
int count=m_vecGroups.size();//总数
vector <TGroupInfo*>::iterator Iter;
for ( Iter = m_vecGroups.begin( ) ; Iter != m_vecGroups.end( ) ; Iter++ )
{
TGroupInfo* groupinfo=*Iter;
//计算按钮的坐标
if (index==0)
{
CPoint leftPoint(0,0);
CSize bsize(groupClient.Width(),GROUPCTRL_HIGHT);
CRect rect(leftPoint,bsize);
groupinfo->Button->MoveWindow(rect);
}
else
{
//从下往上数的高度是bottom-(count-index)*buttonHight
CSize bsize(groupClient.Width(),GROUPCTRL_HIGHT);
CPoint leftPoint(0,groupClient.bottom-(count-index)*GROUPCTRL_HIGHT);
CRect rect(leftPoint,bsize);
groupinfo->Button->MoveWindow(rect);
}
//计算按钮的所对应窗口的坐标,即传来的pwnd
if (groupinfo->m_pWnd)//移动到按钮的下面
{
CPoint leftPoint(0,index*GROUPCTRL_HIGHT+GROUPCTRL_HIGHT);
CSize bsize(groupClient.Width(),groupClient.Height()-count*GROUPCTRL_HIGHT);
CRect rect(leftPoint,bsize);
groupinfo->m_pWnd->MoveWindow(rect);
if (index==0)
{
groupinfo->m_pWnd->ShowWindow(SW_SHOW);
}
else
{
groupinfo->m_pWnd->ShowWindow(SW_HIDE);
}
}
index++;
m_initBUttonID++;
}
GetClientRect(&m_rtClient);
return true;
}
void QQGroupCtrl::ChangeView(QQGroupButton* selectButton)
{
vector <TGroupInfo*>::iterator Iter;
int selectIndex=0;
BOOL bPtIn=FALSE;//鼠标是否点中按钮
int count=m_vecGroups.size();
//得到上一次按钮,隐藏上次的pwnd
TGroupInfo* &lastinfo=m_vecGroups.at(m_iDown);
if (lastinfo->m_pWnd)
{
lastinfo->m_pWnd->ShowWindow(SW_HIDE);
}
//得到选择索引
for ( Iter = m_vecGroups.begin( ) ; Iter != m_vecGroups.end( ) ; Iter++ )
{
TGroupInfo* groupinfo=*Iter;
if (selectButton==groupinfo->Button)
{
bPtIn=TRUE;
break;
}
selectIndex++;
}
if (!bPtIn)//没有点中
{
return ;
}
int i=0;
for ( Iter = m_vecGroups.begin( ) ; Iter != m_vecGroups.end( ) ; Iter++ )
{
TGroupInfo* groupinfo=*Iter;
//所选择的分组以及其以上的所有分组,均上移
if (i<=selectIndex)
{
groupinfo->Button->MoveWindow(0,i*GROUPCTRL_HIGHT,m_rtClient.Width(),GROUPCTRL_HIGHT,TRUE);
}
//所选择的分组以下的分组下移,不包括自己
else
{
if (selectIndex+1<count)
groupinfo->Button->MoveWindow(0,m_rtClient.bottom-(count-i)*GROUPCTRL_HIGHT,m_rtClient.Width(),GROUPCTRL_HIGHT,TRUE);
}
i++;//索引值
}
//显示所选择的分组窗口
TGroupInfo* ¤tInfo=m_vecGroups.at(selectIndex);
if (currentInfo->m_pWnd)
{
currentInfo->m_pWnd->ShowWindow(SW_SHOW);//显示本次的pwnd
}
//保存选中的按钮索引
m_iDown=selectIndex;
//UpdateWindow和Invalidate都不行,只能这样刷新了
ShowWindow(SW_HIDE);
ShowWindow(SW_SHOW);
}
原理不太难,在AddOneGroup传来的视图控件位置,是根据按钮的多少事先来调整的。而你每次按分组控件时候,所按分组以及以上的分组全部上移,所按分组的下移部分全部下移。同时,先保存一个上次按钮的索引,来隐藏上次的视图,再显示新的所选索引的视图即可。
使用代码:
QQGroupCtrl* gCtrl=new QQGroupCtrl;
gCtrl->Create(L"group",WS_VISIBLE | WS_CHILD, rect, this, 10000);
gCtrl->AddOneGroup(L"我的好友",NULL);
gCtrl->AddOneGroup(L"我的群组",NULL);
gCtrl->AddOneGroup(L"最近联系人",NULL);
gCtrl->AddOneGroup(L"自定义",NULL);
效果图
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/rabbitguiming/archive/2009/05/03/4144278.aspx