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

OFGeminiAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.