6

Is FindAncestor searches an element in whole Visual tree of Window?

If yes, then how can I improve on it ??

Is binding data error thrown if we access property of object by finding an element with Find Ancestor & no such a element exist?

If yes, then how can I resolve such a error.

In my case binding error is throwing on output window. To solve this error, I tried with setting FallbackValue,but now it gives me warning instead of error that is the only difference. Everything else is same as error.

Can someone tell me How exactly FindAncestor works??

Amol Bavannavar
  • 2,062
  • 2
  • 15
  • 36
  • It search for the first (or second, third... if you set the AncestorLevel) element that matches the type in the current element parents. So it won't search the whole VisualTree, it will search parents until it finds what you want – nkoniishvt Dec 22 '14 at 11:47
  • But if no such a element found then??... I know little about 'Ancestor Level'.. but it is useful only if their are more than one element of same type exists in its parent hierarchy?? – Amol Bavannavar Dec 22 '14 at 11:49
  • @AmolNavannavar Indeed, the default value is 1. It no elements are found you'll probably have a binding error (it won't crash the app) – nkoniishvt Dec 22 '14 at 11:51
  • @nkoniishvt but it slow's down your application's performance.. – Amol Bavannavar Dec 22 '14 at 12:05
  • 1
    A binding error shouldn't reach the production. If you're searching for an ancestor of a specific type you're sure you have one. So it won't slow down your app – nkoniishvt Dec 22 '14 at 12:08

3 Answers3

4

If you want to know how FindAncestor works internally, you should read the internal code. http://referencesource.microsoft.com/#PresentationFramework/Framework/MS/Internal/Data/ObjectRef.cs,6a2d9d6630cad93d

You should try and not use FindAncestor that much. It can be slow, plus the children shouldn't rely on the knownledge of "there exist somewhere a parent who has what I need".

That said, FindAncestor itself can be also your friend at times.

It depends on your case, but for example, it's common to have a DataGridRow which uses FindAncestor in order to find information about DataGrid or some other parent element.

The problem with that is: IT'S SUPER SLOW. Say you have 1000 DataGridRows, and each row uses FindAncestor, plus each row has 7 columns, which itself has to traverse through ~200 elements in logical tree. It does not have to be slow, DataGridRow has always the same parent DataGrid, it can be easily cached. Perhaps "One-time cached relativeSources" would be the new concept.

The concept can be like this: write your own relativeSource binding as you've done. Once the binding is done first time, use visual tree helper to find a parent of specific type. If that is done, you can store the found parent IN the direct parent attachewd property, as so:

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

dic[foundType] = actualValue;

Later, you use this cache information in the search of relative source later. Instead of taking O(n), it will take O(1) for the same element / children of parent.

If you know that the parent always exists, you should create the binding in code-behind, for each element that tries to use FindAncestor. This way you avoid traversing the tree.

You could also create a hybrid solution which tracks the changes of visual tree, and mainains "cache". If a DataGridRow asks for "find me relative source off type DataGrid", there is no reason that you need to do it all the time: you could cache it. There's OnVisualChildrenChanged - just an idea, not even 100% sure if it can be done nicely, but this will require extra memory, and dictionary.

This can get very complex, needless to say :-), but would be cool for "side project".

On another side; you should also flatten visual tree, it would gain you a speed.

Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78
1

When using the FindAncestor value of the RelativeSourceMode Enumeration for the RelativeSource.Mode Property, you can also set the level of ancestor to look for using the RelativeSource.AncestorLevel Property. From the last linked page:

Use [a value of] 1 to indicate the one nearest to the binding target element.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Yes, I know that but what if their is no such a element exists in its parent hierarchy???.. @Sheridan I want to improve that??.. I wan't internal working of FindAncestor... – Amol Bavannavar Dec 22 '14 at 12:03
  • If no such element exists, then you will receive a `Binding` error... why would you be using `RelativeSource` when that source element might not exist in the first place? *That* is your problem, not the `FindAncestor` functionality. Either way, the entire visual tree can be walked in a fraction of a second, so I really can't understand your desperation in trying to reduce this time. Using the [`VisualTreeHelper` Class](http://msdn.microsoft.com/en-us/library/system.windows.media.visualtreehelper(v=vs.110).aspx), you can easily write your own code to see how quickly the tree can be walked. – Sheridan Dec 22 '14 at 12:14
  • Yes I used a `Converter` for the same ... Check `this link` of my thread http://stackoverflow.com/q/27182878/4112271 .. But the problem is I wan't use `converter` if `FindAncestor` doing the same thing.. – Amol Bavannavar Dec 22 '14 at 12:18
  • Why don't you tell us what your *actual* problem is? – Sheridan Dec 22 '14 at 12:21
  • I just wanted to `Improve Performance` of traversing element in it's `Parent Heirarchy` but with `FindAncestor` only... – Amol Bavannavar Dec 22 '14 at 12:23
  • Yes, but why do you feel that that is necessary? – Sheridan Dec 22 '14 at 12:34
  • I have a huge hierarchy in our application and also have a complex template of every control. – Amol Bavannavar Dec 22 '14 at 12:36
  • Yes, but what makes you think that `FindAncestor` is causing a problem? Perhaps you can look at some other ways to improve performance: [Optimizing WPF Application Performance](http://msdn.microsoft.com/en-us/library/aa970683(v=vs.110).aspx) – Sheridan Dec 22 '14 at 12:40
  • I know this link and also tested our application with `WPF Performance Suite`. But some control's are giving me high `CPU utilization` on these task's & it is also affecting in `Rendering`... – Amol Bavannavar Dec 22 '14 at 12:44
  • No problem. At least I can confirm that I have never found any performance problems from using the `RelativeSource.FindAncestor` property. – Sheridan Dec 22 '14 at 12:46
  • I have found performance problems of RelativeSource.FindAncestor with my naked eyes. it was 1-2 seconds faster when I removed RelativeSource binding for one of my collection datatemplates (and used attachedproperty instead) – deathrace Dec 28 '16 at 12:32
1

There is not much to tell about "Find Ancestor". It works simple which is why its fast. It works like this: The type of the parent of an element is always being asked. If the type does not match with one you need. The parent becomes actual element and the process is being repeated again. Which is why "Find Ancestor" always works the visual tree up but never down :)

The only possible reason where I think you might feel some performance issues with RelativeSource bindings is when you in ListBox and you have really a nasty item template defined with a bunch of RelativeSource bindings inside. ListBox tends to virtualize stuff that means it keeps track of data items but recreates their containers. To sum up, you start scrolling and the faster you scroll the more often are those visual containers gonna be recreated. In the end everytime a container gets recreated the relative source binding will try seek for given ancestor type. That is the only case that I can think of right now where you will end up lagging few milliseconds. But that is not bad..

Are you experiencing some kind of issue like this? Tell us more about your issue please

Like Sheridan I would let those erros just be :) however if you hate them that much, you could work with bridges

A Bridge is something you will need to implement yourself.

Take a look at this link: http://social.technet.microsoft.com/wiki/contents/articles/12355.wpfhowto-avoid-binding-error-when-removing-a-datagrid-row-with-relativesource-static-bridgerelay.aspx

Basically you put that bridge element somewhere in your Xaml as a resource and when you need RelativeSource you use StaticResource extension instead like this:

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

Try it out

dev hedgehog
  • 8,698
  • 3
  • 28
  • 55
  • I already know this idea for resolving these issues.. but I need `detailed answer` for both question's. Because I don't know how exactly `Find Ancestor` works? – Amol Bavannavar Dec 29 '14 at 11:02
  • There is not much to tell about "Find Ancestor". It works simple which is why its fast. It works like this: The type of the parent of an element is always being asked. If the type does not match with one you need. The parent becomes actual element and the process is being repeated again. Which is why "Find Ancestor" always works the visual tree up but never down :) To use "Find Ancestor" a functional tree is needed. – dev hedgehog Dec 29 '14 at 11:10
  • Hey @dev hedgehog yes I implemented same using `Converter` in which I traversed element until parent != null Or type gets found starting from `Relative Source Self`...http://stackoverflow.com/questions/27182878/how-to-create-converter-to-work-like-findancestor-for-checking-istypefound?lq=1 – Amol Bavannavar Dec 29 '14 at 11:25
  • 1
    But what is the point of writing your own converter that does what RelativeSource already do? When the searching processes cannot find a matching type your dependency property will have the value known as DependencyProperty.UnsetValue... Its a dummy indicator for Binding class that no value is avaiable. The error you see does not appear at runtime. It is just a trace error only being visible to you in debugger/output window. You do not have to worry – dev hedgehog Dec 29 '14 at 11:47
  • I heard you should avoid FindAncestor because it is slow. Knowing that this won't cause Performance issues in most cases helps. – CBFT Nov 04 '21 at 14:10