使用mfc CWnd 自绘实现一个类似于QQ好友的一个控件
来源:互联网 发布:gbk转utf java 编辑:程序博客网 时间:2024/05/02 04:51
使用mfc CWnd 自绘实现一个类似于QQ好友的一个控件
使用CWnd自绘控件其实挺简单的,可以从CWnd派生一个自己命名的类,
响应鼠标移动,鼠标按下事件,在这些响应的事件中,使用双缓存的方式重绘控件的DC;
界面就可以根据用户的操作做出对应的修改。基本原理就是如此。点击打开链接
以QQ的好友管理控件为例,源代码下载地址:http://download.csdn.net/download/weiweiloong/4991943
需要绘制的信息保存在一个std::list中,包含要绘制节点的,图片,坐标值,文字信息等;
用户每次操作后,修改list中缓存的节点值,然后根据这些节点值重新计算修改节点信息值。接着通知界面重绘。
具体请参考源码:
#pragma once
#include <map>
#include <list>
#include <string>
#include <atlimage.h>
using namespace std;
// CCatalogCtrl
/*********************** 好友分组管理 *********************/
// 成员节点
typedef struct _MemberNode
{
int iIndex;
int iParentIndex; // 所属目录编号
string strName;
string strDeclaration; // 好友个性宣言
// 好友信息窗口
// 好友聊天窗口
bool bOnline; // 好友是否在线
bool bSelect; // 节点被选中(高亮)
bool bPress; // 节点被按下(是否放大显示)
CImage imgMemberPic; // 成员头像
int iItemHeight;
int iItemWidth;
int iUserState; // 用户状态
CPoint iItemPos; // 元素位置
CPoint iImgPos; // 头像位置
CPoint iNamePos; // 用户名位置
CPoint iDeclarationPos; // 个性宣言位置
}MemberNode;
// 分组节点
typedef struct _GroupNode
{
int iIndex;
int iMemNum; // 好友人数
int iOnlineNum; // 好友在线人数
string strGroupName; // 分组名称
bool bGroupState; // 分组状态(展开/合并)
CPoint iItemPos; // 元素位置
CPoint iImgPos; // 图片位置
CPoint iInfoPos; // 信息位置
list<MemberNode*> lstMember; // 小组成员
~_GroupNode()
{
// 清空成员链表
list<MemberNode*>::iterator iPos, iEnd;
iEnd = lstMember.end();
for( iPos = lstMember.begin(); iPos != iEnd; iPos++)
{
delete(*iPos);
}
lstMember.clear();
}
}GroupNode;
class CScrollHelper;
// 好友列表控件
class CCatalogCtrl : public CWnd
{
DECLARE_DYNAMIC(CCatalogCtrl)
public:
CCatalogCtrl( CWnd *parentWnd);
virtual ~CCatalogCtrl();
protected:
DECLARE_MESSAGE_MAP()
private:
CPen m_PenItemFrame; // 节点框架
CBrush m_BrushItemRect; // 节点被选中填充画刷
CBrush m_BrushItemMove; // 鼠标移动过程元素的颜色
CDC m_TempDC; // 记录要绘图的部分DC内容1
CFont m_TextFont; // 桌面文字字体
CImage m_imgGroupOpen; // 分组展开图标
CImage m_imgGroupClose; // 分组合并图标
int m_iChildHeight; // 分组高度
int m_iChildWidth; // 分组宽度
int m_iCtrlHeight; // 控件高度
int m_iCtrlWidth; // 控件宽度
CPoint m_CtrlPos; // 控件绘制相对位置
int m_iAllItemsNum; // 控件所有节点个数
private:
list<GroupNode*> m_lstGroup; // 分组信息
CScrollHelper *m_pScrollHelper; // 滚动条类
private:
int ClearGroupLst(); // 清空分组链表
int ClearMemberLst(); // 清空成员链表
int CurrPosAttribute( CPoint pos); // 当前点击位置是分组、节点、空白
int InitCtrl(); // 初始化当前控件
int ProcessDrawInfo( CDC &TempDC); // 初始化绘制信息
void DrawScrollInfo( CDC &TempDC); // 绘制滚动条改变后的窗口视图
int ReCaculateItemsPos( CDC &TempDC); // 某个节点元素改变后,重新调整所有节点位置
public:
bool InsertNewGroup( int iIndex, string strName); // 插入一个分组
bool ModifyGroupName( int iIndex, string strName); // 修改一个分组名称
bool DelCurrGroup( int iIndex); // 删除一个分组
bool InsertNewItem( int iGroupIndex, MemberNode *pMemInfo); // 插入一个好友
bool DelCurrItem( int iMumIndex); // 删除一个好友
bool MoveItem( int iItemIndex, int iSrcGroup, int iDeskGroup); // 将一个好友从一个分组移动到另外一个分组
private:
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnPaint();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message);
public:
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
};
// CatalogCtrl.cpp : 实现文件
//
#include "stdafx.h"
#include "CCatalogDlg.h"
#include "CatalogCtrl.h"
#include ".\catalogctrl.h"
#include "ScrollHelper.h"
#define ITEM_HEIGHT 25 // 默认状态元素高度
/****************************** 局部相对位置 *****************************/
#define PIC_GROUP_POS_X 4 // 分组图像位置x
#define PIC_GROUP_POS_Y 9 // 分组图像位置y
#define PIC_MEMBER_POS_X 0 // 分组成员图像位置 x
#define PIC_MEMBER_POS_Y 0 // 分组成员图像位置 y
#define INFO_MEMBER_POS_X 25 // 分组成员名称位置 x
#define INFO_MEMBER_POS_Y 0 // 分组成员名称位置 y
#define DECLAR_MEM_POS_X 0 // 分组成员个性宣言位置x
#define DECLAR_MEM_POS_Y 0 // 分组成员个性宣言位置y
// CCatalogCtrl
IMPLEMENT_DYNAMIC(CCatalogCtrl, CWnd)
CCatalogCtrl::CCatalogCtrl( CWnd *parentWnd)
{
m_TextFont.CreateFont(14, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Arial"));
// 边框画笔
m_PenItemFrame.CreatePen( PS_SOLID, 1, RGB( 250, 0, 200));
m_imgGroupOpen.Load( theApp.GetPath() + "GroupOpen.PNG");
m_imgGroupClose.Load( theApp.GetPath() + "GroupClose.PNG");
m_CtrlPos.x = 0;
m_CtrlPos.y = 0;
m_iCtrlHeight = 350; // 控件高度
m_iCtrlWidth = 300; // 控件宽度
m_iAllItemsNum = 0;
// 给当前窗口创建滚动条对象
m_pScrollHelper = new CScrollHelper;
m_pScrollHelper ->AttachWnd(this);
// 创建带滚动条的窗口
Create( NULL, _T("CatalogCtrl"), WS_CHILD | WS_VISIBLE |WS_HSCROLL | WS_VSCROLL,
CRect(0, 0, 0, 0), parentWnd, 0, NULL);
InitCtrl();
}
CCatalogCtrl::~CCatalogCtrl()
{
delete m_pScrollHelper;
ClearGroupLst();
}
BEGIN_MESSAGE_MAP(CCatalogCtrl, CWnd)
ON_WM_TIMER()
ON_WM_SIZE()
ON_WM_RBUTTONDOWN()
ON_WM_PAINT()
ON_WM_CREATE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_MOUSEMOVE()
ON_WM_VSCROLL()
ON_WM_MOUSEACTIVATE()
ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()
// CCatalogCtrl 消息处理程序
void CCatalogCtrl::OnTimer(UINT nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CWnd::OnTimer(nIDEvent);
}
void CCatalogCtrl::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
}
void CCatalogCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CWnd::OnRButtonDown(nFlags, point);
}
void CCatalogCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
CBitmap graph;
CDC TempDC;
CRect rcClient;
GetClientRect(&rcClient);
TempDC.CreateCompatibleDC(&dc);
graph.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
CBitmap *pOldBitmap = (CBitmap*)TempDC.SelectObject(&graph);
// 绘制滚动条移动后的界面
DrawScrollInfo( TempDC);
dc.BitBlt( 0, 0, rcClient.right, rcClient.bottom, &TempDC, 0, 0, SRCCOPY);
TempDC.SelectObject(pOldBitmap);
TempDC.DeleteDC();
}
int CCatalogCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
return 0;
}
void CCatalogCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
list<GroupNode*>::iterator iPos, iEnd;
iEnd = m_lstGroup.end();
GroupNode *pGroup = NULL;
for( iPos = m_lstGroup.begin(); iPos != iEnd; iPos++)
{
pGroup = (*iPos);
// 判断点击位置在哪个节点
if((point.x >= pGroup ->iItemPos.x)&&(point.x <= (pGroup ->iItemPos.x + m_iChildWidth))
&&(point.y >= pGroup ->iItemPos.y) &&(point.y <= (pGroup ->iItemPos.y + m_iChildHeight)))
{
pGroup ->bGroupState = !(pGroup ->bGroupState);
break;
}
// 遍历分组下的成员链表
list<MemberNode*>::iterator ipos, iend;
iend = pGroup ->lstMember.end();
MemberNode *pMember = NULL;
for( ipos = pGroup ->lstMember.begin(); ipos != iend; ipos++)
{
pMember = ( *ipos);
if((point.x >= pMember ->iItemPos.x)&&(point.x <= (pMember ->iItemPos.x + m_iChildWidth))
&&(point.y >= pMember ->iItemPos.y) &&(point.y <= (pMember ->iItemPos.y + m_iChildHeight)))
{
pMember ->iItemHeight = ITEM_HEIGHT*2;
pMember ->bPress = true;
}
else
{
pMember ->iItemHeight = ITEM_HEIGHT;
pMember ->bPress = false;
}
}
}
Invalidate();
CWnd::OnLButtonDown(nFlags, point);
}
void CCatalogCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CWnd::OnLButtonDblClk(nFlags, point);
}
void CCatalogCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
list<GroupNode*>::iterator iPos, iEnd;
iEnd = m_lstGroup.end();
GroupNode *pGroup = NULL;
for( iPos = m_lstGroup.begin(); iPos != iEnd; iPos++)
{
pGroup = (*iPos);
// 遍历分组下的成员链表
list<MemberNode*>::iterator ipos, iend;
iend = pGroup ->lstMember.end();
MemberNode *pMember = NULL;
for( ipos = pGroup ->lstMember.begin(); ipos != iend; ipos++)
{
pMember = ( *ipos);
if((point.x >= pMember ->iItemPos.x)&&(point.x <= (pMember ->iItemPos.x + m_iChildWidth))
&&(point.y >= pMember ->iItemPos.y) &&(point.y <= (pMember ->iItemPos.y + pMember ->iItemHeight)))
{
pMember ->bSelect = true;
}
else
{
pMember ->bSelect = false;
}
}
}
Invalidate();
CWnd::OnMouseMove(nFlags, point);
}
// 滚动条垂直方向滑动
void CCatalogCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_pScrollHelper ->OnVScroll(nSBCode, nPos, pScrollBar);
Invalidate();
}
int CCatalogCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
int status = CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
// We handle this message so that when user clicks once in the
// window, it will be given the focus, and this will allow
// mousewheel messages to be directed to this window.
SetFocus();
return status;
}
BOOL CCatalogCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
BOOL wasScrolled = m_pScrollHelper->OnMouseWheel(nFlags, zDelta, pt);
return wasScrolled;
}
/********************************* private:*********************************/
// 清空分组链表
int CCatalogCtrl::ClearGroupLst()
{
list<GroupNode*>::iterator iPos, iEnd;
iEnd = m_lstGroup.end();
for( iPos = m_lstGroup.begin(); iPos != iEnd; iPos++)
{
delete(*iPos);
}
m_lstGroup.clear();
return 1;
}
// 当前点击位置是分组、节点、空白
int CCatalogCtrl::CurrPosAttribute( CPoint pos)
{
return 1;
}
// 绘制滚动条改变后的窗口视图
void CCatalogCtrl::DrawScrollInfo( CDC &TempDC)
{
int x = 0;
int y = 0;
// Offset starting position due to scrolling.
int iCurrX = m_pScrollHelper ->GetScrollPos().cx;
int iCurrY = m_pScrollHelper ->GetScrollPos().cy;
x -= iCurrX;
y -= iCurrY;
// 元素起始坐标
m_CtrlPos.x = x;
m_CtrlPos.y = y;
// 重新计算每个元素的位置
ReCaculateItemsPos( TempDC);
}
// 某个节点元素改变后,重新调整所有节点位置
int CCatalogCtrl::ReCaculateItemsPos( CDC &TempDC)
{
ProcessDrawInfo( TempDC);
return 0;
}
// 初始化当前控件
int CCatalogCtrl::InitCtrl()
{
m_iChildHeight = ITEM_HEIGHT;
GroupNode *pGroupItem = new GroupNode();
pGroupItem ->bGroupState = false;
pGroupItem ->iItemPos.x = 0;
pGroupItem ->iItemPos.y = 0;
pGroupItem ->iImgPos.x = 0;
pGroupItem ->iImgPos.y = 0;
pGroupItem ->iIndex = 1;
pGroupItem ->iMemNum = 0;
pGroupItem ->iOnlineNum = 0;
pGroupItem ->strGroupName = "我的好友";
m_iAllItemsNum ++;
m_lstGroup.push_back( pGroupItem);
return 1;
}
// 初始化绘制信息
int CCatalogCtrl::ProcessDrawInfo( CDC &TempDC)
{
CRect rcClient;
this ->GetClientRect(rcClient);
m_iChildWidth = rcClient.Width();
//TempDC.RoundRect( 0, 0, rcClient.Width(), rcClient.Height(), 0, 0);
CPen *pOldPen = TempDC.SelectObject( &m_PenItemFrame);
CFont *pOldFont = TempDC.SelectObject( &m_TextFont);
list<GroupNode*>::iterator iPos, iEnd;
iEnd = m_lstGroup.end();
CDC imgDC;
imgDC.CreateCompatibleDC(&TempDC);
GroupNode *pGroup = NULL;
for( iPos = m_lstGroup.begin(); iPos != iEnd; iPos++)
{
pGroup = (*iPos);
pGroup ->iItemPos.x = m_CtrlPos.x;
pGroup ->iItemPos.y = m_CtrlPos.y;
if( pGroup ->bGroupState == false)
{
imgDC.SelectObject( m_imgGroupClose);
TRACE("关闭图形\r\n");
}
else
{
imgDC.SelectObject( m_imgGroupOpen);
TRACE("展开图形\r\n");
}
TempDC.RoundRect( 0, m_CtrlPos.y, m_iChildWidth, m_CtrlPos.y + m_iChildHeight, 0, 0);
TempDC.StretchBlt( 4, m_CtrlPos.y + 9, 13, 13, &imgDC,0, 0, 13, 13, SRCCOPY);
TempDC.SetBkMode( TRANSPARENT);
CString strGroupInfo = (*iPos) ->strGroupName.c_str();
strGroupInfo += "(";
CString strNum;
strNum.Format("%d", (*iPos) ->iMemNum);
strGroupInfo += strNum;
strGroupInfo += ",";
strNum.Format("%d", (*iPos) ->iOnlineNum);
strGroupInfo += strNum;
strGroupInfo += ")";
TempDC.TextOut( 18, m_CtrlPos.y + 8, strGroupInfo);
// 下一个元素绘制位置
m_CtrlPos.y += m_iChildHeight;
// 绘制组成员
if( pGroup ->bGroupState == true)
{
list<MemberNode*>::iterator ipos, iend;
iend = pGroup ->lstMember.end();
MemberNode *pMember = NULL;
int iItemRow = 1;
for( ipos = pGroup ->lstMember.begin(); ipos != iend; ipos++)
{
pMember = ( *ipos);
int iHeight = pMember ->iItemHeight - 6;
TempDC.RoundRect( 0, m_CtrlPos.y, m_iChildWidth, m_CtrlPos.y + pMember ->iItemHeight, 0, 0);
if( pMember ->bSelect == true)
{
CBrush brush( RGB( 100, 150, 150));
TempDC.FillRect( CRect( 1, m_CtrlPos.y + 1, m_iChildWidth - 1, m_CtrlPos.y + pMember ->iItemHeight - 1), &brush);
}
imgDC.SelectObject( pMember ->imgMemberPic);
int iPicWidth = pMember ->imgMemberPic.GetWidth();
int iPicHeight= pMember ->imgMemberPic.GetHeight();
TempDC.SetStretchBltMode( HALFTONE);
TempDC.StretchBlt( 3, m_CtrlPos.y + 3, iHeight, iHeight, &imgDC,0, 0, iPicWidth, iPicHeight, SRCCOPY);
CString strMumInfo = pMember ->strName.c_str();
if( pMember ->bPress == true)
{
TempDC.TextOut( iHeight + 6, m_CtrlPos.y + 2, strMumInfo);
strMumInfo = "(";
strMumInfo += pMember ->strDeclaration.c_str();
strMumInfo += ")";
TempDC.TextOut( iHeight + 6, m_CtrlPos.y + 16, strMumInfo);
}
else
{
strMumInfo += "(";
strMumInfo += pMember ->strDeclaration.c_str();
strMumInfo += ")";
TempDC.TextOut( iHeight + 6, m_CtrlPos.y + 2, strMumInfo);
}
pMember ->iItemPos.x = 0;
pMember ->iItemPos.y = m_CtrlPos.y;
m_CtrlPos.y += pMember ->iItemHeight;
iItemRow++;
}
}
}
TempDC.SelectObject( pOldPen);
TempDC.SelectObject( pOldFont);
return 1;
}
/********************************* public:*********************************/
// 插入一个分组
bool CCatalogCtrl::InsertNewGroup( int iIndex, string strName)
{
m_iChildHeight = ITEM_HEIGHT;
GroupNode *pGroupItem = new GroupNode();
pGroupItem ->bGroupState = false;
pGroupItem ->iItemPos.x = 0;
pGroupItem ->iItemPos.y = 0;
pGroupItem ->iImgPos.x = 0;
pGroupItem ->iImgPos.y = 0;
pGroupItem ->iIndex = (int)m_lstGroup.size() + 1;
pGroupItem ->iMemNum = 0;
pGroupItem ->iOnlineNum = 0;
pGroupItem ->strGroupName = strName;
m_lstGroup.push_back( pGroupItem);
Invalidate();
m_iAllItemsNum++;
return false;
}
// 修改一个分组名称
bool CCatalogCtrl::ModifyGroupName( int iIndex, string strName)
{
return false;
}
// 删除一个分组
bool CCatalogCtrl::DelCurrGroup( int iIndex)
{
m_iAllItemsNum++;
return false;
}
// 插入一个好友
bool CCatalogCtrl::InsertNewItem( int iGroupIndex, MemberNode *pMemInfo)
{
list<GroupNode*>::iterator iPos, iEnd;
iEnd = m_lstGroup.end();
GroupNode *pGroup = NULL;
for( iPos = m_lstGroup.begin(); iPos != iEnd; iPos++)
{
pGroup = (*iPos);
if( iGroupIndex == pGroup ->iIndex)
{
pGroup ->iMemNum++;
pGroup ->lstMember.push_back( pMemInfo);
m_iAllItemsNum ++;
break;
}
}
Invalidate();
if( m_iAllItemsNum > 14)
{
m_iCtrlHeight += ITEM_HEIGHT;
m_pScrollHelper ->SetDisplaySize( 0, m_iCtrlHeight);
}
return false;
}
// 删除一个好友
bool CCatalogCtrl::DelCurrItem( int iMumIndex)
{
return false;
}
// 将一个好友从一个分组移动到另外一个分组
bool CCatalogCtrl::MoveItem( int iItemIndex, int iSrcGroup, int iDeskGroup)
{
return false;
}
- 使用mfc CWnd 自绘实现一个类似于QQ好友的一个控件
- 【Android】实现类似于QQ将好友的头像用作快捷方式。
- MFC——自绘控件(从CWND派生的edit控件)
- 一个QQ好友的个人说明
- 分享一个类似于MFC的字符串类
- VS2010/MFC:一个简易的恶作剧QQ好友的聊天轰炸机
- MFC控件自绘篇---实现QQ风格
- Pyqt4下使用QListWidget控件实现的类似QQ好友列表效果控件
- MFC仿QQ好友列表控件
- mfc自绘控件的实现 收藏
- mfc自绘控件的实现
- iOS开发UI篇章使用UItableview完成一个简单的QQ好友列表(一)
- UI基础第十四弹:使用UItableview完成一个简单的QQ好友列表(一)
- UI基础第十四弹:使用UItableview完成一个简单的QQ好友列表(一)
- 使用UItableview完成一个简单的QQ好友列表(二)
- iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)
- iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(二)
- iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)
- DelphiXE3下的字符串
- jquery插件开发
- 自动为分区表增加分区存储过程
- TLD取经之路(4)-- 始于足下--tldInit.m中涉及到的相关函数
- spring注解方式自动注入接口的实现子类
- 使用mfc CWnd 自绘实现一个类似于QQ好友的一个控件
- 基础加强_注解Annotation
- ios多台电脑,共用一个证书密密钥
- SAP BO与SAP业务系统的连接方式
- exit与return的区别
- Struts2返回JSON数据的具体应用范例
- java mail 笔记
- C++之const成员函数
- Java 图片加水印