Duilib界面库学习笔记


1.Duilib的下载编译

由于一些事件google无法访问,托管在其上的项目也无法检出也基本无人更新,因此从第三方代码仓库同步下载代码,

地址如下:

1.https://github.com/CodeBees/duilib-Ex-Debug

2.https://github.com/shaoyuan1943/Duilib_Ext

两个仓库的代码是不同的,但根据更新时间来看,第一个比较新一些所以选择了第一个,下载方式是SVN Checkout

下载完后用VS2013打开直接编译会出现库文件找不到,如图:


在工程属性里把它删除就OK了,具体如图:


删除后:


照此删除其他版本工程属性配置,这样就可以编译生成4个DLL文件,分别是 多字节类型的Debug Release版本  和 Unicode类型的Debug Release版本 文件的路径可以参考工程属性的生成文件路径 如图:




2.CListContainerElementUI列表头拖动对齐功能扩展(不完美版本 参考第3条完美版)

为了在listitem中图文并茂所以用到CListContainerElementUI控件,但是发现没有对齐,且拖动列表头,没有效果。

具体如图:

正确的情况:


错误的情况:

 具体原因是CListContainerElementUI中没有实现SetPos函数,此函数来自于子类CContainerUI,虽继承与它但并没有实现自己的功能,实现原理是:传入表头,根据表头动态调整坐标,从而实现对齐,切拖动对齐功能,具体实现步骤如下:


第一步:DuiLib工程中的UIList.h文件的CListContainerElementUI类添加

void SetPos(RECT rc); 

CListHeaderUI *m_pHeader;

如图:



第二步:DuiLib工程中的UIList.cpp文件的添加函数

void CListContainerElementUI::SetPos(RECT rc)
{
	CContainerUI::SetPos(rc);

	if (m_pOwner == NULL)
	{
		return;
	}
	if (m_pHeader == NULL)
	{
		return;
	}
	TListInfoUI* pInfo = m_pOwner->GetListInfo();
	int nCount = m_items.GetSize();
	for (int i = 0; i < nCount; i++)
	{
		CControlUI *pHorizontalLayout = static_cast<CControlUI*>(m_items[i]);
		CListHeaderItemUI *pHeaderItem = static_cast<CListHeaderItemUI*>(m_pHeader->GetItemAt(i));
		if (pHorizontalLayout != NULL && pHeaderItem != NULL)
		{
			RECT rtHeader = pHeaderItem->GetPos();
			RECT rt = pHorizontalLayout->GetPos();
			rt.left = rtHeader.left;
			rt.right = rtHeader.right;
			pHorizontalLayout->SetPos(rt);
		}
	}
}
然后编译Duilib库 生成新的库文件 应用新的库文件到自己的工程


第三步:自己的工程中添加传入列表头代码

//增加日志
BOOL CFrameWindowWnd::LogAdd(PWCHAR pwComputer, PWCHAR pwEvent, PWCHAR pwParamiter)
{
	WCHAR pwTime[50] = { 0 }, pwItemValue[50] = { 0 };
	CListContainerElementUI *pListContainer = NULL;
	CHorizontalLayoutUI *pListItemContainer = NULL;
	CControlUI * pHorn = NULL;
	CLabelUI * pTime = NULL;
	CTextUI * pInfo = NULL;

	//得到日志列表控件
	m_pLogList = static_cast<CListUI*>(m_PaintManager.FindControl(_T("LogList")));

	//创建一个列表容器
	pListContainer = new CListContainerElementUI;
	pListContainer->ApplyAttributeList(_T("height=\"16\""));
	pListContainer->m_pHeader = m_pLogList->GetHeader();
	m_pLogList->Add(pListContainer);

	//第一列数据 
	pListItemContainer = new CHorizontalLayoutUI;
	pListContainer->Add(pListItemContainer);

	//创建一个CLabelUI控件 显示喇叭
	pHorn = new CLabelUI;
	pHorn->ApplyAttributeList(L"bkimage=\"horn.png\" width=\"16\" height=\"16\"");
	pListItemContainer->Add(pHorn);

	//创建一个Label控件 显示当前系统时间 
	myPublic.GetCurrentSystemTime(pwTime, _countof(pwTime));
	StringCchPrintf(pwItemValue, _countof(pwItemValue), L"text=\"%ws\" padding=\"5, 0, 0, 0\"", pwTime);
	pTime = new CLabelUI;
	pTime->ApplyAttributeList(pwItemValue);
	pListItemContainer->Add(pTime);

	//第二列数据
	pListItemContainer = new CHorizontalLayoutUI;
	pListContainer->Add(pListItemContainer);

	//创建Text控件 显示具体信息
	pInfo = new CTextUI;
	StringCchPrintf(pwItemValue, _countof(pwItemValue), L"text=\"%ws\"", pwComputer);
	pInfo->ApplyAttributeList(pwItemValue);
	pListItemContainer->Add(pInfo);

	//第三列数据
	pListItemContainer = new CHorizontalLayoutUI;
	pListContainer->Add(pListItemContainer);

	pInfo = new CTextUI;
	StringCchPrintf(pwItemValue, _countof(pwItemValue), L"text=\"%ws\"", pwEvent);
	pInfo->ApplyAttributeList(pwItemValue);
	pListItemContainer->Add(pInfo);

	//第四列数据
	pListItemContainer = new CHorizontalLayoutUI;
	pListContainer->Add(pListItemContainer);

	pInfo = new CTextUI;
	StringCchPrintf(pwItemValue, _countof(pwItemValue), L"text=\"%ws\"", pwParamiter);
	pInfo->ApplyAttributeList(pwItemValue);
	pListItemContainer->Add(pInfo);

	return FALSE;
}

其中

pListContainer->m_pHeader = m_pLogList->GetHeader();

便是新增加的代码,调用

LogAdd(L"*", L"Listener protocol start success!", L"");
LogAdd(L"*", L"Listener start success!", L"");

增加两行后的效果如图:




3.CListContainerElementUI列表头拖动及滚动条对齐扩展

由于方法2中如果是有水平滚动条 就会出现错乱 所以改写实现代码 更好的用于对齐 具体步骤如下:

UIList.h中添加void SetPos(RECT rc);

UIList.cpp中实现方法:

void CListContainerElementUI::SetPos(RECT rc)
{
	CControlUI::SetPos(rc);
	rc = m_rcItem;

	// Adjust for inset  
	rc.left += m_rcInset.left;
	rc.top += m_rcInset.top;
	rc.right -= m_rcInset.right;
	rc.bottom -= m_rcInset.bottom;

	TListInfoUI *plistinfo = GetOwner()->GetListInfo();

	// Determine the width of elements that are sizeable  
	SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top };

	for (int it2 = 0; it2 < m_items.GetSize(); it2++)
	{
		CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
		if (!pControl->IsVisible())
			continue;
		if (pControl->IsFloat())
		{
			SetFloatPos(it2);
			continue;
		}
		RECT rcPadding = pControl->GetPadding();
		SIZE sz = pControl->EstimateSize(szAvailable);
		if (sz.cx == 0)
		{
			if (sz.cx < pControl->GetMinWidth())
				sz.cx = pControl->GetMinWidth();
			if (sz.cx > pControl->GetMaxWidth())
				sz.cx = pControl->GetMaxWidth();
		}
		else
		{
			if (sz.cx < pControl->GetMinWidth())
				sz.cx = pControl->GetMinWidth();
			if (sz.cx > pControl->GetMaxWidth())
				sz.cx = pControl->GetMaxWidth();
		}

		sz.cy = pControl->GetFixedHeight();
		if (sz.cy == 0)
			sz.cy = rc.bottom - rc.top - rcPadding.top - rcPadding.bottom;
		if (sz.cy < 0)
			sz.cy = 0;
		if (sz.cy < pControl->GetMinHeight())
			sz.cy = pControl->GetMinHeight();
		if (sz.cy > pControl->GetMaxHeight())
			sz.cy = pControl->GetMaxHeight();


		RECT rcCtrl = { plistinfo->rcColumn[it2].left + rcPadding.left,
			rc.top + rcPadding.top,
			plistinfo->rcColumn[it2].right + rcPadding.left,
			rc.top + sz.cy + rcPadding.top + rcPadding.bottom };
		pControl->SetPos(rcCtrl);
	}
}
这样一来会发现启动界面是list中没有内容显示 但是拖动下列表头或者水平滚动条就会出现内容了 所以需要手动执行下SetPos函数:

m_pLogList = static_cast<CListUI*>(m_PaintManager.FindControl(_T("LogList")));
m_pLogList->SetPos(m_pLogList->GetPos());

这样一来看上去就正常了,其实不然,如果拉动水平滚动条还是会出现错乱情况,由于水平滚动条消息是相应鼠标消息实现的,所以添加窗口的鼠标消息

在HandleMessage函数中添加:

case WM_LBUTTONUP:	   lRes = OnLButtonUp(uMsg, wParam, lParam, bHandled); break;
case WM_MOUSEMOVE:	   lRes = OnMouseMove(uMsg, wParam, lParam, bHandled); break;
处理函数具体代码如下:

//WM_LBUTTONUP消息处理函数
LRESULT CFrameWindowWnd::OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	//如果客户列表是显示状态
	if (m_pClientList->IsVisible())
	{
		m_pClientList->SetPos(m_pClientList->GetPos());
	}

	//如果日志列表水平滚动条是显示状态
	if (m_pLogList->GetHorizontalScrollBar()->IsVisible())
	{
		m_pLogList->SetPos(m_pLogList->GetPos());
	}

	//监听规则列表水平滚动条是显示状态
	if (m_pListenRuleList->GetHorizontalScrollBar()->IsVisible())
	{
		m_pListenRuleList->SetPos(m_pListenRuleList->GetPos());
	}
	
	bHandled = FALSE;


	return 0;
}

//WM_MOUSEMOVE消息处理函数
LRESULT CFrameWindowWnd::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	//如果客户列表是显示状态
	if (m_pClientList->IsVisible())
	{
		m_pClientList->SetPos(m_pClientList->GetPos());
	}

	//如果日志列表水平滚动条是显示状态
	if (m_pLogList->GetHorizontalScrollBar()->IsVisible())
	{
		m_pLogList->SetPos(m_pLogList->GetPos());
	}

	//监听规则列表水平滚动条是显示状态
	if (m_pListenRuleList->GetHorizontalScrollBar()->IsVisible())
	{
		m_pListenRuleList->SetPos(m_pListenRuleList->GetPos());
	}

	bHandled = FALSE;

	return 0;
}

最后效果如图:


可以发现水平滚动条不会造成列表错乱了,这个时候如果使用TreeView控件会出现不显示的问题,

原因在于CTreeNodeUI继承于CListContainerElementUI,CListContainerElementUI继承于CContainerUI,而CListContainerElementUI本身没有SetPos函数,这个函数是我们自己实现的,CTreeNodeUI本身是调用CContainerUI::SetPos(rc),由于我们的修改造成他并没有调用这个函数,修改具体步骤如下:

1.UITreeView.h

在CTreeNodeUI类中增加void SetPos(RECT rc);声明

2.UITreeView.cpp

增加类函数:

void CTreeNodeUI::SetPos(RECT rc)
{
	CContainerUI::SetPos(rc);
}
这样一来就跳过了我们添加的 CListContainerElementUI::SetPos函数 从而调用了原本的CContainerUI::SetPos(rc), 修改后TreeView显示正常



4.为Duilib添加菜单控件

Duilib界面库默认没有菜单控件,根据demo中的菜单实现,添加文件到库中重新编译从而添加菜单控件功能,具体步骤如下

添加相应文件到库如图:


这些文件是从Duilib库的MenuDemo中拷贝的,打包下载地址:http://download.csdn.net/detail/whatday/8337157

把文件拷贝到DuiLib\Control目录,在UIlib.h文件最后添加:

#include "Control/UIMenu.h"
#include "Control/UICrack.h"
然后重新编译Duilib库,copy库文件到自己的工程目录下,这个时候就可以使用新添加的Menu控件了,使用方法如下:

CMenuWnd* pMenu = new CMenuWnd(m_hWnd);
CPoint point = msg.ptMouse;
ClientToScreen(m_hWnd, &point);
STRINGorID xml(L"ConnectMenu.xml");
pMenu->Init(NULL, xml, _T("xml"), point);

函数截图如下:


功能是添加到当前类的Notify消息处理函数中的  其中CMenuWnd是新添加的菜单类,m_hWnd是当前类继承的CWindowWnd类的成员,msg.ptMouse是当前鼠标的坐标,ConnectMenu.xml是菜单对应的XML文件,具体如下:

<?xml version="1.0" encoding="utf-8"?>
<Window width="100">
  <Font name="微软雅黑" size="12" bold="false" default="true" />
  <Default name="Menu" value="inset="2,2,2,2" itemtextpadding="30,0,0,0" bkimage="file='menu_bk.png' corner='30,8,8,8'" hole="false""/>
  <Menu>
    <MenuElement text="     Disconnect          " />
	<MenuElement text="     1s Auto Flush       " />
	<MenuElement text="     3s Auto Flush       " />
	<MenuElement text="     5s Auto Flush       " />
	<MenuElement text="     Flush               " />
  </Menu>
</Window>

菜单对应的皮肤图片可以自己在网上下载,至此从控件的添加,使用就全部完成了,最后的效果如下:




5.多窗口 多XML的应用

创建第二个或多个独立窗口只需要在主窗口类的某个事件中加入创建代码即可,部分代码如下:

...
else if (wcscmp(msg.pSender->GetName(), _T("PcInfoBtn")) == 0)
	{
		CPcInfo* pPcInfo = new CPcInfo(&pFrame->m_pClientInfo[0]);
		if (pPcInfo == NULL)
		{
			OutputDebugString(L"\r\n[CFrameWindowWnd::ClickMessage] - 创建窗口类失败!\r\n");
			return FALSE;
		}

		pPcInfo->Create(NULL, _T("RKShell_PC_INFO"), UI_WNDSTYLE_FRAME, WS_EX_STATICEDGE | WS_EX_APPWINDOW);
		pPcInfo->ShowWindow(true);
	}
...
这个代码和创建主窗口代码基本一样,其中CPcInfo是第二个窗口类,只需要拷贝主窗口代码,然后修改下相应的事件,比如关闭按钮主窗口是::PostQuitMessage(0L);这里需要修改为this->Close(); 这样才能保存子窗口关闭是不会关闭主窗口,其他的事件也是如此.


多XML的应用典型的是用于TabLayout元素

如图:
如果全部写在一个XML中 将会十分臃肿 所以需要多个XML分模块来写 具体做法如下:

<TabLayout name="Clientswitch" selectedid="0">
	<HorizontalLayout inset="1,0,0,0">
		<Include source="Disk.xml" />
	</HorizontalLayout>
	<HorizontalLayout>
		<Include source="Controll.xml" />
	</HorizontalLayout>
	<HorizontalLayout>
		<Include source="Shell.xml" />
	</HorizontalLayout>
	<HorizontalLayout>
		<Include source="PortMap.xml" />
	</HorizontalLayout>
</TabLayout>
用include来包含新的xml,子XML的Controll.xml实例如下:

<?xml version="1.0" encoding="UTF-8"?>
<Window>
	<Label text="OK" align="center" valign="center" font="3" textcolor="#FFFF0000"  />
</Window>
主Main.xml部分实例如下:

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<Window size="800,630" caption="0,0,0,32" mininfo="350,300" roundcorner="5,5,5,5" sizebox="4,4,4,4">
	<Font name="SimSun" size="12" bold="false" default="true"/>
	<Font name="SimSun" size="13" bold="true"/>
	<Font name="SimSun" size="13" bold="false"/>
	<Font name="SimSun" size="64" bold="true" italic="true"/>
	<Font name="微软雅黑" size="12" bold="false"/>
...
可以看出和主XML差不多的,这里的font="3" 是对应的Main.xml中的<Font name="SimSun" size="64" bold="true" italic="true"/> 由于是直接包含代码所以在Controll.xml中写入默认属性是没有意义的, 子XML中默认属性无效,需要放在主XML中



6.窗口滚动条不能拖动

一般情况是WM_TIMER中没有转发消息 bHandled没有设置为FALSE,滚动条使用到的WM_TIMER消息,所以必须把消息设置为继续转发(bHandled=FALSE;),这样才能正常。



  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值