TTreeView gets corrupted after Windows Theme change

146 views Asked by At

My applications in C++Builder use a VCL TTreeview with nodes of several derived TTreeNode classes. After changing the Windows Theme, the treeview somehow gets recreated, not just repainted. During the recreation the derived treenode destructors get called, but their constructors are not called. It appears the new nodes are all of the basic TTreeNode type instead of the derived types. As a result all methods and properties of the derived classes are lost in the process.

In addition, in the recreation the nodes obtain new addresses in TreeView->Items (TTreeNodes), which will become a problem if the theme change occurs during a long-time action upon a certain node ending with the creation of a child node, i.e. the child has to be added to a parent address that does not exist anymore.

Is there a way to prevent a theme change to have an effect on the TTreeView and its nodes? Or any other solution?

2

There are 2 answers

5
Remy Lebeau On

During the recreation the derived treenode destructors get called, but their constructors are not called. It appears the new nodes are all of the basic TTreeNode type instead of the derived types.

Correct. During HWND recreation, before its HWND is destroyed, TTreeView saves its node data to a temporary memory stream and destroys its node objects, and then after a new HWND becomes available, it creates new node objects and restores the saved node data into them. But, when saving and restoring that data, it has no concept of your derived classes, and so it can't persist them correctly.

If all of your nodes are the same derived type, then you should be OK by simply using the TTreeView.OnCreateNodeClass event or overriding the virtual TTreeView.CreateNode() method. But, if your nodes are using multiple derived types, then you are just SOL, sorry. There is simply no way for the native functionality to know which derived type to recreate for which node.

In addition, in the recreation the nodes obtain new addresses in TreeView->Items (TTreeNodes)

Yes, because they are new objects.

which will become a problem if the theme change occurs during a long-time action upon a certain node ending with the creation of a child node, i.e. the child has to be added to a parent address that does not exist anymore.

What are you doing that takes so long that it spans across a window recreation at all? Don't save TTreeNode references to begin with, and you won't have this problem. If you need to track custom data, use the TTreeNode.Data property instead, that is correctly persisted during window recreation.

Is there a way to prevent a theme change to have an effect on the TTreeView, and its nodes?

Not that I'm aware of, no.

Or any other solution?

Handle the window recreation yourself. Set the TTreeView.CreateWndRestores property to false, and then override the virtual DestroyWnd() method to save the node data however you want when the ControlState property has the csRecreating flag, and override the virtual CreateWnd() method to restore that data as needed. For instance, you could save each node's class name, and then use that to create your derived node objects again.

0
Johan van Ooijen On

I certainly want to prevent the nodes from being recreated. So I derived a TTreeView class overriding CreateWnd() and DestroyWnd() (and a helper boolean variable recreating):

void __fastcall TreeView_c::CreateWnd(void)
  {
  if (recreating)  recreating= false;
  else TreeView::CreateWnd();
  }

void __fastcall TreeView_c::DestroyWnd(void)
  {
  if (CreateWndRestores  &&
      Items->Count > 0   &&
      ControlState.Contains(csRecreating))
       recreating= true;
  else TTreeView::DestroyWnd();
  }

This seems to work, there are no more runtime errors after a Windows theme change. But it just seems too simple to be correct, am I not testing enough themes?

A strange thing is that a theme change does get into DestroyWnd(), setting recreating to true, but does not get into CreateWnd(). I don't understand why not?