2009-08-19 4 views
9

DataTemplate에서 요소의 바인딩 경로를 가져 오는 방법은 무엇입니까? 내 XAML은 다음과 같습니다C#/WPF : 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"/> 

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

가 어떻게 TextBlock.Text의 바인딩 경로에 대해 동일한 얻을 수있는 "정상"GridViewColumnHeader.DisplayMemberBinding에 대한 바인딩 경로입니다 얻으려면?

답변

10

위대한 질문입니다. 코드와 XAML은 구분되며 코드를 사용하여 어디서부터 시작해야할지 명확하게 알 수 없습니다. 또한 DataTemplate은 BAML로 컴파일되므로 런타임에는 액세스 할 수 없습니다.

바인딩 경로를 찾는 데는 최소한 두 가지 전략이 있습니다.

첫 번째 전략은 경로를 정적 변수로 어딘가에 저장합니다.

숨김 코드 :

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> 

두 번째 전략Snoop 또는 WPF Inspector을 여는. 목표는 프로그래밍 방식으로 관심있는 TextBlock에 대한 시각적 트리를 검색하는 것입니다. 그러나 ListView에 많은 TextBlock이있을 수 있습니다. 사실, 헤더는 아마도 하나를 사용하고있을 것입니다. 따라서 첫 번째 단계는 셀의 TextBlock의 고유 한 조상을 식별하는 것입니다. 시각적 트리를 보면 ScrollContentPresenter (고유해야하는 템플릿 부분 이름 포함)와 GridViewRowPresenter라는 두 가지 괜찮은 후보가 있습니다. 조상이 관심있는 TextBlock과 가장 가까운 것이 가장 좋습니다. 이렇게하면 다른 TextBlock이 검색 결과를 왜곡 할 가능성이 줄어 듭니다. 따라서 GridViewRowPresenter가 좋습니다.

enter image description here

하나 또는 두 개의 유틸리티 메소드는 비주얼 트리 탐색을 수행하기 위해 첨가된다.

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

이제 시각적 트리를 통한 두 번의 검색이 수행되며 첫 번째 검색 결과는 두 번째 검색의 루트 역할을합니다. ListView부터는 GridViewRowPresenter가 있습니다. GridViewRowPresenter로 시작하여 TextBlock이 발견됩니다. 텍스트 바인딩이 쿼리되고 경로가 최종적으로 액세스됩니다.

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

이 검색이 작동하려면 ListView에의 ControlTemplates 및 DataTemplates가 실제 시각적 요소로 팽창되어야 함을 유의하는 것이 중요합니다. 인플레이션이 발생하지 않았다면 그 요소는 존재하지 않는다. 메인 윈도우의 컨스트럭터에서 먼저 검색을 시도한 다음 윈도우의 OnSourceInitialized에서 시도하여 테스트 할 수 있습니다. 또한 모든 오류 검사가 간결하게 생략되었습니다.

마지막으로이 두 번째 전략은 도 원격으로 방탄입니다. 새로운 ControlTemplates와 DataTemplate이 사용될 때 WPF 요소는 임의로 복잡한 시각적 구성을 가질 수 있습니다. 그러나 어떤 상황에서든 문제를 해결할 수있는 방법에 대해 생각해 보는 것이 좋습니다.

+1

@ Andrew-Van-Berg OMG! 고맙습니다! Snoop 및 WPF Inspector는 최고의 물건입니다! –

+0

@Alexander Van Berg - 고마워요. – Peter

관련 문제