2

I try to make my own ContentControl that derives from Control to fully understand dark wpf tree concepts. For now, i just implemented the logical part (Content) of the ContentControl.

My code behind :

[ContentProperty("Content")]
public class MyContentControl : Control
{
    public MyContentControl()
    {

    }

    public Object Content
    {
        get { return (Object)GetValue(ContentProperty); }
        set { SetValue(ContentProperty, value); }
    }

    public static readonly DependencyProperty ContentProperty =
        DependencyProperty.Register("Content", typeof(Object), typeof(MyContentControl), new UIPropertyMetadata());

}

XAML :

<StackPanel x:Name="stackPanel">
    <TextBlock Visibility="Collapsed" x:Name="textBlock" Text="Hello World"/>
    <ContentControl>
        <TextBlock Background="LightBlue" Text="{Binding Text, ElementName=textBlock}"/>
    </ContentControl>
    <local:MyContentControl>
        <TextBlock Text="{Binding Text, ElementName=textBlock}"/>
    </local:MyContentControl>
</StackPanel>

I got the following binding error :

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=textBlock'. BindingExpression:Path=Text; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

It is like the inner TextBlock can't go up in the logical tree and find the original textblock on which it should bind. I wasn't able to set myContentControl as the parent of the Content object.

Any idee?

Thanks for your time.

Jonas

Jonas
  • 665
  • 7
  • 14

2 Answers2

4

Relevant question: Binding ElementName. Does it use Visual Tree or Logical Tree

The binding you want is not possible because the same instance of MyContentControl could theoretically be used somewhere else in the application where the element "textBlock" is not in the scope.

If you want to do this type of binding you could use a Resource instead:

xmlns:clr="clr-namespace:System;assembly=mscorlib"

<StackPanel>
    <StackPanel.Resources>
        <clr:String x:Key="MyText">Hanky Panky</clr:String>
    </StackPanel.Resources>
    <TextBlock Text="{StaticResource MyText}" />

    <ContentControl>
        <TextBlock Text="{Binding Source={StaticResource MyText}}" />
    </ContentControl>
</StackPanel>
Community
  • 1
  • 1
Bas
  • 26,772
  • 8
  • 53
  • 86
  • Thanks for your answer. I need to think about it again but your link was very helpful. I don't want to change the xaml but i'd like to know in which way i should modify the code behind MyContentControl in the waay it can have exactly the same behavior as the ContentControl. – Jonas Nov 30 '11 at 14:36
2

I Just had to apply FrameworkElement.AddLogicalChild and FrameworkElement.RemoveLogicalChild when the ContentChanged and the binding is correctly apply (Verified with WPF Inspector).

So all this is about LogicalTree (and maybe the xaml namescope is inherited from logical parent). The TextBlock inside MyContentControl get the MyContentControl as Parent when MyContentControl.AddLogicalChild(TextBlock) is called.

My Code :

[ContentProperty("Content")]
public class MyContentControl : Control
{
    public MyContentControl()
    {
        Content = new UIElementCollection(this, this);
    }


    public Object Content
    {
        get { return (Object)GetValue(ContentProperty); }
        set { SetValue(ContentProperty, value); }
    }

    public static readonly DependencyProperty ContentProperty =
        DependencyProperty.Register("Content", typeof(Object), typeof(MyContentControl), new UIPropertyMetadata(new PropertyChangedCallback(OnContentChanged)));

    public static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MyContentControl c = (MyContentControl)d;

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

        c.AddLogicalChild(e.NewValue);
    }

    protected override System.Collections.IEnumerator LogicalChildren
    {
        get
        {
            List<Object> l = new List<object>();
            if (Content != null)
                l.Add(Content);

            return l.GetEnumerator();
        }
    }
}
Jonas
  • 665
  • 7
  • 14