Posted by: Zeeshan Amjad | January 11, 2011

Traversing Visual Tree Revisited


We already saw one example of traversing visual tree here. But at that time we only display that information. We can do lot more than that and create very powerful user interface with it. Here we create one combo box inside the list box here. In that example we change the background color of the text when we change the color selection from the combo box. Now our requirement is little bit change. Instead of changing the background color of the text block if we press the “Select Color” button which is present below the color combo box of each list box item.

Now we created one utility function to traverse the visual tree and return the required item. Here we take the advantage of generic rather than creating different version for different item. Here is our generic version of traversing visual tree.

Code Snippet
  1. public T TraverseVisualTree<T>(DependencyObject root) where T : Visual
  2. {
  3.     T control = null;
  4.  
  5.     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
  6.     {
  7.         var child = VisualTreeHelper.GetChild(root, i);
  8.  
  9.         control = child as T;
  10.  
  11.         if (control == null)
  12.         {
  13.             control = TraverseVisualTree<T>(child);
  14.         }
  15.         else
  16.         {
  17.             return control;
  18.         }
  19.     }
  20.  
  21.     return control;
  22. }

 

Now we are going to add button inside the data template stack panel. Then write the button handler of it. Inside the button handler we uses the ItemContainerGenerator property of list box (in fact property of ItemsControl class) to get the list box item. Here is a class diagram of ItemContainerGenerator.

ItemContainerGenerator

Then we call our utility method to get the combo box and text block and then set the background color of text block with the selected items of the combo box. The only missing piece is that we have to use the type converter to convert string into Brush. Here is a piece of code to do this.

Code Snippet
  1. private void btnSelect_Click(object sender, RoutedEventArgs e)
  2. {
  3.     Button btn = sender as Button;
  4.  
  5.     if (btn != null)
  6.     {
  7.         ListBoxItem lbi = list.ItemContainerGenerator.ContainerFromItem(btn.DataContext) as ListBoxItem;
  8.  
  9.         ComboBox cmb = TraverseVisualTree<ComboBox>(lbi);
  10.         TextBlock txt = TraverseVisualTree<TextBlock>(lbi);
  11.  
  12.         if (cmb.SelectedItem != null)
  13.         {
  14.             Brush brush = (Brush)TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromString(cmb.SelectedItem.ToString());
  15.             txt.Background = brush;
  16.         }
  17.     }
  18. }

 

Here is complete XAML code of the program.

Code Snippet
  1. <Window x:Class="WpfCombo.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4.         Title="Combo inside Listbox" Height="400" Width="400">
  5.     <Grid>
  6.         <ListBox Name="list" Margin="5" HorizontalContentAlignment="Stretch">
  7.             <ListBox.ItemTemplate>
  8.                 <DataTemplate>
  9.                     <Border Margin="3" CornerRadius="5" BorderBrush="Brown" BorderThickness="2">
  10.                         <StackPanel>
  11.                             <TextBlock Margin="2" Text="{Binding Name}"/>
  12.                             <ComboBox Margin="2" Name="cmbColors" ItemsSource="{Binding Colors}"
  13.                                       HorizontalContentAlignment="Stretch">
  14.                                 <ComboBox.ItemTemplate>
  15.                                     <DataTemplate>
  16.                                         <Grid>
  17.                                             <Grid.ColumnDefinitions>
  18.                                                 <ColumnDefinition/>
  19.                                                 <ColumnDefinition Width="2*"/>
  20.                                             </Grid.ColumnDefinitions>
  21.                                             <Rectangle Grid.Column="0" Margin="2, 1" Fill="{Binding}"/>
  22.                                             <TextBlock Grid.Column="1" Margin="2, 1" Text="{Binding}"/>
  23.                                                                                 </Grid>
  24.                                     </DataTemplate>
  25.                                 </ComboBox.ItemTemplate>
  26.                             </ComboBox>
  27.                             <Button Name="btnSelect" Content="Set Color" Margin="3" Click="btnSelect_Click"/>
  28.                         </StackPanel>
  29.                     </Border>
  30.                 </DataTemplate>
  31.             </ListBox.ItemTemplate>
  32.         </ListBox>
  33.     </Grid>
  34. </Window>

 

Here is complete C# code of the program.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. using System.Windows.Media.Media3D;
  15. using System.Collections.ObjectModel;
  16. using System.ComponentModel;
  17.  
  18. namespace WpfCombo
  19. {
  20.     /// <summary>
  21.     /// Interaction logic for MainWindow.xaml
  22.     /// </summary>
  23.     public partial class MainWindow : Window
  24.     {
  25.         public MainWindow()
  26.         {
  27.             InitializeComponent();
  28.  
  29.             ObservableCollection<string> colors = new ObservableCollection<string>();
  30.  
  31.             String[] allColors = Enum.GetNames(typeof(System.Drawing.KnownColor));
  32.  
  33.             foreach (String color in allColors)
  34.             {
  35.                 colors.Add(color);
  36.             }
  37.  
  38.             ObservableCollection<States> states = new ObservableCollection<States>();
  39.  
  40.             states.Add(new States("California", colors));
  41.             states.Add(new States("Maryland", colors));
  42.             states.Add(new States("Washington", colors));
  43.             states.Add(new States("New York", colors));
  44.             states.Add(new States("Taxes", colors));
  45.  
  46.             list.ItemsSource = states;
  47.         }
  48.  
  49.         private void btnSelect_Click(object sender, RoutedEventArgs e)
  50.         {
  51.             Button btn = sender as Button;
  52.  
  53.             if (btn != null)
  54.             {
  55.                 ListBoxItem lbi = list.ItemContainerGenerator.ContainerFromItem(btn.DataContext) as ListBoxItem;
  56.  
  57.                 ComboBox cmb = TraverseVisualTree<ComboBox>(lbi);
  58.                 TextBlock txt = TraverseVisualTree<TextBlock>(lbi);
  59.  
  60.                 if (cmb.SelectedItem != null)
  61.                 {
  62.                     Brush brush = (Brush)TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromString(cmb.SelectedItem.ToString());
  63.                     txt.Background = brush;
  64.                 }
  65.             }
  66.         }
  67.  
  68.         public T TraverseVisualTree<T>(DependencyObject root) where T : Visual
  69.         {
  70.             T control = null;
  71.  
  72.             for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
  73.             {
  74.                 var child = VisualTreeHelper.GetChild(root, i);
  75.  
  76.                 control = child as T;
  77.  
  78.                 if (control == null)
  79.                 {
  80.                     control = TraverseVisualTree<T>(child);
  81.                 }
  82.                 else
  83.                 {
  84.                     return control;
  85.                 }
  86.             }
  87.  
  88.             return control;
  89.         }
  90.     }
  91.  
  92.     public class States
  93.     {
  94.         public States()
  95.         {
  96.         }
  97.  
  98.         public States(String name, ObservableCollection<string> colors)
  99.         {
  100.             Name = name;
  101.             Colors = colors;
  102.         }
  103.  
  104.         public String Name
  105.         { get; set; }
  106.  
  107.         public ObservableCollection<String> Colors
  108.         { get; set; }
  109.     }
  110. }

 

Here is the output of the program.

VisualTreeRevisited

Advertisements

Responses

  1. […] already saw couple of example of traversing visual tree here and here. But in both example we don’t care about the name of the control defined in XAML we only care […]


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

Categories

%d bloggers like this: