Friday, May 27, 2011

Programmatically selecting items in WPF Treeview

[Beginner level]


Hi All,


Today I am going to talk about a common problem of programmatically selecting items in the WPF Treeview. This problem is discussed in many forums but I want to present two contradicting approaches with a full explanations so that you can decide what is best.


I consider the following (somewhat common) case: I have some data represented as a tree. I decide to show my data in the UI inside a WPF Treeview and some general list of items (with no specific structure). When the user selects an item in the list the same item is selected in the tree. A good example is the Solution Explorer in your Visual Studio. You select a tab with your code and the relevant file is selected in the Solution Explorer tree.


For simplicity my data items look like this:

  public class DataItem
  {
    private static int _counter;
    public DataItem()
    {
      Name = string.Format("Item {0}"Interlocked.Increment(ref _counter));
      SubItems = new List<DataItem>();
    }
    public string Name
    {
      get;
      set;
    }
 
    public DataItem Parent
    {
      get;
      set;
    }
 
    public List<DataItem> SubItems
    {
      get;
      set;
    }
  }
An item has a unique name property which looks like "Item N" generated in the constructor. This is not important and done only to distinguish the various items.

My window contains a Treeview which looks like this:
        <TreeView x:Name="_mainTree" ItemsSource="{Binding Root}" >
            <TreeView.Resources>
                
                <HierarchicalDataTemplate DataType="{x:Type local:DataItem}" ItemsSource="{Binding SubItems}" >
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" Margin="2,0,0,0" VerticalAlignment="Center" x:Name="TextArea" >
                        </TextBlock>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>

In the main window code I have the following:
 public partial class MainWindow : Window
  {
 
    private const int MaxLevel = 3;
    private const int ItemsPerLevel = 100;
 
    public MainWindow()
    {
      InitializeComponent();
      DataContext = this;
      Root = new ObservableCollection<DataItem>();
      DataItem rootItem = new DataItem();
      rootItem.IsSelected = true;
      AddSubItems(rootItem, 1);
      
      Root.Add(rootItem);
    }
 
    public ObservableCollection<DataItem> Root
    {
      get;
      set;
    }
 
    private void AddSubItems(DataItem node, int level)
    {
      if (level == MaxLevel)
        return;
      for (int i = 0; i < ItemsPerLevel; i++)
      {
        DataItem subitem = new DataItem();
        AddSubItems(subitem,level+1);
        subitem.Parent = node;
        node.SubItems.Add(subitem);
      }
    }

(I assume you have some experience with WPF and binding so you understand why this code would populate the tree)
The method AddSubitems generates some levels of tree items (2 in the above code) each containing 
ItemsPerLevel (100) items.

This is where the two approaches split...

The "I don't care about data/UI separation" approach
Since I don't care about data and UI separation I will simply add two properties to my DataItem:
IsSelected to mark when an item is selected and IsExpanded because when an internal item is selected I would like to see it.
The DataItem code now looks like this:
  public class DataItem:INotifyPropertyChanged
  {
    private static int _counter;
    public string Name
    {
      get;
      set;
    }
 
    private bool _isSelected;
    public bool IsSelected
    {
      get
      {
        return _isSelected;
      }
      set
      {
        _isSelected = value;
        OnPropertyChanged("IsSelected");
      }
    }
 
    private bool _isExpanded;
    public bool IsExpanded
    {
      get
      {
        return _isExpanded;
      }
      set
      {
        _isExpanded = value;
        OnPropertyChanged("IsExpanded");
      }
    }
 
    public DataItem Parent
    {
      get;
      set;
    }
 
    public DataItem()
    {
      Name = string.Format("Item {0}"Interlocked.Increment(ref _counter));
      SubItems = new List<DataItem>();
    }
 
    public List<DataItem> SubItems
    {
      get;
      set;
    }
 
    private void OnPropertyChanged(string name)
    {
      if (PropertyChanged != null)
        PropertyChanged(thisnew PropertyChangedEventArgs(name));
    }
    public event PropertyChangedEventHandler PropertyChanged;
  }
One additional change you might notice is the introduction of the INotifyPropertyChanged interface which will help me to update the UI elements using binding.

Now all that remains is to hook the new properties to the Treeview and we have our solution.
I add the following style declaration to the resources tag of the tree:
 <Style TargetType="{x:Type TreeViewItem}">
     <Setter Property="IsSelected"  Value="{Binding IsSelected, Mode=TwoWay}"/>
     <Setter Property="IsExpanded"  Value="{Binding IsExpanded, Mode=TwoWay}"/>
 </Style>

Thats it, we are ready. I run the code:
      Root[0].SubItems[0].SubItems[0].IsSelected = true;
And the root element is being selected. Hmm... Something went wrong. I clearly did not select the root element! Checking the debugger reveals that the IsSelected property on the root element is indeed "true" but on the the DataItem Root[0].SubItems[0].SubItems[0] it is also "true". 
The Treeview allows only one selected item so I have a problem here. 

The problem is quite simple. The WPF Treeview was designed to save time and memory consumption (Yei!) so it doesn't generate the tree items for the tree items you cannot see. So when you select a tree item which is not visible and was not previously created it does the next best thing and selecting the closest item in the hierarchy which is visible.
To solve this issue I change the IsExpanded property to look like:
    private bool _isExpanded;
    public bool IsExpanded
    {
      get
      {
        return _isExpanded;
      }
      set
      {
        _isExpanded = value;
        if (_isExpanded && Parent != null)
          Parent.IsExpanded = true;
        OnPropertyChanged("IsExpanded");
      }
    }
Now all I need to do is to expand the tree item so it will be seen. This for example can be done by calling:
      Root[0].SubItems[0].SubItems[0].IsSelected = true;
      Root[0].SubItems[0].SubItems[0].IsExpanded = true;
The "I don't mix my UI with my Data" approach
I return to the original code. I don't want to edit the data item so I would have to handle the selection manually. For this I introduce the SelectItem method in the main window that looks like this:

    private void SelectItem(DataItem node)
    {
      Stack<DataItem> nodesStack = new Stack<DataItem>();
      DataItem currentNode = node;
 
      while (currentNode != null)
      {
        nodesStack.Push(currentNode);
        currentNode = currentNode.Parent;
      }
 
      TreeViewItem currentItem = _mainTree.ItemContainerGenerator.ContainerFromItem(_mainTree.Items[0]) as TreeViewItem;
      currentNode = nodesStack.Pop();
 
      if (currentItem.Header != currentNode)
        return;
 
      while (nodesStack.Count > 0)
      {
        if (currentItem.IsExpanded == false)
        {
          currentItem.IsExpanded = true;
          currentItem.UpdateLayout();
        }
 
        currentNode = nodesStack.Pop();
        foreach (DataItem innerItem in currentItem.Items)
        {
          if (innerItem == currentNode)
          {
            currentItem = currentItem.ItemContainerGenerator.ContainerFromItem(innerItem) as TreeViewItem;
            break;
          }
        }
 
      }
 
      currentItem.IsSelected = true;
    }
What I am doing here is quite simple. WPF lets me get the TreeViewItem that holds my DataItem by using the ItemContainerGenerator. But, if you read the previous section you know that the containers are generated only once you see them so I am forced to start at the root and manually expand each tree level until I reach the required tree item. One thing to notice is that it isn't enough to set IsExpanded to true on a tree item. To build the container you must instruct WPF to update the layout and build the containers. Another thing to notice is that ItemContainerGenerator searches only within the current level of the tree so you must search each level separately.

I run test the code using the following line:
SelectItem(Root[0].SubItems[0].SubItems[0]);
And everything works great.

But.... (and I am ignoring the fact that you have lot's of calls to update layout, this can be optimized)

There is a problem. It is not immediately visible but try to increase the number of generated elements to 10000 and the number of levels to only 1.
    private const int MaxLevel = 2;
    private const int ItemsPerLevel = 10000;
I run the application and expand the root tree item and wait. And wait... Still waiting... I waited about 10 seconds for the tree item to expand because it has so many containers to generate. Thankfully the WPF engineers thought about this issue and enabled Virtualization on the tree. To make long story short it means that you can tell WPF not to generate containers for tree items which are not actually visible to the user. I do so by adding the following attribute to my Treeview element in the XAML:
VirtualizingStackPanel.IsVirtualizing="True"

Run the application, expand the root tree item and it takes less than 1 second. Thank you WPF engineers. Only one problem. My selection method assumes that every expanded tree item creates ALL of the container inside it. And surly enough trying to select a tree item which is not in the visible area results in an exception.

Conclusion
I presented two methods to programatically select an item within the tree. I want to note that in both methods the SelectedItem property of the tree is updated correctly. Which method you may use is both a philosophical and a design question. Perhaps you have a third, better method for this case. Please share it with me and my readers using the comments.

Thank you for reading,
Boris.

Friday, May 20, 2011

Lambda expressions, anonymous classes and memory leaks.

[Intermediate level]
[This blog post was originally published on an internal company blog on February 20th 2011]


Hi All,



Couple days back I was writing code as usual and I decided to use a lambda expression as a function. The code I wrote looked something like this:

 public void Moo()
    {
      int x = 10;
      SomeEvent += (i) => { Console.Out.WriteLine(x); };
    }
Then, I stopped for a second. What is going on here? I just used a local variable in a method that is going to be called long after the stack frame where this local variable was defined is gone. This doesn't make any sense. Clearly, once the program counter (instruction pointer) leaves the method Moo, the variable x defined on the stack will be freed. I ran the code and everything worked perfectly, 10 was printed on the screen once SomeEvent was fired. Strange!
I decided to ask a friend... "What is going on here?" I asked. "Maybe it's like anonymous class" he said. This actually made some sense to I decided to dig deeper using my trusty (but no longer free) friend: Reflector.

Before I begin copy-pasting IL code, a word about anonymous classes. In .NET 3.5 Microsoft introduced LINQ which allows easy queries on various types of collections. But they noticed a problem. You can have many different types of queries on the same collection and every time a result record would look different. For example if you have a DB table with the colums ID, Name and Title. Sometimes you want to retrieve only the name, sometimes only the title, sometimes you want to count the number of rows for each name (so the result is (Count, Name)). Previously the user would have to define a new class or struct for each return type but anonymous classes solve this by using the "var" keyword.
(Yes, this is the correct place to use "var" and not when you are too lazy to write the actual variable type!)
I will not give a concrete example on how to use LINQ but for example if I want to define a new class that has two fields, Count and Name I can do it like:

var p = new { Name = "Boris"Count = 10 };

Now I can access p.Count or p.Name as usual; Anyway... I wrote a small class to see what IL created. The content of the file is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
  public delegate void HowItWorksHandler(int number);
  class Class1
  {
    public event HowItWorksHandler SomeEvent;
    public Class1()
    {
    }
    public void Moo()
    {
      int x = 10;
      SomeEvent += (i) => { Console.Out.WriteLine(x); };
    }
    public void Honey()
    {
      var p = new { Name = "Boris", Count = 10 };
    }
    public void Sheep()
    {
      SomeEvent += (i) => { Console.Out.WriteLine(i + 1); };
    }
    public void Memory()
    {
      MemoryHolder mh = new MemoryHolder();
      SomeEvent += (x) => { mh.BigMemory = 10;};
    }
    void Class1_How(int number)
    {
      throw new NotImplementedException();
    } 
  }
  public class MemoryHolder
  {
    public int BigMemory;
    public MemoryHolder()
    {
      Console.Out.WriteLine("Memory holder created");
    }
    ~MemoryHolder()
    {
      Console.Out.WriteLine("Memory holder free");
    }
  }
}
Now lets start from the end... For the method Honey where I used an anonymous class a new class was created as expected. It was located in the dll but with no namespace and had quite a full definition:
[CompilerGenerated, DebuggerDisplay(@"\{ Name = {Name}, Count = {Count} }", Type="<Anonymous Type>")]
internal sealed class <>f__AnonymousType0<<Name>j__TPar, <Count>j__TPar>
{
    // Fields
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly <Count>j__TPar <Count>i__Field;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly <Name>j__TPar <Name>i__Field;

    // Methods
    [DebuggerHidden]
    public <>f__AnonymousType0(<Name>j__TPar Name, <Count>j__TPar Count);
    [DebuggerHidden]
    public override bool Equals(object value);
    [DebuggerHidden]
    public override int GetHashCode();
    [DebuggerHidden]
    public override string ToString();

    // Properties
    public <Count>j__TPar Count { get; }
    public <Name>j__TPar Name { get; }
}

Nothing of much interest here but note that the compiler overrides the default methods to some less generic implementation. But, the really interesting method is the method Moo. Let's look at the IL there:

.method public hidebysig instance void Moo() cil managed
{
    .maxstack 4
    .locals init (
        [0] class ConsoleApplication3.Class1/<>c__DisplayClass1 CS$<>8__locals2)
    L_0000: newobj instance void ConsoleApplication3.Class1/<>c__DisplayClass1::.ctor()
    L_0005: stloc.0 
    L_0006: nop 
    L_0007: ldloc.0 
    L_0008: ldc.i4.s 10
    L_000a: stfld int32 ConsoleApplication3.Class1/<>c__DisplayClass1::x
    L_000f: ldarg.0 
    L_0010: ldloc.0 
    L_0011: ldftn instance void ConsoleApplication3.Class1/<>c__DisplayClass1::<Moo>b__0(int32)
    L_0017: newobj instance void ConsoleApplication3.HowItWorksHandler::.ctor(object, native int)
    L_001c: call instance void ConsoleApplication3.Class1::add_SomeEvent(class ConsoleApplication3.HowItWorksHandler)
    L_0021: nop 
    L_0022: nop 
    L_0023: ret 
}
Look at that, a new class named c__DisplayClass1 was defined, it has a local variable x and a method called Moo. When we do our += to the event a new instance is created (L_0000), the local variable x is copied to the instance variable x (L_000a) and the method that is 
being added to the event is the method Moo of this new instance (L_0011 - L_0017). Now lets look at the class code:

[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
    // Fields
    public int x;

    // Methods
    public void <Moo>b__0(int i)
    {
        Console.Out.WriteLine(this.x);
    }
}
What a nice surprise, the compiler generated method Moo is exactly the same as our code inside the lambda expression :)
Now all the code makes sense! To make sure, I added a method named Sheep which does not use a local variable. In this case a new method
was added to our existing class (Class1) and it looks like this:

[CompilerGenerated]
private static void <Sheep>b__3(int i)
{
    Console.Out.WriteLine((int) (i + 1));
}
No surprises here.
If you made it to this point then you should be wondering... if this is what happens when I use a local variable in a lambda expression isn't that a huge potential for a memory leak?
Well, the answer is YES! This is a huge potential for a memory leak.
What would happen in this code:

      using (StreamReader reader = new StreamReader("MyFile"))

      {
        SomeEvent += (x) => { string s = reader.ReadToEnd(); };
      }
The stream instance was copied onto an anonymous class which is stored god knows where but it was closed once the using statement is over. An even worse case is when you think the variable will get garbage collected but it is actually held by some anonymous method (see the method Memory in the original code that simulates a memory holding class, the finalizer of the MemoryHolder class is never called after the Memory method is called, no matter how many times you call GC.Collect()).
Conclusion
1) Using anonymous classes and methods or lambda expressions has some overheads and garbage classes/methods created.
2) Using local variables in lambda expressions can cause memory leaks and other issues.
3) It seems that the functionality of anonymous classes and of local variables in lambda  expressions is not the same.
4) Using "var" instead of an actual type name doesn't make your code cool.
On a personal note, I am using .NET since version 1.1 and I have the feeling that in each version they add more and more "AutoMagical" (http://en.wiktionary.org/wiki/automagicalconcepts which make your code shorter but not very maintainable. I think the best example for the most "AutoMagical" feature is Binding in WPF.
You write some string in a XAML file and some magic changes the value of the ViewModel. I personally try to avoid any "AutoMagical" behavior and no one
has yet to convince me otherwise (maybe I am just used to Delphi where you could debug the code up to the assembler commands :)). My honest
advice for you is to consider the same.
Thanks for reading,
Boris.