How can I Improve performance of RelativeSource FindAncestor? - wpf

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

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.

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.

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

Related

How do I determine AncestorLevel for RelativeSource in WPF?

In a WPF application using a fairly standard MVVM pattern, I need to bind from a row in a DataTable (i.e. deep inside the visual tree) to the data context of the whole window. I am assuming the only way to do this is to use Mode=RelativeSource, but it requires that I specify the AncestorLevel.
How do I determine the AncestorLevel?
Why should I need to specify this at all when I know there is only ever one window? In other words, why can't I simply specify the type I want to bind to and have the binding engine reverse up the tree until it finds the first object of the required type?
If I do figure out the AncestorLevel, doesn't this make the code brittle? (If I change the nesting of the visual elements, it will obviously break.)
If there is no good solution involving RelativeSource, I thought I could take an alternative approach and 'propagate' the page-level property down through the logical tree to the individual items in the list. Is there an accepted pattern for this?
Has RelativeSource ever had a way of traversing up the tree searching only by type (or am I imagining this)?
See my question, similar issue was raised there:
WPF finding ancestor for binding
In short - when you give the ancestor type, it is searched upwards the elements tree until the ancestor of that given type is found (Level 1). Should you need to search two levels up (e.g. you have a grid in a grid and you're aiming for the "outer" grid), you specify AncestorLevel=2 and the ancestor is then a second element of that particular type while traversing the elements tree.
How do I determine the AncestorLevel?
By looking at the visual tree and figuring out the number of occurances of a specific type of parent element. There can only be only one top-level parent window though.
Why should I need to specify this at all when I know there is only ever one window?
You don't have to. It's perfectly fine and also very common to only specify the AncestorType like this:
{Binding SomePropertyOfTheWindow, RelativeSource={RelativeSource AncestorType=Window}}" />
If I do figure out the AncestorLevel, doesn't this make the code brittle? (If I change the nesting of the visual elements, it will obviously break.)
Yes, this is correct, but this is how you tell the binding engine to which particular item of a specific type that you want to bind to - assuming there are several to choose from.
Although this is not binding to a Relative source, but another way of binding without the complexities of Relative and type.
Another option instead of binding to a specific type of control, you can name your controls in the xaml with x:Name. Then you can bind directly to that control and property on it. Ex:
<SomeControl x:Name="IWantThisControl" />
[bunch of other controls]
<YourOtherControl ThisControlProperty={Binding ElementName=IWantThisControl, Path=PropertyOnTheOtherControl}" />
It does not matter the nesting level of the controls when you use the binding to ElementName reference.

Is it possible to walk a wpf TreeView via TreeViewItems instead of its bound content?

I'm having real problems with TreeViews BringIntoView implementation when trying to select things in the tree that are bound via ItemSource and ItemTemplate. It just doesn't work, I've tried pretty much all the fancy ways i could find via google and most don't work and if they do the wrong thing is always selected.
It would be be easier for me if i could just walk the tree one level at a time via treeview items instead of the the things i'd bound in.
Is there a way of "mapping" my bound hierarchical data template items back as treeviewitems?
NOTE: it would appear part of the problem with bring into view is that the items im loading into the tree is done in a "lazy" manner where the children are created until they shown - i have to do it this way as the dataset is huge.
To summarize: I can quickly search through my dataset and ascertain a "nodes" full hierarchical path quite quickly i.e. i can get "root\p1\p2\p3\data" easily enough, but I cant get a treeview thats bound to the dataset to display it using bringintoview. If I could walk the tree using treevitems i could find the node myself and call bringintoview on that. thats the plan anyway.
mucho tacks.
EDIT:
After some more searching I have found this http://blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx. Which works buts also has problems - I can't initially expand the root object, and I can't work out how to seperate the ItemsPanel and ItemContainerStyle into toplevel styles/resources so i can share them about my other treeviews easily.

Is it wrong to access TreeViewItems in WPF’s TreeView?

I’ve been having issues with the TreeView in WPF. This control makes it very hard to access the TreeViewItems it’s showing.
On several occasions I have worked around the need to access a TreeViewItem, for example I’ve accepted the fact that I’m not supposed to access a node’s parent via TreeView (and am supposed to instead keep track of the parent myself). I’ve been doing this for two reasons: first, it’s obviously extremely hard to get at the TreeViewItems, and secondly, I’ve been told that it’s hard because I’m not supposed to need them if I do things right.
However, this time I really see no way around this.
Basically, all I want is, given one of my viewmodel instances, scroll the tree view to it. This is trivial if I could just get the corresponding TreeViewItem.
Am I doing things wrong again by trying to get at the TreeViewItem, or would that be the right approach?
Take a look at Simplifying the WPF TreeView by Using the ViewModel Pattern article by Josh Smith. I hope it helps.
Admittedly this is not straightforward but you can probably still do this while keeping a separation which does not require you to access the TreeViewItems knowingly. The essence in WPF is binding as already noted by Kent Boogaart in your other question, here however you need to somehow deal with events. Your view-model needs to fire a BringIntoView event of its own while the view needs to react.
The easiest method might be to add a EventSetter on Loaded to make the TreeViewItems subscribe to said event on their DataContext which should be your view-model (if it isn't you can wait for DataContextChanged).
No, I dont see in what way accessing the items of a treeview is wrong.
I think the difficulties you are encountering are because you aren't seeing the treeview as it should be.
A leaf has a parent, but no children.
A node can have a parent, and can have children.
A node without a parent is a root.
Based on these principles (SourceMaking Composite pattern) you should be able to do whatever you want using recursivity. (in both XAML and code)
I’ve come to the conclusion that it can’t be altogether wrong. The first piece of evidence comes from Bea Stollnitz’s post about ListView: if one of the WPF developers explains how this might be done, it can’t be that wrong.
The other piece of evidence comes from this highly-voted question/answer: MVVM madness. MVVM undoubtedly has its benefits, but sometimes the cost of following MVVM is so high that it’s just silly following through with it, especially in a small one-man application. Do you really want to expose IsSelected and IsExpanded the way you’re supposed to?
As a result, I felt justified to try and figure out how to expose the TreeViewItem corresponding to an item with less effort from the developer, under the assumption that they will never need the more advanced features that resulted in TreeViewItems being this hard to access (like displaying the same ViewModels in multiple different controls... how often have you needed that!...)
I posted the result of this effort as an answer on another question.

WP7 ListBoxItem ContentPresenter Measure Time

Hopefully someone can help me or at least enlighten me a bit. After the release of the Mango Beta tools I have played a lot with the new WP7 Profiler and one thing does bothers me.
Every single listbox with just a bit more complicated items is taking forever to draw. For an example - a listbox that has item consisting of Border->Grid->6xTextBoxes (each showing single symbol) takes 303ms per listbox item. Of them 288ms are spent for the ListBoxItem's ContentPresenter Measure Time (excl.).
I know that Measure is kind of costly operation but still this is beyond madness. Any thoughts would be appreciated.
I can only confirm that MeasureOverride uses to be the most time-consuming operation. (And you don't need a profiler to find it out.) I can only guess why does it take so long.
First, MeasureOverride travels recursively through the visual tree of the ListBoxItem. Somewhere at the end of this call chain there's probably a call into native code that returns actual size.
Hence keeping simple item structure is of utmost importance.
Your structure seems to be overcomplicated. If you look at TextBox template, you will see that its structure is Grid > Border > ContentPresenter. I can't judge what is behind the ContentPresenter since I don't know your code, I just wonder why don't you use TextBlock (where possible) - it is so much more efficient.
As an experiment I would try to implement MyListBoxItem.MeasureOverride and return fixed size without calling base implementation. I have no idea if it works, but it could bring surprising results.
Second thing you may investigate when implementing your own list items is the frequency of the calls to MeasureOverride. (Or just look at the profiler output.) If the number of calls is too high, then the layout might take more loops than necessary. Perhaps there are some conflicting sizing requirements which need several steps to resolve.
You mention 40 items. That should not play any role. ListBox constructs around 3 screens of ListBoxItem's, remaining items are constructed (most often reused) on as-needed basis. If the Measure time increases by increasing the item number, then look for problem in the data implementation.
Make sure your data is fully loaded in an async action and only adding item to your ListBox is running on UI Thread
Make sure you use Grid instead of StackPanel in your template
The following article is also very useful:
http://blogs.msdn.com/b/slmperf/archive/2010/10/06/silverlight-for-windows-phone-7-listbox-scroll-performance.aspx

Access Elements inside a DataTemplate... How to for more than 1 DataTemplate?

I've got 2 DataTemplates defined for a Listbox Control. 1 Template is for the UnSelected State and the other one is for the Selected State(showing more detail than the UnSelected State).
I followed the example here:
Link
about how to access the Elements inside the DataTemplates from Code behind.
I get it right, but it only finds and returns an element of the UnSelected DataTemplate. But when i search for an element in the Selected DataTemplate i get a NullReferenceException.
What could i be doing wrong?
Setting keyboard focus might be one reason you need to access the datatemplate elements. MVVM will not solve that issue and the FocusManager doesn't set keyboard focus.
What you are doing wrong?
I would say what you are doing wrong is trying to access elements inside the DataTemplate from code-behind. Naughty, naughty!
All joking aside, 99.9% of the time I see someone trying to access an element inside a DataTemplate from code, it is because their application is poorly designed, with no (or few) bindings and no view model at all. Many beginners tend to store their data directly in UI elements rather than using a view model. I think it is because their minds have been corrupted by experience VB, WinForms, and ASP.NET where it was the "normal" way to do it. There are a thousand reasons to use a view model and bind your data instead of storing them in UI elements. Look up "model view view model" online for more details.
Now to answer your question:
Any given ListBoxItem can only have one DataTemplate at a time. This is because its ContentPresenter has only one ContentTemplate property, and this property cannot have two different values.
Because of this, the visual tree under a ListBoxItem will always be generated from one a specific template, not a combination of several templates. If you change the ItemTemplate of the ListBox or otherwise update ListBoxItem.ContentTemplate, the visual tree produced by the old template will be thrown away and a new one built.
Let me say that again: If you change data templates, the visual tree produced by the old data template will be thrown away and a new visual tree built.
You can have a hundred data templates defined and usable on a given ListBoxItem, but only one at a time can have a visual tree instantiated for it. And these are the only elements that actually exist as part of the visual tree. All other templates exist only as templates - there are no actual elements created for them.
To put it another way: It is meaningless to ask about how to find elements in two different visual trees instantiated by two different templates on the same control, because a single control cannot have two different templates active at the same time.
Hope this clears things up for you.
Final advice: Do read up on MVVM, and stop trying to access elements inside DataTemplates ASAP. However if you think you might be in that 0.1% who actually do have valid reasons to access elements inside templates, write back with your actual reason for wanting to do so and maybe I can provide further guidance.

Resources