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

Posted

技术标签:

【中文标题】C#/WPF:获取 DataTemplate 中元素的绑定路径【英文标题】:C#/WPF: Get Binding Path of an Element in a DataTemplate 【发布时间】:2010-11-20 20:40:05 【问题描述】:

如何获取 DataTemplate 中元素的绑定路径? 我的 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"/>

获取“普通”GridViewColumnHeader.DisplayMemberBinding的绑定路径是

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

TextBlock.Text 的绑定路径如何获得相同的结果?

【问题讨论】:

【参考方案1】:

这是一个很好的问题。代码和 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。事实上,Header 很可能是在使用一个。因此,第一步是识别 cell 的 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。查询其Text绑定,最终访问路径。

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 元素可以具有任意复杂的视觉组合。但是,这是思考在任何情况下如何解决问题的一个很好的起点。

【讨论】:

@Andrew-Van-Berg OMG!谢谢! Snoop 和 WPF Inspector 是有史以来最好的东西! @Alexander Van Berg - 谢谢先生

以上是关于C#/WPF:获取 DataTemplate 中元素的绑定路径的主要内容,如果未能解决你的问题,请参考以下文章

WPF -- DataTemplate与ControlTemplate结合使用

WPF 菜单事件绑定 DataTemplate下button Command事件绑定 DataTemplate遍历实体数据

WPF为没有DataTemplate属性的控件创建DataTemplate

c#WPF如何删除listview标题分隔符行?

WPF在代码中创建DataTemplate时候的异常

WPF中Control Template和DataTemplate的区别