Recorriendo elementos del VisualTree
Posted on : 19-02-2010 | By : Arturo Molina | In : Silverlight
Tags: VisualTreeHelper
0
Todo elemento desplegado en nuestra aplicación de Silverlight pertenece a una estructura jerárquica conocida como el VisualTree. La clase VisualTreeHelper nos provee de varios métodos para recorrer dicha estructura entre ellos:
- GetChildrenCount – Obtiene el número de Hijos que tiene el UIElement
- GetChild – Obtiene un hijo del UIElement
NOTA: El propósito del siguiente ejemplo es ilustrar el uso del VisualTree. Hay muchas maneras de hacer lo siguiente, como cambiar directamente la fuente de datos para reflejar los cambios en la Interfaz de Usuario. De hecho, si no se usa adecuadamente, el VisualTreeHelper puede hacer nuestra aplicación lenta.
Primero, vamos a usar una clase llamada ListInfo. Esta clase implementa la interfaz INotifyPropertyChanged y tiene solo dos propiedades: Name y Check. Además cuenta con un método estático llamado getInfo() que devuelve una ObservableCollection de 3 elementos:
public class ListInfo : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
NotifyPropertyChange("Name");
}
}
private bool check;
public bool Check
{
get
{
return check;
}
set
{
check = value;
NotifyPropertyChange("Check");
}
}
public static ObservableCollection<ListInfo> getInfo()
{
ObservableCollection<ListInfo> data = new ObservableCollection<ListInfo>();
data.Add(new ListInfo { Name = "Texto", Check = true });
data.Add(new ListInfo { Name = "Qwerty", Check = false });
data.Add(new ListInfo { Name = "Asdf", Check = true });
return data;
}
public void NotifyPropertyChange(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
A continuación tenemos nuestro XAML, que es un simple ListBox con un ItemTemplate definido:
<StackPanel x:Name="LayoutRoot">
<ListBox x:Name="myList"
Width="450"
SelectionChanged="myList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Check, Mode=TwoWay}"
IsEnabled="True"
Content="{Binding Name, Mode=TwoWay}"
Margin="5,0,0,0" />
<TextBlock Text="Al cambiar el elemento cambia nuestro CheckBox"
Margin="5,0,0,0"
Foreground="Blue" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Finalmente, nuestro CodeBehind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
myList.ItemsSource = ListInfo.getInfo();
}
private void myList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListInfo current = myList.SelectedItem as ListInfo;
List<UIElement> checkBoxes = new List<UIElement>();
getChildTypes(myList, typeof(CheckBox), ref checkBoxes);
foreach (UIElement element in checkBoxes)
{
CheckBox checkBox = (CheckBox)element;
if (checkBox.Content.ToString() == current.Name)
{
checkBox.IsChecked = !checkBox.IsChecked;
}
}
}
public void getChildTypes(UIElement parent, Type elementType, ref List<UIElement> items)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (child.GetType() == elementType)
{
items.Add(child);
}
getChildTypes(child, elementType, ref items);
}
}
}
}
El método que nos interesa es getChildTypes. En este caso le mandamos el padre de los elementos que estamos buscando, en este caso, el ListBox myList. Después el tipo de elementos que queremos que nos regrese, osea, ChecBox. Finalmente le mandamos un valor de referencia de tipo List<UIElement>, en el cuál obtendremos nuestros resultados.
La lógica es sencilla, es un método recursivo que recorre todos los hijos y va agregando a la lista todos los que coindicen con el tipo indicado por elementType.
Pueden ver el resultado aquí.






