How to debug a DependencyProperty

Debugging the InitializeComponent can be tricky.
Let’s assume a basic xaml as a proof of concept.


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

Intercepting the event of the Foreground Property of the TextBlock is difficult because we can’t subclass TextBlock (otherwise the style will be lost) and we can’t override a Freezable. Should I have no other solutions, I would set a breakpoint on the following converter


[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;
        }

linking it to the TextBlock with a static resource


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

After starting the Debug, one can see the initial default Foreground assigned to "#FF000000" and the starting condition is in the stack

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  

Then there is a second hit on the breakpoint and the source of the actual Foreground color can be finally found in the stack

>   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

Please note, if you are interested, in a further effort the PropertyIndex shown above (linked to the Foreground DependencyProperty) could be traced back to the: BamlSchemaContext, a Non-Public Member of the xamlReader in the WpfXamlLoader.LoadBaml, that contains the System.Xaml.IXamlLineInfo.LineNumber

Here is a starting draft of how to automate the stack trace analysis

[ValueConversion(typeof(Color), typeof(String))] [Serializable] public class 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;
}

here is the view of the Property found from the VS Debugger

debug

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s