Search code examples
c++wxwidgetstreecontrol

Add/Remove node to wxDataViewCtrl


I'm developing an application using wxWidgets v3.2.1 and using wxDataViewModel and wxDataViewCtrl to display a data model in tree fashion with multiple columns. I'm following the dataview example from wxWidget samples.

My application doesn't need a root node so I want to hide the root node. To do this, I followed this thread from wxWidget forum and updated the GetChildren method as follows:

unsigned int GetChildren(const wxDataViewItem &parent,
                        wxDataViewItemArray &array) const wxOVERRIDE
{
    DataModelNode *node = static_cast<DataModelNode *>(parent.GetID());

    if (node == nullptr)
    {
        return GetChildren(wxDataViewItem(m_Root), array);
        // array.Add(wxDataViewItem((void *)m_Root));
        // return 1;
    }
}

After the above change, the root is no longer visible but adding and removing nodes from the tree doesn't work as expected. Deleting a node doesn't remove the node immediately from the wxDataViewCtrl. Adding a child node doesn't add the node to the tree.

Tree with root node, behaving as expected Tree With root node

Tree with hidden root node, adding/removing node doesn't work Tree without root node

To demonstrate this issue I've created a sample application, the source code of which can be found here https://gist.github.com/aamirglb/8a597dcf48b4bc773746fb9c5a0b08f1.

I would like to have a tree without a root node and able to add/remove the nodes to the tree. Any help on resolving this issue is highly appreciated.

Update

I'm able to reproduce the same issue in dataview sample as well. I Updated MyMusicTreeModel::GetChildren as follows, then Add Mozart, Delete selected button doesn't work.

unsigned int MyMusicTreeModel::GetChildren(const wxDataViewItem &parent,
                                           wxDataViewItemArray &array) const
{
    MyMusicTreeModelNode *node = (MyMusicTreeModelNode *)parent.GetID();
    if (!node)
    {
        //// this line causes the issue ////////////////
        return GetChildren(wxDataViewItem(m_root), array);
        // array.Add(wxDataViewItem((void *)m_root));
        // return 1;
    }

    if (node == m_classical)
    {
        MyMusicTreeModel *model = const_cast<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;
}

Update 2 Here is the the diff of dataview sample:

diff --git a/mymodels.cpp b/mymodels.cpp
index 5963dfd..052b009 100644
--- a/mymodels.cpp
+++ b/mymodels.cpp
@@ -304,8 +304,7 @@ unsigned int MyMusicTreeModel::GetChildren(const wxDataViewItem &parent,
     MyMusicTreeModelNode *node = (MyMusicTreeModelNode *)parent.GetID();
     if (!node)
     {
-        array.Add(wxDataViewItem((void *)m_root));
-        return 1;
+        return GetChildren(wxDataViewItem(m_root), array);
     }

     if (node == m_classical)

Solution

  • wxDataViewModel::Cleared() must be called after adding/remove a node from the data model. wxDataViewModel::Cleared() method will force the wxDataViewCtrl to reread the data from the model again. So after adding or removing the node, wxDataViewCtrl must be instructed to refresh itself with the latest data from the data model.

    To hide the root node from wxDataViewCtrl, the wxDataViewModel::GetChildren() method must be implemented as follows:

    unsigned int GetChildren(const wxDataViewItem &parent,
                                        wxDataViewItemArray &array) const wxOVERRIDE
    {
        DataModelNode *node = static_cast<DataModelNode *>(parent.GetID());
    
        if (node == nullptr)
        {
            int count = m_Root->GetChildCount();
            for (int i = 0; i < count; ++i)
            {
                // Instead of root node, return the 1st level children
                array.Add(wxDataViewItem(static_cast<void *>(m_Root->GetNthChild(i))));
            }
            return count;
        }
    }
    

    Here is the link to the working version of the application:

    https://gist.github.com/aamirglb/bc50b7bb794a84f0a2c871e7e961e993

    And here the the output from the application:

    enter image description here