How to use a ContentPresenter inside a UserControl
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.
Th1sD0t
Updated on June 15, 2022Comments
-
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 about 8 yearsAt 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 about 8 yearsThat'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 about 8 yearsThink this could work also if the second ControlTemplate's Type would be "Button".
-
Th1sD0t about 8 yearsI 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 about 8 yearsWorks, too. But I wanted to know how things work in XAML - I really don't like decorating Code with Attributes.
-
Gopichandar about 8 yearsThat will be a big topic to cover. May be this answers your first question.
-
Gopichandar about 8 yearsTo 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 about 8 yearsOh yes, how did I miss that? - Edited it in.
-
Thiago Romam over 7 yearsAwesome tip! since it isn't a template override, you are allowed to define names and links with code behind easily
-
Glaucus over 5 yearsWhere does _modernButton come from?
-
Sriram Sakthivel over 5 years@Glaucus That's what OP named his UserControl as.
-
Krythic over 4 yearsAfter 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.