views:

33

answers:

2

I am trying to create a template for a content control such as Button or HeaderedContentControl etc. where the text is underlined.

I just want to underline the text when Content="This text is underlined" is specified.

It must continue to work as normal if Content is another UIElement.

Most posts asking this same question are satisfied with modifying the template to only work for a string as content. Scott Gu has a good article about styling buttons but doesn't address this issue.

The following sample will work if you actually pass in Content as an instance of type TextBlock but not as a string. Surely the visual tree has a TextBlock so it should style it. Perhaps this is a Sivlerlight limitation.

This example shows black text and big red text when I want it to display both as big red text.

    <navigation:Page.Resources>
    <Style TargetType="TextBlock" x:Key="style123">
        <Setter Property="Foreground"  Value="Red"/>
        <Setter Property="FontSize" Value="72"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="TextDecorations" Value="Underline"/>
    </Style>
</navigation:Page.Resources>

<StackPanel>        

    <!-- This doesn't work and shows black text -->
    <ContentPresenter Content="Small black text">
        <ContentPresenter.Resources>
            <Style TargetType="TextBlock" BasedOn="{StaticResource style123}"/>
        </ContentPresenter.Resources>
    </ContentPresenter>

    <!-- This works and shows red text -->
    <ContentPresenter>
        <ContentPresenter.Content>
            <TextBlock Text="This is big red text"/>
        </ContentPresenter.Content>

        <ContentPresenter.Resources>
            <Style TargetType="TextBlock" BasedOn="{StaticResource style123}"/>
        </ContentPresenter.Resources>
    </ContentPresenter>

</StackPanel>
+1  A: 

You could subclass whatever actual ContentControl (i.e. Button) you are using and override OnContentChanged in order to reset the Content property to an underlined TextBlock if the newContent is a string. In the case that the newContent is not a string it would perform in the usual way.

public class UnderlineButton : Button
{
    protected override void OnContentChanged(object oldContent, object newContent)
    {
        if (newContent is string)
        {
            TextBlock textBlock = new TextBlock();
            textBlock.Text = newContent as string;
            textBlock.TextDecorations = TextDecorations.Underline;
            this.Content = textBlock;
        }

        base.OnContentChanged(oldContent, newContent);
    }
}

It's kind of annoying to subclass just to accomplish this but it avoids messy style templates and subclassing ContentPresenter.

Dan Auclair
thanks! wouldn't this need to be an else condition for base.OCC()?
Simon_Weaver
I don't think so, you are always going to want to notify the base control of the content change. In the case that the newContent is a string you are just "jumping in" and switching out the actual content first.
Dan Auclair
ahh thats right... this is OnContentChanged as opposed to OnContentChanging (which may not actually exit - I didnt check). looking like this solution is the best for Silverlight right now!
Simon_Weaver
A: 

Try this example, using a DataTemplate to custom-render string content (I've just set the background to red):

<ContentControl Content="{Binding YourData}" >
  <ContentControl.Resources>
    <DataTemplate DataType="{x:Type s:String}">
      <TextBlock Text="{Binding}" Background="Red" />
    </DataTemplate>
  </ContentControl.Resources>
</ContentControl>

EDIT: just as a note, you could pull this out into a ContentControl style rather than applying it inline each time, if you need better reusability...

Dan Puzey
i did try to get something like this working before but couldn't get it working with silverlight. i know this is WPF cos in silverlight we don't even have Background on a TextBlock! :-(
Simon_Weaver
So did you get it finally working? what was your solution?
Mamta Dalal