1

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)
Aamir
  • 1,974
  • 1
  • 14
  • 18
  • are you able to modify the dataview sample to demonstrate the issue? Also - what platform are you using? – Igor May 03 '23 at 16:35
  • I’m on Windows 10, and using MSVC 2022 compiler. – Aamir May 03 '23 at 17:04
  • Yes, I've used dataview as an example and create a sample application to demonstrate the issue. The complete code is uploaded to gist. – Aamir May 03 '23 at 17:13
  • 1
    can you modify the sample to see the problem with the minimal possible modification and upload the diff here? Or just try to run the sample and see if you can reproduce the probelm on the unmodified one... – Igor May 03 '23 at 17:51
  • I will try to modify the sample to demonstrate the issue – Aamir May 03 '23 at 17:53
  • @Igor I'm able to reproduce the issue in `dataview` sample as well. I've updated the question to reflect this. – Aamir May 03 '23 at 18:09
  • can you place just the diff to the sample? Its hard to know what was changed... – Igor May 03 '23 at 18:13
  • @Igor I've added the diff of the sample to the OP. Thanks for your time, highly appreciated. – Aamir May 03 '23 at 18:53
  • If I use Aamir's patch in "Update 2 Here is the the diff of dataview sample:" for the dataview sample, I can see the original root node “My Music” is not shown in the tree, instead the "Pop music" and "Classical music" is shown. But this change has a lot issues, for example, when you click on the "Add Mozart" button, nothing happens. – ollydbg23 Jul 10 '23 at 08:37
  • I just wrote a comment in the wxWidgets' forum, and there the doublemax(who wrote the patch) has said that his patch is "quick-and-dirty patch". So maybe there are some other changes needed. See here: [Re: wxDataViewCtrl how to Hide the RootItem](https://forums.wxwidgets.org/viewtopic.php?p=219519#p219519). But I am not sure where to fix the "m_root" issue. So, I still need some help. – ollydbg23 Jul 10 '23 at 09:38

3 Answers3

1

It doesn't really make sense to say that you "don't need a root node" because there is always a root node in wxDVC. However this true root node is actually never shown, so you must be thinking of its child as the "root node that you don't need". And it's perfectly possible not to have this one, of course: you just need to return the real top-level nodes (directories in your case, AFAICS) as children of this root node.

However in no case does it make sense to forward GetChildren() to another parent, this breaks the definition of a tree and while I didn't follow the details, it's not surprising at all that doing it breaks things -- just don't do that.

VZ.
  • 21,740
  • 3
  • 39
  • 42
  • I was following the recommendation from here https://forums.wxwidgets.org/viewtopic.php?t=43764. What will be the correct way to hide the root node? – Aamir May 03 '23 at 20:17
  • The very first answer in the linked thread is correct, so I'm not sure why you didn't follow this. Again, there is no root node to hide because it's _always_ hidden. You can have as many of your own "roots" at the top level as you need. – VZ. May 04 '23 at 16:03
  • I'm following the answer from the post. If you see the second gif file attached in OP, "Directory" node is the top level node, not the root node. But with this implementation, whenever I try to add or delete another top level or child node it doesn't work. – Aamir May 04 '23 at 17:17
  • Can you please show how `GetChildren()` should be implemented in my case? – Aamir May 04 '23 at 17:18
  • It should return your top-level nodes ("Directory-N") for the root node. – VZ. May 04 '23 at 18:58
  • Hi, VZ. Currently, we have such code in the constructor: `m_root = new MyMusicTreeModelNode( NULL, "My Music" );`, you see the `m_root`'s parent is NULL, and later the "Pop music" and "Classical music" is created under the `m_root`. If I don't want to "break the definition of the tree", do you mean that the `m_pop = new MyMusicTreeModelNode(NULL, "Pop music" ); and m_classical = new MyMusicTreeModelNode(NULL, "Classical music" );`. So that the two nodes are under the NULL node? Thanks. – ollydbg23 Jul 10 '23 at 09:50
  • @ollydbg23 This seems to be quite unrelated to this question. I said that returning children of another item from `GetChildren()` broke the definition of a tree, and nothing at all about your case... – VZ. Jul 10 '23 at 21:51
  • Hi, VZ, I have changed the node tree in the wx's dataview sample, and now I see it works as expected. See my answer here: https://stackoverflow.com/a/76654735/154911 I have added a node tree flow chart in this answer. – ollydbg23 Jul 11 '23 at 00:53
1

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

Aamir
  • 1,974
  • 1
  • 14
  • 18
  • Hi, I just begin to learn dataview control myself. I think the link in this answer named "treeview_solved.cpp" is broken, can you check it? Thanks. – ollydbg23 Jul 03 '23 at 06:56
  • Hello, Thanks for pointing out. I've updated the link, please check now. – Aamir Jul 03 '23 at 07:04
  • OK, the link is correct now. I see that you have `wxCommandEvent e; OnExpandAll(e);` function call in several places, is that by design? – ollydbg23 Jul 03 '23 at 07:10
  • Its a dirty way to do it. Since this is just a demo app, I did it that way. Ideally I'll have the expand all logic in a function and then call this function upon button click. – Aamir Jul 03 '23 at 07:12
  • OK, but I'm not sure why operation on a node(I mean add a node or remove a node), the tree got collapsed. So I have to expand all. Is that the native feature of this control? – ollydbg23 Jul 03 '23 at 07:24
  • I think it’s a native behavior from the control, but I need to investigate this to be 100% sure. – Aamir Jul 03 '23 at 07:53
  • 1
    I'm currently try to implement the "drag and drop" feature based on your code. Hopefully it can be done soon. – ollydbg23 Jul 03 '23 at 08:13
  • 1
    Just FYI, calling `Cleared()` works but is excessive as it recreates the entire tree. Usually calling `ItemAdded()` or `ItemDeleted()` is enough and is much more efficient. It's true that you need to notify the tree about the changes to the model, however. – VZ. Jul 10 '23 at 21:53
  • @VZ upon deleting an item I'm calling `ItemDeleted()` but this doesn't remove the item from the control. If I call `Cleared()` then only the item is removed from the control.My OP was about this issue only. I'm not sure what wrong I'm doing. Probably if you see my implementation in the gist you might be able to guide me. Thanks. – Aamir Jul 17 '23 at 10:00
1

I think I have found the solution here.

I just make a collection of all the top level nodes, and now both the "Pop music" and "Classical music" are the children of the NULL node(the virtual root node).

I post the full patch file here: Re: wxDataViewCtrl how to Hide the RootItem in the wxWidgets' forum.

This avoids the Cleared() function calls mentioned in Aamir's answer.

EDIT: I added a tree structure about the node.

Here the the tree structure before and after my changes, see below(I draw it in ascii art flowchart)

                                                  
 +------+                                         
 | NULL |                                         
 +---/--+                                         
     |      +----------+        +---------+       
     \------| My Music |--/-----|Pop Music|       
            +----------+  |     +---------+       
                          |                       
                          |     +---------------+ 
                          ------|Classical Music| 
                                +---------------+ 
                                                  
                The wx dataview sample tree       
                                                  
                                                  
                                                  
      +------+                                    
      | NULL |                                    
      +---/--+                                    
          |        +---------+                    
          \--/-----|Pop Music|                    
             |     +---------+                    
             |                                    
             |     +---------------+              
             ------|Classical Music|              
                   +---------------+              
                                                  
                     The modified tree            
                                                  
ollydbg23
  • 1,124
  • 1
  • 12
  • 38