文章转自: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<m_comFont.GetCount();i++) { m_comFont.GetLBText(i,temp); if (temp.Compare(m_cfRich.szFaceName)==0) { m_comFont.SetCurSel(i); break; } } return TRUE; }
使用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<CExpression *,CExpression *> 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