wxTreeCtrl::EnsureVisible expands only one item

53 views
Skip to first unread message

Dan Korn

unread,
Jun 12, 2025, 2:59:58 PMJun 12
to wx-users
Hello,

We have been using wxWidgets for many years in our product on Windows and Mac.

One of our dialogs shows a wxTreeCtrl with a lot of nested items, and there's a Search box where the user can type a string, and the control highlights all matching nodes.  It also calls EnsureVisible() for each matching node.

On Mac, and on Windows in wxWidgets 3.1 (and all versions prior to 3.2), this would expand every matching node.  Successive calls to EnsureVisible() would leave open previous nodes on which it had been called.  So after going through the tree recursively and highlighting and opening up each match, the user can see them all easily (though they may need to scroll the window).

Now, however, in wxWidgets 3.2 on Windows, when EnsureVisible() is called, all previously opened nodes are closed.  So only the last matching item is left open.  This is a regression, as users are confused because they no longer see all the matching items.

Was this an intentional change to wxTreeCtrl?  Is there any workaround, so that all matching nodes can be opened?

Thanks,
Dan

Vadim Zeitlin

unread,
Jun 12, 2025, 4:23:46 PMJun 12
to wx-u...@googlegroups.com
On Thu, 12 Jun 2025 11:59:57 -0700 (PDT) Dan Korn wrote:

DK> Now, however, in wxWidgets 3.2 on Windows, when EnsureVisible() is called,
DK> all previously opened nodes are closed. So only the last matching item is
DK> left open. This is a regression, as users are confused because they no
DK> longer see all the matching items.
DK>
DK> Was this an intentional change to wxTreeCtrl? Is there any workaround, so
DK> that all matching nodes can be opened?

This is very surprising because wxTreeCtrl::EnsureVisible() is basically
unchanged since the very first version-controlled version from 1998. It is
also an extremely simple function which just sends TVM_ENSUREVISIBLE to the
tree control.

Could the change be actually due to a change in behaviour in Windows
itself or do you really see different behaviour from different wx versions
under the same version of the OS?

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
https://www.tt-solutions.com/

Dan Korn

unread,
Jun 12, 2025, 7:51:50 PMJun 12
to wx-users
Hello Vadim,

Thank you for the reply.

I do indeed see a difference in behavior between wxWidgets 3.1 and 3.2, on the same Windows machine.  Specifically, between versions 3.1.2 and 3.2.4.  I'm on Windows 10 Pro, but the same issue occurs on Windows 11 with wxWidgets 3.2.

I also see the call to TreeView_EnsureVisible (the macro equivalent of TVM_ENSUREVISIBLE) in wxTreeCtrl::EnsureVisible() in src/msw/treectrl.cpp. And that does seem to have been unchanged for quite some time.  Nevertheless, there is this difference in behavior when it's called multiple times.

I suspect that the change is due to something else in the code handling the change in the selected or activated node when this occurs, or possibly in response to our code's call to SetItemBackgroundColour().

Do you have any idea how to find the relevant code change and work around it?

We do need other changes in wxWidgets 3.2, so rolling back completely is not a good solution.

Thanks,
Dan

Vadim Zeitlin

unread,
Jun 12, 2025, 8:02:50 PMJun 12
to wx-u...@googlegroups.com
On Thu, 12 Jun 2025 16:51:50 -0700 (PDT) Dan Korn wrote:

DK> I do indeed see a difference in behavior between wxWidgets 3.1 and 3.2,
DK> on the same Windows machine. Specifically, between versions 3.1.2 and
DK> 3.2.4. I'm on Windows 10 Pro, but the same issue occurs on Windows 11
DK> with wxWidgets 3.2.

OK, thanks, so we can't blame Microsoft this time. Well, it was worth a
try...

DK> I also see the call to TreeView_EnsureVisible (the macro equivalent of
DK> TVM_ENSUREVISIBLE) in wxTreeCtrl::EnsureVisible() in
DK> src/msw/treectrl.cpp. And that does seem to have been unchanged for
DK> quite some time. Nevertheless, there is this difference in behavior
DK> when it's called multiple times.

So you can't reproduce this in the treectrl sample? It has menu item for
making the last item visible and if the problem could be reproduced there,
it would make it much simpler for me to debug it. Of course, if it can't,
but you can provide a simple patch to the sample allowing to do it, please
do it and please open an issue on GitHub and attach this patch to it.

DK> Do you have any idea how to find the relevant code change and work
DK> around it?

The brute force solution would be to run git-bisect between 3.1.2 and
3.2.4. There are ~8000 commits there, but this is still just 13 iterations,
so bisect shouldn't be too onerous to do. And finding the commit which
broke it should give at least some idea of what the problem is.

DK> We do need other changes in wxWidgets 3.2, so rolling back completely
DK> is not a good solution.

BTW, have you tried 3.3.0? I don't see any changes that could affect this,
but if it works there, upgrading directly to it could be a solution.

Otherwise, I'd really need some way of reproducing the problem to be able
to do anything about it.

Good luck,

Dan Korn

unread,
Jun 13, 2025, 3:06:16 PMJun 13
to wx-users
Thanks Vadim.

I can reproduce the problem in the TreeCtrl sample, by replacing the function MyFrame::OnEnsureVisible() with this:

/// BEGIN CODE

static void MarkMatchingItems(wxTreeCtrl* tree, const wxTreeItemId& id, wxString searchText)
{
    wxString itemText = tree->GetItemText(id).MakeLower();

    bool bMatch = false;
    if (searchText.IsEmpty())
    {
        // If empty, match nothing.
    }
    else
    {
        tree->Collapse(id);
        bMatch = itemText.Find(searchText) != wxNOT_FOUND;
    }

    //tree->SetItemBold(id, bMatch);
#ifdef macintosh
    tree->SetItemTextColour(id, bMatch ? *wxBLACK : tree->GetForegroundColour()); // *wxWHITE
#endif
    tree->SetItemBackgroundColour(id, bMatch ? *wxCYAN : tree->GetBackgroundColour()); // *wxWHITE
    if (bMatch)
    {
        tree->EnsureVisible(id);
        //tree->ScrollTo(id);
    }

    wxTreeItemIdValue cookie;
    wxTreeItemId child = tree->GetFirstChild(id, cookie);
    while (child.IsOk())
    {
        MarkMatchingItems(tree, child, searchText);
        child = tree->GetNextSibling(child); // tree->GetNextChild(child, cookie);
    }
}

void MyFrame::OnEnsureVisible(wxCommandEvent& WXUNUSED(event))
{
    //const wxTreeItemId
    //    idLast = m_treeCtrl->GetLastTreeITem();
    //if ( idLast.IsOk() )
    //    m_treeCtrl->EnsureVisible(idLast);
    //else
    //    wxLogMessage("No last item");

    m_treeCtrl->Freeze();
    MarkMatchingItems(m_treeCtrl, m_treeCtrl->GetRootItem(), "3");
    m_treeCtrl->Thaw();
}

/// END CODE

With this change, when you select the "Make the last item visible" menu item, it does mark all the items with a "3" in their text in Cyan, but only the last couple nodes are visible.  The rest are hidden by collapsed parent nodes.

In wxWidgets 3.1, all the nodes that are marked in Cyan are also visible, with their parents expanded.

A couple notes about this:

One, if the calls to m_treeCtrl->Freeze() and m_treeCtrl->Thaw() are removed, then all the marked nodes are visible.  However, in our app, there are many hundreds of nodes, and without the Freeze call, the tree lags and flickers a lot when doing a search, so removing that from our app is not a good option.  This suggests that something about how things are handled while the control is frozen has changed, and this is likely a side effect of that.  I suspect that it has to do with the behavior of wxTreeCtrl::Collapse() while frozen.

Two, if I replace the call to tree->EnsureVisible(id) with tree->ScrollTo(id), it works correctly on Windows and shows all the marked nodes.  However, on Mac, the call to ScrollTo() seems to do nothing at all.

So, I have a workaround for now, which is to #ifdef the code to call ScrollTo() on Windows and EnsureVisible() on Mac.  That said, it would be good to get to the bottom of this so that the Windows code works the same as in 3.1.

Thanks,
Dan


Vadim Zeitlin

unread,
Jul 8, 2025, 11:00:43 AMJul 8
to wx-u...@googlegroups.com
On Fri, 13 Jun 2025 12:06:16 -0700 (PDT) Dan Korn wrote:

DK> I can reproduce the problem in the TreeCtrl sample, by replacing the
DK> function MyFrame::OnEnsureVisible() with this:

Thanks, I can indeed reproduce the problem using this code.

DK> In wxWidgets 3.1, all the nodes that are marked in Cyan are also visible,
DK> with their parents expanded.

I hadn't seen the offending commit originally because I was looking at the
changes since the last 3.1 release, 3.1.7, but it turns out you must have
been using 3.1.2 or earlier because this got broken in e8712b3c56
(Make wxTreeCtrl::EnsureVisible() work while frozen in wxMSW, 2019-07-09)
which appeared in 3.1.3, see https://github.com/wxWidgets/wxWidgets/issues/18435

I've fixed this in 80fe572c55 (Fix multiple wxTreeCtrl::EnsureVisible()
while frozen in wxMSW, 2025-07-08) now, so it should work again in 3.3.1
when it is released soon.

Thanks again for reporting this!
Reply all
Reply to author
Forward
0 new messages