• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2821
  • Last Modified:

WPF ContentPresenter Default Template

Can someone provide an example of how you would override a ContentPresenter's default template when the content is null. I think there are probably a variety of ways to solve this, I'm just wondering what the options are.
<Window.Resources>
  <Style TargetType="Button">
  <!-- Define a style here -->
  </Style>
</Window.Resources>
<StackPanel>
  <!-- This will behave like a normal button -->
  <Button>Caption</Button>

  <!-- This will have my template rather than being empty -->
  <Button/>
</StackPanel>

Open in new window

0
OFGemini
Asked:
OFGemini
  • 4
  • 4
2 Solutions
 
CuteBugCommented:
Assign a x:Key name to the Style. This way you can specify which button will have the template.
<Window.Resources>
        <Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid Margin="2">
                            <Border Name="border" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}" Background="ForestGreen" BorderBrush="Red" CornerRadius="5" BorderThickness="1" Margin="4">
                                <ContentPresenter RecognizesAccessKey="True" />
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <!-- This will behave like a normal button -->
        <Button>Caption</Button>

        <!-- This will have my template rather than being empty -->
        <Button Style="{StaticResource CustomButtonStyle}" Height="40">Custom Button</Button>
    </StackPanel>

Open in new window

0
 
OFGeminiAuthor Commented:
That works, but unfortunately, it's not exactly what I was thinking of. I'd like to not override the control template of the button (as that would break the button's styling). Rather, I simply modify the way it handles null content.  I'm not sure if this can be done purely through styles, or if it would require a custom DataTemplateSelector class.

The following example does not work, but it illustrates the general concept:
Xaml solution:
<Window.Resources>
  <Style TargetType="Button">
    <Style.Resources>
      <!-- DataType="{x:Null}" will throw an exception! -->
      <DataTemplate DataType="{x:Null}">
        <ContentPresenter Content="{Binding Path=...UIProperties}" />
      </DataTemplate>
      <DataTemplate DataType="{x:Type view:UIProperties}">
        <TextBlock Text="{Binding Title}"/>
      </DataTemplate>
    </Style.Resources>
  </Style>
</Window.Resources>

Open in new window

0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
OFGeminiAuthor Commented:
Ok, I kind of solved my own problem...  But the solution is still not exactly correct, because it doesn't really handle the case when content is null, but uses a special default value.

Any comments / suggestions?
// MyButton.cs
public class MyButton : Button
{
  static MyButton()
  {
    Type forType = typeof(MyButton);
    FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(forType));
    ContentControl.ContentProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(new NullContent()));
  }

  internal struct NullContent { }
}

// Generic.xaml
<Style x:Key="{x:Type ctrl:MyButton}" TargetType="{x:Type ctrl:MyButton}" BasedOn="{StaticResource {x:Type Button}}">
  <Style.Resources>
    <DataTemplate DataType="{x:Type view:UIProperties}">
      <TextBlock Text="{Binding Title}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ctrl:MyButton+NullContent}">
      <ContentPresenter Content="{Binding Path=...UIProperties}" />
    </DataTemplate>
  </Style.Resources>
</Style>

Open in new window

0
 
CuteBugCommented:
In order to set no content for the button, all you have to do, in the control template which I specified, is remove the ContentPresenter tag altogether.
<Window.Resources>
        <Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid Margin="2">
                            <Border Name="border" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}" Background="ForestGreen" BorderBrush="Red" CornerRadius="5" BorderThickness="1" Margin="4">
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <!-- This will behave like a normal button -->
        <Button>Caption</Button>

        <!-- This will have my template rather than being empty -->
        <Button Style="{StaticResource CustomButtonStyle}" Height="40">Custom Button</Button>
    </StackPanel>

Open in new window

0
 
OFGeminiAuthor Commented:
Again, that's not what I'm looking for. I don't want to redefine the Template for the control, just what's shown as it's content. Internally, the Button control has a ContentPresenter wrapped with ButtonChrome. I don't want to modify this Template in any way. I just want to modify what the internal ContentPresenter displays when the Content property is null.
0
 
CuteBugCommented:
Ok Got your point now.

For that you need to set the Button.ContentTemplate property which is of type DataTemplate.

In the code below, the second button has no content, but the ContentTemplate has been modified to show a shape as the content.
<StackPanel>
        <!-- This will behave like a normal button -->
        <Button>Caption</Button>

        <!-- This will have my template rather than being empty -->
        <Button Height="40">
            <Button.ContentTemplate>
                <DataTemplate>
                    <Grid>
                        <Border Background="ForestGreen" BorderBrush="Red" CornerRadius="5" BorderThickness="1" Margin="4">
                            <Ellipse Width="50" Height="30"></Ellipse>
                        </Border>
                    </Grid>
                </DataTemplate>
            </Button.ContentTemplate>
        </Button>
    </StackPanel>

Open in new window

0
 
OFGeminiAuthor Commented:
This slight modification of my previous code effectively does what I wanted. When Content property is null, even after it's been modified from it's original value, it is automatically coerced to a NullContent.
// MyButton.cs
public class MyButton : Button
{
  static MyButton()
  {
    Type forType = typeof(MyButton);
    FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(forType));
    ContentControl.ContentProperty.OverrideMetadata(forType, new FrameworkPropertyMetadata(default(NullContent), null, CoerceContent));
  }

  public static object CoerceContent(DependencyObject d, object value)
  {
    return (value == null)
      ? default(NullContent)
      : value;
  }

  internal struct NullContent { }
}

// Generic.xaml
<Style x:Key="{x:Type ctrl:MyButton}" TargetType="{x:Type ctrl:MyButton}" BasedOn="{StaticResource {x:Type Button}}">
  <Style.Resources>
    <DataTemplate DataType="{x:Type view:UIProperties}">
      <TextBlock Text="{Binding Title}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ctrl:MyButton+NullContent}">
      <ContentPresenter Content="{Binding Path=...UIProperties}" />
    </DataTemplate>
  </Style.Resources>
</Style>

Open in new window

0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 4
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now