How to use a ContentPresenter inside a UserControl

15,442

Solution 1

Here we go.

<UserControl x:Class="SGDB.UI.Controls.ModernButton"
     xmlns:local="clr-namespace:SGDB.UI.Controls"
     xmlns:converter="clr-namespace:SGDB.UI.Converter"
     x:Name="_modernButton">

    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <Button Content="{TemplateBinding Content}">
                 <Button.Resources>
                    <converter:EnumToColorConverter x:Key="ColorConverter"/>
                  </Button.Resources>
            <Button.Template >
                <ControlTemplate TargetType="Button">
                    <Border Width="{Binding Size,
                                    ElementName=_modernButton}"
                    Height="{Binding Size,
                                     ElementName=_modernButton}"
                    BorderBrush="Black"
                    BorderThickness="0.8,0.8,3,3">
                        <Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}">
                            <ContentPresenter />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Button.Template>
            </Button>
        </ControlTemplate>
    </UserControl.Template>
</UserControl>

Solution 2

Use the ContentPropertyAttribute to instruct the xaml to set this property instead of the actual Content property.

[ContentProperty("InnerContent")]
public partial class ModernButton : UserControl
{
    public ModernButton()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty InnerContentProperty =
        DependencyProperty.Register("InnerContent", typeof(object), typeof(ModernButton));

    public object InnerContent
    {
        get { return (object)GetValue(InnerContentProperty); }
        set { SetValue(InnerContentProperty, value); }
    }
}

Then in your xaml, Bind the Content Presenter to use InnerContent property instead.

<ContentPresenter Content="{Binding InnerContent, ElementName=_modernButton}"/>

This way you can do the following without replacing the actual content.

<control:ModernButton Size="200" BackgroundColor="Light">
    TEST
</control:ModernButton>

Solution 3

Let's assume that youre UserControl is:

<UserControl x:Class="QuickAndDirtyAttempt.Decorator" ....
      <UserControl.Template>
        <ControlTemplate TargetType="{x:Type local:Decorator}">
          <StackPanel Orientation="Vertical">
            <Label>Foo</Label>
            <ContentPresenter/>
            <Label>Bar</Label>
          </StackPanel>
        </ControlTemplate>
      </UserControl.Template>
</UserControl>

Note the TargetType property on the template: without it the project will happily compile, but the ContentPresenter will not work. And then:

<Window ... >
    <StackPanel Orientation="Vertical">
        <local:Decorator>
            <Label Background="Wheat">User supplied content here</Label>
        </local:Decorator>
    </StackPanel>
</Window> 

I strongly recommend you to read this before implementing anything

Solution 4

Simple; Just circumvent and replace the UserControl's Template.

  <UserControl.Template>
        <ControlTemplate TargetType="{x:Type UserControl}">
            <Button Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}">
                <Button.Resources>
                   <converter:EnumToColorConverter x:Key="ColorConverter"/>
                </Button.Resources>
                <Button.Template>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border Width="{Binding Size,
                                        ElementName=_modernButton}"
                        Height="{Binding Size,
                                         ElementName=_modernButton}"
                        BorderBrush="Black"
                        BorderThickness="0.8,0.8,3,3">
                            <Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}">
                                <ContentPresenter />
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Button.Template>
            </Button>
        </ControlTemplate>
    </UserControl.Template>

All a user control is (at least it terms of XAML and its template), is a Border with a ContentPresenter inside it. The ContentPresenter being the only important part, really.

So all you do is gut out its Template and feed the Content property the UserControl has into something a little different; in this case, your button.

This is the difference between making a usercontrol out of other controls, and shoving some controls into a user control. Making the usercontrol out of other controls gives you much more power.

Share:
15,442
Th1sD0t
Author by

Th1sD0t

Updated on June 15, 2022

Comments

  • Th1sD0t
    Th1sD0t almost 2 years

    I'd like to create a UserControl (in this case a square-Button with defined Backgroundcolors) which can host it's own content.

    UserControl:

    <UserControl x:Class="SGDB.UI.Controls.ModernButton"
             xmlns:local="clr-namespace:SGDB.UI.Controls"
             xmlns:converter="clr-namespace:SGDB.UI.Converter"
             x:Name="_modernButton">
    <Button>
        <Button.Resources>
            <converter:EnumToColorConverter x:Key="ColorConverter"/>
        </Button.Resources>
        <Button.Template>
            <ControlTemplate>
                <Border Width="{Binding Size, ElementName=_modernButton}" Height="{Binding Size, ElementName=_modernButton}" BorderBrush="Black" BorderThickness="0.8,0.8,3,3">
                    <Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}">
                        <ContentPresenter/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Button.Template>
    </Button>
    

    Now, as you may expect it, if I use this Control inside my MainView everthing works just fine until I define some Content.

    Using:

    <control:ModernButton Size="200" BackgroundColor="Light">
        TEST
    </control:ModernButton>
    

    In this case "TEST" will override the whole Content of the UserControl (the whole Button Template). I guess this happens because The Button inside the UserControl is defined as "Content" itself and it will get overridden when defining new Content.

    So the final question is: Is it possible to achieve what I'm looking for? if yes: How? How could I "redirect" the Content I'm defining in my MainView into the self-defined ContentPresenter inside my Button Template instead of the UserControls's ContentPresenter?

    If possible I don't want to create a new dp-propery which hosts my Content, e.g.:

    <controls:MordernButton Size="200" BackgroundColor="Light">
        <controls:ModernButton.Content>
            I don't want this, if possible
        </controls:ModernButton.Content>
    </controls:ModernButton>
    
  • Th1sD0t
    Th1sD0t about 8 years
    At first it seemed very good - but when passing some Content ("TEST" or <TextBlock Text="Test"/>) nothing shows up - the Control stays empty (except it's own color)
  • Th1sD0t
    Th1sD0t about 8 years
    That's it - could you explain why the Button's TargetType had such an big impact on the ContentPresenter ? And if you are on it, maybe you could explain why the Button's Templatebinding stops WPF from clearing the whole Content when passing some Content into a Control? Thanks in advance :)
  • Th1sD0t
    Th1sD0t about 8 years
    Think this could work also if the second ControlTemplate's Type would be "Button".
  • Th1sD0t
    Th1sD0t about 8 years
    I read this but over read the part that says: "Without the TargetType the Project will happily compile but the ContentPresenter won't work". You deserve my upvote because the CodeProject explains why one should not use my solution :)
  • Th1sD0t
    Th1sD0t about 8 years
    Works, too. But I wanted to know how things work in XAML - I really don't like decorating Code with Attributes.
  • Gopichandar
    Gopichandar about 8 years
    That will be a big topic to cover. May be this answers your first question.
  • Gopichandar
    Gopichandar about 8 years
    To answer you second question. Its not Button's template binding that helps us to stop clearing the whole content. Its Usercontrol's template binding did its job.
  • Logan
    Logan about 8 years
    Oh yes, how did I miss that? - Edited it in.
  • Thiago Romam
    Thiago Romam over 7 years
    Awesome tip! since it isn't a template override, you are allowed to define names and links with code behind easily
  • Glaucus
    Glaucus over 5 years
    Where does _modernButton come from?
  • Sriram Sakthivel
    Sriram Sakthivel over 5 years
    @Glaucus That's what OP named his UserControl as.
  • Krythic
    Krythic over 4 years
    After trying to make a custom titled frame, this really does seem to be the best solution. I did learn a lot of other stuff, though, which makes hosting various forms of content more dynamic. Thank you.