Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I Improve performance of RelativeSource FindAncestor?

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??

like image 584
Amol Bavannavar Avatar asked Dec 22 '14 11:12

Amol Bavannavar


1 Answers

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.

like image 53
Erti-Chris Eelmaa Avatar answered Oct 07 '22 05:10

Erti-Chris Eelmaa