Search code examples
wpfuser-controlswpf-controlsdependency-propertiestextblock

passing a TextBlock or collection of inlines to a child UserControl in WPF


I have created a UserControl called InfoBox that acts like a fancy textblock (extra buttons etc). It works fine. I can use it in Blend like so:

<myNS:InfoBox Text="Some Text"/>

where 'Text' is a dependency property:

 public static readonly DependencyProperty TextProperty =
     DependencyProperty.Register("Text", typeof(string), typeof(InfoBox),
     new UIPropertyMetadata(null,ValueChanged));

and handled like so:

    private static void ValueChanged(DependencyObject dpo,
                                     DependencyPropertyChangedEventArgs args)
    {
        ((InfoBox)dpo).TextBlock.Text = (string)args.NewValue;
    }

When I add the control in Blend, it shows up with its design-time sample text, until I specify Text="Something", in which case "Something" magically shows up in the designer. Perfect!

But now I want to pass more than text, I want to be able to use all the funky functionality you get inline with a textblock. Run, italic, etc...

Why does the following not work?

<myNS:InfoBox>
        <myNS:InfoBox.ReferenceBlock>
             <TextBlock>
                <Run Language="en-gb" Text="SampleSample"/><LineBreak/>
                <Run Language="en-gb"/><LineBreak/>
                <Run Language="en-gb" Text="MoreMoreMore"/>
             </TextBlock>   
        <myNS:InfoBox.ReferenceBlock>           
</myNS:InfoBox>

.

     public static readonly DependencyProperty ReferenceBlockProperty =
        DependencyProperty.Register("ReferenceBlock", typeof(TextBlock), 
        typeof(InfoBox), new UIPropertyMetadata(null, ReferenceBlockReceived));

[...]


     private static void ReferenceBlockReceived(DependencyObject dpo,
            DependencyPropertyChangedEventArgs args)
    {
        var textblock = (TextBlock)args.NewValue;
        if (textblock != null)
        {
            ((InfoBox)dpo).TextBlock.Inlines.Clear();
            ((InfoBox)dpo).TextBlock.Inlines.AddRange(textblock.Inlines);
        }
    }

The TextBlock received by the handler is completely empty for some reason. I appreciate any help. This WPF stuff is tough!


Solution

  • Unfortunately it's not so simple. TextBlock supports elements of type Run through a dependency property called Inlines along with a couple of interfaces. It would be possible but difficult to reproduce this behaviour in your fancy text box.

    I recommend you download Jetbrain's free decompiler DotPeek which will allow you to study the implementation of TextBlock for an idea of what's required.