Dui缓慢飘出窗体的实现
有时候,某个子窗体在隐藏状态下执行某个任务,执行结束用户是看不到的,一下子弹出一个窗体告诉用户任务已完成显得又太唐突,于是,自己写了一个缓慢飘出的窗体提示用户,任务已完成。
(1)这个子窗体是使用Dui生成的窗体,并用Dui的控件来维护。
(2)这个子窗体是使用单独的线程来维护,因为缓慢飘出这个动作会占用很多时间,放在主窗体所在的主线程里面运行会导致主窗体卡死。
(3)析构时执行子窗体的线程需要完全终止,再释放窗体对象的资源,因为线程的强制杀死是异步的,有可能线程没被终止,而窗体对象的资源被释放掉,会造成线程运行时找不到资源,程序异常。
首先是派生自Dui的WindowImplBase类,因为它为子类实现了绝大多数
方法,包括最重要的绘制以及消息处理。
class CUiBaseWindow : public WindowImplBase
{
public:
explicit CUiBaseWindow(LPCTSTR pszXmlPath) :m_strXmlPath(pszXmlPath)
{}
virtual LPCTSTR GetWindowClassName() const
{
return _T("FeiYouWindow");
}
virtual CDuiString GetSkinFile()
{
return m_strXmlPath;
}
virtual CDuiString GetSkinFolder()
{
return _T("");
}
protected:
CDuiString m_strXmlPath;
};
//主窗体
class CUiMainWindow : public CUiBaseWindow
{
typedef std::vector<std::wstring> vecWstrHintInfo;
private:
//主窗体中的提示框控件 也就是Dui缓慢飘出窗体控件
CUiHintControl* m_hintWindowControl;
CUiHintWindow* m_hintWindow;
//C++11 互斥锁
std::mutex m_mutex;
vecWstrHintInfo m_vecWstrHintInfo;
//boost线程
boost::thread* m_hintInfoThread;
public:
CUiMainWindow(LPCTSTR pzsXmlPath);
~CUiMainWindow();
CControlUI* CreateControl(LPCTSTR pstrClass);
void InitWindow();
void Notify(TNotifyUI& msg);
LRESULT HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled);
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
void AddHintInfo(std::wstring);
std::wstring ReadHintInfo();
void ThreadShowHintInfo();
void TimerShowHintInfo();
};
飘出窗体是使用Dui的控件维护的,下面是我的主窗体的xml,主窗体中的其他控件被我删除了,只留下了自定义控件–缓慢飘出窗体的控件。
<?xml version="1.0" encoding="UTF-8"?>
<Window size="1000,680" caption="0,0,0,100" showshadow="true" shadowsize="2" shadowcolor="#FF050505" maxinfo="1000,680" >
<VerticalLayout >
<HorizontalLayout bkcolor="FF2A8AE0" height="100" >
</HorizontalLayout>
<VerticalLayout height="549" bkcolor="#FFFAFAFA" >
</VerticalLayout >
<Control height="1" bkcolor="#FFCECECE" />
<HorizontalLayout bkcolor="#FFE6E6E6" width="1000">
<HintWinow pos ="300,2" width="400" height="25" float="true" /> <!--<飘出窗体控件>-->
</HorizontalLayout>
</VerticalLayout >
</Window>
下面是子窗体控件的实现已经子窗体的xml
**class CUiHintWindow :public CUiBaseWindow
{
private:
CLabelUI* m_label;
public:
CUiHintWindow(LPCTSTR pzsXmlPath);
~CUiHintWindow();
void InitWindow();
void Notify(TNotifyUI& msg);
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
CLabelUI* GetLabelHintInfo();
};
class CUiHintControl :public CControlUI
{
public:
CUiHintControl(CUiHintWindow*);
~CUiHintControl();
LPCTSTR GetClass() const;
void SetInternVisible(bool bVisible = true);
void SetPos(RECT rc);
void DoEvent(TEventUI& event);
CUiHintWindow* GetHintWindow();
protected:
HWND m_hwnd;
CUiHintWindow* m_pHintWindow;
};
CUiHintWindow::CUiHintWindow(LPCTSTR pzsXmlPath) :CUiBaseWindow(pzsXmlPath)
{
}
CUiHintWindow::~CUiHintWindow()
{
}
void CUiHintWindow::InitWindow()
{
m_label = static_cast<CLabelUI*>(m_PaintManager.FindControl(_T("labelHintInfo")));
}
CLabelUI* CUiHintWindow::GetLabelHintInfo()
{
return m_label;
}
void CUiHintWindow::Notify(TNotifyUI& msg)
{
if (msg.sType == _T("click"))
{
CDuiString strCtrlName = msg.pSender->GetName();
if (strCtrlName == _T("btnClose"))
{
ShowWindow(false);
}
}
}
LRESULT CUiHintWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (WM_NCLBUTTONDBLCLK != uMsg)
{
return WindowImplBase::HandleMessage(uMsg, wParam, lParam);
}
return 0;
}
CUiHintControl::CUiHintControl(CUiHintWindow* pHintWindow)
{
m_pHintWindow = NULL;
m_pHintWindow = pHintWindow;
}
CUiHintControl::~CUiHintControl()
{
}
LPCTSTR CUiHintControl::GetClass() const
{
return _T("uiHintControl");
}
//关键点1,使用dui控件的显示与隐藏,控制窗体的显示与隐藏。
void CUiHintControl::SetInternVisible(bool bVisible)
{
//控件无需显示,因为即使显示也会被窗体给覆盖掉
CControlUI::SetInternVisible(false);
::ShowWindow(m_pHintWindow->GetHWND(), bVisible ? SW_SHOW : SW_HIDE);
}
//关键点2,使用dui控件的位置,也就是xml中获取的位置控制窗体的位置。
void CUiHintControl::SetPos(RECT rc)
{
CControlUI::SetPos(rc);
::SetWindowPos(m_pHintWindow->GetHWND(), NULL, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
}
void CUiHintControl::DoEvent(TEventUI& event)
{
if (event.Type == UIEVENT_SETCURSOR)
{
::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
return;
}
CControlUI::DoEvent(event);
}
CUiHintWindow* CUiHintControl::GetHintWindow()
{
return m_pHintWindow;
}
//子窗体的xml
<?xml version="1.0" encoding="UTF-8"?>
<Window size="400,25" maxinfo="400,25" >
<Font name="微软雅黑" size="15" bold="true" italic="false" />
<Font name="微软雅黑" size="12" bold="true" italic="false" />
<Font name="微软雅黑" size="12" bold="false" italic="false" />
<Font name="微软雅黑" size="14" bold="false" italic="false" />
<Font name="微软雅黑" size="12" bold="true" italic="false" />
<Font name="微软雅黑" size="10" bold="true" italic="false" />
<HorizontalLayout height="25" bkcolor="#FF2A8AE0">
<Label name="labelHintInfo" align="left" font="1" />
<Control />
<Button name="btnClose" width="25" height="20" normalimage="file='download\close.png' source='0,0,25,20'" hotimage="file='download\close.png' source='25,0,50,20'" selectedimage="file='download\close.png' source='50,0,75,20'" />
</HorizontalLayout>
</Window>
下面是主窗体的缓慢显示窗体的实现代码
CUiMainWindow::CUiMainWindow(LPCTSTR pzsXmlPath) : CUiBaseWindow(pzsXmlPath)
{
m_hintWindow = NULL;
}
CUiMainWindow::~CUiMainWindow()
{
if (m_hintInfoThread)
{
/*先完全中断线程,因为中断线程是异步的,所以join等待线程完全被中断,避免m_hintWindow资源被释放,却被没完全中断的线程访问,造成程序异常。
m_hintInfoThread->interrupt();
m_hintInfoThread->join();
delete m_hintInfoThread;
m_hintInfoThread = NULL;
}
if (m_hintWindow)
{
delete m_hintWindow;
m_hintWindow = NULL;
}
}
CControlUI* CUiMainWindow::CreateControl(LPCTSTR pstrClass)
{
if (_tcscmp(pstrClass, _T("HintWinow")) == 0)
{
//创建缓慢飘出窗体
m_hintWindow = new CUiHintWindow(_T("HintWindow.xml"));
m_hintWindow->Create(m_hWnd, _T("HintWindow"), UI_WNDSTYLE_CHILD&~WS_VISIBLE, 0);
//将缓慢飘出窗体与主窗体中的自定义控件相关联
m_hintWindowControl = new CUiHintControl(m_hintWindow);
return m_hintWindowControl;
}
return NULL;
}
void CUiMainWindow::InitWindow()
{
//创建线程
m_hintInfoThread = new boost::thread(boost::bind(&CUiFyMainWindow::ThreadShowHintInfo,this));//&CUiFyMainWindow::ThreadShowHintInfo, this);
for (int n = 0; n < 20;n++)
{
//往缓慢飘出窗体加入任务提示语句
AddHintInfo(L"HintInfoHintInfoHintInfo");
}
}
void CUiMainWindow::Notify(TNotifyUI& msg)
{
if (msg.sType == _T("click"))
{
}
}
LRESULT CUiMainWindow::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled)
{
if (uMsg == WM_KEYDOWN)
{
switch (wParam)
{
case VK_RETURN:
break;
case VK_ESCAPE:
bHandled = true;
break;
case VK_SPACE:
bHandled = true;
break;
case SC_CLOSE:
bHandled = true;
break;
default:
break;
}
}
else if (uMsg == WM_SYSCOMMAND)
{
switch (wParam)
{
case SC_CLOSE:
{
ShowWindow(false);
bHandled = true;
}
}
}
return false;
}
LRESULT CUiMainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (WM_NCLBUTTONDBLCLK != uMsg)
{
return WindowImplBase::HandleMessage(uMsg, wParam, lParam);
}
return 0;
}
//因为缓慢窗体飘出和主窗体占用的是不同的线程,需要给
//m_vecWstrHintInfo加上互斥量,避免发生读写访问冲突。
void CUiMainWindow::AddHintInfo(std::wstring wstrHintInfo)
{
m_mutex.lock();
m_vecWstrHintInfo.push_back(wstrHintInfo);
m_mutex.unlock();
}
std::wstring CUiMainWindow::ReadHintInfo()
{
m_mutex.lock();
std::wstring wstrHintInfo;
if (int n =m_vecWstrHintInfo.size())
{
wstrHintInfo = *m_vecWstrHintInfo.begin();
m_vecWstrHintInfo.erase(m_vecWstrHintInfo.begin());
}
m_mutex.unlock();
return wstrHintInfo;
}
void CUiMainWindow::ThreadShowHintInfo()
{
while (true)
{
boost::this_thread::sleep(boost::posix_time::seconds(1));
TimerShowHintInfo();
}
}
void CUiMainWindow::TimerShowHintInfo()
{
std::wstring wstrHintInfo = ReadHintInfo();
if (!wstrHintInfo.empty())
{
RECT recPos = m_hintWindowControl->GetPos();
m_hintWindow->GetLabelHintInfo()->SetText(wstrHintInfo.c_str());
m_hintWindow->ShowWindow(true);
for (int n = 1; n < 10; n++)
{
LONG lHeight = (recPos.bottom - recPos.top) / n;
::SetWindowPos(m_hintWindow->GetHWND(), NULL, recPos.left, recPos.top + lHeight, recPos.right - recPos.left,
recPos.bottom - recPos.top - lHeight, SWP_NOZORDER | SWP_NOACTIVATE);
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
::SetWindowPos(m_hintWindow->GetHWND(), NULL, recPos.left, recPos.top, recPos.right - recPos.left,
recPos.bottom - recPos.top, SWP_NOZORDER | SWP_NOACTIVATE);
boost::this_thread::sleep(boost::posix_time::seconds(5));
for (int n = 10; n > 0; n--)
{
LONG lHeight = (recPos.bottom - recPos.top) / n;
::SetWindowPos(m_hintWindow->GetHWND(), NULL, recPos.left, recPos.top + lHeight, recPos.right - recPos.left,
recPos.bottom - recPos.top - lHeight, SWP_NOZORDER | SWP_NOACTIVATE);
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
m_hintWindow->ShowWindow(false);
}
}