GacUI Demo:列表的虛擬模式,不需要為每一個列表項分配記憶體的一種顯示方法
阿新 • • 發佈:2018-12-27
GacUI的所有列表控制元件都支援虛擬模式。虛擬模式是一種不需要為每一個列表項分配記憶體的一種顯示方法。在開始的時候,需要高速列表一共有多少個列表項。之後,列表控制元件在渲染的時候,會跟資料來源要求獲取某一個下標所包含的資料,並且在這個資料一直處於螢幕上的時候,只會跟資料來源獲取一次。完整的程式碼可以在http://www.gaclib.net/Demos/Controls.ListBox.VirtualMode/Demo.html看到。先上圖:
先看建立介面的程式碼。一般來說,所有可以隨著視窗的變化自動排版的控制元件組織方法,都是使用一個或多個GuiTableComposition來實現的。
class VirtualModeWindow : public GuiWindow
{
private:
GuiVirtualTextList* listBox;
GuiButton* buttonIncrease;
GuiButton* buttonDecrease;
DataSource* dataSource;
void buttonIncrease_Clicked(GuiGraphicsComposition * sender, GuiEventArgs& arguments)
{
dataSource->SetCount(dataSource->Count()+100000);
}
void buttonDecrease_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
dataSource->SetCount(dataSource->Count()-100000);
}
public:
VirtualModeWindow()
:GuiWindow(GetCurrentTheme() ->CreateWindowStyle())
{
this->SetText(L"Controls.ListBox.VirtualMode");
GuiTableComposition* table=new GuiTableComposition;
table->SetRowsAndColumns(3, 2);
table->SetCellPadding(3);
table->SetAlignmentToParent(Margin(0, 0, 0, 0));
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetRowOption(1, GuiCellOption::MinSizeOption());
table->SetRowOption(2, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(1, GuiCellOption::MinSizeOption());
this->GetContainerComposition()->AddChild(table);
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 0, 3, 1);
dataSource=new DataSource;
listBox=new GuiVirtualTextList(GetCurrentTheme()->CreateTextListStyle(), GetCurrentTheme()->CreateTextListItemStyle(), dataSource);
listBox->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
listBox->SetHorizontalAlwaysVisible(false);
cell->AddChild(listBox->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 1, 1, 1);
buttonIncrease=g::NewButton();
buttonIncrease->SetText(L"Increase 100000 Items");
buttonIncrease->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
buttonIncrease->Clicked.AttachMethod(this, &VirtualModeWindow::buttonIncrease_Clicked);
cell->AddChild(buttonIncrease->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(1, 1, 1, 1);
buttonDecrease=g::NewButton();
buttonDecrease->SetText(L"Decrease 100000 Items");
buttonDecrease->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
buttonDecrease->Clicked.AttachMethod(this, &VirtualModeWindow::buttonDecrease_Clicked);
cell->AddChild(buttonDecrease->GetBoundsComposition());
}
// set the preferred minimum client sizethis->GetBoundsComposition()->SetPreferredMinSize(Size(480, 480));
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()this->ForceCalculateSizeImmediately();
// move to the screen centerthis->MoveToScreenCenter();
}
};
GuiVirtualTextList就是隻有虛擬模式的GuiTextList。事實上GuiVirtualTextList是GuiTextList的基類,而GuiTextList.GetItems()返回的物件也是一個數據源。因此非虛擬模式其實也是通過虛擬模式來實現的。在資料比較少的時候,非虛擬模式操作起來十分的簡單,而在資料比較多的時候,虛擬模式可以帶來很好的效能。上面的程式碼建立了一個DataSource類來做資料來源,並且有一個SetCount的函式用來更改列表裡面的數量的總量,然後每一個列表項的內容都是Item xxx。這是怎麼做到的呢?我們來看資料來源的程式碼:
class DataSource : public list::ItemProviderBase, private list::TextItemStyleProvider::ITextItemView
{
protected:
int count;
public:
DataSource()
:count(100000)
{
}
void SetCount(int newCount)
{
if(0<=newCount)
{
int oldCount=count;
count=newCount;
// this->InvokeOnItemModified(affected-items-start, affected-items-count, new-items-count);
// this function notifies the list control to update it's content and scroll barsif(oldCount<newCount)
{
// insertthis->InvokeOnItemModified(oldCount, 0, newCount-oldCount);
}
elseif(oldCount>newCount)
{
// deletethis->InvokeOnItemModified(newCount, oldCount-newCount, 0);
}
}
}
// GuiListControl::IItemProviderint Count()
{
return count;
}
IDescriptable* RequestView(const WString& identifier)
{
if(identifier==list::TextItemStyleProvider::ITextItemView::Identifier)
{
returnthis;
}
elseif(identifier==GuiListControl::IItemPrimaryTextView::Identifier)
{
returnthis;
}
else
{
return0;
}
}
void ReleaseView(IDescriptable* view)
{
}
// list::TextItemStyleProvider::ITextItemView
WString GetText(int itemIndex)
{
return L"Item "+itow(itemIndex+1);
}
bool GetChecked(int itemIndex)
{
// DataSource don't support check statereturnfalse;
}
void SetCheckedSilently(int itemIndex, bool value)
{
// DataSource don't support check state }
// GuiListControl::IItemPrimaryTextView
WString GetPrimaryTextViewText(int itemIndex)
{
return GetText(itemIndex+1);
}
bool ContainsPrimaryText(int itemIndex)
{
returntrue;
}
};
對於GuiVirtualTextList來說,只需要實現vl::presentation::controls::list::TextItemStyleProvider::ITextItemView就可以了。GacUIIncludes.h裡面已經有了using namespace vl::presentation::controls,所以在這裡只需要從list::開始寫。list::TextItemStyleProvider::ITextItemView還要求實現GuiListControl::IItemPrimaryTextView。在目前的GacUI裡面,IItemPrimaryTextView是專門為下拉框準備的。因為下拉框允許接受任何一種列表物件當做下拉內容,所以GacUI的列表資料來源預設都要求實現IItemPrimaryTextView。
實現資料來源的時候,其實並不要求資料來源類繼承自ITextItemView和IItemPrimaryTextView。因為GacUI都是通過RequestView來獲取一個View的介面指標的,程式碼如上。實現這兩個View也很簡單,在這裡就不贅述了。
GuiTextList就介紹到這裡了,接下來的幾個Demo都將是關於ListView的。下一個Demo是ListView山寨Windows 7的資源管理器介面,可以在http://www.gaclib.net/Demos/Controls.ListView.ViewSwitching/Demo.html看到。具體內容將在下一篇部落格中闡述。
先看建立介面的程式碼。一般來說,所有可以隨著視窗的變化自動排版的控制元件組織方法,都是使用一個或多個GuiTableComposition來實現的。
class
{
private:
GuiVirtualTextList* listBox;
GuiButton* buttonIncrease;
GuiButton* buttonDecrease;
DataSource* dataSource;
void buttonIncrease_Clicked(GuiGraphicsComposition
{
dataSource->SetCount(dataSource->Count()+100000);
}
void buttonDecrease_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
dataSource->SetCount(dataSource->Count()-100000);
}
public:
VirtualModeWindow()
:GuiWindow(GetCurrentTheme()
{
this->SetText(L"Controls.ListBox.VirtualMode");
GuiTableComposition* table=new GuiTableComposition;
table->SetRowsAndColumns(3, 2);
table->SetCellPadding(3);
table->SetAlignmentToParent(Margin(0, 0, 0, 0));
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetRowOption(1, GuiCellOption::MinSizeOption());
table->SetRowOption(2, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(1, GuiCellOption::MinSizeOption());
this->GetContainerComposition()->AddChild(table);
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 0, 3, 1);
dataSource=new DataSource;
listBox=new GuiVirtualTextList(GetCurrentTheme()->CreateTextListStyle(), GetCurrentTheme()->CreateTextListItemStyle(), dataSource);
listBox->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
listBox->SetHorizontalAlwaysVisible(false);
cell->AddChild(listBox->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 1, 1, 1);
buttonIncrease=g::NewButton();
buttonIncrease->SetText(L"Increase 100000 Items");
buttonIncrease->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
buttonIncrease->Clicked.AttachMethod(this, &VirtualModeWindow::buttonIncrease_Clicked);
cell->AddChild(buttonIncrease->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(1, 1, 1, 1);
buttonDecrease=g::NewButton();
buttonDecrease->SetText(L"Decrease 100000 Items");
buttonDecrease->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
buttonDecrease->Clicked.AttachMethod(this, &VirtualModeWindow::buttonDecrease_Clicked);
cell->AddChild(buttonDecrease->GetBoundsComposition());
}
// set the preferred minimum client sizethis->GetBoundsComposition()->SetPreferredMinSize(Size(480, 480));
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()this->ForceCalculateSizeImmediately();
// move to the screen centerthis->MoveToScreenCenter();
}
};
GuiVirtualTextList就是隻有虛擬模式的GuiTextList。事實上GuiVirtualTextList是GuiTextList的基類,而GuiTextList.GetItems()返回的物件也是一個數據源。因此非虛擬模式其實也是通過虛擬模式來實現的。在資料比較少的時候,非虛擬模式操作起來十分的簡單,而在資料比較多的時候,虛擬模式可以帶來很好的效能。上面的程式碼建立了一個DataSource類來做資料來源,並且有一個SetCount的函式用來更改列表裡面的數量的總量,然後每一個列表項的內容都是Item xxx。這是怎麼做到的呢?我們來看資料來源的程式碼:
class DataSource : public list::ItemProviderBase, private list::TextItemStyleProvider::ITextItemView
{
protected:
int count;
public:
DataSource()
:count(100000)
{
}
void SetCount(int newCount)
{
if(0<=newCount)
{
int oldCount=count;
count=newCount;
// this->InvokeOnItemModified(affected-items-start, affected-items-count, new-items-count);
// this function notifies the list control to update it's content and scroll barsif(oldCount<newCount)
{
// insertthis->InvokeOnItemModified(oldCount, 0, newCount-oldCount);
}
elseif(oldCount>newCount)
{
// deletethis->InvokeOnItemModified(newCount, oldCount-newCount, 0);
}
}
}
// GuiListControl::IItemProviderint Count()
{
return count;
}
IDescriptable* RequestView(const WString& identifier)
{
if(identifier==list::TextItemStyleProvider::ITextItemView::Identifier)
{
returnthis;
}
elseif(identifier==GuiListControl::IItemPrimaryTextView::Identifier)
{
returnthis;
}
else
{
return0;
}
}
void ReleaseView(IDescriptable* view)
{
}
// list::TextItemStyleProvider::ITextItemView
WString GetText(int itemIndex)
{
return L"Item "+itow(itemIndex+1);
}
bool GetChecked(int itemIndex)
{
// DataSource don't support check statereturnfalse;
}
void SetCheckedSilently(int itemIndex, bool value)
{
// DataSource don't support check state }
// GuiListControl::IItemPrimaryTextView
WString GetPrimaryTextViewText(int itemIndex)
{
return GetText(itemIndex+1);
}
bool ContainsPrimaryText(int itemIndex)
{
returntrue;
}
};
對於GuiVirtualTextList來說,只需要實現vl::presentation::controls::list::TextItemStyleProvider::ITextItemView就可以了。GacUIIncludes.h裡面已經有了using namespace vl::presentation::controls,所以在這裡只需要從list::開始寫。list::TextItemStyleProvider::ITextItemView還要求實現GuiListControl::IItemPrimaryTextView。在目前的GacUI裡面,IItemPrimaryTextView是專門為下拉框準備的。因為下拉框允許接受任何一種列表物件當做下拉內容,所以GacUI的列表資料來源預設都要求實現IItemPrimaryTextView。
實現資料來源的時候,其實並不要求資料來源類繼承自ITextItemView和IItemPrimaryTextView。因為GacUI都是通過RequestView來獲取一個View的介面指標的,程式碼如上。實現這兩個View也很簡單,在這裡就不贅述了。
GuiTextList就介紹到這裡了,接下來的幾個Demo都將是關於ListView的。下一個Demo是ListView山寨Windows 7的資源管理器介面,可以在http://www.gaclib.net/Demos/Controls.ListView.ViewSwitching/Demo.html看到。具體內容將在下一篇部落格中闡述。