2009-08-19 25 views
9

Bir DataTemplate içinde bir öğe bağlama yolunu nasıl alabilirim? Benim XAML şöyle görünür:C#/WPF: Bir DataTemplate içinde bir öğenin bağlanma yolunu alın

<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"/> 

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

nasıl TextBlock.Text bağlanması Yolu için aynı alabilirsiniz "normal" GridViewColumnHeader.DisplayMemberBinding için Cilt Yolu edilir almak için mi?

cevap

10

Bu harika bir soru. Kod ile XAML arasında bir ayrım var, ve kod olarak, bakmaya nereden başlayacağınız hemen belli değil. Ayrıca, DataTemplate BAML içine derlenmiştir, bu yüzden çalışma zamanında çok erişilebilir değildir.

Bağlamanın yolunu bulmak için en az iki strateji vardır.

ilk strateji, yolu bir yerde statik değişken olarak kaydediyor.

-arkasında kod:

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> 

İkinci stratejiSnoop veya WPF Inspector açıyor. Amaç, TextBlock'un görsel ağacını programlı olarak aramaktır. Ancak, ListView'de birçok TextBlock olabilir. Aslında, Header muhtemelen bir tane kullanıyor. Yani, ilk adım, hücresinin TextBlock'un benzersiz bir atağını tanımlamaktır. Görsel ağaca bakıldığında, iki uygun aday vardır: ScrollContentPresenter (benzersiz olması gereken bir şablon parçası adıyla) ve bir GridViewRowPresenter. Ataların ilgisini çeken TextBlock'a yakın olması en iyisidir. Bu, diğer TextBlock'ların arama sonuçlarını bozma olasılığını azaltır. Böylece GridViewRowPresenter tercih edilir.

enter image description here

bir ya da iki yardımcı yöntem görsel ağacı arama gerçekleştirmek için ilave edilir.

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(); 
    } 
} 

Şimdi, görsel ağaç üzerinden iki arama ikinci arama için root olarak hareket eden ilk arama sonucu, gerçekleştirilmektedir. ListView ile başlayan bir GridViewRowPresenter bulunur. Bu GridViewRowPresenter ile başlayan bir TextBlock bulunur. Metin bağlaması sorgulanır ve son olarak yola erişilir.

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

Bu arama çalışması için ListView ControlTemplates ve datatemplates gerçek görsel öğelerin içine şişirilmiş gerektiğine dikkat etmek önemlidir. Enflasyon gerçekleşmemişse, unsurlar mevcut değildir. Bunu önce ana pencerenin çalışmasında aramayı deneyerek ve ardından pencerenin OnSourceInitialized bölümünde denemeyi deneyebilirsiniz. Ayrıca, tüm hata denetimi kısalık için dışarıda bırakılmıştır.

Son olarak, bu ikinci strateji kurşun geçirmez uzaktan bile olduğunu. Yeni ControlTemplates ve DataTemplates kullanıldığında WPF elemanları rasgele karmaşık görsel kompozisyonlara sahip olabilir. Ancak, hangi durumda olursanız olun bu sorunu nasıl çözebileceğinizi düşünmek için iyi bir başlangıç ​​noktasıdır.

+1

@ Andrew-Van-Berg OMG! Teşekkür ederim! Snoop ve WPF Müfettişi, gelmiş geçmiş en iyi şey! –

+0

@Alexander Van Berg - Teşekkürler bayım – Peter

İlgili konular