Posted by: Zeeshan Amjad | January 12, 2011

Traversing Visual Tree by name


We 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 about the type of the control. But if we have more than one control with the same type, such as we have two text boxes to display state name and its capital then how can we get anyone of that.

To solve this problem, first we have to define the name for both text block in XAML. Then modify our recursive method of traverse visual tree to find the control that not only matches the type, but as well as its name too. Here is our new modified method.

Code Snippet
  1. public T TraverseVisualTree<T>(DependencyObject root, String name) 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.         string childName = child.GetValue(NameProperty) as string;
  10.         control = child as T;
  11.  
  12.         if (childName == name)
  13.         {
  14.             return control;
  15.         }
  16.         else
  17.         {
  18.             control = TraverseVisualTree<T>(child, name);
  19.  
  20.             if (control != null)
  21.             {
  22.                 return control;
  23.             }
  24.         }
  25.     }
  26.  
  27.     return control;
  28. }

 

This method accepts one more parameter name. We have to insert the name of the control in string and this method traverse the visual tree to see if any such control exists. Now we are going to define two text block in our XAML.

Code Snippet
  1. <TextBlock Name="txtState" FontWeight="Bold" Margin="2" Text="{Binding Name}"/>
  2. <TextBlock Name="txtCapital" Margin="2" Text="{Binding Capital}"/>

Here the name of first text block is “txtState” and name of the second one is “txtCapital”. If we want to get the first text block then it is very simple.

Code Snippet
  1. TextBlock txt = TraverseVisualTree<TextBlock>(lbi, "txtState");

 

Similarly we can get the combo box and change the property of specific text block. Here is a modified button handler. Note here we are going to modify the foreground color property of only txtState text block.

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, "cmbColors");
  10.         TextBlock txt = TraverseVisualTree<TextBlock>(lbi, "txtState");
  11.  
  12.         if (cmb.SelectedItem != null)
  13.         {
  14.             Brush brush = (Brush)TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromString(cmb.SelectedItem.ToString());
  15.             txt.Foreground = brush;
  16.         }
  17.     }
  18. }

 

We also modified XAML little bit. We not only added one more text block, but display the combo box and button on the same line using one more grid layout. 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" Loaded="Window_Loaded">
  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 Name="txtState" FontWeight="Bold" Margin="2" Text="{Binding Name}"/>
  12.                             <TextBlock Name="txtCapital" Margin="2" Text="{Binding Capital}"/>
  13.                             <Grid>
  14.                                 <Grid.ColumnDefinitions>
  15.                                     <ColumnDefinition Width="2*"/>
  16.                                     <ColumnDefinition/>
  17.                                 </Grid.ColumnDefinitions>
  18.                                 <ComboBox Grid.Column="0" Margin="2" Name="cmbColors" ItemsSource="{Binding Colors}"
  19.                                       HorizontalContentAlignment="Stretch">
  20.                                     <ComboBox.ItemTemplate>
  21.                                         <DataTemplate>
  22.                                             <Grid>
  23.                                                 <Grid.ColumnDefinitions>
  24.                                                     <ColumnDefinition/>
  25.                                                     <ColumnDefinition Width="2*"/>
  26.                                                 </Grid.ColumnDefinitions>
  27.                                                 <Rectangle Grid.Column="0" Margin="2, 1" Fill="{Binding}"/>
  28.                                                 <TextBlock Grid.Column="1" Margin="2, 1" Text="{Binding}"/>
  29.                                             </Grid>
  30.                                         </DataTemplate>
  31.                                     </ComboBox.ItemTemplate>
  32.                                 </ComboBox>
  33.                                 <Button Grid.Column="1" Name="btnSelect" Content="Set Color" Margin="3" Click="btnSelect_Click"/>
  34.                             </Grid>
  35.                         </StackPanel>
  36.                     </Border>
  37.                 </DataTemplate>
  38.             </ListBox.ItemTemplate>
  39.         </ListBox>
  40.     </Grid>
  41. </Window>

And 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", "Sacramento", colors));
  41.             states.Add(new States("Maryland", "Annapolis", colors));
  42.             states.Add(new States("Washington", "Olympia", colors));
  43.             states.Add(new States("New York", "Albany", colors));
  44.             states.Add(new States("Texas", "Austin", 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, "cmbColors");
  58.                 TextBlock txt = TraverseVisualTree<TextBlock>(lbi, "txtState");
  59.  
  60.                 if (cmb.SelectedItem != null)
  61.                 {
  62.                     Brush brush = (Brush)TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromString(cmb.SelectedItem.ToString());
  63.                     txt.Foreground = brush;
  64.                 }
  65.             }
  66.         }
  67.  
  68.         public T TraverseVisualTree<T>(DependencyObject root, String name) 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.                 string childName = child.GetValue(NameProperty) as string;
  77.                 control = child as T;
  78.  
  79.                 if (childName == name)
  80.                 {
  81.                     return control;
  82.                 }
  83.                 else
  84.                 {
  85.                     control = TraverseVisualTree<T>(child, name);
  86.  
  87.                     if (control != null)
  88.                     {
  89.                         return control;
  90.                     }
  91.                 }
  92.             }
  93.  
  94.             return control;
  95.         }
  96.  
  97.         private void Window_Loaded(object sender, RoutedEventArgs e)
  98.         {
  99.  
  100.         }
  101.     }
  102.  
  103.     public class States
  104.     {
  105.         public States()
  106.         {
  107.         }
  108.  
  109.         public States(String name, String capital, ObservableCollection<string> colors)
  110.         {
  111.             Name = name;
  112.             Capital = capital;
  113.             Colors = colors;
  114.         }
  115.  
  116.         public String Name
  117.         { get; set; }
  118.  
  119.         public String Capital
  120.         { get; set; }
  121.  
  122.         public ObservableCollection<String> Colors
  123.         { get; set; }
  124.     }
  125. }

 

Here is the out put of the program.

VisualTreeByNameOutput

Advertisements

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: