有个坑爹的说法:其实Direct UI只是一个思想,要实现这个思想,还要靠自己。
采用windowless方式用api或gdi实现ui的绘制。
DirectUI意为直接在父窗口上绘图(Paint on parent dc directly)。
子窗口不以窗口句柄的形式创建,只是逻辑上的窗口,绘制在父窗口之上。
DirectUI技术的实现步骤和难点:
1、窗口的子类化,截获窗口的消息。
2、封装自己的控件,并将自己的控件绘制到该窗口上
3、封装窗口的消息,并分发到自己的控件上,让自己的控件根据消息进行相应和绘制
4、根据不同的行为发送自定义消息给窗口,以便程序进行调用。
5、一般窗口上控件的组织使用XML来描述
首先,要实现一个好的DirectUI,必须设计出一套逻辑性、可编程性及可扩展性非常强的层布局器(Layout Manager ),为什么这样说呢?
因为DirectUI的核心设计思想就是界面自绘,既然很多控件都要用自绘的形式表现在界面上,那么控件与控件之间,一定存在某些逻辑布局上的依赖关系,我们可以把一个app分解成3D构架,横向为X,纵向为Y,内外为Z,那么好了,A控件跟B控件的X、Y相同,但是我要实现成B为A打底,成为A的背景(当然A会绘制成透明背景模式),那么A与B之间只存在Z的关系,即层次性。由此可以看出,一个好的Layout Manager是DirectUI绘制必不可少的关键点,可以将UI所要表达的所有控件(windowless,全部需要绘制出来的)层次分明的一一展现。(题外话:你可以通过任何手段将控件的逻辑位置等信息解析到LM中)
其次,ReDraw,第一点我已经阐明了控件之间层的关系,接下来自然是要通过LM(Layout Manager)将控件一一表现到界面上。如果在你的项目中木有LM,那么你就惨了,你无法判断应该用什么顺序依次往你的程序界面上绘制控件:(,就像一张空白的画纸,而你想往上面画很多漂亮的猫猫狗狗,那究竟是先画猫还是先画狗呢。。。打住,通过LM,你完全很清楚先画什么后画什么,LM设计的时候可以要求传入控件要有父控件节点,哦可!那么在ReDraw的时候,先遍历LM,绘制父节点,再绘制子节点,这样一来,界面就基本具有逻辑雏形了。
最后,该绘制的控件已经都绘制出来了,剩下唯一需要你实现的就是如何响应用户对这些控件的操作,这个应该不用多说了,无非就是捕获用户当前LButtonDone/LButtonUp等鼠标Click事件及rect,然后根据LM反馈出属于那种控件,以及该控件对应的事件即可。
以下转载地址:http://blog.sina.com.cn/s/blog_4c3b2dc20100s8w6.html
if( uMsg == WM_CREATE ) { m_pm.Init(m_hWnd); CDialogBuilder builder; CControlUI* pRoot = builder.Create(GetDialogResource()); ASSERT(pRoot && "Failed to parse XML"); m_pm.AttachDialog(pRoot); m_pm.AddNotifier(this); Init(); return 0; } LRESULT lRes = 0; if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
void CButtonUI::Event(TEventUI& event) { if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK )//按下 { if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled() ) { //判断是否在区域内,调用IsEnable m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED; //修改外观 Invalidate(); } } if( event.Type == UIEVENT_BUTTONUP ) { if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { if( ::PtInRect(&m_rcItem, event.ptMouse) ) Activate(); m_uButtonState &= ~(UISTATE_PUSHED | UISTATE_CAPTURED); Invalidate(); } }
bool CButtonUI::Activate() { if( !CControlUI::Activate() ) return false; if( m_pManager != NULL ) m_pManager->SendNotify(this, _T("click")); return true; }
void CPaintManagerUI::SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam , LPARAM lParam ) { TNotifyUI Msg; Msg.pSender = pControl; Msg.sType = pstrMessage; Msg.wParam = 0; Msg.lParam = 0; SendNotify(Msg); } void CPaintManagerUI::SendNotify(TNotifyUI& Msg) { // Pre-fill some standard members Msg.ptMouse = m_ptLastMousePos; Msg.dwTimestamp = ::GetTickCount(); // Allow sender control to react Msg.pSender->Notify(Msg); // Send to all listeners for( int i = 0; i < m_aNotifiers.GetSize(); i++ ) { static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg); //发送给所有接口 } }
void CSearchPageWnd::Notify(TNotifyUI& msg) { if( msg.sType == _T("click") ) { if( msg.pSender->GetName() == _T("ok") ) { CStandardPageWnd* pWindow = new CEditPageWnd; pWindow->Create(m_hWnd, NULL, UI_WNDSTYLE_FRAME, 0L); } if( msg.pSender->GetName() == _T("cancel") ) Close(); } CStandardPageWnd::Notify(msg); }