【问题标题】:C#/WPF: Get Binding Path of an Element in a DataTemplateC#/WPF:获取 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 的绑定路径如何获得相同的结果?

【问题讨论】:

    标签: c# wpf binding datatemplate


    【解决方案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>
    

    第二种策略是打开SnoopWPF 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 - 谢谢先生
    猜你喜欢
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-29
    • 1970-01-01
    • 2015-09-09
    相关资源
    最近更新 更多