Possible odd TreeView bug (sample included)

Using some custom style with a StyleSelector for my TreeView, I found a really strange bug, more related to ItemContainerGenerator, I think. I had to implement some drag and drop, and it appears that switching (my removing and re-inserting them) two sub-items in a sub-collection of a previously switched item in the parent collection used as ItemsSource for my TreeView lead to some unexpected error :

The container passed to StyleSelector.SelectStyle while reinserting the sub-items seems "invalid" ! It isn't related to the TreeView anymore : it isn't able to find ressources and if you try getting its parent ItemsControl, it is now null !

View the sample application I made to test this bug, since explaining here doesn't make the problem very clear : click on the Test button and an exception will occur while re-inserting sub-items. The code is very simple and is a basic data-binded TreeView. Of course this sample is basic, and the style selector is useless since there is only one style here, but that is not the case in a real application. Also note that you can wait indefinitely between the two collections modifications, the problem will still occurs.

Sample : http://mrjul0.free.fr/WPFTreeViewBug.zip



Answer this question

Possible odd TreeView bug (sample included)

  • Debboy

    Zhou Yong's answer isn't quite right. What's really happening is that the TreeViewItem corresponding to your MainItem1 gets removed from the tree when you remove MainItem1 from the main collection, but this TreeViewItem continues to listen to events from its subcollection. When you re-insert an item into this collection (mainItem1.Items.Insert(0, subItem1)), the old TreeViewItem hears the event and generates a new container (TreeViewItem) for it. This calls into your code to get a style, but your code can't find the resource because the container is no longer connected to the main tree.

    I recommend using TryFindResource instead of FindResource, and returning null if the resource cannot be found.

    Meanwhile, I'm sure your next question is "why does a TreeViewItem that has been removed from the tree continue to listen to events, generate containers for items, and behave as if it were still attached " The answer is that WPF tries to do as little as possible when a container is removed -- this helps the perf of scrolling a UI-virtualized list where removing containers from the tree is a common operation. Normally those containers will be GC'd pretty soon, so there's no point in wasting cycles to clean them up. However in this case, it looks as if WPF has done too little. I will see that a bug is opened about this.



  • srjing2

    FindResource look resource up the tree, the same way StaticResource does. Proof is that the list shows normally in all situations : the style is found (put a breakpoint on SelectStyle at the start of the program, or at any "normal" collection updating) ! Only when double switching the collection the bug happens. Note that if I replace the sub-items ObservableCollection by a Collection, the problem disappears, but I lose automatic binding capabilities...
  • EisenB

    Duh. I don't need tools - just need to look at the repro with my eyes open.

    The explanation is simple. The repro adds new items to mainItem1, but not to mainItem0. Both of these get removed from the tree, but that doesn't cause any harm until you add sub-items. At which point we generated a new "sub-item" container underneath a disconnected "item" container. (BTW, it's mainItem1 that has the problem, and mainItem0 that doesn't - not the other way around.)



  • amirkanan

    Thank you for your answer, Sam. I now clearly understand why this happens. I hope this bug will be resolved, even though the workaround is pretty simple.
  • Shark_

    Well, not if I use Application.Windows/MainWindow to find them. But I still don't understand how the tree is normally constructed everytime, even after insertion/deletions, the resource is still found ; but not after the "double-switch". Just try it, you could wait for hours between the main switch and the sub switch, the resource lookup will fail... it seems very odd to me. Well it doesn't matter anymore since it works by using Application.Windows, but still, I would like to understand.
  • Farhan H Soomro

    You mean when you find resources under Window rather than ItemsControl, you still get caught by this exception

    Sheva


  • Lariamon

    FindResource in your style selector will walk through the logical tree to look for the named or other theme related resoures before layout pass, the problem here is that when your press the "test" button, you update the data bound collections, and the ItemContainerGenerator will generate the container aka TreeViewItem as part of the layout pass, but in your style selector, the FindResource call is made before layout pass, and a valid logical tree is not constructed at this particular instance, so you are get caught, if you move the resource into the Window.Resources tag, and in your style selector, you do something like this, you are fine with FindResource:

    public override Style SelectStyle(object item, DependencyObject container)
    {
      ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
      return Application.Current.MainWindow.FindResource("ItemStyle") as Style;
    }


    Sheva


  • Tryst

    Sam, you are right, I've closely examined the code, found that the removed TreeViewItems still listen to the collection changed events, causing ItemContainerGenerator to generate containers for the items, at that time, I got quite confused by this, but now your explanation for it is quite clear, and makes sense

    Edit: just a quick question, It seems that  only TreeViewItem for mainItem0 causes this problem, TreeViewItem for mainItem1 is fine here, I think both of those TreeViewItems gets removed here, Could you please give some explanation to it here
    Sheva


  • SOTY_Programmer

    Thank you for your answer. In my real application, I do put the style in the Window.Resources section, even though this is not the main window, but I'll manage. However, I still fail to understand the logic behind this. I mean, if I create a button to just switch MainItems, it will work fine ; same thing with a SubItems switch. But if I click the MainItems button, and then like five minutes later the SubItems one, the "bug" will appear. How come
  • yoshikatsu

    It's not a TreeView bug, it's a bug in your code, the "ItemStyle" is not defined on ItemsControl, it's in Grid.Resources.

    Sheva


  • Tim Cools

    To answer that I'll need to look at the repro with better tools than I have at the moment (I'm at home on vacation). I'll look into this after I return to work on Jan 2.

  • Possible odd TreeView bug (sample included)