關於duilib中的list的擴展探索

今天在做一個程序的界面時,需要在一個列表中顯示除文字以外的其他控件,如:EditButtonCombo等;我做界面使用的是duilib,其自帶的CListUI並不能滿足此項功能,需要進行擴展,在此記錄,以便後續使用需要。

 

先看一下實現的效果:

 

 

今天我們的擴展主要包含如下部分:

1. 表頭支持控件

在ListUI中,表頭是CListHeaderItemUI,而這個類是從CControlUI繼承而來,爲了能支持在其中顯示其他控件,它需要是一個容器,那麼需要將其父類改成CContainerUI,調整基類後,需要同時將CListHeaderItemUI類中引用 CControlUI的地方全部改成CContainerUI,如:

 

LPVOID CListHeaderItemUI::GetInterface(LPCTSTR pstrName)
{
    if( _tcscmp(pstrName, DUI_CTR_LISTHEADERITEM) == 0 ) return this;
    return CContainerUI::GetInterface(pstrName);
}

如果此處不改,將無法從XML文件中加載內嵌控件。

還需要修改其他如DoEvent事件中的CControlUI,否則會導致表頭無法拖拉,切記。

改造後,我們可以從XML文件加載了,XML文件可以這樣寫:

<List name="listex" bkcolor="#FFFFFFFF" inset="0,0,0,0" itemshowhtml="true" vscrollbar="true" hscrollbar="true" headerbkimage="file='list_header_bg.png'" itemhotimage="file='tree_hot.bmp' corner='2,1,2,1'" itemselectedimage="file='tree_select.bmp' corner='2,1,2,1'" itemalign="center" itembkcolor="#FFE2DDDF" itemaltbk="true" hscrollbar="false" menu="true">
<ListHeader height="24" menu="true">
<ListHeaderItem text="" inset="1,0,1,0" minwidth="60" endellipsis="true" font="1" width="95"  normalimage="headerctrl_normal.bmp" hotimage="headerctrl_hot.bmp" pushedimage="headerctrl_down.bmp" sepimage="Headerctrl_Sperator.bmp" sepwidth="2">
<VerticalLayout inset="1,0,5,0">
<CheckBox name="selall" endellipsis="true"  text="全選" textcolor="#FF386382" hottextcolor="#FF386382" selectedtextcolor="#FF386382" disabledtextcolor="#FFbac0c5" textpadding="20,3,0,0" align="left" selectedimage="file='checkbox_p.png' dest='0,2,15,17'" normalimage="file='checkbox_h.png' dest='0,2,15,17'"  />
</VerticalLayout>
</ListHeaderItem>
<ListHeaderItem text="域名" minwidth="100" endellipsis="true" font="1" width="200"  normalimage="headerctrl_normal.bmp" hotimage="headerctrl_hot.bmp" pushedimage="headerctrl_down.bmp" sepimage="Headerctrl_Sperator.bmp" sepwidth="2"/>
<ListHeaderItem text="描述" minwidth="120" endellipsis="true" font="1" width="150"  normalimage="headerctrl_normal.bmp" hotimage="headerctrl_hot.bmp" pushedimage="headerctrl_down.bmp" sepimage="Headerctrl_Sperator.bmp" sepwidth="2"/>
</ListHeader>


這樣顯示出來的效果如下:

 

注意:

此處需要將內嵌控件的ListHeaderItem 添加一個inset屬性,控制內嵌的控件不要鋪滿整個ListHeaderItem ,否則表頭拖動不了,如:

ListHeaderItem text="" inset="1,0,1,0"

 

2. 列表項支持控件

CListUI的某一行CListContainerElementUI繼承至容器CContainerUI,這樣一來我們只需要將需要的控件添加到此容器中,即可正確的顯示相關的控件了,此處並不需要做調整,只是這樣一來會帶來一個問題,那就是所添加的列的寬度無法與表頭的寬度保持一致。

要解決這個問題,需要給CListContainerElementUI添加SetPos函數,在此函數中,重新校正數據列寬與表頭寬度一致,具體如下:

void 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]);
// if (pHorizontalLayout != NULL)
// {
// RECT rtHeader = pHeaderItem->GetPos();
// RECT rt = pHorizontalLayout->GetPos();
// rt.left = pInfo->rcColumn[i].left;
// rt.right = pInfo->rcColumn[i].right;
// pHorizontalLayout->SetPos(rt);
// }
 
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);
}
}
 	}
CListHeaderUI *m_pHeader;

此處往列表項中添加了表頭的指針,需要在插入一行的時候,將表頭的指針傳遞進來,用於在SetPos的時候獲取表頭寬度。

注意以上代碼中的註釋部分,原本打算從TListInfoUI的rcColumn中獲取表頭項寬度的,但發現新添加行時,rcCulumn中的值全是0,需要在插入行前主動調用一次CListUI::SetPos(GetPos());才能正常,使用起來較麻煩,且容易忘記;即使是添加上了,測試發現獲取到的位置有一定的偏移,所以採用將Header傳入,實時獲取了。

以下是一行數據的XML文件描述:

<?xml version="1.0" encoding="UTF-8"?>
<Window>
  <ListContainerElement >
<CheckBox name="selectme" endellipsis="true"  text="親,選我吧!" textcolor="#FF386382" hottextcolor="#FF386382" selectedtextcolor="#FF386382" disabledtextcolor="#FFbac0c5" textpadding="20,3,0,0" align="left" selectedimage="file='checkbox_p.png' dest='0,2,15,17'" normalimage="file='checkbox_h.png' dest='0,2,15,17'"  />
<HorizontalLayout inset="4,4,4,4">
<Edit text="測試文本" bordersize="1" height="20" bordercolor="#FF4775CC" name="domain" ></Edit>
</HorizontalLayout>
<HorizontalLayout >
<VerticalLayout>
<Button text="按鈕1" width="50" pushedimage="button_down.bmp" hotimage="button_over.bmp" normalimage="button_nor.bmp" name="ttt" ></Button>
<Button text="按鈕2" width="50" pushedimage="button_down.bmp" hotimage="button_over.bmp" normalimage="button_nor.bmp" name="ttt" ></Button>
<Label text="這是從XML文件中加載的列表項"></Label>
</VerticalLayout>
</HorizontalLayout>
  </ListContainerElement>
</Window>
注意,此處需要確保ListContainerElement 的子控件個數不少於列表的列數

 在代碼中加載此XML文件,將行數據添加到列表中:

CListUIEx *pList = static_cast<CListUIEx*>(m_PaintManager.FindControl(_T("listex")));
CDialogBuilder builder;
CListContainerElementUI* pLine = (CListContainerElementUI*)(builder.Create(_T("sigle_list_item_column.xml"),(UINT)0, this));
if( pLine != NULL ) 
{
pList->InsertItem(0, 60, pLine); //此函數是經過二次封裝的
}
 加載的效果如下:

 

這樣我們可以在列表的不同項中顯示任意內容,甚至是一個完整的窗口了。

 

3. 通過代碼動態添加列及列表項

 

以上的處理均是調整後從XML加載相應的加載已經配置好的列表進行顯示,在完成上述工作後,我這邊進一步封裝了幾個函數,以便於動態的添加列[指定內嵌控件]、動態的插入行以及動態的在某一行列中添加控件。

 1). 以下代碼用於動態的添加列:

BOOL CListUIEx::InsertColumn(
 int nCol,
 CListHeaderItemUI *pHeaderItem
 )
{
CListHeaderUI *pHeader = CListUI::GetHeader();
if (pHeader == NULL)
{
return FALSE;
}
if (pHeader->AddAt(pHeaderItem, nCol))
{
return TRUE;
}
 
delete pHeaderItem;
pHeaderItem = NULL;
return FALSE;
}
 
BOOL CListUIEx::SetHeaderItemData(int nColumn, CControlUI* pControl)
{
CListHeaderUI *pHeader = CListUI::GetHeader();
if (pHeader == NULL)
{
return FALSE;
}
CListHeaderItemUI *pHeaderItem = (CListHeaderItemUI *)pHeader->GetItemAt(nColumn);
pHeaderItem->Add(pControl);
}
調用代碼如下[添加一列,並且向此列中嵌入一個CheckBox]

CListHeaderItemUI *pHeaderItem = new CListHeaderItemUI;
pHeaderItem->SetTextStyle(DT_RIGHT|DT_VCENTER|DT_SINGLELINE);
pHeaderItem->SetText("新增列  ");
pHeaderItem->SetAttribute(_T("sepimage"), _T("Headerctrl_Sperator.bmp"));
pHeaderItem->SetAttribute(_T("sepwidth"), _T("1"));
pHeaderItem->SetAttribute(_T("pushedimage"), _T("headerctrl_down.bmp"));
pHeaderItem->SetAttribute(_T("hotimage"), _T("headerctrl_hot.bmp"));
pHeaderItem->SetAttribute(_T("normalimage"), _T("headerctrl_normal.bmp"));
pHeaderItem->SetFixedWidth(150);
pList->InsertColumn(3, pHeaderItem);
 
CCheckBoxUI *pBtnUI = new CCheckBoxUI;
pBtnUI->SetText("選擇");
pBtnUI->SetAttribute(_T("selectedimage"), _T("file='checkbox_p.png' dest='0,2,15,17'"));
pBtnUI->SetAttribute(_T("normalimage"), _T("file='checkbox_h.png' dest='0,2,15,17'"));
pBtnUI->SetAttribute(_T("textpadding"), _T("20,3,0,0"));
pBtnUI->SetAttribute(_T("align"), _T("right"));
pBtnUI->SetFloat(true);
pBtnUI->SetAttribute("pos", "20,3, 65, 20");
 
pList->SetHeaderItemData(3, pBtnUI);
2). 以下代碼用於動態的添加行,動態的指定一列的內容: 

int CListUIEx::InsertItem(int nItem, int nHeight)
{
CListContainerElementUIEx *pListItem = new CListContainerElementUIEx;
pListItem->SetFixedHeight(nHeight);/*固定一個行高*/
pListItem->SetList(this);
 
CListHeaderUI *pHeader = CListUI::GetHeader();
if (NULL != pHeader)
{
int nHeaderCount = pHeader->GetCount();
for (int i = 0; i < nHeaderCount; i++)
{
pListItem->InsertColumn(i);
}
}
if ( !CListUI::AddAt(pListItem, nItem) )
{
delete pListItem;
pListItem = NULL;
return -1;
}
return nItem;
}
 
void CListUIEx::SetItemData(int nItem,
int nColumn,
LPCTSTR Text, LPCTSTR Name)
{
//存放文本
CHorizontalLayoutUI *pSubHor = GetListSubItem(nItem, nColumn);
CLabelUI *pLabel = new CLabelUI;
pLabel->SetText(Text);//控件屬性就根據需求設置吧,我簡單設置一下
pLabel->SetTextStyle(DT_CENTER);
pLabel->SetAttribute("endellipsis", "true");
pSubHor->SetAttribute("inset", "3,1,3,1");
pLabel->SetName(Name);
pSubHor->Add(pLabel);//添加到父控件
}
 
void CListUIEx::SetItemData(int nItem, int nColumn,CControlUI* pControl)
{
CHorizontalLayoutUI *pSubHor = GetListSubItem(nItem, nColumn);
pSubHor->SetAttribute("inset", "3,0,3,1");
pSubHor->Add(pControl);//添加到父控件
}

通過如下代碼來添加一行數據:

CListUIEx *pList = static_cast<CListUIEx*>(m_PaintManager.FindControl(_T("listex")));
int nIndex = pList->GetCount();
pList->InsertItem(nIndex);
 
CEditUI *pControl = new CEditUI; 
pControl->SetText("");
pControl->SetName("edit");
pControl->SetBorderColor(RGB(255, 0, 0));
pControl->SetAttribute("bordersize", "1");
pControl->SetAttribute("bordercolor", "#FF4775CC");
pList->SetItemData(nIndex, 0, pControl);
 
CButtonUI *pBtnUI = new CButtonUI;
pBtnUI->SetText("添加");
pBtnUI->SetFixedWidth(60);
pBtnUI->SetAttribute(_T("pushedimage"), _T("button_down.bmp"));
pBtnUI->SetAttribute(_T("hotimage"), _T("button_over.bmp"));
pBtnUI->SetAttribute(_T("normalimage"), _T("button_nor.bmp"));
pList->SetItemData(nIndex, 1, pBtnUI);
 
pList->SetItemData(nIndex, 2, "這是一行動態添加的數據", "testid");
if (pList->GetHeader()->GetCount() > 3)
{
pList->SetItemData(nIndex, 3, "新增列數據", "testid1");
}

至此,ListUI的擴展就告一段落了,目前已經完全滿足了的使用需求,相信也能滿足絕大部分其他人的需求了,測試程序的完整效果圖如下:

 

 

測試程序代碼下載地址:http://download.csdn.net/detail/tragicguy/7087559

  

後記:

擴展此控件,參考了文章:http://blog.csdn.net/xdrt81y/article/details/17588961

 此份測試代碼改自 羣友 【朗】 的 ListExtension.

 同時特別感謝羣友 tojen 的幫助!

duilib功能確實很強大,給我這種UI小白帶來了希望,希望有越來越多的高級控件能納入的基礎源碼庫中,方便大衆;也希望其他擴展過duilib功能的大俠,放出代碼來,造福衆人。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章