Is possible to make a WPF RelativeSource binding with AncestorType pointing to a base type?
Asked Answered
B

1

8

I want to bind a property to the parent container view having a ViewModel in its DataContext.

This code works perfectly well when the parent is a direct instance of ConcreteClassView:

Property="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:ConcreteClassView}}, Path=DataContext.Name}"

However, the parent is not found when trying to locate it via a base class or a interface. Sample:

PropertyB="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:BaseClassView}}, Path=DataContext.Name}"

PropertyB="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:INamedElementView}}, Path=DataContext.Name}"

Giving that:

class ConcreteClassView : BaseClassView, INamedElementView { }

Ok, Let's assume that FindAncestor, AncestorType needs the concrete type to work.

But there is any workaround to locate ancestors just based on base classes or implementing a given Interface?

Thxs.

Butterwort answered 2/11, 2012 at 11:22 Comment(5)
Strange, AncestorType must work with base classes as well.Emileeemili
Did you check if the namespace is right? Maybe INamedElementView is in some other namespace?Emileeemili
Thanks @Emileeemili All of them are defined in the same namespace. And the ty alias references it.Butterwort
@pjmolina: did you solved the issue? Was it one of the cases I described in my answer?Proprietor
Sorry @Proprietor This issue was 4 years ago. I don't have the context to repro it nowadays.Butterwort
P
9

FindAncestor, AncestorType do work with base classes, so your assumption is wrong.

Here is proof: This works

<HeaderedContentControl Tag="ABC">
    <TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</HeaderedContentControl>

It works also with interface (Button implements ICommandSource):

<Button Tag="ABC">
    <TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ICommandSource}}" />
</Button>

(Tested in .NET 4.5)

So why your code does not work?

  1. There may be another element derived from ty:BaseClassView in your visual tree between the binding target and the element you are looking for.

This doesn't work:

<HeaderedContentControl Tag="ABC">
    <Label>
        <TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </Label>
</HeaderedContentControl>

Label is also inherited from ContentControl, so Binding Source is Label in this case

  1. Visual Tree may be disconnected. For example Popup control is part of Logical Tree, but it has its own visual tree, so you can't use RelativeSource FindAncestor inside popup to look for parents outside popup. Please note, that elements are removed from visual tree also when you set Visibility="Collapsed"

How to debug?

  1. You can use converter to debug your binding. Just specify RelativeSource and some fake converter and leave the path empty. You can then place breakpoint to your converter, where value is be your binding source.

  2. Use loaded event of your element with binding to write all visual parents to Debug window

EDIT: Now in Visual Studio 2015, you can use Live Visual Tree explorer, to inspect the visual tree at runtime, (similarly like Browsers' developers tools can inspect dom elements). Using this tool you should be able to find bug in your application within few seconds.

https://msdn.microsoft.com/en-us/library/mt270227.aspx

Proprietor answered 3/4, 2015 at 11:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.