我的程序中有一堆文本块对象,所有这些对象的前景色都是白色的。我想找出导致这种情况的原因。
在Visual Studio中是否有任何方法可以追溯属性值的来源,无论是通过模板还是包含对象?
编辑: 在发出赏金之后,这个问题得到了很多有趣的回应,提出了各种各样的方法。我认为那里有一些东西,但到目前为止,我还没有弄清楚如何更有效地应用这些,而不仅仅是通过代码进行试错。如果有人想要收取费用并将其中一个回复推进为有用的话,我正在观看。
答案 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
)可以追溯到:
BamlSchemaContext
,xamlReader
中WpfXamlLoader.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调试器中找到的属性的视图
答案 2 :(得分:1)
WPF检查器具有跟踪样式的能力: https://wpfinspector.codeplex.com/,请参阅样式跟踪部分。那你在找什么?
答案 3 :(得分:1)
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();
}
}
}
<强>输出强>
答案 4 :(得分:0)
我认为您已在资源字典中为文本块编写了一种通用样式
尝试通过
覆盖样式 <TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}">
</Style>
</TextBlock.Resources>