本文共 21871 字,大约阅读时间需要 72 分钟。
引入duilib时请确保引入头文件开始时先引入COMUTIL.H头文件
#include "COMUTIL.H" #include "UIlib.h"
duilib基本程序结构
在stdafx.h文件中加入
#include "COMUTIL.H"#include "UIlib.h"using namespace DuiLib;
#ifndef LoginView_h__#define LoginView_h__class LoginView : public WindowImplBase// 只能放在最后,否则其消息路由出问题{public: DUI_DECLARE_MESSAGE_MAP()public: LoginView(void); ~LoginView(void); void on_btn_click( DuiLib::TNotifyUI &msg ); virtual CDuiString GetSkinFolder() { return _T ("skin\\"); } virtual CDuiString GetSkinFile() { return _T("login.xml"); } virtual LPCTSTR GetWindowClassName( void ) const { return _T("LoginView"); }};#endif // LoginView_h__
#include "StdAfx.h"#include "LoginView.h"DUI_BEGIN_MESSAGE_MAP(LoginView, WindowImplBase)DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,on_btn_click)DUI_END_MESSAGE_MAP()LoginView::LoginView(void){}LoginView::~LoginView(void){}void LoginView::on_btn_click( DuiLib::TNotifyUI &msg ){}
// chatme.cpp : 定义应用程序的入口点。//#include "stdafx.h"#include "chatme.h"#include "LoginView.h"int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ CPaintManagerUI::SetInstance(hInstance); CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath()); HRESULT Hr = ::CoInitialize(NULL); if( FAILED(Hr) ) return 0; LoginView login_view; login_view.Create(NULL, _T("LoginView"), UI_WNDSTYLE_DIALOG, WS_EX_STATICEDGE | WS_EX_APPWINDOW); login_view.CenterWindow(); CPaintManagerUI::MessageLoop(); ::CoUninitialize(); return 0;}
问:如何把资源放入zip? 答: 先SetResourcePath设置资源目录,再SetResourceZip设置压缩资源文件名 问:如何设置窗体的初始化大小? 答:设置XML文件的Window标签的size属性。 问:如何设置鼠标可拖动窗体的范围大小? 答:设置XML文件的Window标签的caption属性。 问:如何设置窗体可以通过拖动边缘改变大小? 答:在窗体创建函数的第三个参数设置为UI_WNDSTYLE_FRAME才可响应拖动改变大小,和双击标题事件。 问:为何鼠标移动到边缘没有改变窗体大小的箭头出现,不能通过拖动改变窗体大小? 答:设置window标签的sizebox属性,例如sizebox="2,2,2,2" 问:窗体不可双击最大化如何实现? 答:在窗体创建函数的第三个参数设置为UI_WNDSTYLE_DIALOG。 问:应用程序exe图标如何设置? 答:使用窗体成员函数SetIcon,参数为资源icon的id。 问:初始化时,最大化窗体如何实现? 答:调用窗体的SendMessage给窗体发送最大化消息SC_MAXIMIZE,SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE,0); 问:动态改变窗体的大小如何实现? 答:使用窗体函数ResizeClient,参数分别重设的宽和高。 问:如何设置窗体屏幕居中显示? 答:使用窗体的CenterWindow函数。 问:窗体透明度如何设置? 答:设置window标签属性bktrans="true" alpha="200" alpha的值为0-255。这种设置是全体窗体透明度,所有控件都将变透明。 如果想单纯设置背景透明度控件不透明度,可以制作半透明的背景图片,设置window标签的bktrans="true",并且不设置alpha属性,切记!此时背景透明,其它控件不透明。 单独设置某个控件的透明度,可以使用图片的fade属性,或者mask属性。fade表示设置图片透明度,取值0-255。mask为设置透明的颜色。 问:默认设置的图片为拉伸平铺模式,如何设置不拉伸显示? 答:设置图片的source和dest属性,soure="左,上,右,下" dest="左,上,右,下" 表示将source区域的图片显示到按钮的dest区域上。这里的右和下是指坐标,不是指宽度和高度。右=左+width.下=top+height。 问:如何设置选定编辑框文字的背景颜色? 答:设置nativebkcolor属性。
问:如何设置按钮的鼠标悬浮时的字体颜色? 答:设置按钮的hottextcolor属性,相对的还有pushedtextcolor和focustextcolor. 问:如何设置按钮按下时字体的颜色? 答:设置按钮的pushedtextcolor属性,相对的还有hottextcolor和focustextcolor.
问:如何自定义xml控件? 答:自定义控件和复杂的控件类型都是由简单基本控件组成。 在写好一个自定义的控件xml模板后, CDialogBuilder dlg_builder; CControlUI * pControl = dlg_builder.Create("item.xml"); 注意这里的item.xml要放在主界面的xml所在的文件夹内,并且无需在指定路径了。 该函数返回一个CControlUI的一个句柄,得到这样一个句柄就是一个控件了。 如果要获取复杂控件的某个子控件的句柄,然后想通过该句柄改变子控件的状态。 首先给这个子控件取一个名字,然后可以通过pControl的FindSubControl("name")来获取该 控件的句柄了。得到句柄后就可以设置它的所有属性了,例如 pbtn->SetAttribute(_T("normalimage"), _T("file='images\\downlist_ok.png' dest='20,14,32,26'"));就能更改它的状态图片了。 问:多线程下如何更改dui的界面信息? 答:线程里不要操作界面的信息,应该通过SendMessage或者PostMessage给界面的m_hWnd发送自定义消息。然后在界面的消息循环里面在做操作界面的动作。 自定义处理的消息处理函数可以从重写方法 LRESULT MyWnd::HandleCustomMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { //这里处理完后,bHandled置true 返回基类,让基类去操作 return WindowImplBase::HandleCustomMessage(uMsg, wParam, lParam, bHandled); } 具体可以如下使用: #define ON_PERCENT_MSG WM_USER + 500 然后在线程函数中发送消息给界面 int DownloadView::on_percent( double percent, int index, INT_PTR user_data ) { ::SendMessage(m_hWnd, ON_PERCENT_MSG, (WPARAM)&percent, (LPARAM)user_data); return 0; } 最后在界面消息循环中进行处理消息 LRESULT DownloadView::HandleCustomMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { switch (uMsg) { case ON_PERCENT_MSG: // 处理界面相关的操作 break; default: break; } return WindowImplBase::HandleCustomMessage(uMsg, wParam, lParam, bHandled); } 问:如何让使用duilib的win32工程支持MFC? 答: 1、在stdafx.h加入以下 #define VC_EXTRALEAN #include <afxwin.h> // MFC 核心组件和标准组件 #include <afxext.h> // MFC 扩展 #include <afxdisp.h> // MFC 自动化类 #ifndef _AFX_NO_OLE_SUPPORT #include <afxdtctl.h> // MFC 对 Internet Explorer 4 公共控件的支持 #endif #ifndef _AFX_NO_AFXCMN_SUPPORT #include <afxcmn.h> // MFC 对 Windows 公共控件的支持 #endif // _AFX_NO_AFXCMN_SUPPORT 2、在程序初始化main的开始加入mfc的初始化。 // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); return 1; } 3、设置页属性-->常规-->使用mfc设置为 Use MFC in a Shared DLL 4、设置C++-->代码生成-->运行时库根据debug或者release设置为MDD或者MD。#pragma once#define DUI_MFCCTRL_COMMAND_MSG _T("MFC_CTRL_NOTIFY_MSG")#define DUI_MFCCTRL_NOTIFY_MSG _T("MFC_CTRL_COMMAND_MSG")// 封装MFC控件到DUI控件中,实现duilib中嵌入MFC控件// 通过维护一个HWND实现class CDUIMFCCtrlWrapper : public DuiLib::CControlUI, public DuiLib::IMessageFilterUI{public: CDUIMFCCtrlWrapper(void) : m_hWnd(NULL), m_bAddedMessageFilter(FALSE){} ~CDUIMFCCtrlWrapper(void){} // 绑定 BOOL Attach(HWND hWndNew); HWND Detach();protected: // 控制显示 virtual void SetInternVisible(bool bVisible = true); // 控制位置 virtual void SetPos(RECT rc); // 对控件消息的分派,例如对发送给如对命令消息和通知消息进行分派 // 通过SendNotify实现,可以在OnNotify中进行响应。 virtual LRESULT MessageHandler( UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled ); virtual void SetManager( DuiLib::CPaintManagerUI* pManager, DuiLib::CControlUI* pParent, bool bInit = true );protected: HWND m_hWnd; BOOL m_bAddedMessageFilter; // 防止重复设置消息监听};
#include "StdAfx.h"#include "DUIMFCCtrlWrapper.h"using namespace DuiLib;void CDUIMFCCtrlWrapper::SetManager( CPaintManagerUI* pManager, CControlUI* pParent, bool bInit /*= true */ ){ if (pManager && !m_bAddedMessageFilter) { m_bAddedMessageFilter = TRUE; // 设置本控件封装能接收到消息,从而进行分派 pManager->AddMessageFilter(this); } CControlUI::SetManager(pManager, pParent, false);}LRESULT CDUIMFCCtrlWrapper::MessageHandler( UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled ){ bHandled = TRUE; // 如果是命令消息和通知消息等MFC控件消息则不用再传递下去 if (uMsg == WM_NOTIFY) { NMHDR* pNMHDR = (NMHDR*)lParam; HWND hWndCtrl = pNMHDR->hwndFrom; UINT nId = LOWORD(wParam); int nCode = pNMHDR->code; ASSERT(NULL != hWndCtrl); ASSERT(::IsWindow(hWndCtrl)); typedef struct _CtrlNotifyStruct { NMHDR* pNMHDR; int nCode; }CtrlNotifyStruct; CtrlNotifyStruct ns; ns.pNMHDR = pNMHDR; ns.nCode = nCode; m_pManager->SendNotify(this, DUI_MFCCTRL_COMMAND_MSG, WPARAM(nId), LPARAM(&ns)); } else if (WM_COMMAND == uMsg) { UINT nID = LOWORD(wParam); HWND hWndCtrl = (HWND)lParam; int nCode = HIWORD(wParam); m_pManager->SendNotify(this, DUI_MFCCTRL_NOTIFY_MSG, nID, nCode); } //else if(XTPWM_PROPERTYGRID_NOTIFY == uMsg) //{ // m_pManager->SendNotify(this, DUI_MFCCTRL_NOTIFY_MSG, wParam, lParam); //} else { // 否则该消息需要继续传递下去 bHandled = FALSE; return 1; } return 0;}void CDUIMFCCtrlWrapper::SetPos( RECT rc ){ __super::SetPos(rc); ::SetWindowPos(m_hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);}void CDUIMFCCtrlWrapper::SetInternVisible( bool bVisible /*= true*/ ){ __super::SetInternVisible(bVisible); ::ShowWindow(m_hWnd, bVisible);}HWND CDUIMFCCtrlWrapper::Detach(){ HWND hWnd = m_hWnd; m_hWnd = NULL; return hWnd;}BOOL CDUIMFCCtrlWrapper::Attach( HWND hWndNew ){ if (! ::IsWindow(hWndNew)) { return FALSE; } m_hWnd = hWndNew; return TRUE;}
#include "stdafx.h"#include "DUIMFCCtrlWrapper.h"#include "Mycug.h"using namespace DuiLib;class CFrameWindowWnd : public WindowImplBase{ DUI_DECLARE_MESSAGE_MAP()public: protected: virtual CDuiString GetSkinFolder() { return CPaintManagerUI::GetInstancePath(); } virtual CDuiString GetSkinFile() { return _T("test.xml"); } virtual LPCTSTR GetWindowClassName( void ) const { return _T("CFrameWindowWnd"); } virtual CControlUI* CreateControl( LPCTSTR pstrClass ) { if (_tcscmp(pstrClass, _T("MfcCtrl")) == 0) { return new CDUIMFCCtrlWrapper(); } return NULL; } void OnClick(TNotifyUI& msg) { CDuiString sCtrlName = msg.pSender->GetName(); if( sCtrlName == _T("closebtn") ) { PostMessage(WM_QUIT, 0, 0); return; } else if( sCtrlName == _T("minbtn")) { SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); return; } else if( sCtrlName == _T("maxbtn")) { SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); return; } else if( sCtrlName == _T("restorebtn")) { SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); return; } } virtual void InitWindow() { CDUIMFCCtrlWrapper* pCtrl = (CDUIMFCCtrlWrapper*)m_PaintManager.FindControl(_T("grid")); if (pCtrl) { m_GridCtrl.CreateGrid(WS_CHILD|WS_VISIBLE,CRect(0,0,0,0),CWnd::FromHandle(GetHWND()),1234); pCtrl->Attach(m_GridCtrl.GetSafeHwnd()); } } MyCug m_GridCtrl;};
DUI_BEGIN_MESSAGE_MAP(CFrameWindowWnd, WindowImplBase) DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick)DUI_END_MESSAGE_MAP()int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow){ // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); return 1; } CPaintManagerUI::SetInstance(hInstance); CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath()); HRESULT Hr = ::CoInitialize(NULL); if( FAILED(Hr) ) return 0; CFrameWindowWnd* pFrame = new CFrameWindowWnd(); if( pFrame == NULL ) return 0; pFrame->Create(NULL, _T("测试"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE); pFrame->CenterWindow(); pFrame->ShowWindow(true); CPaintManagerUI::MessageLoop(); ::CoUninitialize(); return 0;}duilib绘图部分 图像的绘制大部分使用了绘制引擎的DrawImageString函数,该函数实现知道图像名称绘制到指定目标区域的功能 并且通过指定pStrModify的属性能够进行不同需求的绘制,例如,仅扣取源图像的某个位置绘制到目标的某个区域、 设置四边圆角绘制、为bmp等指定某种颜色为透明颜色、平铺或者拉伸绘制、设置透明度等。 DrawImageString最终又调用CRenderEngine::DrawImage进行绘制。
// hDc HDC句柄// CPaintManagerUI句柄// rc 目标画布的大小// rcPaint 绘制区域// pStrImage 为源图像的名称,不需提供路径,函数内部会自动加上资源位置的路径,路径为CPaintManagerUI::SetResourcePath设置的路径。// pStrModify 设置绘制属性,含义为:// 2、file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' // mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false'// source和dest表示从源图像的source区域贴到目标图像的dest区域,mask表示让某颜色为透明色,例如mask="#FF000000",设置黑色为透明色。// xtiled,ytiled 设置为true表示横向和纵向的图像不拉伸显示而是平铺显示// hole 为true表示不绘制中间部分,为某些场合提高性能bool CRenderEngine::DrawImageString(HDC hDC, CPaintManagerUI* pManager, const RECT& rc, const RECT& rcPaint, LPCTSTR pStrImage, LPCTSTR pStrModify){ if ((pManager == NULL) || (hDC == NULL)) return false; // 1、aaa.jpg // 2、file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' // mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false'}void CRenderEngine::DrawImage(HDC hDC, HBITMAP hBitmap, const RECT& rc, const RECT& rcPaint, const RECT& rcBmpPart, const RECT& rcCorners, bool alphaChannel, BYTE uFade, bool hole, bool xtiled, bool ytiled)duilib中所有元素的显示都在整个消息循环的WM_PAINT中进行绘制。 第一次绘制之前会发送一个名为_T("windowinit")的通知。 然后为CControlUI的绘制,绘制的顺序为:背景颜色->背景图->状态图->文本->边框 会顺序调用CControlUI的以下函数。
PaintBkColor(hDC); // 绘制背景颜色PaintBkImage(hDC); // 绘制背景图PaintStatusImage(hDC); // 绘制状态图PaintText(hDC); // 绘制文本PaintBorder(hDC); // 绘制边框所以有需求在界面上动态绘制一些内容时,可以通过CControlUI进行子类化,然后重写PaintStatusImage, 在PaintStatusImage里面进行绘图操作。绘制时可以直接调用duilib绘制引擎进行绘制,比较便捷。当然也可以使用GDI+等库直接进行绘制。 duilib的图像文件的加载是通过一个开源库stb_image.c实现。 并只使用了该库的stbi_load_from_memory和stbi_image_free这两个功能函数。 该库的链接地址:http://nothings.org/stb_image.c 该库对图片的格式的支持情况说明如下: JPEG 支持Baseline标准型的JPEG,不支持渐进式Progressive的JPEG PNG 仅支持8位的png图像 BMP 不支持1bpp的bmp,不支持行程编码RLE的bmp PSD 紧显示合成的图像,不支持额外的通道 GIF *comp always reports as 4-channel 所以在duilib中使用的图片时需要注意到这个限制,否则有些图片将显示不出来。 duilib中把图片文件加载到内存后,由库stb_image.c解析并最终转为DIB处理。 duilib支持从图片文件、从包含图片文件的压缩包文件和VC资源中载入图片。 载入后最终返回TImageInfo结构体指针,包含HBITMAP、图像宽度、图像高度、alpha通道、mask和资源类型等信息。 载入函数为:TImageInfo* CRenderEngine::LoadImage(STRINGorID bitmap, LPCTSTR type, DWORD mask); duilib中所有的图片资源只从加载一次,然后保存在CPaintManagerUI中的m_mImageHash成员中,该成员为一个 hashmap存储TImageInfo*信息,当已经存在要绘制显示的图片信息时,直接从map中获取显示,否则从文件加载 在添加到map中。 问:如何管理duilib界面皮肤资源? 答:把所有XML文件在同一个目录下,图片资源另外放在一个地方。当如下进行布局资源时,skin目录为设置资源的目录,imagedir1目录放置1.xml文件利用到的图片资源,imagedir2放置2.xml文件利用到的资源。 然后1.xml文件文件中的图片资源名称需要加上相对于当前xml文件的路径信息即可。例如:normalimage="imagedir1/1.png"这样的形式。使用设计器为XML添加资源时会自动转化为相对路径。 skin/ 1.xml 2.xml imagedir1/ 1.png 2.png imagedir2/ 1.png 2.png 需注意一个问题,那就是字体的管理。字体对于一个窗口来说是全局的,索引从0开始,font = 0表示使用排在第一位的字体。如果一个界面被拆分为多个XML文件管理,并且当每个单独的文件自己添加字体时,联合起来的时候,会跟其它的字体发生冲突,即索引不是原来的索引了。索引得从全局来看。按照全局的方式进行设置好索引后,对于每个xml文件的预览操作时,又看不到所见即所得的效果了。 关于设计器 问题:为什么给控件设置name属性后保存,但该属性信息丢失? 答:设置控件名称,需要注意,名称中不能含有该控件类名的字段,例如设置其中一个选项按钮的名字为OptionUIMyTest,将不会被保存,因为默认的OptionUI1、OptionUI2等为设计器默认使用的名字当存在OptionUI时,设计器认为是默认的,并非用户设定,所以没有保存该信息到XML文件中。 问:如何把一个内容很长的xml文件分为拆分进行管理? 答:可以使用ChildLayout布局。指定xmlfile属性设置为引用的xml文件。例如以下例子,必须注意一个问题,就是tab_main.xml设置上<Window>标签,并且不要设置size属性,否则会跟主窗体冲突,该大小会作为主窗体的大小而发生错误。 <TabLayout name="MainTab" width="870" height="362"> <ChildLayout xmlfile="tab_main.xml" /> <VerticalLayout padding="1,0,1,1" bkcolor="#FFFFFFFF" /> </TabLayout> 除了使用ChildLayout布局外,可以使用Include标签,例如: <Include source="scrollbar.xml" />
转载地址:http://islws.baihongyu.com/