Why does binding fail when binding a child element to another element when the parent succeeds?
Asked Answered
U

2

4

Say I have two classes that can reference a third UI object (in this example a button).

In addition, the parent class can contain an element of the child class.

If they both are bound to the same control, the same way, the child will fail but the parent succeed.

Is this a bug in WPF?


The parent :

class MyFrameworkElement : FrameworkElement
{
    // A depenedency property that will contain a child element sub-element
    private static readonly DependencyProperty ChildElementProperty =
                    DependencyProperty.Register("ChildElement",
                    typeof(MyChildElement),
                    typeof(MyFrameworkElement),
                    new PropertyMetadata());

    [Category("ChildProperties")]
    public MyChildElement ChildElement
    {
        set { SetValue(ChildElementProperty, value); }
        get { return (MyChildElement)GetValue(ChildElementProperty); }
    }


    // Now, a reference to some other control, in this case we will bind a button to it!
    public UIElement ButtonReferenceInParent
    {
        get { return (UIElement)GetValue(ButtonReferenceInParentProperty); }
        set { SetValue(ButtonReferenceInParentProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ButtonReferenceInParent.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ButtonReferenceInParentProperty =
        DependencyProperty.Register("ButtonReferenceInParent", typeof(UIElement), typeof(MyFrameworkElement), new UIPropertyMetadata(null));

And then the child :

public class MyChildElement : FrameworkElement
{
    public UIElement ButtonReferenceInChild
    {
        get { return (UIElement)GetValue(ButtonReferenceInChildProperty); }
        set { SetValue(ButtonReferenceInChildProperty, value); }
    }

    public static readonly DependencyProperty ButtonReferenceInChildProperty =
        DependencyProperty.Register("ButtonReferenceInChild", typeof(UIElement), typeof(MyChildElement), new UIPropertyMetadata(null));
}

OK -

Now say I Add them to my XAML like this :

<Grid>
    <my:MyFrameworkElement x:Name="ParentName" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ButtonReferenceInParent="{Binding ElementName=buttonisme}">
        <my:MyFrameworkElement.ChildElement>
            <my:MyChildElement x:Name="ChildName" ButtonReferenceInChild="{Binding ElementName=buttonisme}"/>
        </my:MyFrameworkElement.ChildElement>
    </my:MyFrameworkElement>
    
    <Button x:Name="buttonisme" Click="buttonisme_Click" />
</Grid>

Why does the binding work on the parent but then fail on the child, when I am using the EXACT same notation?


Here is my test code...

     Console.WriteLine("Parent button reference is {0}", ParentName.ButtonReferenceInParent);

        if (ChildName.ButtonReferenceInChild == null)
        {
            Console.WriteLine("Child button reference is null!");
        } 
        else
        {
            Console.WriteLine("Child button is {0}", ChildName.ButtonReferenceInChild);
        }

And here is the test result...

Parent button reference is System.Windows.Controls.Button

Child button reference is null!

Ulaulah answered 1/10, 2012 at 9:55 Comment(0)
U
7

The short answer to a long question is that Microsoft doesn't expect you to derive from FrameworkElement without doing a little plumbing.

Just doing derivation, breaks the logical tree which is used when doing binding by element name.

You probably also have to plum up the visual tree, and overload the arrange/measure parts of framework element. (We don't do that here as we aren't visual in the example.)

In this specific case we need to add any children of your object to the logical tree or break the ability to bind child elements.

A good example of someone who solved this is here

Information on overriding the logical tree is here

Anyways, the code needed to fix this SIMPLE example only relied on the logical tree (as the child object isn't really visual.)

Adding this function and changing the dependency property makes the binding work.

        private static readonly DependencyProperty ChildElementProperty =
                    DependencyProperty.Register("ChildElement",
                    typeof(MyChildElement),
                    typeof(MyFrameworkElement),
                    new PropertyMetadata(OnChildElementChanged));

    private static void OnChildElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MyFrameworkElement control = d as MyFrameworkElement;

        if (e.OldValue != null)
        {
            control.RemoveLogicalChild(e.OldValue);
        }

        control.AddLogicalChild(e.NewValue);
    }
Ulaulah answered 10/10, 2012 at 7:42 Comment(0)
L
-1

First, when you setup the xaml like this:

<my:MyFrameworkElement x:Name="ParentName" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ButtonReferenceInParent="{Binding ElementName=buttonisme}"/>
<my:MyChildElement x:Name="ChildName" ButtonReferenceInChild="{Binding ElementName=buttonisme}"/>

It works. I did this because I suspect a visual tree upwards traversal search for the Element Name you use in the binding.

I am still figuring out how the binding can be succesfull in your nested scenario. But maybe this may give you some hint...

Lusterware answered 1/10, 2012 at 10:21 Comment(1)
That limitation would be rather .... Odd. I often bound things that are at the same level (on different branches) Like binding a radio button that is part of a group to a checkbox on a different child group. Could it have something to do with the fact the child isn't a UIElement?... messing with that doesn't seem to fix it.Ulaulah

© 2022 - 2024 — McMap. All rights reserved.