How to determine where the property value of a XAML comes from?

I have a bunch of textblock objects in my program, and the foreground color of all of them is white. I want to find out why this is happening.

Are there any tools in Visual Studio to track the origin of a property value, whether it be a template or an object?

EDIT: After giving out generosity, this question got a ton of interesting answers with a lot of suggested approaches. I think there is something there, but so far I have not been able to figure out how to apply any of these methods more efficiently than just trial and error searching through code. If someone wants to take responsibility and make one of the answers useful, I look.

+6
source share
5 answers

I tend to use Snoop to call around the visual tree when I need to understand where the associated values ​​came from. You can use it to look at your management properties and follow either the bindings to DataContexts, or walk up and down the tree to see where the value can be inherited.

Launch Snoop, drag the crosshairs through the WPF application (or refresh the process list and select from the drop-down list), then place the mouse pointer over the TextBlock control and Ctrl-Shift-LeftClick. Snoop will enlarge the visual tree on your control and allow you to view or edit your control properties.

+2
source

There is already an answer showing a guided tool.

Here I expand my comment on debugging InitializeComponent. Suppose basic xaml is 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 Foreground The TextBlock property is difficult because we cannot subclass TextBlock (otherwise the style will be lost) and we cannot override Freezable . If I have no other solutions, I would set a breakpoint on the next 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; } 

associating it with a 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 Debug, you can see the initial default Foreground assigned to "#FF000000" , and the initial condition is on 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 at the breakpoint, and the source of the actual foreground color can finally be found on 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 

Note that if you are interested, the further PropertyIndex effort shown above (related to Foreground DependencyProperty ) can be traced back to: BamlSchemaContext , the non-public xamlReader member in WpfXamlLoader.LoadBaml , which contains System.Xaml.IXamlLineInfo.LineNumber

Edit

Here's a starter project on how to automate 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 a view of a property found from VS Debugger enter image description here

+2
source

The WPF inspector has the ability to track styles: https://wpfinspector.codeplex.com/ , see the section "Tracing styles". Is this what you are looking for?

+1
source

I think you wrote a general style for a text block in a resource dictionary

Just try redefining the style

  <TextBlock.Resources> <Style TargetType="{x:Type TextBlock}"> </Style> </TextBlock.Resources> 
0
source

You can use the DependencyPropertyHelper.GetValueSource method.

DependencyPropertyHelper is a static class, and here is an example of how you can use it:

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

Output of WPF example application

0
source

Source: https://habr.com/ru/post/983554/


All Articles