在windows下开发界面,现在很多公司会用到duilib,这个库其实不难,只要你有win32和MFC的开发经验,很容易上手,但是很多新手没有MFC的经验,为了让新手们可以快速上手,解决工作上的问题。今天我跟大家说说在duilib下,如何自定义控件,并且可以根据主窗口大小改变自己的大小,至于使用场景很多地方都会用到,比如播放器。
好了我们先看一下,在duilib下如何定义一个自定义控件,在duilib下,所有的控件都派生于CControlUI,所以我们的自定义控件也要继承这个类,具体代码如下,很简单,自己看。
class CWndUI : public CControlUI
{
public:
CWndUI() : m_hWnd(NULL){}
virtual void SetVisible(bool bVisible = true)
{
__super::SetVisible(bVisible);
::ShowWindow(m_hWnd, bVisible);
}
virtual void SetInternVisible(bool bVisible = true)
{
__super::SetInternVisible(bVisible);
::ShowWindow(m_hWnd, bVisible);
}
virtual void 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);
}
BOOL Attach(HWND hWndNew)
{
if (!::IsWindow(hWndNew))
{
return FALSE;
}
m_hWnd = hWndNew;
return TRUE;
}
HWND Detach()
{
HWND hWnd = m_hWnd;
m_hWnd = NULL;
return hWnd;
}
HWND GetHWND()
{
return m_hWnd;
}
protected:
HWND m_hWnd;
};
现在,我们已经定义了自己的控件,看看在XML中如何使用,具体XML如下:
在这个XML文件里,Wnd1就是我们的自定义控件,当duilib读取这个文件的时候,如果遇到不认识的控件,比如Wnd1,这时duilib框架会调用我们的CreateControl函数,所以我们需要在这个函数里完成自定义窗口的创建,绑定,具体代码如下:
virtual CControlUI* CreateControl(LPCTSTR pstrClassName)
{
CDialogBuilder builder;
if (_tcsicmp(pstrClassName, _T("Caption")) == 0)
{
CControlUI* pUI = builder.Create(_T("caption.xml"), NULL, NULL, &m_PaintManager, NULL);
return pUI;
}
else if (_tcsicmp(pstrClassName, _T("Wnd1")) == 0)
{
CWndUI *pUI = new CWndUI;
HWND hWnd = CreateWindow(_T("MyWnd"), _T("win32"), WS_VISIBLE | WS_CHILD , 0, 0, 0, 0, m_PaintManager.GetPaintWindow(), NULL, NULL, NULL);
pUI->Attach(hWnd);
pUI->OnSize += MakeDelegate(this, &CDuiFrameWnd::OnTargetSizeChanged);
return pUI;
}
else if (_tcsicmp(pstrClassName, _T("StatusPanel")) == 0)
{
CControlUI* pUI = builder.Create(_T("status.xml"), NULL, NULL, &m_PaintManager, NULL);
return pUI;
}
return WindowImplBase::CreateControl(pstrClassName);
}
至此我们的自定义控件就创建完成了,但是如果大家仔细观察,会发现在CreateWindow的时候,我们使用的是MyWnd窗口类,这个类是我们自己定义的窗口,它的功能就是定时在窗口填充红色,它的定义和窗口处理函数如下:
LRESULT CALLBACK WndDisplayProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// int wmId, wmEvent;
switch (message)
{
case WM_CREATE:
{
::SetTimer(hWnd, ID_TIMER_SHOW_PIC, 33, NULL);
}
break;
case WM_DESTROY:
{
::KillTimer(hWnd, ID_TIMER_SHOW_PIC);
}
break;
case WM_TIMER:
{
HDC hDC = ::GetDC(hWnd);
RECT rc = { 0 };
GetClientRect(hWnd, &rc);
HBRUSH hbr = ::CreateSolidBrush(RGB(0xff, 0x00, 0x00));
::FillRect(hDC, &rc, hbr);
::DeleteObject(hbr);
::ReleaseDC(hWnd, hDC);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ATOM MyRegisterWnd(TCHAR *szClass, WNDPROC proc)
{
WNDCLASS wcls;
// check to see if class already registered
if (GetClassInfo(GetModuleHandle(NULL), szClass, &wcls))
{
return 1;// name already registered - ok if it was us
}
// Use standard "button" control as a template.
GetClassInfo(NULL, _T("button"), &wcls);
// set new values
wcls.style |= CS_DBLCLKS; // Make it to receive double clicks
wcls.lpfnWndProc = proc;
wcls.hInstance = GetModuleHandle(NULL);
wcls.lpszClassName = szClass;
return RegisterClass(&wcls);
}
现在窗口已经创建完成,具体效果如下:
中间红色的部分就是我们的自定义窗口,现在我们还需要处理一个细节,就是WM_SIZE消息,当我们改变主窗口大小的时候,自定义窗口也需要做相应的调整,这就需要我们给自定义窗口添加一个WM_SIZE消息处理函数,在前面的CreateControl中,我们已经为它绑定了一个SIZE消息处理函数,现在看看它的具体代码:
bool OnTargetSizeChanged(void* param)
{
CWndUI * pUI = static_cast(m_PaintManager.FindControl(_T("wndMedia")));
if (!pUI)
return false;
const RECT& rc_pos = pUI->GetPos();
::MoveWindow(pUI->GetHWND(), rc_pos.left, rc_pos.top, rc_pos.right - rc_pos.left, rc_pos.bottom - rc_pos.top, TRUE);
return true;
}
好了,到此一个自定义窗口就创建成功,并且可以响应WM_SIZE消息。需要代码的可以到下面去下载:
https://download.csdn.net/download/u011711997/10386074