这是一个非常好的问题。在代码和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中可能有许多TextBlocks,事实上,Header可能正在使用其中之一。因此,第一步是确定单元格TextBlock的唯一祖先。查看可视化树,有两个不错的选择:一个ScrollContentPresenter(带有模板部件名称,应该是唯一的)和一个GridViewRowPresenter。最好是祖先靠近所需的TextBlock。这样可以减少其他TextBlocks扭曲搜索结果的可能性。因此,GridViewRowPresenter更可取。
![enter image description here](https://istack.dev59.com/wLQOU.webp)
添加了一两个实用方法来执行可视化树搜索。
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 和 DataTemplates 时,WPF 元素可能具有任意复杂的视觉组合。然而,这是思考如何解决您所处情况下的问题的良好起点。