柚子快報(bào)激活碼778899分享:c++ SOUI總結(jié)之常用功能
柚子快報(bào)激活碼778899分享:c++ SOUI總結(jié)之常用功能
常用功能
XML特殊字符顯示
空格 ( )
Tab ( )
回車 ( )
換行 ( )
& (&)
< (<)
> (>)
‘ (')
“ (")
帶滾動(dòng)條的自動(dòng)滾動(dòng)
SRichEdit* pWnd = FindChildByName2
if (pWnd)
{
?? pWnd->ReplaceSel(strtext);
?? pWnd->SetScrollPos(TRUE,0x7fffffff,FALSE);
?? static int i = 0;
?? if (i > 0x7fffffff)
?? {
????? pWnd->SetWindowText(NULL);
????? i = 0;
?? }
?? i++;
}
控件的顯示與隱藏
?
?
?
???
?
模態(tài)對(duì)話框
uires.idx
??
xml
???
???????
???
???
??? ??
?? ??? ?? ?? ??????????? pointer_hour="img.clock:pointer_hour" ?? ??????????? pointer_minute="img.clock:pointer_minute" ?? ??????????? pointer_second="img.clock:pointer_second" ?? ??????? /> ?? ?????? ??? ??
???
頭文件
#pragma once
using namespace SOUI;
#include "helper/SMatrix.h"
namespace SOUI{
class SClock : public SWindow
{
?? SOUI_CLASS_NAME(SClock, L"clock")
public:
?? SClock();
?? enum {TIMER_REFRESH = 1};
protected:
??? SMatrix InitMatrix(double angle, CPoint ¢er);
??? double GetMinuteSecondAngle(int nValue);
??? double GetHourAngle(int nHour,int nMinute);
protected:
?? int? OnCreate(void*);
?? void OnPaint(SOUI::IRenderTarget * pRT);
?? void OnTimer(char cTimerID);
?? SOUI_MSG_MAP_BEGIN()
????? MSG_WM_PAINT_EX(OnPaint)
????? MSG_WM_TIMER_EX(OnTimer)
????? MSG_WM_CREATE(OnCreate)
?? SOUI_MSG_MAP_END()
protected:
?? SOUI_ATTRS_BEGIN()
????? ATTR_IMAGEAUTOREF(L"pointer_hour",?? pointer_hour, TRUE)
????? ATTR_IMAGEAUTOREF(L"pointer_minute", pointer_minute, TRUE)
????? ATTR_IMAGEAUTOREF(L"pointer_second", pointer_second, TRUE)
?? SOUI_ATTRS_END()
?? CAutoRefPtr
?? CAutoRefPtr
?? CAutoRefPtr
};
}
源文件
#include "stdafx.h"
#include "sclock.h"
namespace SOUI{
SClock::SClock()
{
}
void SClock::OnPaint(SOUI::IRenderTarget * pRT)
{
?? SWindow::OnPaint(pRT);
?? CRect rcClient;
?? GetClientRect(&rcClient);
?? CPoint center = rcClient.CenterPoint();
?? // 計(jì)算矩形
?? // 35 * 16
?? CRect rcDraw(center, SOUI::CPoint(center.x + 200, center.y + 32));
?? rcDraw.OffsetRect(-35, -16);
?? CRect rcSrc(0, 0, 200, 32);
??? SYSTEMTIME last_refresh_time;
??? ::GetLocalTime(&last_refresh_time);
??? {
??????? double angle = GetHourAngle(last_refresh_time.wHour,last_refresh_time.wMinute);
??????? SMatrix form = InitMatrix(angle,? center);
??????? pRT->SetTransform(&form, NULL);
??????? pRT->DrawBitmapEx(rcDraw, pointer_hour, &rcSrc, EM_STRETCH, 255);
??? }
??? {
??????? double angle = GetMinuteSecondAngle(last_refresh_time.wMinute);
??????? SMatrix form = InitMatrix(angle, center);
??????? pRT->SetTransform(&form, NULL);
??????? pRT->DrawBitmapEx(rcDraw, pointer_minute, &rcSrc, EM_STRETCH, 255);
??? }
??? {
??????? double angle = GetMinuteSecondAngle(last_refresh_time.wSecond);
??????? SMatrix form = InitMatrix(angle, center);
??????? pRT->SetTransform(&form, NULL);
????? ??pRT->DrawBitmapEx(rcDraw, pointer_second, &rcSrc, EM_STRETCH, 255);
??? }
?? pRT->SetTransform(&SMatrix());
}
SMatrix SClock::InitMatrix(double angle, CPoint ¢er)
{
??? //先平移到center,再旋轉(zhuǎn)angle,再平移-center
??? return SMatrix().translate((FLOAT)center.x,(FLOAT)center.y)
??????????????????? .rotate((FLOAT)angle-90)
??????????????????? .translate((FLOAT)-center.x,(FLOAT)-center.y);
}
double SClock::GetMinuteSecondAngle(int nValue)
{
??? return (double)nValue * ((double)360 / 60);
}
double SClock::GetHourAngle(int nHour,int nMinute)
{
?? double base = (double)(nHour % 12) * ((double)360 / 12);
?? base += (double)nMinute * ((double)360 / 12 / 60);
?? return base;
}
void SClock::OnTimer(char cTimerID)
{
?? Invalidate();
}
int SClock::OnCreate(void*)
{
?? SetTimer(TIMER_REFRESH, 200);
?? return 0;
}
}
調(diào)用
SHostDialog dlgClock(UIRES.LAYOUT.dlg_clock);
dlgClock.DoModal();
模態(tài)對(duì)話框2
uires.idx文件
??
Xml文件
?
?
???
?????
?????
?????
???
???
???
???
???
?
頭文件
#pragma once
namespace SOUI
{
??? class CFormatMsgDlg : public SHostDialog
??? {
??? public:
??????? CFormatMsgDlg(void);
??????? ~CFormatMsgDlg(void);
BOOL OnInitDialog(HWND wnd, LPARAM lInitParam);
??????? void OnOK();
??????? SStringT m_strMsg;? //消息XML
??????? int????? m_nRepeat; //重復(fù)次數(shù)
??????? EVENT_MAP_BEGIN()
??????????? EVENT_ID_COMMAND(IDOK,OnOK)
??????? EVENT_MAP_END()
BEGIN_MSG_MAP_EX(CFormatMsgDlg )
?? MSG_WM_INITDIALOG(OnInitDialog)
?? CHAIN_MSG_MAP(SHostDialog)
?? REFLECT_NOTIFICATIONS_EX()
END_MSG_MAP()
??? };
}
源文件
#include "stdafx.h"
#include "FormatMsgDlg.h"
namespace SOUI
{
??? CFormatMsgDlg::CFormatMsgDlg(void):SHostDialog(_T("layout:dlg_formatmsg"))
??? {
??????? m_nRepeat=1;
??? }
??? CFormatMsgDlg::~CFormatMsgDlg(void)
??? {
}
BOOL CFormatMsgDlg::OnInitDialog(HWND wnd, LPARAM lInitParam)
{
}
??? void CFormatMsgDlg::OnOK()
??? {
??????? SRichEdit *pEdit = FindChildByName2
??????? m_strMsg = pEdit->GetWindowText();
??????? SEdit *pRepeat = FindChildByName2
??????? SStringT strRepeat = pRepeat->GetWindowText();
??????? m_nRepeat = _tstoi(strRepeat);
??????? SHostDialog::OnOK();
??? }
}
調(diào)用
CFormatMsgDlg formatMsgDlg;
if(formatMsgDlg.DoModal()==IDOK)
{
}
非模態(tài)對(duì)話框
Xml文件
?
??
????
????
??????
??????
??????
??????
????
????
??????
??????
????
??
?
頭文件
#pragma once
class CSetSkinWnd:public SHostWnd
{
SOUI_CLASS_NAME(CSetSkinWnd, L"dlgserialset")
public:
?? CSetSkinWnd();
?? ~CSetSkinWnd();
?? void OnClose();
?? BOOL OnInitDialog(HWND wndFocus, LPARAM lInitParam);
public:
?? EVENT_MAP_BEGIN()
????? EVENT_NAME_COMMAND(L"btn_close",OnClose)
?? EVENT_MAP_END()
?? BEGIN_MSG_MAP_EX(CSetSkinWnd)
????? MSG_WM_INITDIALOG(OnInitDialog)
????? CHAIN_MSG_MAP(SHostWnd)? //注意這個(gè)消息放在最后
????? REFLECT_NOTIFICATIONS_EX()
?? END_MSG_MAP()
};
源文件
#include "stdafx.h"
#include "CSetSkinWnd.h"
CSetSkinWnd::CSetSkinWnd():SHostWnd(_T("LAYOUT:set_skin_wnd"))
{
}
CSetSkinWnd::~CSetSkinWnd()
{
}
BOOL CSetSkinWnd::OnInitDialog(HWND wndFocus, LPARAM lInitParam)
{
?? return TRUE;
}
void CSetSkinWnd::OnClose()
{
?? CSimpleWnd::DestroyWindow();
}
調(diào)用
m_dlgSetSkin = new CSetSkinWnd;
if (m_dlgSetSkin->m_hWnd)
{
?? SetForegroundWindow(m_dlgSetSkin->m_hWnd);
?? FlashWindow(m_dlgSetSkin->m_hWnd, TRUE);
}
else
{
?? m_dlgSetSkin->Create(NULL);
m_dlgSetSkin->SendMessage(WM_INITDIALOG);
?? m_dlgSetSkin->CenterWindow(GetDesktopWindow());
?? m_dlgSetSkin->ShowWindow(SW_SHOWNORMAL);
}
m_dlgSetSkin->Create(NULL);這條語(yǔ)句是創(chuàng)建的時(shí)候制定一個(gè)父窗口,如果不指定,那么在點(diǎn)擊主窗口的時(shí)候這個(gè)新建的窗口會(huì)被遮住,如果指定為m_hwnd主窗口,那么同樣會(huì)顯示在前面。
m_dlgSetSkin->CenterWindow(GetDesktopWindow());這條語(yǔ)句是讓新建的窗口顯示在屏幕的中間,如果想讓窗口顯示在主界面的中間,改為m_dlgSetSkin->CenterWindow(m_hwnd)即可。
在m_dlgSetSkin->SendMessage(WM_INITDIALOG);發(fā)送消息時(shí)可以帶上一些附加參數(shù),如
m_DlgSerialSet->SendMessage(WM_INITDIALOG, (WPARAM)this, (WPARAM)this);
然后在OnInitDialog中將它轉(zhuǎn)換過(guò)來(lái)
其實(shí)第一個(gè)參數(shù)wnd對(duì)應(yīng)WPARAM,第二個(gè)參數(shù)對(duì)應(yīng)lInitParam。
控件重繪篇
class SListBoxEx :public SListBox
{
public:
?? SOUI_CLASS_NAME(SListBoxEx, L"listboxex")
?? SListBoxEx(){}
?? virtual ~SListBoxEx(){}
protected:
?? void OnLButtonUp(UINT nFlags,CPoint pt);
?? SOUI_ATTRS_BEGIN()
?? SOUI_ATTRS_END()
?? SOUI_MSG_MAP_BEGIN()
?? MSG_WM_LBUTTONUP(OnLButtonUp)
?? SOUI_MSG_MAP_END()
};
注意:1.想要在繼承類中獲取鼠標(biāo)左鍵彈起事件,就需要添加SOUI_MSG_MAP_BEGIN()、MSG_WM_LBUTTONUP(OnLButtonUp)、SOUI_MSG_MAP_END()三條語(yǔ)句。
每寫(xiě)一個(gè)新的控件都要進(jìn)行注冊(cè)才能用,如:
SApplication *theApp=new SApplication(pRenderFactory,hInstance);
theApp->RegisterWindowClass
主窗口分流篇
Main.h文件
class CMainDlg : public SHostWnd
{
public:
?? CMainDlg(LPCTSTR xmlName);
?? ~CMainDlg();
??
protected:
?? int OnCreate(LPCREATESTRUCT lpCreateStruct);
?? BOOL OnInitDialog(HWND wndFocus, LPARAM lInitParam);
?? void OnClose();//關(guān)閉響應(yīng)函數(shù)
?? void OnMaximize();//最大化響應(yīng)函數(shù)
?? void OnRestore();//恢復(fù)響應(yīng)函數(shù)
??? void OnMinimize();//最小化響應(yīng)函數(shù)
protected:
?? //soui消息
?? EVENT_MAP_BEGIN()
????? CHAIN_EVENT_MAP_MEMBER(m_uiFlashDlg)
????? EVENT_NAME_COMMAND(L"btn_close", OnClose)
????? EVENT_NAME_COMMAND(L"btn_min", OnMinimize)
????? EVENT_NAME_COMMAND(L"btn_max", OnMaximize)
?????? EVENT_NAME_COMMAND(L"btn_restore", OnRestore)
?? EVENT_MAP_END()
?? //HostWnd真實(shí)窗口消息處理
?? BEGIN_MSG_MAP_EX(CMainDlg)
????? CHAIN_MSG_MAP_MEMBER(m_uiFlashDlg)
????? MSG_WM_CREATE(OnCreate)
????? MSG_WM_CLOSE(OnClose)
????? MSG_WM_INITDIALOG(OnInitDialog)
????? CHAIN_MSG_MAP(SHostWnd)
????? REFLECT_NOTIFICATIONS_EX()
?? END_MSG_MAP()
private:
?? BOOL???????? m_bLayoutInited;
?? CUIFlashDlg????? m_uiFlashDlg;
}
Main.cpp文件
CMainDlg::CMainDlg(LPCTSTR xmlName) : SHostWnd(xmlName)
{
?? m_bLayoutInited = FALSE;
}
CMainDlg::~CMainDlg()
{
}
int CMainDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
?? SetMsgHandled(FALSE);
?? return 0;
}
BOOL CMainDlg::OnInitDialog(HWND hWnd, LPARAM lParam)
{
?? theApp.m_pMainDlg = this;
?? m_uiFlashDlg.OnInit(this,m_hWnd);
?? ::SetTimer(m_hWnd,1,200,NULL);//創(chuàng)建定時(shí)器,同時(shí)會(huì)向其他類發(fā)送消息
?? return 0;
}
void CMainDlg::OnClose()
{
?? if(!m_uiFlashDlg.OnClose())
????? return;
??? theApp.m_singleInst.Release();
??? ::DestroyWindow(m_hWnd);
}
void CMainDlg::OnMaximize()
{
?? SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE);
}
void CMainDlg::OnRestore()
{
?? SendMessage(WM_SYSCOMMAND, SC_RESTORE);
}
void CMainDlg::OnMinimize()
{
?? SendMessage(WM_SYSCOMMAND, SC_MINIMIZE);
}
子窗口為父窗口承擔(dān)任務(wù)
CUIFlashDlg.h頭文件
class CUIFlashDlg
{
public:
?? CUIFlashDlg();
?? ~CUIFlashDlg();
?? void OnInit(SHostWnd* pRoot,HWND hWnd);
?? void OnDestory();
?? BOOL OnClose();
protected:
?? EVENT_MAP_BEGIN()
????? EVENT_CHECK_SENDER_ROOT(m_pHostWnd)
?? EVENT_MAP_BREAK()
?? BEGIN_MSG_MAP_EX(CUIFlashDlg)
????? MSG_WM_DESTROY(OnDestory)
?? END_MSG_MAP()
private:
?? HWND m_hWnd;
?? SHostWnd *m_pHostWnd;
};
CUIFlashDlg.cpp源文件
CUIFlashDlg::CUIFlashDlg()
{
?? m_pHostWnd = NULL;
?? m_hWnd = NULL;
}
CUIFlashDlg::~CUIFlashDlg()
{
}
void CUIFlashDlg::OnInit(SHostWnd* pRoot,HWND hWnd)
{
?? m_pHostWnd = pRoot;
?? m_hWnd = hWnd;
}
BOOL CUIFlashDlg::OnClose()
{
?? m_pHostWnd = NULL;
?? return TRUE;
}
void CUIFlashDlg::OnDestory()
{
?? SetMsgHandled(FALSE);
}
在 SOUI 中實(shí)現(xiàn) PreTranslateMessage
在 MFC 中,通??梢酝ㄟ^(guò)重載 CWnd::PreTranslateMessage 這樣一個(gè)虛函數(shù)來(lái)實(shí)現(xiàn)對(duì)一些窗口消息的預(yù)處理。多用于 tooltip 的顯示控制。
在 SOUI 中也實(shí)現(xiàn)了類似的機(jī)制。
要在 SOUI 中實(shí)現(xiàn) PreTranslateMessage,我們首先需要實(shí)現(xiàn)一個(gè)接口:
struct IMessageFilter
{
virtual BOOL PreTranslateMessage(MSG* pMsg) = 0;
};
可以看出,實(shí)現(xiàn)這個(gè)接口和在 MFC 中重載 PreTranslateMessage 是相同的道理。和 MFC 中只需要重載這個(gè)接口不同,在 SOUI 中,除了需要實(shí)現(xiàn) IMessageFilter 外,還需要向當(dāng)前的 MessageLoop 注冊(cè)該 IMessageFilter。
class SOUI_EXP SMessageLoop
{
public:
SArray
// Message filter operations
BOOL AddMessageFilter(IMessageFilter* pMessageFilter);
BOOL RemoveMessageFilter(IMessageFilter* pMessageFilter);
//...
};
上面是 SMessageLoop 兩個(gè)和 IMessageFilter 相關(guān)的方法。
SMessageLoop::AddMessageFilter 向當(dāng)前的 message loop 注冊(cè)一個(gè)
IMessageFilter;
SMessageLoop::RemoveMessageFilter 則向當(dāng)前的 message loop 注銷一個(gè)
IMessageFilter
剩下的問(wèn)題就是如何獲得當(dāng)前的 MessageLoop 了。
在 SHostWnd 或者 SHostDialog 中可以調(diào)用 SHostWnd::GetMsgLoop()方法獲得。
在 SWindow 中,則可以調(diào)用 SWindow::GetContainer()->GetMsgLoop()獲得。
使用示例可以參考 SDropDownWnd 的實(shí)現(xiàn)。
class SOUI_EXP SDropDownWnd : public SHostWnd, public IMessageFilter
{
//...
};
動(dòng)態(tài)的創(chuàng)建控件
1. 動(dòng)態(tài)創(chuàng)建按鈕第一種方式
XML文件
?
?
源代碼
void CMainDlg::OnBtn()
{
?? SWindow* pWnd = FindChildByName(L"wnd");
?? if (pWnd)
?? {
????? SWindow* pChild = pWnd->GetWindow(GSW_FIRSTCHILD);
????? while (pChild)
????? {
???????? SWindow* p = pChild->GetWindow(GSW_NEXTSIBLING);
???????? pChild->DestroyWindow();
???????? pChild = p;
????? }
????? for (int i = 0; i < 5; ++i)
????? {
???????? SStringW strXml;
???????? strXml.Format(L"", 1000 + i, i);
???????? SWindow* pRet = pWnd->CreateChildren(strXml);
???????? //訂閱事件
???????? pRet->GetEventSet()->subscribeEvent(EVT_LBUTTONDOWN, Subscriber(&CMainDlg::OnClick, this));
????? }
?? }
}
bool CMainDlg::OnClick(EventArgs* pArg)
{
?? SButton* pBtn = FindChildByID2
?? if (pBtn)
?? {
????? SStringW str;
????? str.Format(L"%d", pArg->idFrom);
????? SMessageBox(m_hWnd, str, L"ID", MB_OK);
?? }
?? return TRUE;
}
效果如下圖
2. 動(dòng)態(tài)創(chuàng)建按鈕第二種方式
SScrollView* pWnd = FindChildByName2
if (pWnd)
{
??? //首先移除所有子窗口
?? SWindow* pChild = pWnd->GetWindow(GSW_FIRSTCHILD);
?? while (pChild)
?? {
????? SWindow* pNext = pChild->GetWindow(GSW_NEXTSIBLING);
????? pChild->DestroyWindow();
????? pChild = pNext;
?? }
?? SStringT strXml;
?? pugi::xml_document xmlDoc;
?? for (int i = 0;i < 100;i++)
?? {
????? strXml.Format(L"",i + 1000000);
????? xmlDoc.reset();
????? if (!xmlDoc.load_buffer((LPCWSTR)strXml,wcslen(strXml) * sizeof(wchar_t),pugi::parse_default, pugi::encoding_utf16))
????? {
???????? return FALSE;
????? }
????? pChild = new SButton();
????? if (pChild)
????? {
???????? pWnd->InsertChild(pChild);
???????? pChild->InitFromXml(xmlDoc.first_child());
????? }
?? }
//訂閱事件
SButton* pBtn = pChild->FindChildByName2
pBtn->GetEventSet()->subscribeEvent(EVT_LBUTTONDOWN,Subscriber(&CCombineCmdDlg::OnDelete,this));
}
按鈕點(diǎn)擊的響應(yīng)事件
bool CCombineCmdDlg::OnDelete(EventArgs* pArgs)
{
?? SButton* pBtn = (SButton*)pArgs->sender;
?? SWindow* pChild = pBtn->GetParent()->GetParent();
?? SScrollView* pWnd = FindChildByName2
?? if (pWnd)
?? {
????? pWnd->DestroyChild(pChild);
?? }
?? pWnd->RequestRelayout(); //刪除按鈕之后需要重新布局
?? return true;
}
void OnCommandRange(UINT nID);
EVENT_ID_COMMAND_RANGE(1000000,2000000,OnCommandRange)
void CMainDlg::OnCommandRange(UINT nID)
{
?? SStringT str;
?? str.Format(L"ID:%d",nID);
?? SMessageBox(m_hWnd,str,L"提示",MB_OK);
}
按鈕右鍵響應(yīng)事件
pChild->GetEventSet()->subscribeEvent(EVT_CTXMENU,Subscriber(&CMainDlg::OnBtnRClick, this));? //訂閱右鍵點(diǎn)擊事件
bool CMainDlg::OnBtnRClick(EventArgs *pEvt)
{
?? EventCtxMenu *pET = sobj_cast
?? if (pET)
?? {
????? int id = pET->idFrom;
????? SMenu menu;
????? menu.LoadMenu(_T("menu"),_T("SMENU"));
?? ?? CPoint pt;
????? GetCursorPos(&pt);
????? int iR = menu.TrackPopupMenu(0, pt.x, pt.y, m_hWnd);
????? if (iR != 1)
????? {
???????? SLOGFMTE("TrackPopupMenu returns a failure: CEventHandlerEffect::OnBtnRClick(EventArgs *pEvt)");
???????? return FALSE;
????? }
?? }
?? return TRUE;
}
編輯框接受文件拖拽
#pragma once
#include
#include
class CTestDropTarget :public IDropTarget
{
public:
?? CTestDropTarget()
?? {
????? nRef = 0;
?? }
?? virtual ~CTestDropTarget() {}
?? //
?? // IUnknown
?? virtual HRESULT STDMETHODCALLTYPE QueryInterface(
????? /* [in] */ REFIID riid,
????? /* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
?? {
????? HRESULT hr = S_FALSE;
????? if (riid == __uuidof(IUnknown))
???????? *ppvObject = (IUnknown*)this, hr = S_OK;
????? else if (riid == __uuidof(IDropTarget))
???????? *ppvObject = (IDropTarget*)this, hr = S_OK;
????? if (SUCCEEDED(hr)) AddRef();
????? return hr;
?? }
?? virtual ULONG STDMETHODCALLTYPE AddRef(void)
?? {
????? return ++nRef;
?? }
?? virtual ULONG STDMETHODCALLTYPE Release(void)
?? {
????? ULONG uRet = --nRef;
????? if (uRet == 0) delete this;
????? return uRet;
?? }
?? //
?? // IDropTarget
?? virtual HRESULT STDMETHODCALLTYPE DragEnter(
????? /* [unique][in] */ __RPC__in_opt IDataObject *pDataObj,
????? /* [in] */ DWORD grfKeyState,
????? /* [in] */ POINTL pt,
????? /* [out][in] */ __RPC__inout DWORD *pdwEffect)
?? {
????? *pdwEffect = DROPEFFECT_LINK;
????? return S_OK;
?? }
?? virtual HRESULT STDMETHODCALLTYPE DragOver(
????? /* [in] */ DWORD grfKeyState,
????? /* [in] */ POINTL pt,
????? /* [out][in] */ __RPC__inout DWORD *pdwEffect)
?? {
????? *pdwEffect = DROPEFFECT_LINK;
????? return S_OK;
?? }
?? virtual HRESULT STDMETHODCALLTYPE DragLeave(void)
?? {
????? return S_OK;
?? }
protected:
?? int nRef;
};
class CTestDropTarget1 : public CTestDropTarget
{
protected:
?? SWindow *m_pEdit;
public:
?? CTestDropTarget1(SWindow *pEdit) :m_pEdit(pEdit)
?? {
????? if (m_pEdit) m_pEdit->AddRef();
?? }
?? ~CTestDropTarget1()
?? {
????? if (m_pEdit) m_pEdit->Release();
?? }
public:
?? virtual HRESULT STDMETHODCALLTYPE Drop(
????? /* [unique][in] */ __RPC__in_opt IDataObject *pDataObj,
????? /* [in] */ DWORD grfKeyState,
????? /* [in] */ POINTL pt,
????? /* [out][in] */ __RPC__inout DWORD *pdwEffect)
?? {
????? FORMATETC format =
????? {
???????? CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL
????? };
????? STGMEDIUM medium;
????? if (FAILED(pDataObj->GetData(&format, &medium)))
????? {
???????? return S_FALSE;
????? }
????? HDROP hdrop = static_cast
????? if (!hdrop)
????? {
???????? return S_FALSE;
????? }
????? bool success = false;
????? TCHAR filename[MAX_PATH];
????? success = !!DragQueryFile(hdrop, 0, filename, MAX_PATH);
????? DragFinish(hdrop);
????? GlobalUnlock(medium.hGlobal);
????? if (success && m_pEdit)
????? {
???????? m_pEdit->SetWindowText(filename);
????? }
????? *pdwEffect = DROPEFFECT_LINK;
????? return S_OK;
?? }
};
使用如下:
BOOL CMainDlg::OnInitDialog(HWND hWnd, LPARAM lParam)
{
?? m_bLayoutInited = TRUE;
?? ::RegisterDragDrop(m_hWnd, GetDropTarget());
?? SWindow* pWnd = FindChildByName(L"edit");
?? if (pWnd)
?? {
????? RegisterDragDrop(pWnd->GetSwnd(),new CTestDropTarget1(pWnd));
?? }
??
?? return 0;
}
效果如下:
從文件中創(chuàng)建窗口
1.文件資源準(zhǔn)備
?
2.test.xml
?
???
???
?
?
???
???
???
?
3.點(diǎn)擊按鈕創(chuàng)建
void CMainDlg::OnBtn()
{
?? wchar_t szBuf[MAX_PATH] = {};
?? GetModuleFileName(NULL, szBuf, MAX_PATH);
?? wchar_t* pFind = wcsrchr(szBuf, L'\\');
?? if (pFind)
?? {
????? *(pFind + 1) = '\0';
????? wcscat(szBuf, L"filewnd");
?? }
?? SetCurrentDirectory(szBuf);
?? if (GetFileAttributes(L"test.xml") == INVALID_FILE_ATTRIBUTES)
?? {
????? return;
?? }
?? SHostDialog dlg(L"file:test.xml");
?? dlg.DoModal(m_hWnd);
}
效果如下:
控件事件的響應(yīng)
SOUI 中提供了大部分常用的 win32 標(biāo)準(zhǔn)控件的實(shí)現(xiàn),如 pushbutton, checkbox,
radiobox, edit, richedit, listbox, combobox, treectrl, listctrl (report), hotkeyctrl 等。
大部分控件在接收用戶輸入后,會(huì)發(fā)生狀態(tài)的改變,并以事件的形式傳遞給 UI 的所有者。
在 SOUI 中提供了兩種處理事件的方式:
4.8.1 在 SHostWnd 的派生類中重載
virtual BOOL SHostWnd::_HandleEvent(SOUI::EventArgs *pEvt){return FALSE;}
為了更方便的處理事件,SOUI 提供了一組宏來(lái)構(gòu)造這個(gè)事件處理函數(shù),從而提供一種類
似消息映射的事件處理形式。
如 demo 的 CMainDlg 中的實(shí)現(xiàn):
//UI 控件的事件及響應(yīng)函數(shù)映射表
EVENT_MAP_BEGIN()
EVENT_ID_COMMAND(1, OnClose)
EVENT_ID_COMMAND(2, OnMaximize)
EVENT_ID_COMMAND(3, OnRestore)
EVENT_ID_COMMAND(5, OnMinimize)
EVENT_NAME_CONTEXTMENU(L"edit_1140",OnEditMenu)
EVENT_NAME_COMMAND(L"btn_msgbox",OnBtnMsgBox)
EVENT_NAME_COMMAND(L"btnSelectGif",OnBtnSelectGIF)
EVENT_NAME_COMMAND(L"btn_menu",OnBtnMenu)
EVENT_NAME_COMMAND(L"btn_webkit_go",OnBtnWebkitGo)
EVENT_NAME_COMMAND(L"btn_webkit_back",OnBtnWebkitBackward)
EVENT_NAME_COMMAND(L"btn_webkit_fore",OnBtnWebkitForeward)
EVENT_NAME_COMMAND(L"btn_webkit_refresh",OnBtnWebkitRefresh)
EVENT_NAME_COMMAND(L"btn_hidetst",OnBtnHideTest)
EVENT_NAME_COMMAND(L"btn_insert_gif",OnBtnInsertGif2RE)
EVENT_MAP_END()
上面的 EVENT_MAP_BEGIN()和 EVENT_MAP_END()結(jié)合構(gòu)造出一個(gè)_HandleEvent 函數(shù)
的實(shí)現(xiàn),具體可以自己展開(kāi)這兩個(gè)宏查看代碼。
同時(shí) SOUI 也提供了一組解析 SOUI::EventArgs *pEvt 的宏,如上例中的
EVENT_NAME_COMMAND, EVENT_ID_COMMAND 等。
幫助用戶直接從控件的 name 或者 ID 屬性映射到消息響應(yīng)函數(shù)。
這種事件響應(yīng)方式最大的好處是能夠集中處理事件的分發(fā),方便閱讀代碼,同時(shí)也和傳統(tǒng)
的 MFC,WTL 的編程風(fēng)格類似,降低用戶的學(xué)習(xí)成本。
采用事件訂閱的方式響應(yīng)控件事件
雖然事件映射表提供了一種簡(jiǎn)單有效的事件響應(yīng)機(jī)制,由于事件映射表是一種編譯期形成
的靜態(tài)的映射表,對(duì)于在運(yùn)行期動(dòng)態(tài)創(chuàng)建的控件的事件響應(yīng)就無(wú)能為力了。
在 MFC 中,程序員通過(guò)要重載窗口類的 DefWindowProc 來(lái)處理運(yùn)行期間動(dòng)態(tài)創(chuàng)建的控
件發(fā)來(lái)的消息。
這種方式靈活性夠了,但是不夠優(yōu)雅,要在一個(gè)函數(shù)里做大量的 swich 分枝,導(dǎo)致這個(gè)處
理函數(shù)很難維護(hù)。
設(shè)計(jì)模式里的觀察者模式可以比較好的解決這個(gè)問(wèn)題。
為些在 SOUI 中我提供了一種事件訂閱的事件處理模式。
我們先看一下 demo 中怎樣處理列表控件的表頭點(diǎn)擊來(lái)執(zhí)行排序操作:
void CMainDlg::InitListCtrl()
{
64
//找到列表控件
SListCtrl *pList=FindChildByName2
if(pList)
{
//列表控件的唯一子控件即為表頭控件
SWindow *pHeader=pList->GetWindow(GSW_FIRSTCHILD);
//向表頭控件訂閱表明點(diǎn)擊事件,并把它和 OnListHeaderClick 函數(shù)相連。
pHeader->GetEventSet()->subscribeEvent(EVT_HEADER_CLICK,Subscriber(&CMainDlg
::OnListHeaderClick,this));
//省略列表初始化代碼
}
}
//表頭點(diǎn)擊事件處理函數(shù)
bool CMainDlg::OnListHeaderClick(EventArgs *pEvtBase)
{
//事件對(duì)象強(qiáng)制轉(zhuǎn)換
EventHeaderClick *pEvt =(EventHeaderClick*)pEvtBase;
SHeaderCtrl *pHeader=(SHeaderCtrl*)pEvt->sender;
//從表頭控件獲得列表控件對(duì)象
SListCtrl *pList= (SListCtrl*)pHeader->GetParent();
//列表數(shù)據(jù)排序
SHDITEM hditem;
hditem.mask=SHDI_ORDER;
pHeader->GetItem(pEvt->iItem,&hditem);
pList->SortItems(funCmpare,&hditem.iOrder);
return true;
}
通過(guò)事件訂閱可以在運(yùn)行時(shí)方便的將一個(gè)控件的事件關(guān)聯(lián)到一個(gè)處理函數(shù)上,當(dāng)然也可以
隨時(shí)取消訂閱。
同時(shí)事件訂閱也是在腳本中響應(yīng)控件事件的唯一方式(關(guān)于在 SOUI 中使用 LUA 腳本將在
后續(xù)講解)。
多語(yǔ)言翻譯機(jī)制
為 UI 在不同地區(qū)顯示不同的語(yǔ)言是產(chǎn)品國(guó)際化的一個(gè)重要要求。
在 SOUI 中實(shí)現(xiàn)了一套類似 QT 的多語(yǔ)言翻譯機(jī)制:布局 XML 不需要調(diào)整,程序代碼也不
需要調(diào)整,只需要為不同地區(qū)的用戶提供不同的語(yǔ)言翻譯文件即可。
65
在 SOUI 中,我們實(shí)現(xiàn)了一個(gè)使用明文 XML 的語(yǔ)言翻譯模塊:translator.dll
為了使用多語(yǔ)言翻譯,首先需要準(zhǔn)備一個(gè)語(yǔ)言翻譯的 XML 文件。demo 中使用的翻譯文
件如下:
66
可以看到該 XML 中有一個(gè) language 的根節(jié)點(diǎn),該節(jié)點(diǎn)有兩個(gè)屬性:name 和 guid,這
兩個(gè)屬性都是用來(lái)標(biāo)識(shí)該翻譯文件的。
在 language 節(jié)點(diǎn)下,有多個(gè) context 節(jié)點(diǎn),每個(gè) context 節(jié)點(diǎn)有一個(gè) name 屬性(可以
為空),對(duì)應(yīng)一個(gè)翻譯上下文。
每一個(gè) context 下有不同數(shù)量的 message 結(jié)點(diǎn),每個(gè) message 又有兩個(gè)子節(jié)點(diǎn):
source 和 translation。
source 對(duì)應(yīng)需要翻譯的文字,而 translation 則對(duì)應(yīng)翻譯后的文字。
要使用這個(gè)語(yǔ)言翻譯文件,首先需要從 translator.dll 中創(chuàng)建一個(gè) SOUI::ITranslatorMgr
對(duì)象,并將該對(duì)象交給 SOUI::SApplication 管理。
再?gòu)?ItranslatorMgr 對(duì)象創(chuàng)建 SOUI::ITranslator 對(duì)象,并將 Itranslator 對(duì)象添加到
ItranslatorMgr 管理的翻譯列表中。
最后還要為 ITranslator 對(duì)象加載翻譯數(shù)據(jù)源(也就是前面提供的 XML 文件)。
下面是 demo 中使用和語(yǔ)言翻譯相關(guān)的代碼(見(jiàn)_tWinMain 函數(shù))
SApplication *theApp=new SApplication(pRenderFactory,hInstance);//SOUI
APP
CAutoRefPtr
提供
transLoader.CreateInstance("translator.dll",(IObjRef**)&transMgr);//
if(trans)
{//加載語(yǔ)言翻譯包
theApp->SetTranslator(transMgr);
pugi::xml_document xmlLang;
if(theApp->LoadXmlDocment(xmlLang,_T("lang_cn"),
_T("translator")))
{
CAutoRefPtr
transMgr->CreateTranslator(&langCN);
langCN->Load(&xmlLang.child(L"language"),1);
//1=LD_XML
transMgr->InstallTranslator(langCN);
}
}
我們先看一下 editmemu 的 XML:
iconMargin="4" textMargin="8" > 67
在這個(gè) XML 中,根節(jié)點(diǎn)有一個(gè)屬性 trCtx,代表翻譯上下文,對(duì)應(yīng)語(yǔ)言翻譯文件中的
context 中的 name 屬性。
在這個(gè) menu 定義中,所有的菜單項(xiàng)的文字全是英文。其中前面 3 項(xiàng):cut, copy and
paste 在語(yǔ)言翻譯文件中有對(duì)應(yīng)的翻譯項(xiàng)。
下圖為程序運(yùn)行時(shí) edit 的右鍵菜單顯示結(jié)果:
上面演示的是菜單資源的語(yǔ)言翻譯,布局 XML 中的文字的翻譯基本一樣,只需要為布局
的根結(jié)點(diǎn)定義一個(gè)翻譯上下文(trCtx)(沒(méi)有定義時(shí)則從沒(méi)有指定 name 屬性的 context 里
查找翻譯結(jié)果)。
可以參見(jiàn) demo 的"close”按照的 tooltip 的翻譯。
不管是菜單 XML 還是布局 XML,它們都是靜態(tài)的,經(jīng)過(guò)設(shè)計(jì)需要使用代碼往 UI 添加新
的文字,同樣也需要翻譯,這個(gè)時(shí)候如何處理?
其實(shí)看一下靜態(tài)資源翻譯的代碼就知道,要實(shí)現(xiàn)語(yǔ)言翻譯,需要為每一句待翻譯的文字調(diào)
用一個(gè)函數(shù):
SApplication::getSingleton().GetTranslator()->tr(const SStringW &
strSrc,const SStringW & strCtx)
第一個(gè)參數(shù)是等翻譯字符串,第二個(gè)參數(shù)是翻譯上下文。
考慮到語(yǔ)句太長(zhǎng),系統(tǒng)提供了一個(gè)宏:
#define TR(p1,p2)
SApplication::getSingleton().GetTranslator()->tr(p1,p2)
這樣用 TR 就可以實(shí)現(xiàn)文字翻譯了。
使用分層窗口
從 Windows 2K 開(kāi)始,MS 為 UI 開(kāi)發(fā)引入了分層窗口這一窗口風(fēng)格。使用分層窗口,應(yīng)
用程序的主窗口可以是半透明,也可以是逐點(diǎn)半透明(即每一個(gè)像素點(diǎn)的透明度可以不
同)。
可以說(shuō),正是因?yàn)橛辛朔謱哟翱?,?Windows 上開(kāi)發(fā)的應(yīng)用程序的 UI 才真正炫起來(lái)。
在 UI 的主窗口上加一個(gè)分層窗口的風(fēng)格對(duì)于一個(gè)稍有點(diǎn) UI 開(kāi)發(fā)經(jīng)驗(yàn)的程序員來(lái)說(shuō)是非常
簡(jiǎn)單的,本篇要說(shuō)的是在 SOUI 的窗口系統(tǒng)中實(shí)現(xiàn) SOUI 的分層窗口。
正如使用系統(tǒng)的窗口已經(jīng)可以實(shí)現(xiàn)很漂亮的 UI,我們還是會(huì)需要 DirectUI 這樣的 UI 開(kāi)發(fā)
技術(shù);有了系統(tǒng)的分層窗口,我們還是會(huì)需要在 DirectUI Window 之間的分層窗口技
術(shù)。
一個(gè) DirectUI 系統(tǒng)的分層窗口有什么用呢?
分層窗口和一般窗口的關(guān)鍵區(qū)別在在于:一個(gè)分層窗口是一個(gè)相對(duì)獨(dú)立的渲染層,它能將
它的子窗口渲染到這個(gè)窗口上,當(dāng)所有分層窗口都渲染完成后,再按照分層窗口的 zorder
89
順序通過(guò)不同的 alpha 混合技術(shù)進(jìn)行混合;而一般的 DirectUI 系統(tǒng)只有一個(gè)渲染層,所有
窗口按照 zorder 順序依次渲染到這個(gè)渲染層上,缺少了不同渲染層的概念。
一個(gè)簡(jiǎn)單的例子:業(yè)務(wù)可能希望一個(gè)功能面板能夠整體半透明,該功能面版可能包含很多
子窗口。
如果沒(méi)有分層窗口特性,在 DirectUI 中實(shí)現(xiàn)類似的需求很很復(fù)雜(如分別指定每一個(gè)子窗
口的半透明)而且可能導(dǎo)致性能下降、內(nèi)存消耗的提高等問(wèn)題。
如果有了分層窗口,實(shí)現(xiàn)類似的需求就非常簡(jiǎn)單了:只需要為該功能面板指定一個(gè)透明度
就行了。在渲染的時(shí)候,功能面板的子窗口會(huì)以正常的渲染方式渲染到分層窗口的緩存
上,全部渲染完成后,再整體和上一個(gè)渲染層混合,從而得到期望的效果。
在 SOUI 里如何使用分層窗口:
SOUI 采用 XML 定義 UI,要定義分層窗口只需要一個(gè) layeredWindow="1"的屬性即可。
XML 定義:
layeredWindow="1"> name="switch" cursor="hand" tip="click me to show the animator that show or hide the pane"> iconSkin="skin_tree_icon" checkBox="1" font="underline:1"> 90 \nshow or hide the \npane
顯示效果:
做事件分發(fā)處理
不同的 SOUI 控件可以產(chǎn)生不同的事件。SOUI 系統(tǒng)中提供了兩種事件處理方式:事件訂
閱 + 事件處理映射表(參見(jiàn)第八篇:SOUI 中控件事件的響應(yīng))
事件訂閱由于直接將事件及事件處理函數(shù)連接,不存在事件分發(fā)的問(wèn)題,這里主要介紹使
用事件映射表時(shí)的事件分發(fā)。
在回答這個(gè)問(wèn)題前,首先了解一下什么是事件分發(fā)。
在大型項(xiàng)目中,程序邏輯可能非常復(fù)雜,如果將所有 UI 中控件的事件處理集中在一個(gè)消息
/事件映射表里,代碼的可維護(hù)性會(huì)變得非常差。解決這個(gè)問(wèn)題常見(jiàn)的方法就是將事件進(jìn)行
分類(如根據(jù)來(lái)源分類),不同類別的事件采用一個(gè)獨(dú)立的事件處理對(duì)象來(lái)處理,這就是
事件分發(fā)的核心。
目前流行的 UI 通常采用 Tab 控件來(lái)組織 UI,不同的功能放到不同的 Tab 頁(yè)中,不同的
Tab 頁(yè)可能互不相干的功能模塊,對(duì)于類似這樣的情形很自然的會(huì)想到采用事件分發(fā)機(jī)制
來(lái)實(shí)現(xiàn)模塊之間邏輯的解耦(如下圖中 SoTool 采用的 UI)。
在上面的 UI 中,雖然整個(gè) UI 被 TAB 分成了 6 個(gè)頁(yè)面,但是 6 個(gè)頁(yè)面都存在于同一個(gè)宿
主窗口中。
一般情況下,如果 UI 相對(duì)比較簡(jiǎn)單,我們推薦直接在宿主窗口的事件處理映射表中統(tǒng)一處
理控件事件。
但是當(dāng)出現(xiàn)如上圖這樣復(fù)雜的界面時(shí),最好是將不同功能頁(yè)的事件處理在不同的對(duì)象中分
別處理。
在 MFC 中,一個(gè)類要處理消息,這個(gè)類通常派生自 CCmdTarget(可能記錯(cuò)了,太久不
用 MFC 了),主窗口收到的消息會(huì)自動(dòng)路由到這個(gè)消息處理對(duì)象中。
在 WTL 中,WTL 提供了一組消息映射宏:CHAIN_MSG_MAP,
CHAIN_MSG_MAP_MEMBER 等以便將消息分發(fā)到同樣實(shí)現(xiàn)了消息映射表的任意 C++對(duì)
象。
SOUI 的事件分發(fā)采用了 WTL 消息分發(fā)類似的機(jī)制,同樣采用事件映射宏的方式來(lái)構(gòu)造事
件映射表,下面是 SOUI 中幾個(gè)主要的和事件分發(fā)相關(guān)的宏:
#define EVENT_MAP_BEGIN() \
protected: \
virtual BOOL _HandleEvent(SOUI::EventArgs *pEvt)\
{ \
UINT uCode = pEvt->GetID(); \
#define EVENT_MAP_DECLEAR() \
protected: \
virtual BOOL _HandleEvent(SOUI::EventArgs *pEvt);\
#define EVENT_MAP_BEGIN2(classname) \
BOOL classname::_HandleEvent(SOUI::EventArgs *pEvt)\
{ \
UINT uCode = pEvt->GetID(); \
#define EVENT_MAP_END() \
return __super::_HandleEvent(pEvt); \
} \
#define EVENT_MAP_BREAK() \
return FALSE; \
} \
#define CHAIN_EVENT_MAP(ChainClass) \
if(ChainClass::_HandleEvent(pEvt)) \
return TRUE; \
119
#define CHAIN_EVENT_MAP_MEMBER(theChainMember) \
{ \
if(theChainMember._HandleEvent(pEvt)) \
return TRUE; \
}
#define EVENT_CHECK_SENDER_ROOT(pRoot) \
{ \
SWindow *pWnd = sobj_cast
if(!pWnd->IsDescendant(pRoot)) \
return FALSE; \
}
// void OnEvent(EventArgs *pEvt)
#define EVENT_HANDLER(cd, func) \
if(cd == uCode) \
{ \
func(pEvt); return TRUE; \
}
下面是 SoTool 中的 MainDlg 中的事件處理:
//soui 消息
EVENT_MAP_BEGIN()
EVENT_NAME_COMMAND(L"btn_close", OnClose)
EVENT_NAME_COMMAND(L"btn_min", OnMinimize)
EVENT_NAME_COMMAND(L"btn_max", OnMaximize)
EVENT_NAME_COMMAND(L"btn_restore", OnRestore)
CHAIN_EVENT_MAP_MEMBER(m_imgMergerHandler)
CHAIN_EVENT_MAP_MEMBER(m_codeLineCounter)
CHAIN_EVENT_MAP_MEMBER(m_2UnicodeHandler)
CHAIN_EVENT_MAP_MEMBER(m_folderScanHandler)
CHAIN_EVENT_MAP_MEMBER(m_calcMd5Handler)
EVENT_MAP_END()
上面代碼中,EVENT_MAP_BEGIN()和 EVENT_MAP_END()這兩個(gè)宏構(gòu)造出一個(gè)空的事件
處理函數(shù),該函數(shù)自動(dòng)將未處理的事件交給基類的事件處理函數(shù)處理。
如果基類中沒(méi)有事件處理函數(shù),顯然這個(gè)事件映射表編譯不能通過(guò),此時(shí) SOUI 提供了另
一個(gè) EVENT_MAP_BREAK()來(lái)代替。
上面的事件分發(fā)表中,我使用 CHAIN_EVENT_MAP_MEMBER 宏將來(lái)自不同頁(yè)面的控件
事件傳遞到不同的事件處理對(duì)象中。
下面代碼是 m_imgMergerHandler 對(duì)象頭文件。
class CImageMergerHandler : public IFileDropHandler
{
120
friend class CMainDlg;
public:
CImageMergerHandler(void);
~CImageMergerHandler(void);
void OnInit(SWindow *pRoot);
void AddFile(LPCWSTR pszFileName);
protected:
virtual void OnFileDropdown(HDROP hDrop);
void OnSave();
void OnClear();
void OnModeHorz();
void OnModeVert();
EVENT_MAP_BEGIN()
EVENT_CHECK_SENDER_ROOT(m_pPageRoot)
EVENT_NAME_COMMAND(L"btn_save", OnSave)
EVENT_NAME_COMMAND(L"btn_clear", OnClear)
EVENT_NAME_COMMAND(L"radio_horz", OnModeHorz)
EVENT_NAME_COMMAND(L"radio_vert", OnModeVert)
EVENT_MAP_BREAK()
SWindow *m_pPageRoot;
SImgCanvas *m_pImgCanvas;
};
可以看到這里的事件映射表使用了 EVENT_MAP_BREAK 來(lái)結(jié)束。
在 SOUI 中推薦使用控件的 name 屬性來(lái)標(biāo)識(shí)一個(gè)控件(name 屬性是一個(gè) wchar*的字
符串,使用 name 雖然在事件分發(fā)時(shí)采用字符串比較,較基于整數(shù) id 屬性的比較效率低
一點(diǎn),好處在于代碼的可讀性好),不同的頁(yè)面中的控件如果出現(xiàn)相同的 name 該如何識(shí)
別呢?
在 SOUI 中使用了一點(diǎn)小技巧:在事件處理對(duì)象中實(shí)現(xiàn)一個(gè) oninit 函數(shù),該函數(shù)在
maindlg 中處理 WM_INITDIALOG 時(shí)被調(diào)用,在 oninit 中保存了一個(gè)頁(yè)面根節(jié)點(diǎn)的指
針:SWindow *m_pPageRoot;
在事件映射表的開(kāi)始,我們采用 EVENT_CHECK_SENDER_ROOT(m_pPageRoot)這個(gè)宏
來(lái)識(shí)別那些來(lái)自本頁(yè)面的事件。如果事件是來(lái)自其它頁(yè)面則不處理。
使用異步通知
異步通知是客戶端開(kāi)發(fā)中常見(jiàn)的需求,比如在一個(gè)網(wǎng)絡(luò)處理線程中要通知 UI 線程更新等
等。
通常在 Windows 編程中,為了方便,我們一般會(huì)向 UI 線程的窗口句柄 Post/Send 一個(gè)
窗口消息從而達(dá)到將非 UI 線程的事件切換到 UI 線程處理的目的。
在 SOUI 引入通知中心以前要在 SOUI 中處理非 UI 線程事件我也推薦用上面的方法。
使用窗口消息至少有以下兩個(gè)不足:
1、需要在線程中持有一個(gè)窗口句柄。
2、發(fā)出的消息只能在該窗口句柄的消息處理函數(shù)里處理。
SNotifyCenter
最新的 SOUI 引入了一個(gè)新的單例對(duì)象:SNotifyCenter,專門用來(lái)處理這類問(wèn)題。
新的 SNotifyCenter 解決了窗口消息存在的上面的兩個(gè)問(wèn)題:
1、通過(guò)使用全局單例,SNotifyCenter 可以在代碼任意位置獲取它的指針(前提當(dāng)然是要
在它的生命周期內(nèi));
2、使用 SNotifyCenter 產(chǎn)生的通知采用 SOUI 的事件系統(tǒng)來(lái)派發(fā),通過(guò)結(jié)合 SOUI 的事
件訂閱系統(tǒng),用戶可以在任意位置處理發(fā)出的事件。
在介紹如何使用 SNotifyCenter 前,先看一下 NotifyCenter.h 的代碼:
#pragma once
#include
namespace SOUI
{
template
class TAutoEventMapReg
{
typedef TAutoEventMapReg
public:
TAutoEventMapReg()
{
SNotifyCenter::getSingleton().RegisterEventMap(Subscriber(&_thisClass::OnEve
nt,this));
}
~TAutoEventMapReg()
{
SNotifyCenter::getSingleton().UnregisterEventMap(Subscriber(&_thisClass::OnE
vent,this));
}
protected:
bool OnEvent(EventArgs *e){
T * pThis = static_cast
return !!pThis->_HandleEvent(e);
}
};
class SOUI_EXP SNotifyCenter : public SSingleton
, public SEventSet
{
public:
SNotifyCenter(void);
~SNotifyCenter(void);
/**
* FireEventSync
* @brief 觸發(fā)一個(gè)同步通知事件
* @param EventArgs *e -- 事件對(duì)象
* @return
*
* Describe 只能在 UI 線程中調(diào)用
*/
void FireEventSync(EventArgs *e);
/**
* FireEventAsync
* @brief 觸發(fā)一個(gè)異步通知事件
* @param EventArgs *e -- 事件對(duì)象
* @return
*
* Describe 可以在非 UI 線程中調(diào)用,EventArgs *e 必須是從堆上分配的內(nèi)存,調(diào)用后
使用 Release 釋放引用計(jì)數(shù)
*/
void FireEventAsync(EventArgs *e);
/**
* RegisterEventMap
* @brief 注冊(cè)一個(gè)處理通知的對(duì)象
* @param const ISlotFunctor &slot -- 事件處理對(duì)象
* @return
*
* Describe
*/
bool RegisterEventMap(const ISlotFunctor &slot);
/**
* RegisterEventMap
* @brief 注銷一個(gè)處理通知的對(duì)象
* @param const ISlotFunctor &slot -- 事件處理對(duì)象
* @return
*
* Describe
*/
bool UnregisterEventMap(const ISlotFunctor & slot);
protected:
void OnFireEvent(EventArgs *e);
void ExecutePendingEvents();
static VOID CALLBACK OnTimer( HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
);
SCriticalSection m_cs; //線程同步對(duì)象
SList
DWORD m_dwMainTrdID;//主線程 ID
UINT_PTR m_timerID; //定時(shí)器 ID,用來(lái)執(zhí)行異步事件
SList
};
}
在這個(gè)文件中提供了兩個(gè)類,一個(gè)就是 SNotifyCenter,另一個(gè)是
TAutoEventMapReg
可以看到 SNotifyCenter 中除構(gòu)造外只有 4 個(gè) public 方法:
FireEventSync, FireEventAsync 用來(lái)觸發(fā)事件。
RegisterEventMap,UnregisterEventMap 則用來(lái)提供事件處理訂閱。
如何使用 SNotifyCenter?
1、在 main 中實(shí)例化 SNotifyCenter。(不明白可以參考 demo)
2、定義需要通過(guò)通知中心分發(fā)的事件類型,首先定義事件,然后向通知中心注冊(cè),參見(jiàn)
下面代碼:
void CMainDlg::OnBtnStartNotifyThread()
{
if(IsRunning()) return;
SNotifyCenter::getSingleton().addEvent(EVENTID(EventThreadStart));
SNotifyCenter::getSingleton().addEvent(EVENTID(EventThreadStop));
SNotifyCenter::getSingleton().addEvent(EVENTID(EventThread));
EventThreadStart evt(this);
SNotifyCenter::getSingleton().FireEventSync(&evt);
BeginThread();
}
void CMainDlg::OnBtnStopNotifyThread()
{
if(!IsRunning()) return;
EndThread();
EventThreadStop evt(this);
SNotifyCenter::getSingleton().FireEventSync(&evt);
SNotifyCenter::getSingleton().removeEvent(EventThreadStart::EventID);
SNotifyCenter::getSingleton().removeEvent(EventThreadStop::EventID);
SNotifyCenter::getSingleton().removeEvent(EventThread::EventID);
}
3、使需要處理通知中心分發(fā)的事件的對(duì)象從 TAutoEventMapReg 繼承,實(shí)現(xiàn)事件的自
動(dòng)訂閱(方便在事件映射表中統(tǒng)一處理事件),這一步是可選的,你也可以直接使用 SOUI
提供的事件訂閱機(jī)制向通知中心訂閱特定事件。
4、在事件映射表里處理事件(沒(méi)有第 3 步時(shí),則同樣沒(méi)有這一步)。
演示使用SNotifyCenter的異步事件
class EventThread : public TplEventArgs
{
?? SOUI_CLASS_NAME(EventThread,L"on_event_thread")
public:
?? EventThread(SObject *pSender):TplEventArgs
?? enum{EventID=EVT_EXTERNAL_BEGIN+30000};
??
?? int nData;
};
在cpp文件中使用
EventThread *pEvt = new EventThread(this);
pEvt->nData = nSleep;
SNotifyCenter::getSingleton().FireEventAsync(pEvt);
pEvt->Release();
一般在構(gòu)造函數(shù)中添加進(jìn)去,一般可以添加任意條事件,即不限于一條
SNotifyCenter::getSingleton().addEvent(EVENTID(EventThread));
SNotifyCenter::getSingleton().subscribeEvent(EventSwndSize::EventID, Subscriber(&SHostWnd::onRootResize, this));
和回調(diào)函數(shù)差不多
ACTIVATE事件用法
消息映射表中
MSG_WM_ACTIVATE(OnActivate)
聲明及實(shí)現(xiàn)
void OnActivate(UINT nState, BOOL bMinimized, HWND wndOther);
void CSetSkinWnd::OnActivate(UINT nState, BOOL bMinimized, HWND wndOther)
{
?? if (nState == WA_INACTIVE)
{
????? DestroyWindow();
}
?? Else
{
????? SHostWnd::OnActivate(nState, bMinimized, wndOther);
??? }
}
換膚
SSkinLoader類
頭文件
#pragma once
#include "pugixml\pugixml.hpp"
#include "res.mgr\SSkinPool.h"
#include "helper\SplitString.h"
#include "resprovider-zip\zipresprovider-param.h"
#include "resprovider-zip\SResProviderZip.h"
namespace SOUI
{
#define RT_SKIN _T("SkinXml")
?? class SSkinLoader :public SSingleton
?? {
?? public:
????? SSkinLoader(SApplication*);
????? ~SSkinLoader();
????? void LoadSkinFormZip(SStringT respath, const TCHAR *strXmlSkin=_T("LoadSkinXml"));
????? void LoadSkin(SStringT respath,const TCHAR *strXmlSkin = _T("LoadSkinXml"));
?? private:
????? CAutoRefPtr
????? CAutoRefPtr
????? SApplication *m_theApp;
????? BOOL CreateResProvider_ZIP(IObjRef **ppObj);
?? };
}//END SOUI
源文件
#include "stdafx.h"
#include "SSkinLoader.h"
#include "res.mgr\SResProvider.h"
template<>
SSkinLoader * SSingleton
SSkinLoader::SSkinLoader(SApplication* theApp):m_pResProvider(NULL),m_theApp(theApp)
{
?? m_privateSkinPool = new SSkinPool();
?? GETSKINPOOLMGR->PushSkinPool(m_privateSkinPool);
}
SSkinLoader::~SSkinLoader()
{?
}
BOOL SSkinLoader::CreateResProvider_ZIP(IObjRef **ppObj)
{
?? return RESPROVIDER_ZIP::SCreateInstance(ppObj);
}
void SSkinLoader::LoadSkinFormZip(SStringT respath, const TCHAR *strXmlSkin)
{
?? if (m_pResProvider == NULL)
?? {
????? if(CreateResProvider_ZIP((IObjRef**)&m_pResProvider))
????? {
???????? m_theApp->AddResProvider(m_pResProvider,NULL);
????? }
?? }
?? SASSERT(m_pResProvider);
?? ZIPRES_PARAM param;
?? param.ZipFile(m_theApp->GetRenderFactory(), respath, "www.bukengnikengshui.com");
?? if (!m_pResProvider->Init((WPARAM)¶m, 0))
?? {
????? SASSERT(0);
?? }
?? pugi::xml_document xmlDoc;
?? SStringTList strLst;
?? BOOL bLoad = FALSE;
?? if (2 == ParseResID(strXmlSkin, strLst))
?? {
????? bLoad = LOADXML(xmlDoc, strLst[1], strLst[0]);
?? }
?? else
?? {
????? bLoad = LOADXML(xmlDoc, strLst[0], RT_SKIN);
?? }
?? if (bLoad)
?? {
????? if (m_privateSkinPool->GetCount() > 0)
????? {
???????? pugi::xml_node xmlSkin = xmlDoc.child(L"skin").first_child();
???????? SStringW strSkinName, strTypeName;
???????? while (xmlSkin)
???????? {
??????????? strTypeName = xmlSkin.name();
??????????? strSkinName = xmlSkin.attribute(L"name").value();
??????????? if (strSkinName.IsEmpty() || strTypeName.IsEmpty())
??????????? {
??????????????? xmlSkin = xmlSkin.next_sibling();
??????????????? continue;
??????????? }
??????????? xmlSkin.attribute(L"name").set_userdata(1);
??????????? ISkinObj *pSkin = m_privateSkinPool->GetSkin(strSkinName,100);
??????????? if (pSkin)
??????????? {
??????????????? pSkin->InitFromXml(xmlSkin);
??????????? }
??????????? else
??????????? {
??????????????? SASSERT_FMTW(FALSE, L"load skin error,type=%s,name=%s", strTypeName, strSkinName);
??????????? }
??????????? xmlSkin.attribute(L"name").set_userdata(0);
??????????? xmlSkin = xmlSkin.next_sibling();
???????? }
????? }
????? else
????? {
???????? m_privateSkinPool->LoadSkins(xmlDoc.child(L"skin"));
????? }
?? }
}
void SSkinLoader::LoadSkin(SStringT respath,const TCHAR *strXmlSkin)
{??????????????
?? if (m_pResProvider == NULL)
?? {
????? if(CreateResProvider(RES_FILE, (IObjRef**)&m_pResProvider))
????? {
???????? m_theApp->AddResProvider(m_pResProvider, NULL);
????? }
?? }
?? SASSERT(m_pResProvider);
?? if (!m_pResProvider->Init((WPARAM)respath.GetBuffer(0), NULL))
?? {
????? SASSERT(0);
?? }
?? pugi::xml_document xmlDoc;
?? SStringTList strLst;
?? BOOL bLoad=FALSE;
?? if (2 == ParseResID(strXmlSkin, strLst))
?? {
????? bLoad=LOADXML(xmlDoc, strLst[1], strLst[0]);
?? }
?? else
?? {
????? bLoad=LOADXML(xmlDoc, strLst[0], RT_SKIN);
?? }
?? if (bLoad)
?? {
????? if (m_privateSkinPool->GetCount() > 0)
????? {
???????? pugi::xml_node xmlSkin = xmlDoc.child(L"skin").first_child();
???????? SStringW strSkinName, strTypeName;
???????? while (xmlSkin)
???????? {
??????????? strTypeName = xmlSkin.name();
??????????? strSkinName = xmlSkin.attribute(L"name").value();
??????????? if (strSkinName.IsEmpty() || strTypeName.IsEmpty())
??????????? {
??????????????? xmlSkin = xmlSkin.next_sibling();
??????????????? continue;
??????????? }
??????????? xmlSkin.attribute(L"name").set_userdata(1);
??????????? ISkinObj *pSkin = m_privateSkinPool->GetSkin(strSkinName,100);
??????????? if (pSkin)
??????????? {
??????????????? pSkin->InitFromXml(xmlSkin);
??????????? }
??????????? else
??????????? {
??????????????? SASSERT_FMTW(FALSE, L"load skin error,type=%s,name=%s", strTypeName, strSkinName);
??????????? }
??????????? xmlSkin.attribute(L"name").set_userdata(0);
??????????? xmlSkin = xmlSkin.next_sibling();
???????? }
????? }
????? else
????? {
???????? m_privateSkinPool->LoadSkins(xmlDoc.child(L"skin"));
????? }
?? }
}
SDemoSkin類
頭文件
#pragma once
namespace SOUI
{
?? enum SkinType
?? {
????? color,
????? sys,
????? builtin,
?? };
?? struct SkinInfo
?? {
????? COLORREF color;
????? SStringW filepath;
????? RECT margin;
?? };
?? interface ISetOrLoadSkinHandler
?? {
????? virtual bool SaveSkin(SkinType, SkinInfo&) = NULL;???
?? };
?? class? SDemoSkin : public SSkinImgFrame
?? {
????? SOUI_CLASS_NAME(SDemoSkin, L"demoskin")
?? public:
????? SDemoSkin();
????? SDemoSkin(ISetOrLoadSkinHandler *iSkinHander);
?? public:
????? virtual bool SetImage(IBitmap *pImg);
?????
????? bool SetImage(SStringW imgfile);
????? bool SetColor(COLORREF bkColor);
????? void ClearSkin();
????? COLORREF GetThemeColor() const;
????? void SetHander(ISetOrLoadSkinHandler*skinhander);
????? virtual SIZE GetSkinSize();
????? virtual BOOL IgnoreState();
????? bool SaveSkin();
????? bool LoadSkin(SkinType, SkinInfo& saveInf);
????? virtual int GetStates();
????? //不支持自動(dòng)色調(diào)
????? virtual void OnColorize(COLORREF cr){}
?? protected:
????? virtual void _Draw(IRenderTarget *pRT, LPCRECT rcDraw, DWORD dwState, BYTE byAlpha);
?????
????? ISetOrLoadSkinHandler *m_ISetOrLoadSkinHandler;???
????? CSize m_csSize;???
????? bool m_bIsColor;
????? COLORREF m_bkColor;
????? bool m_bAdaptiveImg;????
????? SStringW m_FilePath;?
?? };
}
源文件
#include "stdafx.h"
#include "sdemoskin.h"
#include "helper/SDIBHelper.h"
namespace SOUI
{?
?? SDemoSkin::SDemoSkin():m_csSize(0, 0),m_bkColor(RGB(255, 255, 255)), m_bIsColor(false), m_ISetOrLoadSkinHandler(NULL)
?? {
?? }
?? SDemoSkin::SDemoSkin(ISetOrLoadSkinHandler *handle) : m_csSize(0, 0), m_bkColor(RGB(255, 255, 255)), m_bIsColor(false), m_ISetOrLoadSkinHandler(handle)
?? {
?? }
?? bool SDemoSkin::SetImage(IBitmap *pImg)
?? {
????? if (m_pImg)
???????? m_pImg->Release();
????? m_pImg = pImg;
????? if (m_pImg)
????? ?m_pImg->AddRef();
????? return true;
?? }
?? bool SDemoSkin::SetImage(SStringW imgfile)
?? {
????? m_bIsColor = false;
????? m_FilePath = imgfile;
????? IBitmap *image = LOADIMAGE2(L"file:" + imgfile);
????? if (image)
????? {
???????? SetImage(image);
???????? image->Release();
???????? return true;
????? }
????? return false;
?? }
?? bool SDemoSkin::SetColor(COLORREF bkColor)
?? {
????? m_FilePath.Empty();
????? m_bIsColor = true;???
????? m_bkColor = bkColor;????
????? return true;
?? }
?? void SDemoSkin::ClearSkin()
?? {
????? m_FilePath.Empty();
????? m_bIsColor = false;
????? m_pImg = NULL;
?? }
?? SIZE SDemoSkin::GetSkinSize()
?? {????
????? SIZE ret = { 0, 0 };
????? if (m_pImg)
???????? ret = m_pImg->Size();
????? return ret;
?? }
?? BOOL SDemoSkin::IgnoreState()
?? {
????? return TRUE;
?? }
?? bool SDemoSkin::SaveSkin()
?? {
????? if (m_ISetOrLoadSkinHandler == NULL)
???????? return false;
????? SkinInfo saveInf;
????? if (m_bIsColor)
????? {
???????? saveInf.color = m_bkColor;
???????? return m_ISetOrLoadSkinHandler->SaveSkin(color, saveInf);
????? }
????? else if(!m_FilePath.IsEmpty())
????? {
???????? saveInf.margin = m_rcMargin;
???????? saveInf.filepath = m_FilePath;
???????? return m_ISetOrLoadSkinHandler->SaveSkin(sys, saveInf);
????? }
????? else
????? {
???????? return m_ISetOrLoadSkinHandler->SaveSkin(builtin, saveInf);
????? }
????? return false;
?? }
?? bool SDemoSkin::LoadSkin(SkinType skinType, SkinInfo &skinLoadInf)
?? {
????? switch (skinType)
????? {
????? case color:
???????? return SetColor(skinLoadInf.color);
????? case sys:
???????? m_rcMargin = skinLoadInf.margin;
???????? return SetImage(skinLoadInf.filepath);
????? default:
???????? break;
????? }
????? return false;
?? }
??
?? int SDemoSkin::GetStates()
?? {
????? return 1;
?? }
?? void SDemoSkin::SetHander(ISetOrLoadSkinHandler* skinhander)
?? {
????? m_ISetOrLoadSkinHandler = skinhander;
?? }
?? void SDemoSkin::_Draw(IRenderTarget * pRT, LPCRECT rcDraw, DWORD dwState, BYTE byAlpha)
?? {
????? if (m_bIsColor)
????? {???????
???????? COLORREF bkColor = m_bkColor | (byAlpha << 24);
???????? pRT->FillSolidRect(rcDraw, bkColor);
????? }
????? else if (m_pImg)
????? {
???????? SIZE sz = GetSkinSize();
???????? CPoint pt(0, 0);
???????? CRect rcSour(pt, sz);
???????? pRT->DrawBitmap9Patch(rcDraw, m_pImg, &rcSour, &m_rcMargin, GetExpandMode(), byAlpha);
????? }????
?? }?
?? COLORREF SDemoSkin::GetThemeColor() const
?? {
????? if (m_bIsColor)
???????? return m_bkColor;
????? else if (m_pImg)
???????? return SDIBHelper::CalcAvarageColor(m_pImg);
????? else
???????? return CR_INVALID;
?? }
}
SetSkinWnd類
頭文件
#pragma once
#define MagicNumber 9527
extern UINT g_dwSkinChangeMessage;
struct SKIN_CONFIG_INF
{
?? int id;
?? CRect margin;
};
class CSetSkinWnd : public SHostWnd
{
public:
?? CSetSkinWnd();
?? ~CSetSkinWnd();
?? void OnSetSkin(EventArgs *e);
?? void OnBuiltinSkin();
?? EVENT_MAP_BEGIN()
????? EVENT_NAME_COMMAND(L"btn_close", OnClose)
????? EVENT_ID_RANGE_HANDLER(10, 27, EVT_CMD, OnSetSkin)
????? EVENT_ID_RANGE_HANDLER(30, 48, EVT_CMD, OnColor)?????
????? EVENT_ID_COMMAND(51, OnBuiltinSkin)
?? EVENT_MAP_END()
?? BEGIN_MSG_MAP_EX(CSetSkinWnd)
????? MSG_WM_INITDIALOG(OnInitDialog)
????? MESSAGE_HANDLER(g_dwSkinChangeMessage, OnSkinChangeMessage)
????? CHAIN_MSG_MAP(SHostWnd)
????? REFLECT_NOTIFICATIONS_EX()
?? END_MSG_MAP()
private:
?? SList
?? void OnClose();
?? HRESULT OnSkinChangeMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bHandled);
?? void OnColor(EventArgs * e);
?? BOOL OnInitDialog(HWND wndFocus, LPARAM lInitParam);
protected:
?? long NotifUpdataWindow();
?? void LoadSkinConfigFormXml();
?? CRect GetMargin(int id);
};
源文件
#include "stdafx.h"
#include "SetSkinWnd.h"
#include "SDemoSkin.h"
#include
#include "SSkinLoader.h"
#include
#define SKIN_CHANGE_MSG _T("{D17D208B-25FD-412C-8071-68816D4B1F9B}")
//注冊(cè)皮膚改變消息
UINT g_dwSkinChangeMessage = RegisterWindowMessage(SKIN_CHANGE_MSG);
CSetSkinWnd::CSetSkinWnd() :SHostWnd(_T("LAYOUT:dlg_set_skin"))
{?
?? LoadSkinConfigFormXml();
}
CSetSkinWnd::~CSetSkinWnd()
{
}
HRESULT CSetSkinWnd::OnSkinChangeMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bHandled)
{
?? FindChildByID(MagicNumber)->Invalidate();
?? return S_OK;
}
long CSetSkinWnd::NotifUpdataWindow()
{
?? WPARAM?? wParam = MagicNumber;
?? LPARAM?? lParam = MagicNumber;
?? DWORD?? dwRecipients = BSM_APPLICATIONS;
?? return ::BroadcastSystemMessage(BSF_POSTMESSAGE, &dwRecipients, g_dwSkinChangeMessage, wParam, lParam);
}
void CSetSkinWnd::LoadSkinConfigFormXml()
{
?? SStringT strSkinConfigPath = SApplication::getSingleton().GetAppDir() + _T("\\themes\\themes_config.xml");
?? pugi::xml_document docLoad;
?? pugi::xml_parse_result result = docLoad.load_file(strSkinConfigPath);
?? if (result)
?? {
????? pugi::xml_node skinInf = docLoad.child(L"DEMO_SKIN_CONFIG").child(L"skinInf");
????? while (skinInf)
????? {
???????? SKIN_CONFIG_INF inf;
???????? inf.id = (SkinType)skinInf.attribute(L"id").as_int();
???????? int v1 = 0, v2 = 0, v3 = 0, v4 = 0;
???????? swscanf(skinInf.attribute(L"skin_margin").as_string(), L"%d,%d,%d,%d", &v1, &v2, &v3, &v4);
???????? inf.margin.left = v1;
???????? inf.margin.top = v2;
???????? inf.margin.right = v3;
???????? inf.margin.bottom = v4;
???????? m_skinConfigInf.AddTail(inf);
???????? skinInf=skinInf.next_sibling();
????? }
?? }????
}
BOOL CSetSkinWnd::OnInitDialog(HWND hWnd, LPARAM lParam)
{
?? return 0;
}
CRect CSetSkinWnd::GetMargin(int id)
{
?? SPOSITION headPos= m_skinConfigInf.GetHeadPosition();
?? while (headPos)
?? {
????? SKIN_CONFIG_INF inf= m_skinConfigInf.GetNext(headPos);
????? if (inf.id == id)
????? {
???????? return inf.margin;
????? }
?? }
?? return CRect();
}
void CSetSkinWnd::OnSetSkin(EventArgs * e)
{
?? if (!e)
?? {
????? return;
?? }
?? SWindow *sender = (SWindow*) e->sender;
?? if (!sender)
?? {
????? return;
?? }
?? int nIndex = sender->GetID();
?? SDemoSkin *skin = (SDemoSkin *) GETSKIN(L"demoskinbk",GetScale());
?? SStringT strSkinFile;
?? SStringT strSkinPath = SApplication::getSingleton().GetAppDir() + _T("\\themes\\");
?? strSkinFile.Format(_T("%s%d.png"), strSkinPath, nIndex - 9);
?? SStringT strLoadSkin;
?? strLoadSkin.Format(_T("%s\\themes\\skin%d"), SApplication::getSingleton().GetAppDir(), ((nIndex - 9)%3)+1);
?? SSkinLoader::getSingleton().LoadSkin(strLoadSkin);
?? if (_taccess(strSkinFile, 0) != 0)
?? {
????? SMessageBox(NULL, _T("無(wú)法設(shè)置當(dāng)前主題,找不到系統(tǒng)主題文件。"), _T("警告"), MB_OK);
????? return;
?? }
?? if (skin)
?? {
????? skin->SetImage(S_CT2W(strSkinFile));
????? skin->SetMargin(GetMargin(nIndex-9));
????? NotifUpdataWindow();
?? }
}
void CSetSkinWnd::OnColor(EventArgs * e)
{
?? SWindow *sender = (SWindow*)e->sender;
?? SDemoSkin *skin = (SDemoSkin *)GETSKIN(L"demoskinbk", GetScale());
?? if (skin)
?? {
????? skin->SetColor(sender->GetStyle().m_crBg);
????? NotifUpdataWindow();
?? }
}
void CSetSkinWnd::OnBuiltinSkin()
{
?? SDemoSkin *skin = (SDemoSkin *)GETSKIN(L"demoskinbk", GetScale());
?? if (skin)
?? {
????? skin->ClearSkin();
????? NotifUpdataWindow();
?? }
}
void CSetSkinWnd::OnClose()
{
?? CSimpleWnd::DestroyWindow();
}
項(xiàng)目文件中
在主文件中需要注冊(cè)
theApp->RegisterSkinClass
并且加載一個(gè)皮膚
SSkinLoader *SkinLoader = new SSkinLoader(theApp);
SkinLoader->LoadSkin(SApplication::getSingleton().GetAppDir() + _T("\\themes\\skin1"));
MainDlg類
// MainDlg.h : interface of the CMainDlg class
//
/
#pragma once
#include "skin\SetSkinWnd.h"
#include "skin\SDemoSkin.h"
class CMainDlg : public SHostWnd, public ISetOrLoadSkinHandler
{
public:
?? CMainDlg();
?? ~CMainDlg();
?? void OnClose();
?? void OnMaximize();
?? void OnRestore();
?? void OnMinimize();
?? void OnSize(UINT nType, CSize size);
?? int OnCreate(LPCREATESTRUCT lpCreateStruct);
?? BOOL OnInitDialog(HWND wndFocus, LPARAM lInitParam);
?? void OnSkin();
?? void OnMenu();
?? bool LoadSkin();
?? bool SaveSkin(SkinType skinType, SkinInfo &skinSaveInf);
?? HRESULT OnSkinChangeMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bHandled);
?? void OnCommand(UINT uNotifyCode, int nID, HWND wndCtl);
protected:
?? //soui消息
?? EVENT_MAP_BEGIN()
?? ?? EVENT_NAME_COMMAND(L"btn_close", OnClose)
????? EVENT_NAME_COMMAND(L"btn_min", OnMinimize)
????? EVENT_NAME_COMMAND(L"btn_max", OnMaximize)
????? EVENT_NAME_COMMAND(L"btn_restore", OnRestore)
????? EVENT_NAME_COMMAND(L"btn_skin", OnSkin)
????? EVENT_NAME_COMMAND(L"btn_menu", OnMenu)
?? EVENT_MAP_END()
?????
?? //HostWnd真實(shí)窗口消息處理
?? BEGIN_MSG_MAP_EX(CMainDlg)
????? MSG_WM_CREATE(OnCreate)
????? MSG_WM_INITDIALOG(OnInitDialog)
????? MSG_WM_CLOSE(OnClose)
????? MSG_WM_SIZE(OnSize)
????? MSG_WM_COMMAND(OnCommand)
????? MESSAGE_HANDLER(g_dwSkinChangeMessage, OnSkinChangeMessage)
????? CHAIN_MSG_MAP(SHostWnd)
????? REFLECT_NOTIFICATIONS_EX()
?? END_MSG_MAP()
private:
?? BOOL???????? m_bLayoutInited;??
?? CSetSkinWnd m_SetSkinWnd;
};
源文件
// MainDlg.cpp : implementation of the CMainDlg class
//
/
#include "stdafx.h"
#include "MainDlg.h"?
#include "skin\SDemoSkin.h"
#include "skin\SSkinLoader.h"
??
CMainDlg::CMainDlg() : SHostWnd(_T("LAYOUT:XML_MAINWND"))
{
?? m_bLayoutInited = FALSE;
}
CMainDlg::~CMainDlg()
{
}
int CMainDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
?? SetMsgHandled(FALSE);
?? return 0;
}
BOOL CMainDlg::OnInitDialog(HWND hWnd, LPARAM lParam)
{
?? LoadSkin();
?? m_bLayoutInited = TRUE;
?? return 0;
}
//TODO:消息映射
void CMainDlg::OnClose()
{
?? SDemoSkin *skin = (SDemoSkin *)GETSKIN(L"demoskinbk",GetScale());
?? if (skin)
?? {
????? skin->SaveSkin();
?? }
?? CSimpleWnd::DestroyWindow();
}
void CMainDlg::OnMaximize()
{
?? SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE);
}
void CMainDlg::OnRestore()
{
?? SendMessage(WM_SYSCOMMAND, SC_RESTORE);
}
void CMainDlg::OnMinimize()
{
?? SendMessage(WM_SYSCOMMAND, SC_MINIMIZE);
}
void CMainDlg::OnSkin()
{
?? if (m_SetSkinWnd.m_hWnd)
?? {
????? SetForegroundWindow(m_hWnd);
????? FlashWindow(m_hWnd, TRUE);
????? m_SetSkinWnd.ShowWindow(SW_SHOWNORMAL);
?? }
?? else
?? {
????? m_SetSkinWnd.Create(m_hWnd);
????? m_SetSkinWnd.SendMessage(WM_INITDIALOG);
????? m_SetSkinWnd.CenterWindow(GetDesktopWindow());
????? m_SetSkinWnd.ShowWindow(SW_SHOWNORMAL);
?? }
}
void CMainDlg::OnSize(UINT nType, CSize size)
{
?? SetMsgHandled(FALSE);
?? if (!m_bLayoutInited) return;
??
?? SWindow *pBtnMax = FindChildByName(L"btn_max");
?? SWindow *pBtnRestore = FindChildByName(L"btn_restore");
?? if(!pBtnMax || !pBtnRestore) return;
??
?? if (nType == SIZE_MAXIMIZED)
?? {
????? pBtnRestore->SetVisible(TRUE);
????? pBtnMax->SetVisible(FALSE);
?? }
?? else if (nType == SIZE_RESTORED)
?? {
????? pBtnRestore->SetVisible(FALSE);
????? pBtnMax->SetVisible(TRUE);
?? }
}
HRESULT CMainDlg::OnSkinChangeMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bHandled)
{
?? FindChildByID(9527)->Invalidate();
?? SDemoSkin *skin = (SDemoSkin *) GETSKIN(L"demoskinbk",GetScale());
?? DWORD tm1=GetTickCount();
?? COLORREF crTheme = skin->GetThemeColor();
?? if (crTheme != CR_INVALID)
????? DoColorize(crTheme | 0xff000000);
?? else
????? DoColorize(0);
?? SLOG_INFO("DoColorize spend "< ?? return S_OK; } void LoadSkinFormXml(SDemoSkin *skin, SkinType *skinType, SkinInfo *skininf) { ?? SStringT strSkinConfigPath = SApplication::getSingleton().GetAppDir() + _T("\\themes\\skin_config.xml"); ?? pugi::xml_document docLoad; ?? pugi::xml_parse_result result = docLoad.load_file(strSkinConfigPath); ?? if (result) ?? { ????? pugi::xml_node skinInf = docLoad.child(L"DEMO_SKIN_CONFIG").child(L"skinInf"); ????? *skinType = (SkinType)skinInf.attribute(L"type").as_int(); ????? switch (*skinType) ????? { ???????? //純色只有SkinInfo的color有效 ????? case color: ???????? skininf->color = skinInf.attribute(L"color").as_int(); ???????? break;?????? ???????? //此處為系統(tǒng)皮膚,只需要給文件路徑和margin ????? case sys: ???????? skininf->filepath = skinInf.attribute(L"skin_path").as_string(); ???????? int v1 = 0, v2 = 0, v3 = 0, v4 = 0; ???????? swscanf(skinInf.attribute(L"skin_margin").as_string(), L"%d,%d,%d,%d", &v1, &v2, &v3, &v4); ???????? skininf->margin.left = v1; ???????? skininf->margin.top = v2; ???????? skininf->margin.right = v3; ???????? skininf->margin.bottom = v4; ???????? break; ????? } ?? } } bool CMainDlg::LoadSkin() { ?? SDemoSkin *skin = (SDemoSkin *)GETSKIN(L"demoskinbk",GetScale()); ?? if (skin) ?? { ????? SkinInfo loadInf; ????? SkinType type; ????? LoadSkinFormXml(skin, &type, &loadInf); ????? skin->SetHander(this); ????? return skin->LoadSkin(type, loadInf); ?? } ?? return false; } void SaveSkinInf2File(SkinType skinType, SkinInfo &skinSaveInf) { ?? pugi::xml_document docSave; ?? pugi::xml_node rootNode = docSave.append_child(L"DEMO_SKIN_CONFIG"); ?? pugi::xml_node childSkinType = rootNode.append_child(L"skinInf"); ?? childSkinType.append_attribute(L"type") = skinType; ?? SStringT strSkinConfigPath = SApplication::getSingleton().GetAppDir() + _T("\\themes\\skin_config.xml"); ?? switch (skinType) ?? { ?? case color://純色只有SkinInfo的color有效 ????? childSkinType.append_attribute(L"color") = (int)skinSaveInf.color; ????? break;????????? ?? case sys://此處為系統(tǒng)皮膚,只需要給文件路徑和margin ????? { ???????? childSkinType.append_attribute(L"skin_path") = skinSaveInf.filepath; ???????? SStringW margin; ???????? margin.Format(L"%d,%d,%d,%d", skinSaveInf.margin.left, skinSaveInf.margin.top, skinSaveInf.margin.right, skinSaveInf.margin.bottom); ???????? childSkinType.append_attribute(L"skin_margin") = margin; ????? } ????? break; ?? case builtin: ?? default: ????? break; ?? } ?? docSave.save_file(strSkinConfigPath); } bool CMainDlg::SaveSkin(SkinType skinType, SkinInfo &skinSaveInf) { ?? HRESULT hr = S_OK; ?? SaveSkinInf2File(skinType, skinSaveInf); ?? return hr == S_OK; } void CMainDlg::OnCommand( UINT uNotifyCode, int nID, HWND wndCtl ) { ?? if(uNotifyCode==0) ?? { ????? if (nID == 51) ????? { ???????? //skin1 ????? ?? SSkinLoader::getSingleton().LoadSkin(SApplication::getSingleton().GetAppDir() + _T("\\themes\\skin1")); ???????? SWindow::Invalidate(); ????? } ????? else if (nID == 52) ????? { ???????? //skin2 ????? ?? SSkinLoader::getSingleton().LoadSkin(SApplication::getSingleton().GetAppDir() + _T("\\themes\\skin2")); ???????? SWindow::Invalidate(); ????? } ????? else if (nID == 53) ????? { ???????? //skin3 ????? ?? SSkinLoader::getSingleton().LoadSkin(SApplication::getSingleton().GetAppDir() + _T("\\themes\\skin3")); ???????? SWindow::Invalidate(); ????? } ?? } } void CMainDlg::OnMenu() { ?? CPoint pt; ?? GetCursorPos(&pt); ?? //使用自繪菜單 ?? SMenu menu; ?? menu.LoadMenu(_T("menu_test"),_T("SMENU")); ?? menu.TrackPopupMenu(0,pt.x,pt.y,m_hWnd); } 資源配置 dlg_main.xml ? ??? ???
?????
?????
?????
?????
?????
?????
?????
?????
?????
???
???
???
?????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
???????
?????
???
?????
???
?
Dlg_skinset.xml
?
???
?????
?????
???????
???????
???????
???????
?????
?????
?????
???????
?????????
???????????
???????????
???????????
???????????
???????????
???????????
?????????
?????????
???????????
???????????
???????????
???????????
???????????
???????????
?????????
?????????
???????????
???????????
???????????
???????????
???????????
???????????
?????????
?????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
???????????
?????????
?????????
???????????
???????????
?????????
???????
?????
?????
???
?
Init.xml
?
?
?
?
?
?
?
Skin中
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
Uires.xml
?
???
?
?
???
???
?
?
???
?
?
?
?
?
?
?
???
???
???
???
?
?
???
???
???
???
???
???
???
???
???
???
???
???
???
???
???
???
???
???
?
?
???
?
Menu_test.xml
界面效果
動(dòng)態(tài)創(chuàng)建按鈕小項(xiàng)目
dlg_main.xml
appWnd="1" translucent="1" > ? ???
?????
?????
?????
?????
?????
?????
???
???
?????
???
?
menu.xml
?
uires.idx
?
???
?
?
???
?
?
?
?
?
?
?
?
?
???
?
?
???
?
MainDlg.h
#pragma once
#include
class CMainDlg : public SHostWnd
{
public:
?? CMainDlg();
?? ~CMainDlg();
?? void OnClose();
?? void OnMaximize();
?? void OnRestore();
?? void OnMinimize();
?? void OnSize(UINT nType, CSize size);
?? int OnCreate(LPCREATESTRUCT lpCreateStruct);
?? BOOL OnInitDialog(HWND wndFocus, LPARAM lInitParam);
?? ?
?? void OnBtnAdd();
?? bool OnClick(EventArgs* pArgs);
?? bool OnRClick(EventArgs* pArgs);
?? void OnCommand(UINT uNotifyCode, int nID, HWND wndCtl);
protected:
?? //soui消息
?? EVENT_MAP_BEGIN()
????? EVENT_NAME_COMMAND(L"btn_close", OnClose)
????? EVENT_NAME_COMMAND(L"btn_min", OnMinimize)
????? EVENT_NAME_COMMAND(L"btn_max", OnMaximize)
????? EVENT_NAME_COMMAND(L"btn_restore", OnRestore)
????? EVENT_NAME_COMMAND(L"btnAdd", OnBtnAdd)
?? EVENT_MAP_END()
?????
?? //HostWnd真實(shí)窗口消息處理
?? BEGIN_MSG_MAP_EX(CMainDlg)
????? MSG_WM_CREATE(OnCreate)
????? MSG_WM_INITDIALOG(OnInitDialog)
????? MSG_WM_CLOSE(OnClose)
????? MSG_WM_SIZE(OnSize)
????? MSG_WM_COMMAND(OnCommand)
????? CHAIN_MSG_MAP(SHostWnd)
????? REFLECT_NOTIFICATIONS_EX()
?? END_MSG_MAP()
private:
?? BOOL???????? m_bLayoutInited;??
?? void ClearWnd(SWindow* pWnd);
?? void CreateWnd(SWindow* pWnd);
?? void CreateAdd(SWindow* pWnd);
?? std::vector
?? int m_nRClickID;
};
MainDlg.cpp
#include "stdafx.h"
#include "MainDlg.h"?
??
CMainDlg::CMainDlg() : SHostWnd(_T("LAYOUT:XML_MAINWND"))
{
?? m_bLayoutInited = FALSE;
}
CMainDlg::~CMainDlg()
{
}
int CMainDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
?? SetMsgHandled(FALSE);
?? return 0;
}
BOOL CMainDlg::OnInitDialog(HWND hWnd, LPARAM lParam)
{
?? m_bLayoutInited = TRUE;
?? return 0;
}
//TODO:消息映射
void CMainDlg::OnClose()
{
?? CSimpleWnd::DestroyWindow();
}
void CMainDlg::OnMaximize()
{
?? SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE);
}
void CMainDlg::OnRestore()
{
?? SendMessage(WM_SYSCOMMAND, SC_RESTORE);
}
void CMainDlg::OnMinimize()
{
?? SendMessage(WM_SYSCOMMAND, SC_MINIMIZE);
}
void CMainDlg::OnSize(UINT nType, CSize size)
{
?? SetMsgHandled(FALSE);
?? if (!m_bLayoutInited) return;
??
?? SWindow *pBtnMax = FindChildByName(L"btn_max");
?? SWindow *pBtnRestore = FindChildByName(L"btn_restore");
?? if(!pBtnMax || !pBtnRestore) return;
??
?? if (nType == SIZE_MAXIMIZED)
?? {
????? pBtnRestore->SetVisible(TRUE);
????? pBtnMax->SetVisible(FALSE);
?? }
?? else if (nType == SIZE_RESTORED)
?? {
????? pBtnRestore->SetVisible(FALSE);
????? pBtnMax->SetVisible(TRUE);
?? }
}
void CMainDlg::ClearWnd(SWindow* pWnd)
{
?? SWindow* pChild = pWnd->GetWindow(GSW_FIRSTCHILD);
?? while (pChild)
?? {
????? SWindow* pNext = pChild->GetWindow(GSW_NEXTSIBLING);
????? pChild->DestroyWindow();
????? pChild = pNext;
?? }
}
void CMainDlg::CreateWnd(SWindow* pWnd)
{
?? int nID = 9000;
?? pugi::xml_document doc;
?? int nIndex = 0;
?? for (auto it = m_vec.begin(); it != m_vec.end(); it++, nIndex++)
?? {
????? SStringW strXml;
????? strXml.Format(L"", nID + nIndex, *it);
????? if (!doc.load_buffer(strXml, strXml.GetLength() * sizeof(wchar_t), pugi::parse_default, pugi::encoding_utf16))
????? {
???????? return;
????? }
????? SWindow* pBtn = new SButton;
????? if (pBtn)
????? {
???????? pWnd->InsertChild(pBtn);
???????? pBtn->InitFromXml(doc.first_child());
????? }
?? pBtn->GetEventSet()->subscribeEvent(EVT_CMD,Subscriber(&CMainDlg::OnClick,this));
?? pBtn->GetEventSet()->subscribeEvent(EVT_CTXMENU,Subscriber(&CMainDlg::OnRClick,this));
?? }
?? CreateAdd(pWnd);
?? int nWndNum = m_vec.size() + 1;
?? int nRow = nWndNum + 4 - 1 / 4;
?? SStringW strRowCount;
?? strRowCount.Format(L"%d", nRow);
?? pWnd->SetAttribute(L"rowCount", strRowCount);
}
void CMainDlg::CreateAdd(SWindow* pWnd)
{
?? pugi::xml_document doc;
?? SStringW strXml = L"";
?? if (!doc.load_buffer(strXml, strXml.GetLength() * sizeof(wchar_t), pugi::parse_default, pugi::encoding_utf16))
?? {
????? return;
?? }
?? SWindow* pBtn = new SButton;
?? if (pBtn)
?? {
????? pWnd->InsertChild(pBtn);
????? pBtn->InitFromXml(doc.first_child());
?? }
}
void CMainDlg::OnBtnAdd()
{
?? SWindow* pWnd = FindChildByName(L"wnd");
?? if (pWnd)
?? {
????? ClearWnd(pWnd);
????? static int nIndex = 0;
????? m_vec.push_back(nIndex);
????? CreateWnd(pWnd);
????? nIndex++;
?? }
}
bool CMainDlg::OnClick(EventArgs* pArgs)
{
?? int nID = pArgs->idFrom;
?? SStringW strID;
?? strID.Format(L"L %d", nID);
?? SMessageBox(m_hWnd, strID, L"提示", MB_OK);
?? return true;
}
bool CMainDlg::OnRClick(EventArgs* pArgs)
{
?? int nID = pArgs->idFrom;
?? SStringW strID;
?? strID.Format(L"R %d", nID);
?? m_nRClickID = nID;
?? SMenuEx menu;
?? if (!menu.LoadMenu(L"SMENU:menu"))
?? {
????? return false;
?? }
??
?? CPoint pt;
?? GetCursorPos(&pt);
?? menu.TrackPopupMenu(0, pt.x, pt.y, m_hWnd);
?? return true;
}
void CMainDlg::OnCommand(UINT uNotifyCode, int nID, HWND wndCtl)
{
?? if (uNotifyCode == 0)
?? {
????? if (nID == 1000)
????? {
???????? int nIndex = m_nRClickID - 9000;
???????? m_vec.erase(m_vec.begin() + nIndex);
???????? SWindow* pWnd = FindChildByName(L"wnd");
???????? if (pWnd)
???????? {
??????????? ClearWnd(pWnd);
??????????? CreateWnd(pWnd);
???????? }
????? }????
?? }
}
界面
設(shè)置自定義messagebox
將soui源碼的msgbox的xml文件復(fù)制到自己的項(xiàng)目xml文件夾中,并改名為自己的命名。
?
在uires.idx文件中添加進(jìn)來(lái)
在winmain函數(shù)中添加代碼
//設(shè)置自定義messsagebox
pugi::xml_document xmldoc;
theApp->LoadXmlDocment(xmldoc, L"XML_MSGBOX", L"LAYOUT");
if (xmldoc)
{
?? SetMsgTemplate(xmldoc.child(L"SOUI"));
}
效果如圖
設(shè)置編輯框自定義右鍵菜單
從源碼中的系統(tǒng)資源里面復(fù)制編輯框菜單出來(lái)到自己的項(xiàng)目xml文件夾中,并改名。
在uires.idx中添加進(jìn)來(lái)
在winmain函數(shù)中添加代碼
//設(shè)置編輯框自定義右鍵菜單
theApp->LoadXmlDocment(xmldoc, L"XML_EDITMENU", L"SMENU");
if (xmldoc)
{
?? SRicheditMenuDef::getSingleton().SetMenuXml(xmldoc.child(L"editmenu"));
}
效果如圖
多語(yǔ)言翻譯模塊
//加載多語(yǔ)言翻譯模塊。
CAutoRefPtr
bLoaded=m_pComMgr->CreateTranslator((IObjRef**)&trans);
SASSERT_FMT(bLoaded,_T("load interface [%s] failed!"),_T("translator"));
if(trans)
{//加載語(yǔ)言翻譯包
?? m_pTheApp->SetTranslator(trans);
?? pugi::xml_document xmlLang;
?? if (m_pTheApp->LoadXmlDocment(xmlLang, _T("lang_cn"), _T("translator")))
?? {
????? CAutoRefPtr
????? trans->CreateTranslator(&langCN);
????? langCN->Load(&xmlLang.child(L"language"),1);//1=LD_XML
????? trans->InstallTranslator(langCN);
?? }
}
獲取XML中定義的顏色
繼承類
class CMainDlg : public SHostWnd, public SresProviderMgr
使用函數(shù)
COLORREF color = GetColor(L"@color/backgnd");
就可以取出
?
?
?
?
?
?
?
中的任意顏色。
柚子快報(bào)激活碼778899分享:c++ SOUI總結(jié)之常用功能
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。