您的位置:首页 > 其它

使用mfc CWnd 自绘实现一个类似于QQ好友的一个控件

2013-01-14 17:50 501 查看
使用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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐