Silverlight and WPF ComboBox with TreeView inside

The TreeView is useful control, but it has one shortcoming: it occupies too much space in the application. That’s why I’ve decided to create the custom control which looks like a ComboBox but displays the TreeView instead of the list.

Here is the final result:

Although this control looks quite simple, the actual implementation isn’t clear and takes long time.
Here is the sequence of steps:

1) Custom TreeView and TreeViewItem. They provide the following functionality:

  • Allow to expand and  select an item from a view model (It isn’t possible to select an item of the TreeView using other ways)
  • The event which is fired when a user clicks a TreeViewItem (so it will be possible to close the ComboBox)

2) Interface for an item of the ItemsSource collection

public interface ITreeViewItemModel
{
	string SelectedValuePath { get; }
	string DisplayValuePath { get; }

	bool IsExpanded { get; set; }
	bool IsSelected { get; set; }

	IEnumerable<ITreeViewItemModel> GetHierarchy();
	IEnumerable<ITreeViewItemModel> GetChildren();
}

Members of this interface:

  • IsExpanded – allows to expand the TreeViewItem from the bound view model. Must be implemented as the INotifyPropertyChanged property.
  • IsSelected – allows to select the TreeViewItem from the bound view model. Must be implemented as the INotifyPropertyChanged property.
  • SelectedValuePath – the unique string (unique item id) that will be used to select and expand the treeview control
  • DisplayValuePath – the content that will be displayed at the header when the combobox is closed
  • GetHierarchy – returns the item and its parents
  • GetChildren – returns child items of the current item

3. Create the Combobox

It is the most difficult part of the implementation. I was forced to create many methods to provide the connection between Combobox and TreeView. But although there is many private methods, there is only two public properties:

  • SelectedItem – now it is possible to get or set this item and it will be selected in the treeview.
  • SelectedHierarchy – it wasn’t necessary to create this property, but it wasn’t difficult so I’ve decided to implement it. Use list of strings instead of the actual item.

4. Add it to a view and bind with a view model:

<UserControl.Resources>
    <Windows:HierarchicalDataTemplate x:Key="treeViewDataTemplate" 
                   ItemsSource="{Binding Children}">
        <TextBlock Text="{Binding Title}" />
    </Windows:HierarchicalDataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
        <local:ComboBoxTreeView ItemsSource="{Binding Items}" 
             SelectedItem="{Binding SelectedItem}" 
             ItemTemplate="{StaticResource treeViewDataTemplate}"
             HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>

The ItemTemplate property is obligatory property and must be of the HierarchicalDataTemplate type.

Silverlight version: ComboBoxTreeView.zip
WPF version: WpfComboboxTreeview.zip
The updated source code which supports the ‘SelectionChanged’ event of the combobox: ComboBoxTreeViewEventsSupport.zip

Advertisements

42 Responses to Silverlight and WPF ComboBox with TreeView inside

  1. Chris says:

    How do you set an initial value for this? How would you set Item 1.2 as the initial value in your example?

    I’m trying but it doesn’t work:
    TreeViewItem defaultTreeViewItem = new TreeViewItem();
    defaultTreeViewItem = (TreeViewItem)CBTV.ItemContainerGenerator.ContainerFromItem(CBTV.Items[0]);

    CBTV.SelectedItem = defaultTreeViewItem;

    • vortexwolf says:

      Chris,
      the ItemContainerGenerator property doesn’t work if the items aren’t generated and visible. Moreover, I’ve never used this property and I’ve never addressed to controls directly.
      You should use bindings to create the items and select them.
      Like in the source code attached to the post:

      CBTV.ItemsSource = this.Items;
      CBTV.SelectedItem = this.Items[0].Children[1];
      ...
      public List<SomeHierarchyViewModel> Items { get; set; }

  2. Cool control, very useful. Thanks for sharing the code !

  3. Nikita says:

    Thanks for the good control and code!

  4. Pingback: Silverlight ComboBox with TreeView inside | Silverlight, WPF and Windows 8 Development | Ra Puke Moana

  5. Anonymous says:

    Hi,
    How display selected item in combo box. Items are binding from xml. item source

    Thanks

  6. Jone Polvora says:

    I’ve created a custom implementation of ITreeViewItemModel that holds an instance of a Ria Services entity, and there’s a binding (I’m using MVVM) so I can get the selected entity object on the treeview. When I set the source value of the binding to null, the tree view is not reseting and the header still shows the textblock with the last selected item. How can I address this issue ?

    private SomeHierarchyViewModel _selectedTreeViewItem;
    public SomeHierarchyViewModel SelectedTreeViewItem
    {
    get { return _selectedTreeViewItem; }
    set
    {
    _selectedTreeViewItem = value;
    NotifyOfPropertyChange(() => SelectedTreeViewItem);
    }
    }

  7. calanus says:

    Hi,
    I’m really struggling to get this to work at all in my projects. All I get in the xaml designer is “Object Reference not set to an instance of an object”. Even starting a new silverlight project and copying in all the source code results in the same problem. Any ideas?

  8. Anonymous says:

    Great work !

  9. brentallsop says:

    Hi vortexwolf,

    Thanks for creating this; it’s exactly what I need.

    Although I can build and run your example solutions, I haven’t been able to use this control in my Silverlight applications yet.

    I need to use this in multiple Silverlight applications, so am trying to implement this in a new Silverlight Class Library containing a combo_box_tree_view class. I’m not yet a Silverlight expert, so don’t know if this is the best way to do this.

    Whenever I try to install this new combo_box_tree_view control in an app’s xaml, I’m getting the same “Object reference not set to an instance of an object” calanus was getting. I’ve copied Themes/Generic.xaml into both the class library, and the class library in which I’m inserting this combo_box_tree_view. But this doesn’t fix the problem.

    If I comment out the code in the “OnApplyTemplate” in the combo_box_tree_view class that at least stops the error in the designer, but then things don’t work.

    • vortexwolf says:

      You should copy the file Themes/Generic.xaml exactly to the same folder ‘Themes’ so that it has the same path. Also no need to copy 2 times, 1 time is enough. Here is a screenshot how the solution should look:

      • brentallsop says:

        That helps. I’m getting closer. Now I’m getting “Target Type Mismatch”, instead of the null reference error. Here is a link to a screen shot of my solution. I’ve added your code to the “Combo_Box_Tree_View_Library”. And you can see the “Themes/Generic.xaml” which has a few changes to match my slightly different names.

        I need to use this combo_box_tree control in the Silverlight_Test_Service_Manager project and others.

      • brentallsop says:

        Ah, I got it to work.
        I just brought the project up in Blend, instead of VS 2012. Blend changed the project in some way (had to save) but don’t know how.
        I gave the tree combo box some margin’s in blend.
        Then everything started working in VS 2012 too.
        It all seems like effing magic to me.
        Thanks, vortex, for all the magic!!!

      • brentallsop says:

        I suspect Blend made a reference change to the project, along the lines of the stuff described here:

        http://stackoverflow.com/questions/2104434/xamlparseexception-using-silverlight-toolkit-control-in-expression-blend

        But don’t know for sure.
        Thanks Again!

    • vortexwolf says:

      The name of your control `combo_box_tree_view` is different from the original `ComboboxTreeView`. Try to use Search (Ctrl+Shift+F) to find usages of both words and if they both are used – replace them by one name so that the code becomes consistent.

  10. Prasad says:

    Hi vortexwolf,
    Nice article and example. I am trying do the same with WPF, i am trying to replicate the same in wpf application. Is that possible??

  11. Prasad says:

    i have created the Usercontrol and trying to add into my wpf windows form, its work fine, but no output

  12. arman says:

    Hi, thank you that’s exactly what I needed but why events don’t work with this control ?

  13. Arin says:

    Hi There, very nice control. I tried the WPF one to put it in a separate WPF control library and use it in a project (.NET 4.0) but sometimes (not alway) after rebuilding the solution it’s comes with the following exception : ArgumentException: PropertyMetadata is already registered for type ‘ComboBoxTreeViewEx’. (as you can see I renamed the control to ComboBoxTreeViewEx).
    here is the stack trace:

    at System.Windows.DependencyProperty.ProcessOverrideMetadata(Type forType, PropertyMetadata typeMetadata, DependencyObjectType dType, PropertyMetadata baseMetadata)
    at System.Windows.DependencyProperty.OverrideMetadata(Type forType, PropertyMetadata typeMetadata)
    at HierarchyDropDownControlLibrary.ComboBoxTreeViewEx..ctor()

    I suspect that something is going wrong when applying the style, but not know exactly where:

    DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBoxTreeViewEx), new FrameworkPropertyMetadata(typeof(ComboBoxTreeViewEx)));

    I hope you can help me out with this.

    thnx

    • vortexwolf says:

      Check the file Themes/Generic.xaml, you should rename the class name in the last Style too. Or maybe you have several *.xaml files with default styles, maybe it should have only 1 default style and other styles with x:Key, but I don’t remember. Also you can try to comment the ovverridemetadata line and look what will happen.

  14. Arin says:

    Hi, I have already renamed the class name in the style. commenting the ovverridemetadata is not a good option because the control will never be shown. any other ideas?
    thnx

  15. Arin says:

    I think I found the solution, in the derived control, you should invoke the following method in the static constructor instead of the default one:

    static ComboBoxTreeViewEx()
    {
    DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBoxTreeViewEx), new FrameworkPropertyMetadata(typeof(ComboBoxTreeViewEx)));
    }

    public ComboBoxTreeViewEx()
    {
    //DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBoxTreeViewEx), new FrameworkPropertyMetadata(typeof(ComboBoxTreeViewEx)));
    }

    thanks anyway.

  16. Cool control!

    I’ve tried (with a lot of headscratching) to modify the size of the combobox tree, when it pops up.
    I’m trying to display a large tree, and want to avoid the user having to scroll (also as the mousewheel events don’t seem to be bound to any part of the control I can find)

    I’ve tried modifying the style to set MinHeight, and also hacking in code to modify the size of visual elements with no luck.

    There must be an easier way I’m missing?

  17. Ev says:

    Hey, thank you for this component it is very useful!

    I was trying to change the background color when mouseover, but I coundn’t find where I can do this. Can you help me?
    I would like to change tha background color inside the style with triggers or something like that…

    Thank you for your attention

    • vortexwolf says:

      Now triggers are probably replaced by VisualStateManager like in Silverlight. But by WPF example still has triggers. They are located in Themes/Generic.xaml file, the line Style x:Key=”MyTreeViewItemStyle”.
      For mouse over you can try the following trigger:

      <Trigger SourceName="PART_Header" Property="IsMouseOver" Value="True">
      	<Setter TargetName="Bd" Property="Background" Value="Red"/>
      </Trigger>
      
      • Ev says:

        It worked just fine for the TreeViewItems, but it didin’t work for the ComboBox itself.. I want the background change when the popup is not open yet.

    • vortexwolf says:

      The combobox button style is “x:Key=”ComboBoxReadonlyToggleButton””. You can try to edit background of the Themes:ButtonChrome. If it doesn’t work – you can try to replace it by Border element

  18. chri55w says:

    Really nice control, struggled quite a bit to get it working using MahApps.Metro style, but the result was certainly worth it! Thanks!

  19. Krishna says:

    Hi, Is it possible to select values like First One- Second – Three- Third One in Combobox selected item . Can you please share how we can do that ?

    And also i want to add values in treeview using values in Datatable can you please help how can i frame like treeview with Datatable rows .

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: