前面已经介绍了一下关于wxDataViewModal跟wxDataViewCtrl的相关函数,而且提到了节点的设计。嗯,这节分享一下关于节点的设计。
class MyMusicTreeModelNode;
WX_DEFINE_ARRAY_PTR( MyMusicTreeModelNode*, MyMusicTreeModelNodePtrArray );
class MyMusicTreeModelNode
{
public:
MyMusicTreeModelNode( MyMusicTreeModelNode* parent,
const wxString &title, const wxString &artist,
unsigned int year )
{
m_parent = parent;
m_title = title;
m_artist = artist;
m_year = year;
m_quality = "good";
m_container = false;
}
MyMusicTreeModelNode( MyMusicTreeModelNode* parent,
const wxString &branch )
{
m_parent = parent;
m_title = branch;
m_year = -1;
m_container = true;
}
~MyMusicTreeModelNode()
{
// free all our children nodes
size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++)
{
MyMusicTreeModelNode *child = m_children[i];
delete child;
}
}
bool IsContainer() const
{ return m_container; }
MyMusicTreeModelNode* GetParent()
{ return m_parent; }
MyMusicTreeModelNodePtrArray& GetChildren()
{ return m_children; }
MyMusicTreeModelNode* GetNthChild( unsigned int n )
{ return m_children.Item( n ); }
void Insert( MyMusicTreeModelNode* child, unsigned int n)
{ m_children.Insert( child, n); }
void Append( MyMusicTreeModelNode* child )
{ m_children.Add( child ); }
unsigned int GetChildCount() const
{ return m_children.GetCount(); }
public: // public to avoid getters/setters
wxString m_title;
wxString m_artist;
int m_year;
wxString m_quality;
bool m_container;
private:
MyMusicTreeModelNode *m_parent;
MyMusicTreeModelNodePtrArray m_children;
};
class MyMusicTreeModel: public wxDataViewModel
{
public:
MyMusicTreeModel();
~MyMusicTreeModel()
{
delete m_root;
}
// helper method for wxLog
wxString GetTitle( const wxDataViewItem &item ) const;
wxString GetArtist( const wxDataViewItem &item ) const;
int GetYear( const wxDataViewItem &item ) const;
// helper methods to change the model
void AddToClassical( const wxString &title, const wxString &artist,
unsigned int year );
void Delete( const wxDataViewItem &item );
wxDataViewItem GetNinthItem() const
{
return wxDataViewItem( m_ninth );
}
// override sorting to always sort branches ascendingly
int Compare( const wxDataViewItem &item1, const wxDataViewItem &item2,
unsigned int column, bool ascending ) const wxOVERRIDE;
// implementation of base class virtuals to define model
virtual unsigned int GetColumnCount() const wxOVERRIDE
{
return 6;
}
virtual wxString GetColumnType( unsigned int col ) const wxOVERRIDE
{
if (col == 2)
return "long";
return "string";
}
virtual void GetValue( wxVariant &variant,
const wxDataViewItem &item, unsigned int col ) const wxOVERRIDE;
virtual bool SetValue( const wxVariant &variant,
const wxDataViewItem &item, unsigned int col ) wxOVERRIDE;
virtual bool IsEnabled( const wxDataViewItem &item,
unsigned int col ) const wxOVERRIDE;
virtual wxDataViewItem GetParent( const wxDataViewItem &item ) const wxOVERRIDE;
virtual bool IsContainer( const wxDataViewItem &item ) const wxOVERRIDE;
virtual unsigned int GetChildren( const wxDataViewItem &parent,
wxDataViewItemArray &array ) const wxOVERRIDE;
private:
MyMusicTreeModelNode* m_root;
// pointers to some "special" nodes of the tree:
MyMusicTreeModelNode* m_pop;
MyMusicTreeModelNode* m_classical;
MyMusicTreeModelNode* m_ninth;
// ??
bool m_classicalMusicIsKnownToControl;
};
MyMusicTreeModel::MyMusicTreeModel()
{
m_root = new MyMusicTreeModelNode( NULL, "My Music" );
// setup pop music
m_pop = new MyMusicTreeModelNode( m_root, "Pop music" );
m_pop->Append(
new MyMusicTreeModelNode( m_pop, "You are not alone", "Michael Jackson", 1995 ) );
m_pop->Append(
new MyMusicTreeModelNode( m_pop, "Take a bow", "Madonna", 1994 ) );
m_root->Append( m_pop );
// setup classical music
m_classical = new MyMusicTreeModelNode( m_root, "Classical music" );
m_ninth = new MyMusicTreeModelNode( m_classical, "Ninth symphony",
"Ludwig van Beethoven", 1824 );
m_classical->Append( m_ninth );
m_classical->Append( new MyMusicTreeModelNode( m_classical, "German Requiem",
"Johannes Brahms", 1868 ) );
m_root->Append( m_classical );
m_classicalMusicIsKnownToControl = false;
}
wxString MyMusicTreeModel::GetTitle( const wxDataViewItem &item ) const
{
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
if (!node) // happens if item.IsOk()==false
return wxEmptyString;
return node->m_title;
}
wxString MyMusicTreeModel::GetArtist( const wxDataViewItem &item ) const
{
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
if (!node) // happens if item.IsOk()==false
return wxEmptyString;
return node->m_artist;
}
int MyMusicTreeModel::GetYear( const wxDataViewItem &item ) const
{
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
if (!node) // happens if item.IsOk()==false
return 2000;
return node->m_year;
}
void MyMusicTreeModel::AddToClassical( const wxString &title, const wxString &artist,
unsigned int year )
{
if (!m_classical)
{
wxASSERT(m_root);
// it was removed: restore it
m_classical = new MyMusicTreeModelNode( m_root, "Classical music" );
m_root->Append( m_classical );
// notify control
wxDataViewItem child( (void*) m_classical );
wxDataViewItem parent( (void*) m_root );
ItemAdded( parent, child );
}
// add to the classical music node a new node:
MyMusicTreeModelNode *child_node =
new MyMusicTreeModelNode( m_classical, title, artist, year );
m_classical->Append( child_node );
// FIXME: what's m_classicalMusicIsKnownToControl for?
if (m_classicalMusicIsKnownToControl)
{
// notify control
wxDataViewItem child( (void*) child_node );
wxDataViewItem parent( (void*) m_classical );
ItemAdded( parent, child );
}
}
void MyMusicTreeModel::Delete( const wxDataViewItem &item )
{
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
if (!node) // happens if item.IsOk()==false
return;
wxDataViewItem parent( node->GetParent() );
if (!parent.IsOk())
{
wxASSERT(node == m_root);
// don't make the control completely empty:
wxLogError( "Cannot remove the root item!" );
return;
}
// is the node one of those we keep stored in special pointers?
if (node == m_pop)
m_pop = NULL;
else if (node == m_classical)
m_classical = NULL;
else if (node == m_ninth)
m_ninth = NULL;
// first remove the node from the parent's array of children;
// NOTE: MyMusicTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
node->GetParent()->GetChildren().Remove( node );
// free the node
delete node;
// notify control
ItemDeleted( parent, item );
}
int MyMusicTreeModel::Compare( const wxDataViewItem &item1, const wxDataViewItem &item2,
unsigned int column, bool ascending ) const
{
wxASSERT(item1.IsOk() && item2.IsOk());
// should never happen
if (IsContainer(item1) && IsContainer(item2))
{
wxVariant value1, value2;
GetValue( value1, item1, 0 );
GetValue( value2, item2, 0 );
wxString str1 = value1.GetString();
wxString str2 = value2.GetString();
int res = str1.Cmp( str2 );
if (res) return res;
// items must be different
wxUIntPtr litem1 = (wxUIntPtr) item1.GetID();
wxUIntPtr litem2 = (wxUIntPtr) item2.GetID();
return litem1-litem2;
}
return wxDataViewModel::Compare( item1, item2, column, ascending );
}
void MyMusicTreeModel::GetValue( wxVariant &variant,
const wxDataViewItem &item, unsigned int col ) const
{
wxASSERT(item.IsOk());
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
switch (col)
{
case 0:
variant = node->m_title;
break;
case 1:
variant = node->m_artist;
break;
case 2:
variant = (long) node->m_year;
break;
case 3:
variant = node->m_quality;
break;
case 4:
variant = 80L; // all music is very 80% popular
break;
case 5:
if (GetYear(item) < 1900)
variant = "old";
else
variant = "new";
break;
default:
wxLogError( "MyMusicTreeModel::GetValue: wrong column %d", col );
}
}
bool MyMusicTreeModel::SetValue( const wxVariant &variant,
const wxDataViewItem &item, unsigned int col )
{
wxASSERT(item.IsOk());
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
switch (col)
{
case 0:
node->m_title = variant.GetString();
return true;
case 1:
node->m_artist = variant.GetString();
return true;
case 2:
node->m_year = variant.GetLong();
return true;
case 3:
node->m_quality = variant.GetString();
return true;
default:
wxLogError( "MyMusicTreeModel::SetValue: wrong column" );
}
return false;
}
bool MyMusicTreeModel::IsEnabled( const wxDataViewItem &item,
unsigned int col ) const
{
wxASSERT(item.IsOk());
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
// disable Beethoven's ratings, his pieces can only be good
return !(col == 3 && node->m_artist.EndsWith("Beethoven"));
}
wxDataViewItem MyMusicTreeModel::GetParent( const wxDataViewItem &item ) const
{
// the invisible root node has no parent
if (!item.IsOk())
return wxDataViewItem(0);
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
// "MyMusic" also has no parent
if (node == m_root)
return wxDataViewItem(0);
return wxDataViewItem( (void*) node->GetParent() );
}
bool MyMusicTreeModel::IsContainer( const wxDataViewItem &item ) const
{
// the invisble root node can have children
// (in our model always "MyMusic")
if (!item.IsOk())
return true;
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
return node->IsContainer();
}
unsigned int MyMusicTreeModel::GetChildren( const wxDataViewItem &parent,
wxDataViewItemArray &array ) const
{
MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) parent.GetID();
if (!node)
{
array.Add( wxDataViewItem( (void*) m_root ) );
return 1;
}
if (node == m_classical)
{
MyMusicTreeModel *model = (MyMusicTreeModel*)(const MyMusicTreeModel*) this;
model->m_classicalMusicIsKnownToControl = true;
}
if (node->GetChildCount() == 0)
{
return 0;
}
unsigned int count = node->GetChildren().GetCount();
for (unsigned int pos = 0; pos < count; pos++)
{
MyMusicTreeModelNode *child = node->GetChildren().Item( pos );
array.Add( wxDataViewItem( (void*) child ) );
}
return count;
}
结果如图:
节点数据结构有wxString m_title; wxString m_artist; int m_year;wxString m_quality; 在MyMusicTreeModel构造函数中new出来,并且各个之间的层级关系构建完毕。着重看GetChildren() 跟GetValue()函数
根据上节分享的内容知道GetChildren只会构建所有的节点数据。GetValue()往节点填充数据。
这里分享简单的节点设计思想,当然在实际开发业务过程中遇到大量的数据,我们采用类的方式实现。