2009-08-19 14 views
9

¿Cómo puedo obtener la ruta de enlace de un elemento en un DataTemplate? Mi XAML se ve así:C#/WPF: Obtener la ruta vinculante de un elemento en un DataTemplate

<GridViewColumn Header="Double"> 
    <GridViewColumn.CellTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding TotalValues, Mode=OneWay, StringFormat=\{0:0\'0.00\}, Converter={StaticResource GridValueConverter}}" TextAlignment="Right" Width="auto"/> 
     </DataTemplate> 
    </GridViewColumn.CellTemplate> 
</GridViewColumn> 
<GridViewColumn Header="Comments" DisplayMemberBinding="{Binding Path=Comments, Mode=OneWay}" Width="auto"/> 

para obtener la ruta de unión para el GridViewColumnHeader.DisplayMemberBinding "normal" es

var field = (string)((Binding)((GridViewColumnHeader)e.OriginalSource).Column.DisplayMemberBinding).Path.Path; 

¿Cómo puedo obtener el mismo para la ruta de enlace de TextBlock.Text?

Respuesta

10

Esta es una gran pregunta. Hay una separación entre el código y el XAML, y, en cuanto al código, no es inmediatamente obvio dónde comenzar a buscar. Además, DataTemplate se compila en BAML por lo que no es muy accesible en tiempo de ejecución.

Existen al menos dos estrategias para encontrar la ruta del enlace.

La primera estrategia de guarda la ruta como una variable estática en alguna parte.

de código subyacente:

namespace TempProj 
{ 
    using System.Windows; 

    public partial class MainWindow : Window 
    { 
     static public readonly PropertyPath BindingPath = new PropertyPath("X"); 

     public MainWindow() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

XAML:

<Window x:Class="TempProj.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:TempProj" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <Vector3DCollection x:Key="Coordinates"> 
      <Vector3D X="1" Y="0" Z="0"/> 
      <Vector3D X="0" Y="22" Z="0"/> 
      <Vector3D X="0" Y="0" Z="333"/> 
      <Vector3D X="0" Y="4444" Z="0"/> 
      <Vector3D X="55555" Y="0" Z="0"/> 
     </Vector3DCollection> 
    </Window.Resources> 
    <ListView x:Name="lv" ItemsSource="{StaticResource Coordinates}"> 
     <ListView.View> 
      <GridView> 
       <GridViewColumn Header="{x:Static local:MainWindow.BindingPath}"> 
        <GridViewColumn.CellTemplate> 
         <DataTemplate> 
          <TextBlock Text="{Binding Path={x:Static local:MainWindow.BindingPath}}"/> 
         </DataTemplate> 
        </GridViewColumn.CellTemplate> 
       </GridViewColumn> 
      </GridView> 
     </ListView.View> 
    </ListView> 
</Window> 

El segunda estrategia es la apertura deSnoop o WPF Inspector. El objetivo es buscar mediante programación el árbol visual para el TextBlock de interés. Sin embargo, podría haber muchos TextBlocks en ListView. De hecho, el encabezado probablemente esté usando uno. Por lo tanto, el primer paso es identificar un ancestro único del celda de TextBlock. Al observar el árbol visual, hay dos candidatos decentes: un ScrollContentPresenter (con un nombre de parte de plantilla, que debe ser único) y un GridViewRowPresenter. Lo mejor para el ancestro es estar cerca del TextBlock de interés. Esto disminuye la probabilidad de que otros TextBlocks distorsionen los resultados de búsqueda. Por lo tanto, es preferible GridViewRowPresenter. se añaden

enter image description here

uno o dos métodos de utilidad para realizar la búsqueda árbol visual.

static public class ControlAux 
{ 
    static public IEnumerable<T> GetVisualDescendants<T>(this DependencyObject item) where T : DependencyObject 
    { 
     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(item); ++i) 
     { 
      DependencyObject child = VisualTreeHelper.GetChild(item, i); 
      if (typeof(T) == (child.GetType())) 
      { 
       yield return (T)child; 
      } 
      foreach (T descendant in GetVisualDescendants<T>(child)) 
      { 
       yield return descendant; 
      } 
     } 
    } 
    static public T FindVisualDescendant<T>(this DependencyObject item, string descendantName) where T : DependencyObject 
    { 
     return 
      GetVisualDescendants<T>(item).Where(
      descendant => 
      { 
       var frameworkElement = descendant as FrameworkElement; 
       return frameworkElement != null ? frameworkElement.Name == descendantName : false; 
      }). 
      FirstOrDefault(); 
    } 
} 

Ahora, dos búsquedas a través del árbol visual se llevan a cabo, con el primer resultado de la búsqueda que actúa como la raíz de la segunda búsqueda. Comenzando con ListView, se encuentra un GridViewRowPresenter. Comenzando con ese GridViewRowPresenter, se encuentra un TextBlock. Su enlace de texto se consulta y finalmente se accede a la ruta.

GridViewRowPresenter gvrp = lv.GetVisualDescendants<GridViewRowPresenter>().FirstOrDefault(); 
TextBlock tb = gvrp.GetVisualDescendants<TextBlock>().FirstOrDefault(); 
string path = BindingOperations.GetBinding(tb, TextBlock.TextProperty).Path.Path; 

Es importante señalar que ControlTemplates y DataTemplates del ListView tienen que estar infladas en sus elementos visuales reales para que la búsqueda funcione. Si la inflación no ha sucedido, los elementos no existen. Puede probar esto probando primero la búsqueda en el contructor de la ventana principal y luego intentándolo en OnSourceInitialized de la ventana. Además, todas las comprobaciones de errores se han dejado fuera para abreviar.

Finalmente, esta segunda estrategia es ni siquiera remotamente a prueba de balas. Los elementos de WPF pueden tener composiciones visuales complejas arbitrariamente cuando se utilizan nuevas Plantillas de control y Plantillas de datos. Sin embargo, es un buen punto de partida para pensar cómo resolver el problema en cualquier situación en la que se encuentre.

+1

@ Andrew-Van-Berg OMG! ¡Gracias! ¡Snoop y el inspector de WPF son las mejores cosas! –

+0

@ Alexander Van Berg - Gracias señor – Peter

Cuestiones relacionadas