有没有办法确定 WPF 绑定的声明/创建位置? [英] Is there a way to determine where a WPF Binding is declared/created?
问题描述
我有一个项目抛出了一些数据绑定错误.一个例子是:
System.Windows.Data 错误:4:无法找到引用RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''的绑定源.BindingExpression:Path=HorizontalContentAlignment;数据项=空;目标元素是 'MenuItem' (Name='');目标属性是'HorizontalContentAlignment'(类型'HorizontalAlignment')
我的问题是是否有办法确定此绑定的实际声明位置(在 XAML 中或在代码中声明).
到目前为止我尝试过的:
- 为 System.Windows.Data 命名空间添加了调试跟踪,级别设置为 o All;这并没有产生更多有用的信息
- 尝试在项目中对 Binding 一词进行文本搜索,希望找到所有将 Path 设置为
HorizontalContentAlignment
的绑定表达式;我只找到了一个并删除了它,但我仍然收到消息,这似乎表明那不是错误的..
你知道有什么其他技巧可以让 WPF 吐出一些更有用的信息,关于这个绑定的确切声明位置吗?
更新
经过多一点搜索,我很确定这是由应用于 MenuItem
的样式引起的.但是,我仍然无法确定声明错误绑定的位置..
更新 2
我发现了问题.然而,问题仍然存在,因为发现问题主要是基于错误消息中的有限信息在黑暗中进行搜索.
事实证明,绑定是在样式中声明的.而且样式不在我的应用程序中.这可能是 MenuItem
的默认样式.所以现在为了解决这个问题,我只是在所有 MenuItems
上手动设置了 HorizontalContentAlignment
.错误的原因与操作顺序有关,因为此 MenuItem
是在代码中生成的.我将单独发布一个新问题.
所以,就目前而言,这个故事的寓意是我觉得需要一个更好的机制来确定在哪里声明了错误的绑定.我想看到类似绑定的堆栈跟踪..
如果有人知道任何其他工具或方法来确定在代码或标记中声明绑定的位置,我会将这个问题保留一段时间.
我发布了另一个关于应用于<代码的样式/绑定的问题>MenuItems 在这里.
在调试 WPF 绑定错误时,我发现用分号将错误分开,并从末尾开始
System.Windows.Data 错误:4 :
无法找到引用RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''的绑定源.BindingExpression:Path=HorizontalContentAlignment;
DataItem=null;
目标元素是 'MenuItem' (Name='');
目标属性是'HorizontalContentAlignment'(类型'HorizontalAlignment')
所以从头开始:
#5 告诉您哪个属性包含失败的绑定.在你的情况下,它是
HorizontalContentAlignment
#4 是包含失败属性的元素,它是一个
MenuItem
没有Name
属性来标识它所以某处你有一个
<MenuItem HorizontalContentAlignment="{Binding ...}"/>
导致绑定错误.#3 是
DataItem
或DataContext
,位于目标元素后面.它对您来说似乎是null
,但这不是问题,因为看起来您的绑定没有引用 DataContext.但这确实表明
MenuItem
不是常规VisualTree
的一部分,因为通常DataContext
是从父对象继承的.#2 包含实际的绑定错误和有关绑定的信息.它实际上可以进一步分解为多个部分.
找不到绑定源
参考RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''.
BindingExpression:Path=HorizontalContentAlignment;
无法找到源"意味着绑定找不到要绑定到的源对象,在您的情况下,该源对象应该是
{RelativeSource AncestorType={x:Type ItemsControl}
(FindAncestor
和AncestorLevel=1
是RelativeSource
的默认值,所以我忽略它们)#2 的最后一部分显示了您尝试绑定到的
Path
:HorizontalContentAlignment
所以把它们放在一起,在你的代码中的某个地方有一个 它试图将它的
HorizontalContentAlignment
绑定到一个 ItemsControl.HorizontalContentAlignment
,但绑定找不到ItemsControl
.
您正在使用 RelativeSource FindAncestor
绑定来查找 ItemsControl
,它搜索可视化树以找到最近的 ItemsControl
,并且它没有找到,因此在 VisualTree 层次结构中 MenuItem
的上层一定没有 ItemsControl
.
我经常在 ContextMenus
中看到这个问题,因为它们与您的 XAML 代码的其余部分不是同一 VisualTree
的一部分.(要引用 ContextMenu
附加到的主 VisualTree
中的对象,您可以使用 PlacementTarget
属性,例如 这个例子.
一旦您了解绑定错误,通常很容易在您的 XAML 中找到它的来源.
根据我的应用程序大小,我通常会执行以下操作之一:
在应用程序中搜索绑定错误中的目标元素"(在您的情况下为
MenuItem
),并查看其中是否有任何设置了目标属性"(HorizontalContentAlignment
) 与绑定从绑定错误 (
HorizontalContentAlignment
) 中搜索应用程序的目标属性"以找到导致此问题的绑定从绑定错误中显示的绑定文本中搜索应用程序中相当独特的内容.在您的情况下,您可以尝试在
{x:Type ItemsControl}
上搜索,这将是您的RelativeSource
绑定的一部分,并且此类搜索结果不应该太多一个短语.使用第三方工具,例如 Snoop 或 WPF Inspector 在运行时追踪绑定错误.>
我以前只使用过 Snoop,但要使用它,您需要启动您的应用程序并对它运行 Snoop 以在应用程序运行时检查您的应用程序的 VisualTree.然后,您可以通过在搜索栏中键入MenuItem"之类的内容来搜索 VisualTree,以过滤所有
MenuItems
的 Visual Tree,然后查看它们的属性以找出哪个具有绑定错误(HorizontalContentAlignment
属性会因为绑定错误以红色高亮显示).需要注意的是,如果您的
MenuItem
在ContextMenu
内,那么您需要打开该ContextMenu
以使 MenuItems 成为绘制并显示在 Snoop 中.
I have a project which is throwing some data binding errors. One example is:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
My question is whether there is a way to determine where this binding is actually declared (either it is declared in XAML or in code).
What I've tried so far:
- added a debug trace for the System.Windows.Data namespace with level set o All; this did not produce any more useful information
- tried doing a text-search in the project for the word Binding in hopes of locating all binding expressions that have the Path set to
HorizontalContentAlignment
; I found only one and removed it but I'm still getting the message which seems to indicate that that was not the faulty one..
Do you know of any other tricks to make WPF spit out some more useful information about where exactly is this binding declared?
UPDATE
After a little bit more searching I'm pretty sure this is somehow caused by a style being applied to a MenuItem
. However, I'm still not able to pin-point the location where the faulty binding is being declared..
UPDATE 2
I found the problem. However the question still remains since finding the issue was mostly a matter of searching in the dark based on the limited info in the error message.
As it turns out, the binding is declared in a style. And the style is not in my application. It's probably the default style for MenuItem
. So to fix the issue for now I've just manually set the HorizontalContentAlignment
on all MenuItems
. The reason for the error is somehow related to order of operations as this MenuItem
is generated in code. I'll post a new question on that separately.
So, for now, the moral of the story is that I feel that there needs to be a better mechanism to determine where the faulty binding is declared. I'd like to see something like a stack trace for bindings..
I'm keeping the question open for a little while longer in case somebody knows of any other tools or methods of determining the place where a binding is declared in code or markup.
I've posted another question regarding the style/binding being applied to the MenuItems
here.
When debugging WPF binding errors, I find it easiest to break up the error by the semicolons, and start from the end
System.Windows.Data Error: 4 :
Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment;
DataItem=null;
target element is 'MenuItem' (Name='');
target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
So starting from the end:
#5 tells you what property contains the binding that is failing. In your case, it's
HorizontalContentAlignment
#4 is the element containing the failing property, which is a
MenuItem
without aName
property to identify it bySo somewhere you have a
<MenuItem HorizontalContentAlignment="{Binding ...}" />
that is causing the binding error.#3 is the
DataItem
, orDataContext
, that is behind the target element. It appears to benull
for you, but that's not a problem since it looks like your binding isn't referencing the DataContext.But this does suggest that the
MenuItem
is not part of your regularVisualTree
, since typically theDataContext
is inherited from the parent object.#2 contains the actual binding error and information about the binding. It can actually be further broken up into multiple parts.
Cannot find source for binding
with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''.
BindingExpression:Path=HorizontalContentAlignment;
"Cannot find source" means the binding can't find the source object to bind to, and in your case, that source object should be
{RelativeSource AncestorType={x:Type ItemsControl}
(FindAncestor
andAncestorLevel=1
are defaults for aRelativeSource
, so I'm ignoring those)And the last part of #2 shows the
Path
you are trying to bind to:HorizontalContentAlignment
So to put it all together, somewhere in your code there is a <MenuItem>
which is trying to bind its HorizontalContentAlignment
to an ItemsControl.HorizontalContentAlignment
, but the binding can't find the ItemsControl
.
You're using a RelativeSource FindAncestor
binding to find the ItemsControl
, which searches up the visual tree to find the closest ItemsControl
, and it's not finding one so there must be no ItemsControl
higher up in the VisualTree hierarchy from the MenuItem
.
I often see this problem with ContextMenus
because they are not part of the same VisualTree
as the rest of your XAML code. (To reference the object in the main VisualTree
that a ContextMenu
is attached to, you can use the PlacementTarget
property, like this example.
Once you understand the binding error, its often easy to find the source of it in your XAML.
Depending on my application size, I usually do one of the following:
Search the application for the "target element" from the binding error (in your case,
MenuItem
), and see if any of them are setting the "target property" (HorizontalContentAlignment
) with a bindingSearch the application for the "target property" from the binding error (
HorizontalContentAlignment
) to find the binding causing this problemSearch the application for something fairly unique from the binding text shown in the binding error. In your case, you could try searching on
{x:Type ItemsControl}
which would be part of yourRelativeSource
binding, and there shouldn't be too many search results for such a phrase.Use a 3rd party tool like Snoop or WPF Inspector to track down the binding error at run time.
I've only used Snoop before, but to use it you need to startup your application and run Snoop against it to inspect your application's VisualTree while it's running. You can then search the VisualTree by typing something like "MenuItem" in the search bar to filter the Visual Tree for all
MenuItems
, then look through their properties to find out which one has a binding error (theHorizontalContentAlignment
property will be highlighted in red because of the binding error).It should be noted that if if your
MenuItem
is inside aContextMenu
, then you need to open thatContextMenu
for the MenuItems to be drawn and show up in Snoop.
这篇关于有没有办法确定 WPF 绑定的声明/创建位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!