DuiLib表格类和树控件 虚表实体数据

实际使用中发现,虚表模式编辑表格时,挺啰嗦的。我觉得虚表只适合列表模式下的数据展示,比如中数据库或者文件中读取行去显示。

上一个版本中,实表模式有点鸡肋,不仅速度慢还非常占用内存。这次改动很大,主要是奔着实表模式下提升性能(节约内存和提高速度)去的,我在原有的基础上做改动,结果推倒重来无数次,忍无可忍只好从白板开始重新写了。

CGridListUI、CGridListHeaderUI、GGridListBodyUI、CGridListRowUI、CGridListCellUI统一改名为CGridUI、CGridHeaderUI、GGridBodyUI、CGridRowUI、CGridCellUI,长痛不如短痛吧。

这次改动实表模式有点类似于虚表,CGridBody根据数据内容动态创建控件,数据行使用结构体TRowData保存,单元格数据使用结构体TCellData保存。TRowData和TCellData使用内存池技术new和delete,极大的提高了速度。我测试了100000行6列,也就是60万个单元格,载入表格只需要几秒钟,几乎没感觉,仅占用内存100多MB。过程中有个奇怪的发现,原本TCellData中用std::string保存字符串,析构60万个std::string,我的破电脑需要27秒。换成CDuiString之后不仅速度快了,内存占用还变低了。

建议使用中,有许多大数据表格时,隐藏表格时调用ResetGridBody来清空表格数据,这个操作会把单元格内存归还内存池。显示表格时,重新载入表格数据。原因是,程序结束之前,内存池所占的内存无法还给操作系统,也无法手动调用某个接口去主动清理内存池。

很显然的,不要在GridBody插入任何控件,这里面的控件都是动态创建的。

表格内置了几种单元格类型:分别是文本,编辑框,复选框,下拉框,时间选取,图片,容器。

typedef enum enumGridCellType
{
	celltypeText			= 0,
	celltypeEdit			= 1,
	celltypeCheckBox		= 2,
	celltypeCombo			= 3,
	celltypeDateTime		= 4,
	celltypeDate			= 5,
	celltypeTime			= 6,
	celltypePicture			= 7,
	celltypeContainer		= 8
}GridCellType;

单元格类型看名字就知道什么意思了,比上一个版本多了一个图像类型celltypePicture,允许在单元格中显示图片。celltypeContainer表明是一个容器CHorizontalLayoutUI,可以在单元格中插入自定义控件。celltypeText为默认类型。

下面演示一下如何操作,具体实现请看源代码。

1,设置单元格类型,

//设置整列单元格类型
void SetCellType(int col, GridCellType cellType);

//设置单元格类型
void SetCellType(int row, int col, GridCellType cellType);

作为约定,表格头的单元格类型只能调用void SetCellType(int row, int col, GridCellType cellType)去设置,实际使用中建议在设计器中编辑好表头。

原则上,能设置整列就不要设置单个,它们的保存位置不一样。如果对60万个单元格单独去设置类型,将会至少占用60万*4字节的内存。

单个设置的类型优先于整列设置的类型。

2,初始化图像单元格和容器单元格

实表模式下,单元格控件也是动态创建的,需要提供一个时机去初始化。


void CMainFrame::OnNotifyInitCell(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		int row = msg.wParam;
		int col = msg.lParam;
		CGridCellUI *pCellUI = m_pGrid->GetCellUI(row, col);
		if(!pCellUI) return;

		//在表格第8列,插入2个按钮
		if(col == 8)
		{
			pCellUI->SetChildPadding(10);

			CButtonUI *pButtonModify = new CButtonUI;
			pCellUI->Add(pButtonModify);
			pButtonModify->ApplyAttributeList(_T("style_button"));
			pButtonModify->SetForeImage(_T("images\\edtico.png"));
			pButtonModify->SetFixedWidth(16);
			pButtonModify->SetFixedHeight(16);
			pButtonModify->SetName(_T("grid_innerbutton_modify"));

			CButtonUI *pButtonDelete = new CButtonUI;
			pCellUI->Add(pButtonDelete);
			pButtonDelete->ApplyAttributeList(_T("style_button"));
			pButtonDelete->SetForeImage(_T("images\\delico.png"));
			pButtonDelete->SetFixedWidth(16);
			pButtonDelete->SetFixedHeight(16);
			pButtonDelete->SetName(_T("grid_innerbutton_delete"));	
		}
	}
}

响应消息DUI_MSGTYPE_INITCELL,判断行和列进行初始化。当窗口大小变化时,CGirdBodyUI根据当前大小创建新行时,便会触发这个消息。新创建行只会触发一次。

3,实表数据填充

//表格填充
m_pGrid->SetRowCount(12);
for (int i=0; i<m_pGrid->GetRowCount(); i++)
{
	for (int j=1; j<m_pGrid->GetColumnCount(); j++)
	{
		CString temp; 
		temp.Format(_T("%02d,%02d"), i, j);
		m_pGrid->Cell(i,j).SetText(temp);
	}
}
 
//插入单行
int row = m_pGrid->InsertRow();
for (int j=1; j<m_pGrid->GetColumnCount(); j++)
{
	CString temp; 
	temp.Format(_T("%02d,%02d"), row, j);
	m_pGrid->Cell(row,j).SetText(temp);
}

4,消息响应

//celltypeCombo选中项时
void CMainFrame::OnNotifyItemSelect(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		CDuiString s;
		s.Format(_T("combo cell select item, row=%d, col=%d"), msg.wParam, msg.lParam);
		InsertMsgUI(s);
	}
}

//celltypeCheckBox类型,选中状态变更时
void CMainFrame::OnNotifySelectChanged(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		TCellData *pCellData = m_pGrid->GetCellData(msg.wParam, msg.lParam);
		CDuiString s;
		s.Format(_T("checkbox cell selectchanged, row=%d, col=%d, %s"), msg.wParam, msg.lParam, pCellData->IsCheckBoxCheck() ? _T("Select") : _T("UnSelect"));
		InsertMsgUI(s);
	}
}

//下拉框点击下拉按钮时,还未显示时,更新下拉项
void CMainFrame::OnNotifyPreDropDown(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		CGridCellUI *pCellUI = m_pGrid->GetCellUI(msg.wParam, msg.lParam);
		CComboExUI *pCombo = (CComboExUI *)pCellUI->GetInnerControl();

		CDuiString s;
		s.Format(_T("combo cell predropdown, row=%d, col=%d"), msg.wParam, msg.lParam);
		InsertMsgUI(s);

		pCombo->RemoveAll();
		for (int i=0; i<10; i++)
		{
			CString s;
			s.Format(_T("combo text %d"), i);
			pCombo->AddString(s);
		}
		
	}
}

//下拉框点击下拉按钮之后
void CMainFrame::OnNotifyDropDown(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		CDuiString s;
		s.Format(_T("combo cell dropdown, row=%d, col=%d"), msg.wParam, msg.lParam);
		InsertMsgUI(s);
	}
}

//编辑框开始编辑时
void CMainFrame::OnNotifyStartEdit(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		CDuiString s;
		s.Format(_T("edit cell start edit, row=%d, col=%d"), msg.wParam, msg.lParam);
		InsertMsgUI(s);
	}
}

//编辑框结束编辑时
void CMainFrame::OnNotifyEndEdit(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		CDuiString s;
		s.Format(_T("edit cell end edit, row=%d, col=%d"), msg.wParam, msg.lParam);
		InsertMsgUI(s);
	}
}

//单元格文本内容变更时
void CMainFrame::OnNotifyTextChanged(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		TCellData *pCellData = m_pGrid->GetCellData(msg.wParam, msg.lParam);

		CDuiString s;
		s.Format(_T("edit cell text changed, row=%d, col=%d, text=%s"), msg.wParam, msg.lParam, pCellData->GetText());
		InsertMsgUI(s);
	}
}

5,虚表部分

需要手动调用m_pGrid->SetVirtualGrid(TRUE),这个不能在xml中定义了。

然后调用m_pGrid->SetRowCount(10 * 10000);

虚表数据填充,响应消息DUI_MSGTYPE_DRAWITEM


void CMainFrame::OnNotifyDrawItem(TNotifyUI& msg)
{
	//for virtual grid, 
	if(IsControl(msg, _T("grid_main")))
	{
		int lo = msg.wParam;	//begin row
		int hi = msg.lParam;	//end row

		InsertMsgUiV(_T("DrawRange=%d,%d"), lo, hi);

		int sort_col = m_pGrid->GetSortColumn();
		BOOL bAscending = m_pGrid->GetSortAscending();

		//fill grid content
		for (int i=lo; i<=hi; i++)
		{
			for (int j=1; j<m_pGrid->GetColumnCount(); j++)
			{
				if(sort_col > 0 && !bAscending)
				{
					CDuiString s;
					s.Format(_T("%d,%d"), m_pGrid->GetRowCount()-i, j);
					m_pGrid->GetCellUI(i,j)->SetText(s);
				}
				else
				{
					CDuiString s;
					s.Format(_T("%d,%d"), i, j);
					m_pGrid->GetCellUI(i,j)->SetText(s);
				}
			}
		}
	}
}

和实表的填充有个区别,m_pGrid->GetCellUI(i,j)->SetText(s),获取的是DuiLib原生控件去设置的。

响应点击表格头排序,响应消息DUI_MSGTYPE_SORTITEM。

void CMainFrame::OnNotifySortItem(TNotifyUI& msg)
{
	if(IsControl(msg, _T("grid_main")))
	{
		//for virtual grid, when a sort message is received, we have to refresh the virtual order.
		//and then we receive a drawitem message to sort the local data
		if(m_pGrid->IsVirtualGrid())
		{
                        //这时候可以对本地数据进行排序, 接着会触发OnNotifyDrawItem刷新显示。
			m_pGrid->Refresh(true);
		}
	}
}

注意:虚表能进行的操作很有限,很多函数都不支持。

--------------------------------------------------------------------------------------

根据这个原理,TreeUI也可以做了。我原本想从GridUI继承一个TreeUI,但是诸多麻烦,所以我又从白板开始写TreeUI,把GridUI中能用的代码抄过来。

TreeUI不支持虚表模式,树控件各种递归循环,调用方要构造数组去动态显示的话,还不如直接用我这个实表模式。

--------------------------------------------------------------------------------------

时间有限,精力有限,水平也很有限,不可能花大量时间去测试每个细节,总之就是满足我自己的需求了。如果很荣幸你有在使用,欢迎提出你的建议。

代码共享地址:

https://gitee.com/Liqs99/DuiLib_DuiEditor

https://github.com/xfcanyue/DuiLib_DuiEditor

duilib设计器交流群:819272442

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值