GacUI Demo:列表的虚拟模式,不需要为每一个列表项分配内存的一种显示方法
GacUI的所有列表控件都支持虚拟模式。虚拟模式是一种不需要为每一个列表项分配内存的一种显示方法。在开始的时候,需要高速列表一共有多少个列表项。之后,列表控件在渲染的时候,会跟数据源要求获取某一个下标所包含的数据,并且在这个数据一直处于屏幕上的时候,只会跟数据源获取一次。完整的代码可以在 http://www.gaclib.net/Demos/Controls.ListBox.VirtualMode/Demo.html看到。先上图:
先看创建界面的代码。一般来说,所有可以随着窗口的变化自动排版的控件组织方法,都是使用一个或多个GuiTableComposition来实现的。
GuiVirtualTextList就是只有虚拟模式的GuiTextList。事实上GuiVirtualTextList是GuiTextList的基类,而GuiTextList.GetItems()返回的对象也是一个数据源。因此非虚拟模式其实也是通过虚拟模式来实现的。在数据比较少的时候,非虚拟模式操作起来十分的简单,而在数据比较多的时候,虚拟模式可以带来很好的性能。上面的代码创建了一个DataSource类来做数据源,并且有一个SetCount的函数用来更改列表里面的数量的总量,然后每一个列表项的内容都是Item xxx。这是怎么做到的呢?我们来看数据源的代码:
对于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
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 size
this -> 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 center
this -> MoveToScreenCenter();
}
};
{
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 size
this -> 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 center
this -> 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 bars
if (oldCount < newCount)
{
// insert
this -> InvokeOnItemModified(oldCount, 0 , newCount - oldCount);
}
else if (oldCount > newCount)
{
// delete
this -> InvokeOnItemModified(newCount, oldCount - newCount, 0 );
}
}
}
// GuiListControl::IItemProvider
int Count()
{
return count;
}
IDescriptable * RequestView( const WString & identifier)
{
if (identifier == list::TextItemStyleProvider::ITextItemView::Identifier)
{
return this ;
}
else if (identifier == GuiListControl::IItemPrimaryTextView::Identifier)
{
return this ;
}
else
{
return 0 ;
}
}
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 state
return false ;
}
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)
{
return true ;
}
};
{
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 bars
if (oldCount < newCount)
{
// insert
this -> InvokeOnItemModified(oldCount, 0 , newCount - oldCount);
}
else if (oldCount > newCount)
{
// delete
this -> InvokeOnItemModified(newCount, oldCount - newCount, 0 );
}
}
}
// GuiListControl::IItemProvider
int Count()
{
return count;
}
IDescriptable * RequestView( const WString & identifier)
{
if (identifier == list::TextItemStyleProvider::ITextItemView::Identifier)
{
return this ;
}
else if (identifier == GuiListControl::IItemPrimaryTextView::Identifier)
{
return this ;
}
else
{
return 0 ;
}
}
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 state
return false ;
}
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)
{
return true ;
}
};
对于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看到。具体内容将在下一篇博客中阐述。