如何确定XAML对象属性值的来源?

时间:2015-03-09 23:57:34

标签: wpf

我的程序中有一堆文本块对象,所有这些对象的前景色都是白色的。我想找出导致这种情况的原因。

在Visual Studio中是否有任何方法可以追溯属性值的来源,无论是通过模板还是包含对象?

编辑: 在发出赏金之后,这个问题得到了很多有趣的回应,提出了各种各样的方法。我认为那里有一些东西,但到目前为止,我还没有弄清楚如何更有效地应用这些,而不仅仅是通过代码进行试错。如果有人想要收取费用并将其中一个回复推进为有用的话,我正在观看。

5 个答案:

答案 0 :(得分:2)

当我需要了解绑定值来自何处时,我倾向于使用Snoop来围绕可视化树。您可以使用它来查看控件属性,并遵循绑定到DataContexts或在树中向上和向下走,以查看值可以从何处继承。

启动Snoop,将十字准线拖到WPF应用程序上(或刷新进程列表并从下拉列表中选择),然后将鼠标指针放在TextBlock控件和Ctrl-Shift-LeftClick上。 Snoop将缩放您控件上的可视树,并允许您查看或编辑控件属性。

答案 1 :(得分:2)

已经有一个答案显示了托管工具。

这里我正在扩展关于调试InitializeComponent的评论。 让我们假设一个基本的xaml作为概念证明。

<Grid>
    <Grid.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="Foreground" Value="Blue" />
        </Style>
    </Grid.Resources>

    <Border  BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="200" Margin="10,25,0,0" VerticalAlignment="Top" Width="199"> 
        <TextBlock Name="analysethis" Text = "why is it blue?" TextWrapping="Wrap" />    
    </Border>

</Grid>

拦截Foreground TextBlock属性的事件很困难,因为我们无法继承TextBlock(否则样式将丢失) 我们无法覆盖Freezable。如果我没有其他解决方案,我会在以下转换器上设置断点

[ValueConversion(typeof(Color), typeof(String))]
    public class DebugConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is Color)) return null; // << BP here
            var result = value.ToString();
            return result;
        }

使用静态资源将其链接到TextBlock

        <TextBlock Name="analysethis" TextWrapping="Wrap" Margin="0,81,0,75"
                 Text="{Binding RelativeSource={RelativeSource Self},
               Path=Foreground.Color, Converter={StaticResource ColConv} }" >

启动Debug后,可以看到分配给Foreground的初始默认"#FF000000",并且起始条件位于堆栈中

System.Xaml.dll!System.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(MS.Internal.Xaml.Context.ObjectWriterContext ctx) + 0xc6 byte   
System.Xaml.dll!System.Xaml.XamlObjectWriter.Logic_AssignProvidedValue(MS.Internal.Xaml.Context.ObjectWriterContext ctx) + 0x37 byte    

然后在断点处再次点击,最终可以在堆栈中找到实际前景颜色的来源

>   PresentationFramework.dll!System.Windows.StyleHelper.DoStyleInvalidations(System.Windows.FrameworkElement fe, System.Windows.FrameworkContentElement fce, System.Windows.Style oldStyle, System.Windows.Style newStyle) + 0xcd byte 
>   WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) + 0x757 byte    
+       newEntry    {System.Windows.EffectiveValueEntry}    System.Windows.EffectiveValueEntry
+       _value  {System.Windows.Style}  object {System.Windows.Style}
+       _value  {#FF0000FF} object {System.Windows.Media.SolidColorBrush}
+       _targetType {System.Windows.Controls.TextBlock} System.Type {System.RuntimeType}
>   PresentationFramework.dll!System.Windows.FrameworkElement.UpdateStyleProperty() + 0x63 byte 

请注意,如果您有兴趣,可以进一步努力上面显示的PropertyIndex(链接到Foreground DependencyProperty)可以追溯到: BamlSchemaContextxamlReaderWpfXamlLoader.LoadBaml的非公开会员,其中包含System.Xaml.IXamlLineInfo.LineNumber

修改

以下是如何自动执行堆栈跟踪分析的初稿

[ValueConversion(typeof(Color),typeof(String))]     [序列化]     公共类SolidBrushToColorConverter:IValueConverter     {

protected MethodInfo EffectiveValueEntryValueGetMethod
{
    get
    {
        if (effectiveValueEntryValueGetMethod == null)
        {
            var effectiveValueEntryType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.Name == "EffectiveValueEntry").FirstOrDefault();
            if (effectiveValueEntryType == null)
                throw new InvalidOperationException();

            var effectiveValueEntryValuePropertyInfo = effectiveValueEntryType.GetProperty("Value", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance);
            if (effectiveValueEntryValuePropertyInfo == null)
                throw new InvalidOperationException();

            effectiveValueEntryValueGetMethod = effectiveValueEntryValuePropertyInfo.GetGetMethod(nonPublic: true);
            if (effectiveValueEntryValueGetMethod == null)
                throw new InvalidOperationException();

        }
        return effectiveValueEntryValueGetMethod;
    }
}

protected MethodInfo EffectiveValuesGetMethod
{
    get
    {
        if (effectiveValuesGetMethod == null)
        {
            var dependencyObjectType = typeof(DependencyObject);
            var effectiveValuesPropertyInfo = dependencyObjectType.GetProperty("EffectiveValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance);
            if (effectiveValuesPropertyInfo == null)
                throw new InvalidOperationException();

            effectiveValuesGetMethod = effectiveValuesPropertyInfo.GetGetMethod(nonPublic: true);
            if (effectiveValuesGetMethod == null)
                throw new InvalidOperationException();
        }
        return effectiveValuesGetMethod;
    }
}

#region Private fields
private MethodInfo effectiveValueEntryValueGetMethod;
private MethodInfo effectiveValuesGetMethod;
#endregion

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (!(value is Color)) return null;
    var result = value.ToString();
    if (result.Equals("#FF0000FF")) {
        StackTrace st = new StackTrace();
        foreach (StackFrame frame in st.GetFrames()) {
                if (frame.GetMethod().Name.Equals( "UpdateEffectiveValue" )) {
                    foreach (ParameterInfo info in frame.GetMethod().GetParameters()) {
                        Debug.WriteLine ("parameter name " + info.Name
                                         + ", type " + info.ParameterType.ToString());
                        if (info.Name.Equals("newEntry")) {
                            object newEntry = info.GetRealObject(new StreamingContext()); //SET BreakPoint HERE! (to be continued ...)
                        }
                    }
                }
        }
    }
    return result;
}

这是从VS调试器中找到的属性的视图 enter image description here

答案 2 :(得分:1)

WPF检查器具有跟踪样式的能力: https://wpfinspector.codeplex.com/,请参阅样式跟踪部分。那你在找什么?

答案 3 :(得分:1)

您可以使用DependencyPropertyHelper.GetValueSource方法。

DependencyPropertyHelper是一个静态类,下面是一个如何使用它的示例:

<强> MainWindow.xaml

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Foreground="Red" Loaded="OnWindowLoaded">
    <Window.Resources>
        <Style x:Key="TextBlockStyle" TargetType="TextBlock">
            <Setter Property="Foreground" Value="Blue"/>
        </Style>
    </Window.Resources>
    <StackPanel>     
        <TextBlock x:Name="textBlock1"/>
        <TextBlock x:Name="textBlock2" Style="{StaticResource TextBlockStyle}"/>
        <TextBlock x:Name="textBlock3" Foreground="Green"/>
    </StackPanel>
</Window>

<强> MainWindow.xaml.cs

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnWindowLoaded(object sender, RoutedEventArgs e)
        {
            SetValueSource(this.textBlock1);
            SetValueSource(this.textBlock2);
            SetValueSource(this.textBlock3);
        }

        private static void SetValueSource(TextBlock textBlock)
        {
            textBlock.Text = DependencyPropertyHelper.GetValueSource(textBlock, TextBlock.ForegroundProperty).BaseValueSource.ToString();
        }
    }
}

<强>输出

Output of WPF example application

答案 4 :(得分:0)

我认为您已在资源字典中为文本块编写了一种通用样式

尝试通过

覆盖样式
  <TextBlock.Resources>
                            <Style TargetType="{x:Type TextBlock}">

                            </Style>
                        </TextBlock.Resources>