none
FindAncestor does not work when control is on invisible page inside two nested TabControls

    Question

  • In our WPF application, we have a binding inside a ControlTemplate trigger that is defined like this:

    {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyClassName}}, Path=MyProperty, Converter={StaticResource myConverter}, ConverterParameter={StaticResource myParameter}}

    In almost all cases, this binding works without any problems. But when the control is located inside two nested TabControls (i.e. one TabControl inside another) and if the control is on a tab page that currently is not selected, then the binding fails and the following error message can be seen in the debug output:

    System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyClassName', AncestorLevel='1''. BindingExpression:Path=MyProperty; DataItem=null; target element is 'MyControlClass' (Name='MyControlName'); target property is 'MyTargetProperty' (type 'MyType')

    The error message does not appear if the TabControl is not nested or if the control's tab page currently is visible.

    What I already tried:

    • Unfortunately, I was not able to write a small sample program which demonstrates this behavior.
    • Furthermore, increasing the trace level for binding errors (i.e. PresentationTraceSources.TraceLevel) also did not provide any additional information.
    • Setting a breakpoint in the converter also did not help, because the converter is not executed.

    Is there any way to debug such a problem? E.g. can I instruct WPF to output debugging information that tells me how it tries to search for the ancestor? Or can I cause the debugger to break when the WPF tracer tries to write the above error message?

    • Edited by fmunkert Thursday, March 28, 2013 10:45 AM
    Thursday, March 28, 2013 10:42 AM

Answers

  • Andy,
    Min,

    thanks for your suggestions.

    Since I was unable to narrow down the cause of the problem, I have rewritten the code to use a helper property on the target FrameworkElement, which allowed me to get rid of the FindAncestor functionality. The helper property is a reference to the desired ancestor.

    • Marked as answer by fmunkert Tuesday, April 02, 2013 7:19 AM
    Tuesday, April 02, 2013 7:19 AM

All replies

  • I don't think an invisible page would be rendered.

    So there's probably no control and no ancestor.

    For what it's worth.

    I never like all that findancestor mallarky.

    I prefer to bind a specific property from a viewmodel if I can possibly do so.

    Thursday, March 28, 2013 11:11 AM
  • The invisible page itself is not the problem, because the binding works fine when the control is on an invisible page inside the outer tab control.

    Only when the control is inside a nested tab control that is on an invisible page of the outer tab control, then the problem occurs. Furthermore, it does not happen with a simple example; therefore the problem cause must be somewhere else.

    Unfortunately, binding directly to a specific element's property is not possible in our case.

    Thursday, March 28, 2013 11:14 AM
  • Can you make the thing visible in the designer and use the tree visualiser to check it's doing what you expect?

    Otherwise I would be inclined to find a way to break in code.

    Stick a converter in there somewhere.

    Then walk the tree and see what's going on.

    Thursday, March 28, 2013 11:31 AM
  • Can you make the thing visible in the designer and use the tree visualiser to check it's doing what you expect?

    Unfortunately no, because the window is constructed dynamically at run time.

    Otherwise I would be inclined to find a way to break in code.

    Stick a converter in there somewhere.

    Then walk the tree and see what's going on.

    I already tried that (see my original question). The converter never is called, probably because the binding source cannot be found in the first place.

    Thursday, March 28, 2013 11:35 AM
  • I had in mind some other simpler converter but that probably wouldn't be there neither.

    I reckon your converter probably doesn't get called because it's control isn't rendered.

    There is no control, no converter.

    Is it's all dynamic, is whatever builds this definitely building it?

    This sort of issue is why I prefer to build xaml strings rather than dynamic controls from code.

    You can at least save the thing and see what it does.

    Can you call your dynamic code (or an equivalent) from a parameterless constructor somehow so you can see it in the designer?

    Thursday, March 28, 2013 11:52 AM
  • Hi fmunkert,

    Things I know about the TabControl:

    TabControl only have one ContentPresent, and it only loads the current selected tab content into this ContentPresent. Therefore, unselected content are not presented in the visual tree.

    Because of this, the controls in the unselected content may not load their ControlTemplate until they are added into visual tree once. Same is the DataTemplate.

    I suggest you to investigate in this direction.

    Also, if you mouseover any element in debug mode in visual studio, these is a small icon you can click to the browser the visaul tree visually. I think these can help you find out why "MyClassName" is not there for the binding.

    If there are still problems, could you please elaborate the relation among MyControlClass, MyClassName, and the control template? Also, if there is any DataTemplate involved in this issue, let us know.

    Or, if possible, you can remove the unrelated code and send the code to v-minz at microsoft dot com and let me know how to reproduce the issue.

    Best regards, 


    Min Zhu
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, March 29, 2013 3:14 AM
    Moderator
  • Andy,
    Min,

    thanks for your suggestions.

    Since I was unable to narrow down the cause of the problem, I have rewritten the code to use a helper property on the target FrameworkElement, which allowed me to get rid of the FindAncestor functionality. The helper property is a reference to the desired ancestor.

    • Marked as answer by fmunkert Tuesday, April 02, 2013 7:19 AM
    Tuesday, April 02, 2013 7:19 AM