前情提要: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'" pushedimage="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, m_rcItem.top, pInfo->rcColumn[i].right, m_rcItem.bottom }; rcItem.left += pInfo->rcTextPadding.left; rcItem.right -= pInfo->rcTextPadding.right; rcItem.top += pInfo->rcTextPadding.top; 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-rcOldItem.right; break;
}
} RECT rcNew = {rc.left,rc.top,rc.right+cx,rc.bottom}; CControlUI::SetPos(rcNew); if( m_items.IsEmpty() ) return; rcNew.left += m_rcInset.left; rcNew.top += m_rcInset.top; 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, m_rcItem.top, pInfo->rcColumn[it].right, m_rcItem.bottom }; pControl->SetPos(rcItem); } } else { pControl->SetPos(rcNew); // 所有非float子控件放大到整个客户区 } } }
|