简体   繁体   English

C#/ WPF:获取DataTemplate中元素的绑定路径

[英]C#/WPF: Get Binding Path of an Element in a DataTemplate

How can I get the Binding Path of an Element in a DataTemplate? 如何在DataTemplate中获取元素的绑定路径? My XAML looks like this: 我的XAML看起来像这样:

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

To get the Binding Path for the "normal" GridViewColumnHeader.DisplayMemberBinding is 获取“普通”GridViewColumnHeader.DisplayMemberBinding的绑定路径是

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

How can I get the same for the Binding Path of TextBlock.Text ? 如何在TextBlock.Text的绑定路径中获得相同的内容?

This is a great question. 这是一个很好的问题。 There's a separation between the code and the XAML, and, code-wise, it's not immediately obvious where to start looking. 代码与XAML之间存在分离,而且,代码方面,从哪里开始寻找并不是很明显。 Also, the DataTemplate is compiled into BAML so it's not very accessible at runtime. 此外,DataTemplate被编译为BAML,因此在运行时无法访问它。

There are at least two strategies for finding the binding's path. 找到绑定路径至少有两种策略。

The first strategy is saving the path as a static variable somewhere. 一种策略是将路径保存为某处的静态变量。

Code-behind: 代码隐藏:

namespace TempProj
{
    using System.Windows;

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

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

XAML: 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>

The second strategy is opening Snoop or WPF Inspector . 第二个策略是打开SnoopWPF Inspector The goal is to programmatically search the visual tree for the TextBlock of interest. 目标是以编程方式在可视树中搜索感兴趣的TextBlock。 However, there could many TextBlocks in the ListView. 但是,ListView中可能有许多TextBlock。 In fact, the Header is probably using one. 实际上,Header可能正在使用一个。 So, the first step is to identify a unique ancestor of the cell's TextBlock. 因此,第一步是确定单元格 TextBlock的唯一祖先。 Looking at the visual tree, there are two decent candidates: a ScrollContentPresenter (with a template part name, which should be unique) and a GridViewRowPresenter. 查看可视化树,有两个不错的候选者:ScrollContentPresenter(具有模板部件名称,应该是唯一的)和GridViewRowPresenter。 It's best for the ancestor to be close to the TextBlock of interest. 最好让祖先接近感兴趣的TextBlock。 This decreases the likelihood of other TextBlocks distorting the search results. 这降低了其他TextBlocks扭曲搜索结果的可能性。 Thus, the GridViewRowPresenter is preferable. 因此,GridViewRowPresenter是首选。

在此输入图像描述

One or two utility methods are added to perform the visual tree search. 添加一个或两个实用程序方法以执行可视树搜索。

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

Now, two searches through the visual tree are performed, with the first search result acting as the root for the second search. 现在,通过可视树执行两次搜索,第一次搜索结果充当第二次搜索的根。 Starting with the ListView, a GridViewRowPresenter is found. 从ListView开始,找到GridViewRowPresenter。 Starting with that GridViewRowPresenter, a TextBlock is found. 从GridViewRowPresenter开始,找到一个TextBlock。 Its Text binding is queried and the path is finally accessed. 查询其Text绑定并最终访问路径。

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

It's important to note that the ListView's ControlTemplates and DataTemplates must be inflated into their actual visual elements for the search to work. 重要的是要注意ListView的ControlTemplates和DataTemplates必须膨胀到它们的实际可视元素中,以便搜索起作用。 If the inflation hasn't happened, the elements don't exist. 如果没有发生通货膨胀,那么这些要素就不存在了。 You can test this by first trying the search in the main window's contructor and then trying it in the window's OnSourceInitialized. 您可以通过首先在主窗口的构造函数中尝试搜索然后在窗口的OnSourceInitialized中尝试搜索来测试它。 Also, all error checking has been left out for brevity. 此外,为简洁起见,省略了所有错误检查。

Finally, this second strategy is not even remotely bulletproof. 最后,这第二个策略甚至没有远程防弹。 WPF elements can have arbitrarily complex visual compositions when new ControlTemplates and DataTemplates are used. 当使用新的ControlTemplates和DataTemplates时,WPF元素可以具有任意复杂的视觉合成。 However, it's a good starting point for thinking about how you might solve the problem in whatever situation you're in. 但是,这是一个很好的起点,可以考虑如何在任何情况下解决问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM