WPF: Arboles

Hola a todos

Los elementos de la interface de usuario de una aplicación WPF tienen una relación jerárquica, esta relación toma el nombre de Object Tree, un árbol en el que podemos encontrar todos los objetos anidados que tenemos en nuestra aplicación.
El Object Tree es más un concepto o metáfora que una realidad, pues ciertas propiedades y comportamientos de controles pueden romper esta vista de árbol en tiempo de ejecución. Para evitar esto WPF expone dos arboles distintos que pueden ser accedidos por nuestra aplicación: Logical Tree y Visual Tree.

 

En el gráfico anterior podéis ver el árbol de objetos (Object Tree) de una ventana WPF, y como podemos subdividirlo entre el árbol lógico y el árbol visual
Como sabéis un control en WPF se compone en su forma visual de una plantilla que contiene los controles que dan forma a nuestro control, el Object Tree de WPF contiene información de cada control que tenemos en nuestra ventana, sus hijos y los controles que lo componen en su plantilla.

Logical Tree

El árbol lógico de una ventana en WPF contiene información acerca de la organización jerárquica de los controles en la misma, tendremos normalmente un Objeto Window como principio del árbol del cual se desprenderán de forma ordenada el resto de controles que componen la ventana, tal y como los hayamos definido en código o en Xaml.
El propósito principal de este árbol lógico es el de ofrecer en tiempo de ejecución una representación veraz y realista de nuestros objetos de forma que podamos consultarlos sin preocuparnos de los controles que componen a cada uno, por ejemplo para poder buscar un control de un cierto tipo dentro de nuestra ventana sin tener que recorrer los controles que componen la visualización:
<RichTextBox>
<FlowDocument>
<Paragraph>
Hola <Rectangle Width='10' Height='10' Fill='Red' />
</Paragraph>
</FlowDocument>
</RichTextBox>

Si miramos el Xaml escrito aquí, veremos que tenemos un control Rectangle dentro de un Paragraph que a su vez se encuentra en un FlowDocument, contenido por un RichTextBox. Así si queremos obtener el Padre lógico del control rectangle este será el control Paragraph pues es quien lo contiene. Esta secuencia es la que nos devolvería el Logical Tree, más adelante veremos este mismo ejemplo con el Visual Tree para observar las diferencias entre ambos.

Este es un ejemplo del resultado de recorrer el árbol lógico del Xaml que tenemos arriba:

image

 

Visual Tree

El árbol visual de una ventana WPF contiene información acerca de los controles representados gráficamente en la misma. De esta forma, siguiendo el ejemplo anterior de la Ventana, y como podemos ver en el gráfico anterior, de la Ventana no se desprenderá directamente el contenido de la misma, primero tendremos los controles que componen la plantilla de la ventana y bajo el contentpresenter de la misma encontraremos el resto de controles, junto a su composición interna.

El propósito principal de este árbol visual es el de ofrecer una relación de TODOS los controles dibujados en nuestra ventana, sin distinguir entre controles insertados en la ventana y los controles que componen a estos como puede ser el Border, ContentPresenter y los demás, examinemos el ejemplo anterior:

 

<RichTextBox>
<FlowDocument>
<Paragraph>
Hola <Rectangle Width='10' Height='10' Fill='Red' />
</Paragraph>
</FlowDocument>
</RichTextBox>

En este caso, si queremos obtener el Padre visual de nuestro rectangle usando el árbol visual este nos devolverá como control padre el RichTextBox, en vez del Paragraph, pues los controles Paragraph, FlowDocument, etc… son considerados elementos inline y no controles. Este es un ejemplo del resultado de recorrer el árbol visual del Xaml que tenemos arriba:

image 

 

Diferencias entre Logical Tree y Visual Tree

Como podéis existen grandes diferencias entre el Árbol Lógico y Visual, y sin duda dependiendo de la situación nos será útil recorrer uno u otro, pero, ¿Cuando usar cada uno? Aquí tenéis las principales características de cada uno:

  • Logical Tree:
    • Responsable de la herencia de valores de las DependencyProperties
    • Resuelve referencias a DynamicResources
    • Busqueda de Nombre de elementos para Binding
    • Pasar RoutedEvents a ancestros.
  • Visual Tree:
    • Dibujado de elementos Visuales
    • Propagación de opacidad de elementos
    • Propagación de LayoutTransform y RenderTransform
    • Propagación de la propiedad IsEnabled
    • Hit Testing
    • RelativeSource (FindAncestor)

     

Conclusión

Para poder trabajar con estos arboles disponemos de dos clases en WPF: VisualTreeHelper y LogicalTreeHelper que nos ofrecen métodos para movernos en el árbol de cada uno de forma sencilla, Como ejemplo os dejo dos métodos recursivos que recorren el árbol lógico y el visual añadiendo a un treeview todas las ocurrencias a partir de un control dado:

'Obtenemos todos los controles hijos visuales del control base indicado.
Private Sub Recursive_GetVisualChildrens(ByVal BaseControl As DependencyObject,
ByVal ParentNode As TreeViewItem)
If VisualTreeHelper.GetChildrenCount(BaseControl) > 0 Then
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(BaseControl) - 1
Dim Hijo As New DependencyObject()
Hijo = VisualTreeHelper.GetChild(BaseControl, i)
Dim Nodo As New TreeViewItem
Nodo.Header = Hijo.DependencyObjectType.Name
Recursive_GetVisualChildrens(Hijo, Nodo)
ParentNode.Items.Add(Nodo)
Next
End If
End Sub

'Obtenemos todos los controles hijos lógicos del control base indicado.
Private Sub Recursive_GetLogicalChildrens(ByVal BaseControl As DependencyObject,
ByVal ParentNode As TreeViewItem)
For Each Hijo As Object In LogicalTreeHelper.GetChildren(BaseControl)
Dim Nodo As New TreeViewItem
Nodo.Header = Hijo.GetType().Name
Try
Recursive_GetLogicalChildrens(CType(Hijo, DependencyObject), Nodo)
Catch ex As Exception
End Try
ParentNode.Items.Add(Nodo)
Next
End Sub

Y como ayuda y demostración os dejo una aplicación WPF que hace uso de estos métodos para que juguéis con ella:

image

 

Espero que os haya gustado tanto leer este artículo como a mi escribirlo.

 

Por si tenéis alguna dificultad, a parte por supuesto de poder contactar conmigo tanto dejando mensajes, como en mi twitter, como en msdn o a mi email directamente, os dejo el proyecto completo para descarga.

 

Y recordad, con un simple comentario, podéis hacer feliz a un pequeño bloguero 🙂

 

Muchas gracias por leerme y Happy Coding!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *