文章转自:http://www.gymsaga.com/project/607.html
还是先看效果吧
face界面
功能:字体格式的设置
创建全局的CHARFORMAT2对象,初始化它
ZeroMemory(&m_cfRich,sizeof m_cfRich);
m_cfRich.cbSize = sizeof CHARFORMAT2;
m_cfRich.yHeight = 18*12;
m_cfRich.dwMask = CFM_COLOR | CFM_SIZE | CFM_FACE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
m_cfRich.dwEffects = CFE_ALLCAPS;
m_cfRich.crTextColor = RGB(0,0,0);
StrCpy(m_cfRich.szFaceName,TEXT("宋体"));
调用一下SetRichFormat
void CChatDlg::SetRichFormat()
{
m_SendEdit.SetSel(0, -1);
m_SendEdit.SetWordCharFormat(m_cfRich);
m_SendEdit.SetSel(-1, -1);
m_SendEdit.SetFocus();
}
接下来就是m_cfRich的设置,先看一下系统字体是怎么获取的
BOOL CALLBACK CChatDlg::EnumFamScreenCallBackEx(ENUMLOGFONTEX* pelf, NEWTEXTMETRICEX* lpntm, int FontType, LPVOID pThis)
{
if (FontType&RASTER_FONTTYPE)
{
return TRUE;
}
CString str;
if (FontType & TRUETYPE_FONTTYPE)
{
str=((pelf)->elfLogFont.lfFaceName);
}
int i=((CChatDlg *)pThis)->m_comFont.AddString(str);
return TRUE;
}
BOOL CChatDlg::EnumerateFonts()
{
HDC hDC;
hDC=::GetWindowDC(NULL);
LOGFONT lf;
memset(&lf,0,sizeof(LOGFONT));
lf.lfCharSet=GB2312_CHARSET;
if (!::EnumFontFamiliesEx(hDC,&lf,(FONTENUMPROC)EnumFamScreenCallBackEx,(LPARAM)this,(DWORD) 0))
{
return FALSE;
}
::ReleaseDC(NULL,hDC);
CString strCurrentFont;
CString strPreFont=TEXT("");
int nCount=m_comFont.GetCount();
// 删除重复项
for (int i=nCount;i>0;i--)
{
m_comFont.GetLBText((i-1),strCurrentFont);
if (strCurrentFont==strPreFont)
{
m_comFont.DeleteString(i-1);
}
strPreFont=strCurrentFont;
}
// 删除@字体 循环次序 为 从高到低 因为删除某个元素后, 各个位置可能改变
nCount=m_comFont.GetCount();
for (int i=nCount-1;i>=0;i--)
{
m_comFont.GetLBText(i,strCurrentFont);
strCurrentFont=strCurrentFont.Left(1);
if (strCurrentFont==TEXT("@"))
{
m_comFont.DeleteString(i);
}
}
//设置当前选中项
CString temp;
for (int i=0;i
使用EnumFontFamiliesEx,关于此函数定义请参阅MSDN
关于字体加粗等操作,此处已加粗为例,其他雷同
static bool bBold = true;
if ( bBold )
m_cfRich.dwEffects |= CFE_BOLD;
else
m_cfRich.dwEffects &= ~CFE_BOLD;
bBold = !bBold;
SetRichFormat();
只需要为dwEffects 修改一下属性即可
再说一下这QQ的Face面板,看一下头文件代码
#ifndef EXPRESSION_HEAD_FILE
#define EXPRESSION_HEAD_FILE
#pragma once
//////////////////////////////////////////////////////////////////////////////////
//类型定义
//类说明
class CExpression;
class CExpressionManager;
class CExpressionControl;
//表情信息
struct tagExpressionInfo
{
INT nLength[3]; //描述长度
TCHAR szExpression[3][8]; //表情描述
};
//解释结果
struct tagTranslateResult
{
//数据变量
INT nStartPos; //开始位置
INT nDescribeLen; //描述长度
//对象变量
CExpression * pExpressionItem; //表情信息
};
//数组定义
typedef CArray CExpressionArray; //表情数组
//////////////////////////////////////////////////////////////////////////////////
//表情接口
interface IExpressionSink
{
//选择表情
virtual VOID OnExpressionSelect(CExpression * pExpression, tagExpressionInfo * pExpressionInfo)=NULL;
};
//////////////////////////////////////////////////////////////////////////////////
//聊天表情
class CExpression
{
//友元定义
friend class CExpressionControl;
friend class CExpressionManager;
//变量定义
protected:
WORD m_wIndex; //索引标志
CGIFImage m_ExpressionItem; //表情资源
tagExpressionInfo m_ExpressionInfo; //表情信息
//函数定义
protected:
//构造函数
CExpression();
//析构函数
virtual ~CExpression();
//功能函数
public:
//获取索引
WORD GetIndex() { return m_wIndex; }
//获取信息
tagExpressionInfo *GetExpressionInfo(){ return &m_ExpressionInfo; }
//获取路径
bool GetExpressionPath(LPTSTR pszImagePath, WORD wMaxCount);
//设置表情
bool SetExpressionInfo(WORD wIndex, tagExpressionInfo&ExpressionInfo);
};
//////////////////////////////////////////////////////////////////////////////////
//表情管理
class CExpressionManager
{
//组件变量
protected:
CExpressionArray m_ExpressionArray; //表情数组
//静态变量
protected:
static CExpressionManager * m_pExpressionManager; //对象指针
//函数定义
public:
//构造函数
CExpressionManager();
//析构函数
virtual ~CExpressionManager();
//功能函数
public:
//加载表情
bool LoadExpression();
//解释表情
bool TranslateString(LPCTSTR pszString, tagTranslateResult&TranslateResult);
//表情信息
public:
//表情数目
WORD GetExpressionCount();
//枚举表情
CExpression * GetExpressionItem(WORD wIndex);
//静态函数
public:
//获取对象
static CExpressionManager * GetInstance() { return m_pExpressionManager; }
};
//////////////////////////////////////////////////////////////////////////////////
//表情窗口
class CExpressionControl : public CDialog
{
//变量定义
protected:
WORD m_wItemCount; //表情数目
WORD m_wSelectIndex; //选择索引
//组件变量
protected:
IExpressionSink * m_pIExpressionSink; //表情接口
//函数定义
public:
//构造函数
CExpressionControl();
//析构函数
virtual ~CExpressionControl();
//功能函数
public:
//显示控件
VOID ShowExpression(CWnd * pParentWnd, INT nXPos, INT nYPos, IExpressionSink * pIExpressionSink);
//辅助函数
protected:
//绘画头像
VOID DrawExpression(CDC * pDC, WORD wItemIndex, bool bSelected);
//消息函数
public:
//重画消息
VOID OnPaint();
//绘画背景
BOOL OnEraseBkgnd(CDC * pDC);
//焦点消息
VOID OnKillFocus(CWnd * pNewWnd);
//鼠标消息
VOID OnLButtonDown(UINT nFlags, CPoint Point);
//光标消息
BOOL OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT uMessage);
DECLARE_MESSAGE_MAP()
};
//////////////////////////////////////////////////////////////////////////////////
#endif
我们定义了三个类,CExpression为单个Face的属性,CExpressionManager负责图像的加载使用,注意,此处使用了单例模式,所以在程序初始化的时候,在InitInstance中加载一遍资源即可,不需要每次打开聊天面板都加载资源,同时该类负责解析文本内图像标签,比如在QQ聊天面板中,我们输入/qq,此时出现的是一个亲亲的Gif,为了实现这个效果,我们在创建了配置文件,用来存储这些Face属性,文件格式如下
[Face]
PY000=wx
CN000=微笑
EN000=:)
这里我们定义了三种解析形式,当资源初始化加载的时候,我们也将这些信息存储到容器,这样当用户输出/wx或者/微笑或者/:)时,我们就可以通过遍历容器找到Face的ID,从而显示出Face了,当然此Face的界面仍然不是控件重绘出来的,绘制过程请看源码
关于头像的点击,这里我们定义了接口IExpressionSink,我们只需要重载将聊天面板继承该接口,重载该函数就可以获取该接口的内容,比如:
VOID CChatDlg::OnExpressionSelect( CExpression * pExpression, tagExpressionInfo * pExpressionInfo )
{
//获取路径
TCHAR szImagePath[MAX_PATH]=TEXT("");
pExpression->GetExpressionPath(szImagePath,CountArray(szImagePath));
CString strTime;
CTime tm =CTime::GetCurrentTime();
strTime=tm.Format(TEXT("%Y/%m/%d %X\n"));
strTime.Insert(0,TEXT("丄偙寵ル"));
CHARFORMAT2 cf2;
ZeroMemory(&cf2, sizeof(CHARFORMAT2));
cf2.cbSize = sizeof(CHARFORMAT2);
cf2.dwMask = CFM_COLOR | CFM_SIZE | CFM_FACE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
cf2.dwEffects = CFE_ALLCAPS |CFE_BOLD;
cf2.yHeight = 18*10;
cf2.crTextColor = RGB(0,128,64);
StrCpy(cf2.szFaceName,TEXT("微软雅黑"));
m_RecEdit.InsertString(strTime,cf2);
m_RecEdit.InsertImage(szImagePath);
m_RecEdit.ReplaceSel(TEXT("\n"));
}
这里,当用户选择了头像文件后,将头像插入到了返回消息的Rich中了,至于QQ插入到发送框中的效果,大家有兴趣的话可以继续做一下
今天有朋友问我,之前好友列表的滚动条是怎么绘制上去,那么细,其实,很简单,之前接触过DirectUI设计的很轻松就能知道工作原理,这里我们的好友列表用过CWnd派生,既然是通过窗口派生的,那么我们就能很轻松的在CWnd的一边绘制出这么一个滚动条,当然空有滚动条是不可以的,因为他控制不了界面的移动,所以,我们必须要监听到该CWnd的Windows消息,所有事情都全部到位了,那么我们就可以在这些消息事件中去实现我们想要的效果,可能到这里你还是不理解,那么举一个例子你可能会豁然开朗,还记得我们之前窗口重绘中,关于标题栏上面的按钮是怎么重绘的么,正是无非就是判断的鼠标消息,在WM_NCPAINT中绘制出了按钮,那么滚动条也是一样,只是滚动条包含按钮和滑块控件,使的大家感觉很难而已,当然为了代码的整洁,我们将滚动条单独的抽取出来,就形成了CScrollBarEx,到这里你应该明白了把,CScrollBarEx不是窗口,只是图片胡出来的
到这里我们的QQ重绘课程就结束了,关于QQ界面,如果有什么不懂的地方欢迎留言。
代码下载地址:http://pan.baidu.com/s/1mq19a
CSDN下载地址:http://download.csdn.net/detail/gym1039/6514673