ElementName vs. RelativeResource?
Asked Answered
B

3

20

What of the following TextBlocks' Bindings costs more performance:

<Window  
  x:Name="Me"
  x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:src="clr-namespace:WpfApplication1" 
  Title="MainWindow">
  <StackPanel>
    <TextBlock Text="{Binding Title, ElementName=Me}"/>
    <TextBlock Text="{Binding Title, RelativeSource={RelativeSource AncestorType={x:Type src:MainWindow}}}"/>
  </StackPanel>    
</Window>

I am sure my question might take different when the TextBlocks are in a high nesting level having many siblings and ancestors.

Considerations

(based on personal thoughts only, I might be wrong in each particular one!):

  • ElementName:

    • Might search and compare current element to more control, thru all its children, siblings, uncles and great uncles including ancestors (maybe there is a HashTable of all the registered names?)
    • Getting a Name property of a control should cost less performance than calling GetType.
    • Comparing a string is cheaper than comparing types, especially when you know that most of the controls don't even have their Name set.
  • FindAncestor:

    • Will only iterate thru ancestors, not siblingls 'uncles', 'cousins' etc.
    • Most likely uses GetType to determine ancestor type; GetType costs more performance then a simple Name property getter (maybe DPs are different?)
Bradley answered 30/11, 2010 at 18:24 Comment(1)
Off-performance-topic note (but still ElementName vs RelativeSource): keep in mind that ElementName is 'early-binding', while RelativeSource is 'late-binding', which may lead to unexpected results as shown in this question of mine: #18419541Buttocks
D
31

It's usually a terrible idea to try to answer this sort of thing by arguing about which you think will be faster. Far better to construct an experiment to measure it.

I modified your setup a little - I put the relevant Xaml into a UserControl, and bound to the Name property since UserControl doesn't have a Title property. I then wrote some code to create a new instance of the control and add it to the UI, and used the Stopwatch to measure the time taken to construct and load it. (I start the timing just before constructing the user control, and I stop just after the user control raises its Loaded event.)

I run this code from a DispatcherTimer 20 times a second so I can take lots of measurements in the hope of reducing experimental error. To minimize distortions due to debugging and diagnostic code, I'm running in a Release build, and I only calculate and print the average after 2000 iterations have completed.

After 2000 iterations, the ElementName approach averages 887us.

After 2000 iterations, the RelativeSource approach averages 959us.

So ElementName is, in this particular experiment, slightly quicker than RelativeSource. Loading a trivial UserControl with just a Grid and one TextBlock where there's only one named element, the ElementName approach looks to take 92% of the time to load that the RelativeSource approach takes.

Of course, I'm measuring a small, artificial example here. The performance of the ElementName approach might vary based on how many named elements are in scope. And there may be other unanticipated factors that might produce completely different results in real scenarios. So I would recommend performing similar measurements in the context of a real application if you want to get a better picture.

I repeated the experiment with 10 TextBlocks instead of 1. ElementName then averaged 2020us while the RelativeSource approach averaged 2073us, again over 2000 iterations for both tests. Weirdly, there's a smaller difference here, not just in relative terms, but in absolute terms - the one-element examples showed a difference of 72us, where the ten-element examples showed a difference of 53us.

I'm starting to suspect that I'm causing more variability by running my tests on my main machine, rather than one carefully configured with as little stuff as possible to minimize noise.

One more variation: still with 10 bound text blocks, I added ten more empty, unbound, named text blocks to the user control. The idea here was to introduce more named things - ElementName now has to locate a named item within 11 named things. The average for ElementName is now 2775us. The RelativeSource approach with these extra 10 named elements came out at 3041us.

Again, I suspect variability on my desktop machine here - it seems weird that RelativeSource has performed significantly worse here than in the scenario that should have been more to ElementName's advantage.

Anyway, what does seem reasonably clear is that the cost of loading here is far more sensitive to the number of elements than it is to which style of binding you use. There is apparently a small advantage to ElementName but small enough (and with weird enough results) to cast suspicion on the validity of concluding that it is necessarily faster.

So we could construct more careful experiments to get a better picture. But in my view, if you can't conclusively demonstrate a significant difference in performance when running on an ordinary computer, then it's basically a waste of time arguing about which is quicker.

So in conclusion: performance is the wrong thing to focus on here. Pick whichever makes for more readable code.

Dnepropetrovsk answered 1/12, 2010 at 12:57 Comment(3)
great post. :) I was pretty curious about the performance difference. But, it makes sense to focus on which one makes for more readable xaml instead. :)Grillo
Thanks Ian for your effort on this great answer!Bradley
The performance advantage of Binding.ElementName becomes more apparent the deeper the visual tree is. While the XAML engine has to traverse the tree recursively in order to resolve the Bindig.RelativeSource value, it can use a fast lookup table to retrieve the element that was registered within the current namescope in order to resolve the Binding.ElementName value.Vc
B
4

The later of the two needs to walk the visual tree looking for a particular ancestor type, where as the prior looks directly to the window's namescope for a registered object with that name...my guess would be the later is marginally slower...that said, I don't think there will be a significant performance difference.

Hope it helps,

Aj

Borak answered 30/11, 2010 at 19:48 Comment(2)
Makes sense, but the question is how it looks for a name, cuz if the name has too many siblings, uncles and great uncles, then for each level it will search thru all of them whereas with FindAncestor it won't look for anything but ancestors. In the other hand I think the action itself of getting a type (GetType) of each ancestor might take longer than getting just a name of a sibling, not to mention that many controls are unnamed which the string comparison is even faster. I would appreciate if you could rely your answer on a genuine source such as searching with reflector, msdn etc.Bradley
@Shimmy, I think proper testing would prove reasonably difficult, at least to produce accurate and consistent results anyway. In all honesty, I think it's above my head and thus I didn't want to post something as being more than an educated guess. While on the topic though, I read an article by Josh Smith where he discusses the logical and visual trees; not regarding performance differences but he did include a sample app that walks the visual and logical trees. Perhaps you could use it to gain some insight into your inquiry - codeproject.com/KB/WPF/WpfElementTrees.aspxBorak
L
1

In general the ElementName should be used when possible.

The given example and benchmark example is quite simple. In real world examples the elements have a larger visual tree and the FindAncestor binding must traverse a lot more elements in order to find the element.

I gained SECONDS by changing some FindAncestor bindings to ElementName bindings in a real world application.

IMHO the ElementName binding is more readable, too.

Late answered 23/9, 2020 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.