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

49 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 .

  20. jue4you says:

    The Version ComboBoxTreeViewEventsSupport functions with Silverlight. I wanted to use it with WPF, but, when the Combobox is dropped down, it Shows the items two times. The selected item is shown in the text box.
    Thank you, nevertheless, for this great example. Any idea, why the items in WPF are shown recursively?

    Don’t know, how to insert a screenshot here 😦

    • vortexwolf says:

      It might be because the collection that you use in ItemsSource=”{Binding Items}” contains duplicate items. But if my example without modification does this, then I don’t know, maybe the new version of WPF had some breaking changes. I can’t check it properly now.

      • jue4you says:

        Hi, vortexwolf,

        thanks for the answer. I am still working on this Problem. My step totime is, that I found out, that the ‘Anweisungslambda’ (= engl.: perhaps commandlambda) in the function UpdateItemsSource does this side effect. In Reference, there is a hint, that ‘Anweisungslambdas’ cannot be used for treestructures. Indeed, when this function calls GetChildren(), and the return value is null, the selectAllItemsRecursively(item.GetChildren() function is called once again, without calling allItems.Add(item) in previous step.

        Now I am searching, how this Lambda Syntax: selectAllItemsRecursively = items => { … } can be expressed in normal Code, for getting more Control about the codesteps.

        I think, the Problem is, that your changed Code in GetChildren() gives back a null value. The items given back have Count 3, but Capacity 4, when there only should be 3.

        Do you know, how to express the selectAllItemsRecursively = items => { … } in normal Code. This would surely help me. I’m searching now for hours about that.

        In Internet there is a Version of your example from Syncfusion. But, the Code is hidden in 2 DLLs. Therefore I don’t want to use it. See: syncfusion.com/forums/123434/can-i-set-treeview-as-dropdown-content-in-combobox. Did you know that?

        Your sample is nearly genial!!! But I must find out, why the items are shown recursively.

        My workflow was as following.

        As I realized, that the updated sample ComboBoxTreeViewEventsSupport only functions without this side effect only in sliverlight, I updated the previous Version WpfComboBoxTreeView with the Code changes in files MainWindow.xaml.cs, ComboBoxTreeView.cs, …
        But the Generic.xaml of the Silverlight Version is complete other Code than the Generic.xaml of the WPF Version. Have tried to compare it, but, no Chance, it’s too complex.

        thanks anyway and best regards

  21. jue4you says:

    Hi,

    of Course, that was a lot of nonsense in my post before. The null values are not the Problem. The selectAllItemsRecursively Function works fine. (Have debugged it).
    In the MainWindow.xaml, I had changed first the tag <Windows:HierarchicalDataTemplate x:Key="treeViewDataTemplate … into <HierarchicalDataTemplate x:Key="treeViewDataTemplate …
    because there was a mistake blue underline. Had not found HierarchicalDataTemplate in System.Windows assembly.
    Because you spoke about Bindings, I had checked all Bindings. At least, I detected, that there are two versions of System.Windows.dll. In System.Windows.dll: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Windows.dll, there is no function HierarchicalDataTemplate or better, there is one, but with the hint because of: The type ‘HierarchicalDataTemplate’ supports no direct contents.
    The System.Windows.dll you used is the Version: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v5.0\System.Windows.dll
    There is another dll, that differs: The System.Windows.Controls. There is a Silverlight and a .NETFramework\v4.5 Version. In the Version ComboBoxTreeViewEventsSupport, that works fine in Silverlight, there is another Reference to System.Windows.Browser for Silverlight.

    Surely, i think, this must be the reason, of the different functioning in Silverlight and WPF application.
    Now my next step will be, searching a System.Windows.dll and a System.Windows.Controls.dll from .NETFramework\v.4.5, that is compatible to those Silverlight.dlls.

    If you have Knowledge about that, please Show me.

    thanks anyway again and best regards

    • vortexwolf says:

      I think there might be some issue with HierarchicalDataTemplate in Silverlight. I don’t remember if it was available in standard library, most probably not. I think I used Silverlight Toolkit. I found some article with example that uses Silverlight Toolkit: http://michaelsync.net/2008/10/28/silverlight-toolkit-using-silverlight-treeview-control

      In your case you need to check how your TreeView code looks and if it is something like “controls:TreeView” you should also use “controls:HierarchicalDataTemplate”. Also you might try to download a good assembly of Silverlight Toolkit.

      • jue4you says:

        Thanks a lot for your answer and giving me new ideas, I will check it. I’m tweaking for 2 or 3 weaks now. But I’ve learned something. My Knowledge about data binding is horrible bad.

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: