Telerik blogs

As far as I can tell from the experience of my colleagues with RadControls for ASP.NET AJAX, one of the common feature requests for RadComboBox and RadTreeView is combining them into one piece. Recently we started to receive similar requests for RadComboBox and RadTreeView for Silverlight. Placing a TreeView in a ComboBox sometimes makes a lot of sense, so I decided to research the best way to do it.
 
The first, the simplest and most obvious way is to place a RadTreeView as content of RadComboBox:

<input:RadComboBox>
 <nav:RadTreeView
 ItemTemplate="{StaticResource ItemTemplate}"
 ItemsSource="{StaticResource ItemsSource}" />
</input:RadComboBox>

This works, but it has one important limitation: RadComboBox cannot display the RadTreeView selected item. The problem is that RadTreeView is actually inside a RadComboBoxItem and RadComboBox displays the content of its items. I think that you could create a special ContentPresenter that will display the SelectedItem property of the inner TreeView, but in addition, you should search for ways to avoid other UI glitches, such as RadComboBoxItem highlight around the whole TreeView.
 
I abandoned this approach and created completely different prototype, that follows a WPF article I found on the Internet several months ago (I am unable to find it again, so if you know it, post a comment here with a link and I will give credit to the author). The idea is to replace the RadComboBox ItemsPresenter with a RadTreeView. To do this, I copied the RadComboBox styles into a separate XAML file, effectively creating a new RadComboBox theme (this is something that I will describe in detail in another article). You can see how it works in the attached application.

To keep snippet below short, I removed most of the code, leaving only the elements that demonstrate the idea:

<!-- NonEditableComboBoxTemplate -->
<ControlTemplate x:Key="NonEditableComboBox" TargetType="input:RadComboBox">
 <Grid Name="MainGrid">
  <ToggleButton x:Name="DropDownButton" />
  <ContentPresenter />
  <Popup x:Name="PART_Popup">
   <ScrollViewer x:Name="PART_ScrollViewer">
    <nav:RadTreeView ItemsSource="{TemplateBinding ItemsSource}"
     ItemTemplate="{TemplateBinding ItemTemplate}"
     ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
     ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
     ItemContainerStyleSelector="{TemplateBinding ItemContainerStyleSelector}"
     SelectedValuePath="{TemplateBinding SelectedValuePath}"
     system:ScrollViewer.HorizontalScrollBarVisibility="Disabled"
     system:ScrollViewer.VerticalScrollBarVisibility="Disabled" />
   </ScrollViewer>
  </Popup>
  <Rectangle x:Name="DisabledVisual" />
 </Grid>
</ControlTemplate>

I bound all data related properties of the RadTreeView with the corresponding properties of RadComboBox. I also disabled the integrated scrollbars of RadTreeView. The only thing left is to synchronize RadComboBox.SelectedItem and RadTreeView.SelectedItem. To do that I created a simple class, containing one attached property:

using Telerik.Windows;
public class ComboBoxExtensions
{
 public static bool GetEnableInnerTreeView(DependencyObject obj)
 {
  return (bool)obj.GetValue(EnableInnerTreeViewProperty);
 }
 public static void SetEnableInnerTreeView(DependencyObject obj, bool value)
 {
  obj.SetValue(EnableInnerTreeViewProperty, value);
 }
 public static readonly DependencyProperty EnableInnerTreeViewProperty =
  DependencyProperty.RegisterAttached("EnableInnerTreeView", typeof(bool), typeof(ComboBoxExtensions),
  new System.Windows.PropertyMetadata(InitializeTreeView));
 private static void InitializeTreeView(DependencyObject d,
    DependencyPropertyChangedEventArgs args)
 {
  RadComboBox combo = d as RadComboBox;
  if (combo != null)
  {
   combo.AddHandler(RadTreeView.SelectionChangedEvent,
    new Telerik.Windows.Controls.SelectionChangedEventHandler(
    delegate(object sender, Telerik.Windows.Controls.SelectionChangedEventArgs e)
    {
     RadTreeView tree = e.OriginalSource as RadTreeView;
     if (tree != null)
     {
      combo.SelectedItem = tree.SelectedItem;
      combo.IsDropDownOpen = false;
     }
    }));
  }
 }
}

When you set the attached property on RadComboBox, ComboBoxExtensions attaches a RoutedEvent handler to the RadTreeView's SelectionChanged event. The handler synchronizes the TreeView and the ComboBox, and closes the ComboBox dropdown when you change the selection in the treeview.

Note that the AddHandler extension method will become available after you add "using Telerik.Windows".

The following code demonstrates the RadComboBox declaration:

<input:RadComboBox 
 telerik:RadControl.Theme="{StaticResource TreeInCombo}"
 SelectedValuePath="Text"
 ItemsSource="{StaticResource ItemsSource}"
 ItemTemplate="{StaticResource ItemTemplate}"
 local:ComboBoxExtensions.EnableInnerTreeView="True" />

For completeness, here is the hierarchical ItemTemplate, applied on RadComboBox:

<telerik:HierarchicalDataTemplate x:Key="ItemTemplate" 
  ItemsSource="{Binding Children}"
  ItemTemplate="{StaticResource SubItem}">
 <TextBlock Text="{Binding Text}" />
</telerik:HierarchicalDataTemplate>

Here is the updated source with the latest RadComboBox styles:

TreeInCombo

I hope this article helps. If you have questions just leave a comment here, or write in the RadComboBox forums.

 


Comments

Comments are disabled in preview mode.