duilib进阶教程--改进List控件(转)
前情提要:Duilib源码中的ListDemo,给我们提供了⼀个可以左右拖拉headerItem,下⾯的listitem也跟着变化。但实际⼯作中,往往HeaderItem和listitem都⽐较复杂,不可能只是⼀个text。这就要求他是个容器,可以放option,button,image等。
类似这样的效果:
1 ⾸先改进CListHeaderItemUI
原来CListHeaderItemUI继承的事CControlUI,让他改成继承CContainerUI就好了,另外需要重写void SetPos(RECT rc);否则其⼦控件的有效作⽤范围给headerItem的⼤⼩,拖拉的触发事件失效。
void SetPos(RECT rc); 参考void CHorizontalLayoutUI::SetPos(RECT rc) 拷贝过来就可以了。
另外xml中的写法有变化
<ListHeader height="30" menu="true">
<ListHeaderItem text="" font="1" width="60" hotimage="file='list_bg.png' source='1,0,2,58'" pushedima
ge="file='list_header_pushed.png'" sepimage="file='list_header_sepline.png'" sepwidth="5"> <Option float="true" pos="5,0,0,0" width="50" height="30" name="OptionOfListApplication" normalimage="file='checkbox.png' dest='23,8,37,22' source='0,0,14,14'" hotimage="file='checkbox.png' dest='23,8,37,22' source='14,0,28,14'" pushedimage="file='checkbox.png' dest='23,8,37,22'
source='28,0,42,14'" disabledimage="file='checkbox.png' dest='23,8,37,22' source='42,0,56,14'" hottextcolor="#FF007AFF" selectedforeimage="file='check.png' dest='23,8,37,22'"/>
</ListHeaderItem>
<ListHeaderItem text="Domain" font="1" width="260" hotimage="file='list_bg.png' source='1,0,2,58'" pushedimage="file='list_header_pushed.png'" sepimage="file='list_header_sepline.png'" sepwidth="1"> </ListHeaderItem>
<ListHeaderItem text="Description" font="1" width="240" hotimage="file='list_bg.png' source='1,0,2,58'" pushedimage="file='list_header_pushed.png'" sepimage="file='list_header_sepline.png'" sepwidth="1"> </ListHeaderItem>
</ListHeader>
注意Option 要设置 float="true" pos="5,0,0,0" 两个属性
2 改进CListContainerElementUI
虽然他是个容器,但他对headeritem的拖拉⽆响应,不能随之改变。
源码中void CListContainerElementUI::DrawItemText(HDC hDC, const RECT& rcItem) 这个⽅法直接return 了。需要实现。
void CListContainerElementUI::DrawItemText(HDC hDC, const RECT& rcItem)
{
if( m_pOwner == NULL ) return;
TListInfoUI* pInfo = m_pOwner->GetListInfo();
DWORD iTextColor = pInfo->dwTextColor;
if( (m_uButtonState & UISTATE_HOT) != 0 ) {
iTextColor = pInfo->dwHotTextColor;
}
if( IsSelected() ) {
iTextColor = pInfo->dwSelectedTextColor;
}
if( !IsEnabled() ) {
iTextColor = pInfo->dwDisabledTextColor;
}
for( int i = 0; i < pInfo->nColumns; i++ )
{
RECT rcItem = { pInfo->rcColumn[i].left, p, pInfo->rcColumn[i].right, m_rcItem.bottom };        rcItem.left += pInfo->rcTextPadding.left;
rcItem.right -= pInfo->rcTextPadding.right;

rcItem.bottom -= pInfo->rcTextPadding.bottom;
CControlUI* pControl = GetItemAt(i);
pControl->SetPos(rcItem);
}
}
⽽且需要在DoPaint中调⽤
void CListContainerElementUI::DoPaint(HDC hDC, const RECT& rcPaint)
{
if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
DrawItemText(hDC, m_rcItem);
DrawItemBk(hDC, m_rcItem);
CContainerUI::DoPaint(hDC, rcPaint);
}
另外xml的写法有所改变,不能像以前那样随便写了,CListContainerElementUI下⾯的⼀级⼦控件为每⼀列的容器,也可以不是容器,但如果此列包含多个控件,就必须是个容器,可参考如下写法:
<ListContainerElement  height="58">
<HorizontalLayout float="true" pos="0,0,60,58">
<Option float="true" pos="5,0,0,0" width="50" height="30" name="OptionOfListApplication" normalimage="file='checkbox.png' dest='23,8,37,22' source='0,0,14,14'" hotimage="file='checkbox.png' dest='23,8,37,22' source='14,0,28,14'" pushedimage="file='checkbox.png' dest='23,8,37,22'
source='28,0,42,14'" disabledimage="file='checkbox.png' dest='23,8,37,22' source='42,0,56,14'" hottextcolor="#FF007AFF" selectedforeimage="file='check.png' dest='23,8,37,22'"/>
</HorizontalLayout>
<Label float="true" pos="60,0,320,58" name="docsize" text="2222" align="center" textcolor="#FF333333" width="260"/>
<Label float="true" pos="320,0,560,58" name="appversion" text="3333" align="center"
textcolor="#FF333333"  />
</ListContainerElement>
上篇博客写完,以为改进List达到了项⽬要求,可后来发现诱发了其他的问题,如滚动条部分功能失效,还有程序在运⾏⼀段时间后进⼊了⽆响应状态。
后来在以下三个⽅向进⾏了探索:
1 主要改进还是在DrawItemText 函数中,试图解决由此引起的Bug
结果:不能解决掉,但证明duilib是可以实现的,只是思路和⽅法还没有对。
DrawItemText 本来的作⽤是重绘Text⽂本,在这⾥调⽤setpos,会引起其⽗控件重绘,⽗控件重绘⼜
会调⽤DoPaint,有循环调⽤,程序很容易崩掉。思路不对。
2 重写DoPaint函数
结果:只能对其⼀级⼦控件进⾏背景,⽂本的重绘,没有诱发其他Bug ,但很难实现⼀级⼦控件及它的⼦控件⼀起重绘。
思路:由于CListContainerElementUI继承于CContainerUI,原来只是在最后调⽤了⽗类的DoPaint,根据CContainerUI::DoPaint(hDC, rcPaint);源码进⾏修改,每列的宽度可以得到,所以⼦控件的可以根据头部的宽度重绘。
3 重写SetPos函数
结果:这次终于完美解决,上张图,呵呵!
思路:以前两种⽅案,仍旧掉在CListTextElementUI实现思路的坑⾥。整理思路CListContainerElementUI中的每⼀列可以是⼀个简单控件,也可以是⼀个容器控件,所以只是在DoPaint⾥做⽂章,⽆法满⾜要求。SetPos既然可以实现容器控件的位置,宽⾼的改变,那为什么不重写SetPos呢,拿来CContainerUI的SetPos,进⾏重写。
上代码
void CListContainerElementUI::SetPos(RECT rc){if( m_pOwner == NULL ) return;TListInfoUI* pInfo =
m_pOwner->GetListInfo();int iChangeIndex=0;LONG cx = 0;for( int i = 0; i < pInfo->nColumns; i++ ) {CControlUI* pControl = GetItemAt(i);if(!pControl) break;RECT rcOldItem = pControl->GetPos();if(pInfo->rcColumn[i].right-rcOldItem.right!=0){iChangeIndex =i;cx=pInfo->rcColumn[i].right-
pushedrcOldItem.right;break;}}RECT rcNew = {rc.p,rc.right+cx,rc.bottom};CControlUI::SetPos(rcNew);if( m_items.IsEmpty() ) return;rcNew.left += m_rcInset.p += p;rcNew.right -=
m_rcInset.right;rcNew.bottom -= m_rcInset.bottom;for( int it = 0; it < m_items.GetSize(); it++ ) {CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);if( !pControl->IsVisible() ) continue;if( pControl->IsFloat() ) {if(it>=iChangeIndex){RECT rcItem = { pInfo->rcColumn[it].left, p, pInfo->rcColumn[it].right, m_rcItem.bottom };pControl->SetPos(rcItem);}}else {pControl->SetPos(rcNew); // 所有⾮float⼦控件放⼤到整个客户区}}}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。