위대한 질문입니다. 코드와 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가 좋습니다.
하나 또는 두 개의 유틸리티 메소드는 비주얼 트리 탐색을 수행하기 위해 첨가된다.
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 요소는 임의로 복잡한 시각적 구성을 가질 수 있습니다. 그러나 어떤 상황에서든 문제를 해결할 수있는 방법에 대해 생각해 보는 것이 좋습니다.
@ Andrew-Van-Berg OMG! 고맙습니다! Snoop 및 WPF Inspector는 최고의 물건입니다! –
@Alexander Van Berg - 고마워요. – Peter