70

I'm trying to create a user control that has a Grid with two rows. the first row for a title and the second one for a content that will be defined outside the user control such as a Button in our example.

Somehow I didn't get it to work.

UserControl1 xaml:

  <Grid Background="LightBlue">
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
</Grid>

MainWindow xaml:

 <Grid>
    <local:UserControl1>
        <Button>Click me</Button>
    </local:UserControl1>
</Grid>

The picture below should explain what's my problem: enter image description here

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Erez
  • 6,405
  • 14
  • 70
  • 124

5 Answers5

86

The following code

<local:UserControl1>
    <Button>Click me</Button>
</local:UserControl1>

Means that you set UserControl1's Content property to be that button. This button simply replaces that UserControls1's markup. So all the things that you have in UserControl1.xaml are not there any more.

EDIT

If you want your UserControl to host some markup that will be set somewhere outside of it, you can add a DependencyProperty to it, for example:

    /// <summary>
    /// Gets or sets additional content for the UserControl
    /// </summary>
    public object AdditionalContent
    {
        get { return (object)GetValue(AdditionalContentProperty); }
        set { SetValue(AdditionalContentProperty, value); }
    }
    public static readonly DependencyProperty AdditionalContentProperty =
        DependencyProperty.Register("AdditionalContent", typeof(object), typeof(UserControl1),
          new PropertyMetadata(null));

And add some element to it's markup to host that additional content. Here's an example extending the markup you provided:

<UserControl ... Name="userControl">
    <Grid Background="LightBlue">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
        <ContentPresenter Content="{Binding AdditionalContent, ElementName=userControl}" />
    </Grid>
</UserControl>

Now you can use it as following:

<local:UserControl1>
    <local:UserControl1.AdditionalContent>
        <Button>Click me</Button>
    </local:UserControl1.AdditionalContent>
</local:UserControl1>
Dave Jellison
  • 924
  • 13
  • 22
EvAlex
  • 2,888
  • 1
  • 20
  • 24
  • 1
    Why use an additional dependency property and not just replace the Control Template and bind to the content property? – codekaizen May 03 '12 at 08:06
  • I agree with you. I saw your answer just after I've posted mine. It was me voting your's up – EvAlex May 03 '12 at 09:12
  • The explanation of why it's not working is great, but I found @blindmeis's answer to be more helpful. – TarkaDaal Dec 17 '12 at 11:03
  • 43
    Instead of using `` you can simply use `[ContentProperty("AdditionalContent")]` attribute on the class and then it's possible to have custom controls just inside the control's content. This is great, because no one can destroy it by accidentally setting the content instead of the `AdditionalContent`. – Ondrej Janacek Jul 25 '14 at 13:06
  • Thank you. Your code helped me to built usercontrol that host other control but content of the ContentPresenter not inheriting FontSize property. I have posted a question http://stackoverflow.com/questions/33658362/workaround-for-fontsize-not-getting-inherited-in-contentpresenter-and-contentcon – Amit Hasan Nov 12 '15 at 12:30
  • Have a look at blindmeis's answer. You could use TemplateBinding to set ContentPresenter's Content, FontSize etc. – EvAlex Nov 12 '15 at 13:01
  • @OndrejJanacek that really should be the accepted answer. It gracefully avoids all thye gotchas from some of the workarounds suggested here. – Gusdor Oct 23 '17 at 08:50
  • This solution works great, except for the fact that you cannot name the elements inside the user control. So naming the `Button` in the above example would throw an error. – GregorMohorko Oct 31 '17 at 01:54
  • 3
    What is `CalibrationPoint`? The intellisence couldn't find a possible resolution to that class, is it some custom thing? [[ EDIT ]] sorry I didn't check the method `Register` parameters. I think this should be changed to `UserControl1` to compile. – Bishoy Dec 05 '17 at 10:35
  • This is great! How would you do this for multiple elements in `AdditionalContent`? (If possible, I'd rather change the property to allow multiple items than change the usage of the usercontrol) – derekantrican Sep 22 '20 at 20:42
31

You have to set the ControlTemplate:

<UserControl>
<UserControl.Resources>
    <Style TargetType="{x:Type local:UserControl1}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:UserControl1}">
                    <Grid Background="LightBlue">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="50" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Row="0" Text="Title" FontSize="30" Margin="10,0,0,0"/>
                        <ContentPresenter Grid.Row="1" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>
</UserControl>
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • 11
    Is there a reason why you set it via `UserControl.Resources/Style` instead of just putting the `ControlTemplate` as content of `UserControl.Template`? – LWChris Apr 13 '14 at 23:01
  • 1
    Unfortunately, it looks like setting the `Template` property of a `UserControl` isn't supported: https://social.msdn.microsoft.com/forums/silverlight/en-US/a41ff344-1760-4e2d-afc2-67307372b584/how-can-set-usercontrols-template-in-xaml – James Ko Jan 31 '16 at 20:13
  • 2
    UserControl.Template works for me. I use TargetType="{x:Type UserControl}". – Der_Meister Feb 06 '17 at 07:52
  • 1
    Thank you for this. – Krythic Apr 16 '17 at 07:33
  • 1
    Setting the control template disables the use of x:Name within any content you set. Tested in a .Net 4.7 wpf app. Namescope is already set apparently. – Gusdor Oct 23 '17 at 08:50
10

You can template the user control to add additional visuals like the TextBlock.

<UserControl>
<UserControl.Style>
  <Style TargetType="{x:Type UserControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate>              
          <Grid Background="LightBlue"> 
          <Grid.RowDefinitions> 
            <RowDefinition Height="50" /> 
            <RowDefinition Height="*" /> 
          </Grid.RowDefinitions> 
          <TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/> 
          <ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"  />
          </Grid> 
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</UserControl.Style>
<Button>
  Click me!
</Button>
</UserControl>
codekaizen
  • 26,990
  • 7
  • 84
  • 140
  • 1
    use `` to make it work in a resource dictionary – Jinjinov Dec 28 '18 at 08:04
  • I create a new UserControl that needed to contain content. This is the only solution that seems to work properly without causig ton of other problems during implementation or down the road. E.g. @blindmeis solution does not show any visuals in the designer of the control itself. Only in the designer of the control where the new control is used. There is still the issue where a control set as content of the new control can't have a name attribute. – Rumble Dec 13 '21 at 16:42
9

Use template with

< ContentControl />

Instead of using Content Presenter

So place this:

<UserControl.Style>
        <Style TargetType="{x:Type UserControl}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type UserControl}" >
                          <Grid Background="LightBlue"> 
                           <Grid.RowDefinitions> 
                            <RowDefinition Height="50" /> 
                            <RowDefinition Height="*" /> 
                          </Grid.RowDefinitions> 
                           <TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/> 

                        <ContentControl  Grid.Row="1" Content="{TemplateBinding Content}"  />

                        </Grid> 
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Style>

to your userControl

Alamakanambra
  • 5,845
  • 3
  • 36
  • 43
1

This is the simple general template for a user control (without using styles or properties to set the content):

<UserControl ...>
   <UserControl.Template>
       <ControlTemplate TargetType="{x:Type UserControl}">
           <!-- control contents here -->
           <ContentPresenter/><!-- outside contents go here -->
           <!-- control contents here -->
       </ControlTemplate>
   </UserControl.Template>
</UserControl>

The <ControlTemplate> represents the user control's XAML duplicated for each control.

The <ContentPresenter> is where the Content gets put when consuming the control.

Denis G. Labrecque
  • 1,023
  • 13
  • 29