如何提高RelativeSource FindAncestor的性能?

时间:2014-12-22 11:37:16

标签: wpf binding relativesource findancestor

FindAncestor是否在Window的整个Visual树中搜索元素?

如果是,那么我该如何改进呢?

如果我们通过查找具有Find Ancestor&amp ;;的元素来访问对象的属性,则抛出binding data error没有这样的元素?

如果是,那么我该如何解决这样的错误。

在我的情况下,绑定错误正在抛出输出窗口。为了解决这个错误,我尝试设置FallbackValue,但现在它给了我警告而不是错误,这是唯一的区别。其他一切都和错误一样。

有人可以告诉我FindAncestor究竟是如何运作的吗?

3 个答案:

答案 0 :(得分:4)

如果您想了解FindAncestor内部如何工作,您应该阅读内部代码。 http://referencesource.microsoft.com/#PresentationFramework/Framework/MS/Internal/Data/ObjectRef.cs,6a2d9d6630cad93d

您应该尝试不要使用 FindAncestor那么多。它可能很慢,而且孩子们不应该依赖于“父母哪里有我需要的东西”的知识。

也就是说,FindAncestor本身也可能是你的朋友。

这取决于您的情况,但是,例如,DataGridRow使用FindAncestor来查找有关DataGrid或其他父元素的信息的情况很常见。

问题在于:它超级慢。假设你有1000个DataGridRows,每行使用FindAncestor,每行有7列,它们本身必须遍历逻辑树中的~200个元素。它不必很慢,DataGridRow始终具有相同的父DataGrid,它可以轻松缓存。也许“一次性缓存的relativeSources”将是新的概念。

概念可以是这样的:像你一样编写自己的relativeSource绑定。第一次完成绑定后,使用可视化树助手查找特定类型的父级。如果这样做,您可以将找到的父项存储在直接父attachewd属性中,如下所示:

var dic = myElementThatUsesRelativeSourceBinding.Parent.
      GetCurrentValue(MyCachedRelativeSourceParentsProperty) 
          as Dictionary<Type, UIElement>;

dic[foundType] = actualValue;

稍后,您将在以后搜索相关源时使用此缓存信息。而不是采用O(n),对于父母的相同元素/子元素将采用O(1)。

如果您知道父级始终存在,则应在代码隐藏中为尝试使用FindAncestor的每个元素创建绑定。这样就可以避免遍历树。

您还可以创建一个混合解决方案,跟踪可视树的变化,并保持“缓存”。如果DataGridRow要求“找到我的相对来源类型DataGrid”,则没有理由需要一直这样做:您可以缓存它。有OnVisualChildrenChanged - 只是一个想法,甚至不能100%确定它是否可以很好地完成,但这需要额外的内存和字典。

这可能变得非常复杂,不用说:-),但对于“侧面项目”来说会很酷。

另一方面;你也应该展平视觉树,它会让你获得速度。

答案 1 :(得分:1)

RelativeSourceMode Enumeration使用RelativeSource.Mode PropertyFindAncestor值时,您还可以使用{{3>设置要查找的祖先级别 }}。从上一个链接页面:

  

使用 [值] 1表示最接近绑定目标元素的那个。

答案 2 :(得分:0)

关于“寻找祖先”的说法不多。它工作简单,这就是它快速的原因。它的工作原理如下:始终询问元素的父类型。如果类型与您需要的类型不匹配。父项成为实际元素,并且该过程再次重复。这就是为什么“Find Ancestor”总是在视觉树上工作但从不失败的原因:)

我认为您可能会感觉到RelativeSource绑定的某些性能问题的唯一可能原因是当您进入ListBox时,您确实有一个令人讨厌的项目模板,其中包含一堆RelativeSource绑定。 ListBox倾向于虚拟化东西,这意味着它会跟踪数据项,但会重新创建容器。总而言之,您开始滚动并且滚动得越快,那些可视容器将被重新创建。最后,每次重新创建容器时,相对源绑定将尝试寻找给定的祖先类型。这是我现在能想到的唯一一个你最终会落后几毫秒的情况。但那还不错..

您是否遇到过类似的问题?请告诉我们有关您的问题的更多信息

像Sheridan一样,我会让那些错误只是:)但是如果你恨他们那么多,你可以使用桥梁

您需要自己实施Bridge

请看一下这个链接:http://social.technet.microsoft.com/wiki/contents/articles/12355.wpfhowto-avoid-binding-error-when-removing-a-datagrid-row-with-relativesource-static-bridgerelay.aspx

基本上,您将该桥元素放在Xaml作为资源的某个位置,当您需要RelativeSource时,使用StaticResource扩展名,而不是这样:

Binding="{Binding MyPath, Source={StaticResource MyBridge}}"

试一试

相关问题