How to Add a TreeView Inside a WPF ComboBox: Display Hierarchical Lists and Set Selected Value
The WPF ComboBox is a versatile control for displaying flat lists of items, but when working with hierarchical data (e.g., categories with subcategories, file directories, or nested menus), it falls short. The TreeView control, on the other hand, excels at visualizing hierarchical relationships but lacks the compact dropdown behavior of a ComboBox.
Combining a TreeView inside a ComboBox creates a powerful hybrid control: a dropdown that lets users select from nested lists while maintaining a clean UI. This guide will walk you through building this control step-by-step, from setting up the project to handling selection and styling.
Table of Contents#
- Prerequisites
- Step 1: Set Up the WPF Project
- Step 2: Define the Hierarchical Data Model
- Step 3: Create the ComboBox with TreeView
- Step 4: Style the ComboBox and TreeView
- Step 5: Handle Selection and Set Selected Value
- Step 6: Test the Application
- Troubleshooting Common Issues
- Conclusion
- References
Prerequisites#
- Visual Studio (2019 or later; 2022 recommended).
- Basic knowledge of WPF, XAML, and C#.
- .NET 6 or later (we’ll use .NET 7 in this example, but .NET Framework 4.8+ works too).
Step 1: Set Up the WPF Project#
- Open Visual Studio → Create a new project → Select WPF App (.NET) → Name it
TreeViewComboBoxDemo→ Click Create. - The project will generate a default
MainWindow.xaml(UI) andMainWindow.xaml.cs(code-behind). We’ll work primarily in these files.
Step 2: Define the Hierarchical Data Model#
To display hierarchical data, we need a model class that supports parent-child relationships. Let’s create a Category class with:
Id: Unique identifier for the item.Name: Display text.Children: A collection of childCategoryitems (for nesting).
Create the Category Class#
Add a new class file (Category.cs) to your project:
using System.Collections.ObjectModel;
namespace TreeViewComboBoxDemo
{
public class Category
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public ObservableCollection<Category> Children { get; set; } = new ObservableCollection<Category>();
// Optional: Override ToString() to display "Name" in the ComboBox by default
public override string ToString() => Name;
}
}ObservableCollection<Category>ensures the UI updates if children are added/removed dynamically.- Overriding
ToString()lets theComboBoxdisplay theNameproperty by default (we’ll refine this later withDisplayMemberPath).
Step 3: Create the ComboBox with TreeView#
The default ComboBox uses an ItemsPresenter to display flat items. To host a TreeView, we’ll override the ComboBox’s ControlTemplate to replace the default dropdown with a TreeView inside a Popup.
Key Concepts:#
- ControlTemplate: Defines the visual structure of the
ComboBox, including its dropdown (Popup). - TreeView in Popup: The
Popupwill contain aTreeViewto render hierarchical data. - HierarchicalDataTemplate: Tells the
TreeViewhow to display parent and child items.
Update MainWindow.xaml#
Replace the default Grid in MainWindow.xaml with the following XAML. We’ll break down the code afterward:
<Window x:Class="TreeViewComboBoxDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewComboBoxDemo"
Title="TreeView in ComboBox Demo" Height="450" Width="800">
<Grid Margin="20">
<ComboBox
x:Name="cmbTreeView"
MinWidth="250"
DisplayMemberPath="Name" <!-- Shows the "Name" property in the ComboBox -->
SelectedValuePath="Id"> <!-- Uses "Id" as the SelectedValue -->
<!-- Override the ComboBox template to include a TreeView -->
<ComboBox.Template>
<ControlTemplate TargetType="ComboBox">
<Grid>
<!-- ToggleButton: Opens/closes the dropdown -->
<ToggleButton
x:Name="ToggleButton"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border
x:Name="Border"
Background="White"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/> <!-- Space for the dropdown arrow -->
</Grid.ColumnDefinitions>
<!-- Displays the selected item in the ComboBox -->
<ContentPresenter
Margin="8,0,0,0"
VerticalAlignment="Center"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"/>
<!-- Dropdown arrow -->
<Path
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"
Fill="Gray"
Width="8"
Height="4"/>
</Grid>
</Border>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<!-- Popup: Contains the TreeView -->
<Popup
x:Name="Popup"
IsOpen="{TemplateBinding IsDropDownOpen}"
Placement="Bottom"
AllowsTransparency="True"
PopupAnimation="Slide">
<Border
MinWidth="{TemplateBinding ActualWidth}" <!-- Match ComboBox width -->
MaxHeight="300" <!-- Limit dropdown height -->
Background="White"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="4"
Padding="2">
<!-- TreeView for hierarchical data -->
<TreeView
x:Name="PART_TreeView" <!-- Name for code-behind access -->
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="290">
<!-- Define how to display parent/child items -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock
Text="{Binding Name}"
Padding="2"
FontSize="14"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
</Grid>
</Window>Explanation:#
- ToggleButton: Replaces the default ComboBox button. It controls the
IsDropDownOpenstate (to show/hide the popup) and displays the selected item viaContentPresenter. - Popup: Contains the
TreeViewand appears when the ToggleButton is clicked. ItsMinWidthmatches the ComboBox’s width for consistency. - TreeView: Renders hierarchical data using
HierarchicalDataTemplate, which bindsChildrento nested items and displaysNamefor each item.
Step 4: Style the ComboBox and TreeView#
To improve usability, style the TreeView and ComboBox for better appearance:
Add Styles to MainWindow.xaml#
Add these styles inside the Window.Resources section (above the Grid):
<Window.Resources>
<!-- Style for TreeView items -->
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True"/> <!-- Auto-expand all nodes -->
<Setter Property="Padding" Value="4,2"/> <!-- Add spacing between items -->
<Setter Property="Foreground" Value="#333"/> <!-- Dark gray text -->
<Setter Property="FontSize" Value="14"/>
<Style.Triggers>
<!-- Highlight selected items -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#0078D7"/> <!-- Blue background -->
<Setter Property="Foreground" Value="White"/> <!-- White text -->
</Trigger>
</Style.Triggers>
</Style>
<!-- Style for ComboBox ToggleButton (hover/active states) -->
<Style TargetType="ToggleButton" x:Key="ComboBoxToggleButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border
x:Name="Border"
Background="White"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="4">
<!-- ... (reuse the earlier ToggleButton template here) ... -->
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#0078D7"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#0078D7"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>- TreeViewItem Style: Auto-expands nodes, adds padding, and highlights selected items with a blue background.
- ToggleButton Style: Improves interactivity with hover/active states (border color changes).
Step 5: Handle Selection and Set Selected Value#
The ComboBox’s SelectedItem/SelectedValue should reflect the TreeView’s selected item. We’ll sync them using code-behind.
1. Populate Sample Data#
In MainWindow.xaml.cs, add sample hierarchical data to the Category model:
using System.Collections.ObjectModel;
using System.Windows;
namespace TreeViewComboBoxDemo
{
public partial class MainWindow : Window
{
// Sample data
public ObservableCollection<Category> Categories { get; set; } = new ObservableCollection<Category>();
public MainWindow()
{
InitializeComponent();
DataContext = this; // Bind UI to this window's properties
// Populate sample categories
Categories.Add(new Category
{
Id = 1,
Name = "Electronics",
Children =
{
new Category { Id = 101, Name = "Smartphones" },
new Category { Id = 102, Name = "Laptops" },
new Category
{
Id = 103,
Name = "Accessories",
Children =
{
new Category { Id = 1031, Name = "Chargers" },
new Category { Id = 1032, Name = "Cases" }
}
}
}
});
Categories.Add(new Category
{
Id = 2,
Name = "Books",
Children =
{
new Category { Id = 201, Name = "Fiction" },
new Category { Id = 202, Name = "Non-Fiction" }
}
});
// Bind the TreeView to the sample data
Loaded += (s, e) =>
{
if (cmbTreeView.Template.FindName("PART_TreeView", cmbTreeView) is TreeView treeView)
{
treeView.ItemsSource = Categories;
treeView.SelectedItemChanged += TreeView_SelectedItemChanged; // Handle selection
}
};
}
// Update ComboBox.SelectedItem when TreeView selection changes
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue is Category selectedCategory)
{
cmbTreeView.SelectedItem = selectedCategory; // Sync ComboBox.SelectedItem
cmbTreeView.IsDropDownOpen = false; // Close the dropdown after selection
}
}
}
}2. Key Points:#
- Data Binding: The
TreeView’sItemsSourceis set toCategories(sample data) when the window loads. - Selection Sync: The
TreeView_SelectedItemChangedevent updatescmbTreeView.SelectedItemwith the selectedCategory, and closes the dropdown. - SelectedValue: Since we set
SelectedValuePath="Id"on theComboBox,cmbTreeView.SelectedValuewill return theIdof the selectedCategory.
Step 6: Test the Application#
- Press
F5to run the app. - Click the ComboBox to open the dropdown—you’ll see a hierarchical list of categories.
- Select an item (e.g., "Chargers" under "Accessories")—the ComboBox will display "Chargers", and
cmbTreeView.SelectedValuewill be1031.
Troubleshooting Common Issues#
| Issue | Solution |
|---|---|
| TreeView doesn’t display data | Ensure TreeView.ItemsSource is bound to your data (check Loaded event in code-behind). |
| Selected item doesn’t show in ComboBox | Verify DisplayMemberPath="Name" is set on the ComboBox, and SelectionBoxItem is bound in the ContentPresenter. |
| Dropdown is too small/large | Adjust Popup.MaxHeight and Border.MinWidth in the ControlTemplate. |
| TreeView items aren’t expanding | Ensure HierarchicalDataTemplate.ItemsSource is bound to Children (the child collection in your model). |
Conclusion#
By overriding the ComboBox’s template and embedding a TreeView, you can display hierarchical data in a dropdown. This control is ideal for scenarios like nested category selection, file directory navigation, or multi-level menu systems. With proper styling and selection handling, it integrates seamlessly into WPF applications.