49

I would think this is possible, but the obvious way isn't working.

Currently, I'm doing this:

<ContentControl
    Content="{Binding HurfView.EditedPart}">
    <ContentControl.Resources>
        <Style
            TargetType="ContentControl"
            x:Key="emptytemplate">
            <Style.Triggers>
                <DataTrigger
                    Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Content}"
                    Value="{x:Null}">
                    <Setter
                        Property="ContentControl.Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <Grid
                                    HorizontalAlignment="Stretch"
                                    VerticalAlignment="Stretch">
                                    <TextBlock>EMPTY!</TextBlock>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Resources>
</ContentControl>

I'm not getting any binding errors and this compiles. However, it doesn't produce the expected result. I've also tried the obvious:

<DataTemplate DataType="{x:Null}"><TextBlock>Hurf</TextBlock></DataTemplate>

This won't compile. And attempting to set the content twice fails as well:

<ContentControl
    Content="{Binding HurfView.EditedPart}">
        <TextBlock>DEFAULT DISPLAY</TextBlock>
</ContentControl>

Can I do this without writing a custom template selector?

3 Answers3

60

Simple, you have to bind the content property in the style. Styles won't overwrite a value on a control if there's a binding present, even if the value evaluates to Null. Try this.

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="ContentControl">
            <Setter Property="Content" Value="{Binding HurfView.EditedPart}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Content}" Value="{x:Null}">
                    <Setter Property="ContentControl.Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                    <TextBlock>EMPTY!</TextBlock>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>
Bryan Anderson
  • 15,969
  • 8
  • 68
  • 83
  • No problem it's a very common mistake people make when trying to do more complex things with styles and triggers. – Bryan Anderson Aug 03 '09 at 02:47
  • 2
    Every time I come back for this I'm going to leave a comment. Thanks. –  Jun 15 '11 at 19:48
  • Y Halo thar. Best solution to a failed Converter as well. Just return `null` from the conversion process and trap for it this way. Can't do it using Validation, unfortunately... –  Jul 19 '11 at 16:22
  • It's the gift that keeps on giving, and the beauty of Stackoverflow. – Bryan Anderson Aug 14 '12 at 22:18
  • Well, at least 100 rep this time. –  Aug 15 '12 at 00:34
  • Its so much easier to come back here than to look through my codebase to find another example. Maybe I should just create a custom ContentControl... –  Jan 11 '13 at 17:25
  • Hmm this won't work for me, I'm using the TransitionControl from here, and it crashes because Template.FindName("PART_ContentHost", this); returns null... – RvdK Oct 01 '15 at 13:44
  • @RvdK Name the Grid (or other top-level panel) PART_ContentHost – Bryan Anderson Oct 02 '15 at 14:13
26

Since I stumbled upon this question and had the same problem today, I wanted to contribute another way how I solved the problem. Since I did not like to add another style trigger I used the property TargetNullValue which seems to be a bit more readable than the accepted solution (which works nevertheless):

    <ContentControl>
      <ContentControl.Content>
        <Binding Path="ContentViewModel">
          <Binding.TargetNullValue>
            <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
              <TextBlock>EMPTY!</TextBlock>
            </Grid>
          </Binding.TargetNullValue>
        </Binding>
      </ContentControl.Content>
    </ContentControl>
Herm
  • 2,956
  • 19
  • 32
  • this is an elegant solution with the TargetNullValue. Nice find – juFo Sep 18 '17 at 09:34
  • Works, but in some cases like mine it produces Binding-Errors. Although I also think this one is more readable, check for new Binding-Errors when using it. – Dima Feb 14 '18 at 22:52
  • Also have a look here: [https://stackoverflow.com/questions/22315699/wpf-textblock-targetnullvalue-not-working] to learn about the `FallbackValue` attribute in case `TargetNullValue` alone does not do the trick – Angle.Bracket Feb 05 '19 at 18:33
2

You could return DBNull.Value as the FallbackValue of the Binding for the Content of the ContentControl, and create a DataTemplate for DBNull :

<DataTemplate DataType="{x:Type system:DBNull}">
    <!-- The default template -->
</DataTemplate>

...

<ContentControl Content="{Binding HurfView.EditedPart, FallbackValue={x:Static system:DBNull.Value}}" />
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 4
    That's kinda weird... It was my understanding the preferred method was to return [DependencyProperty.UnsetValue](http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.unsetvalue.aspx). I've done this in some type converters and it worked well... –  Oct 05 '10 at 14:54
  • 2
    @Will, yes, that would probably be better. I wrote that answer more than one year ago, and my understanding of WPF at the time was still very incomplete... – Thomas Levesque Oct 05 '10 at 16:36
  • @Will can you add an answer using DependencyProperty.UnsetValue – Simon_Weaver Mar 01 '13 at 03:38
  • 2
    @Simon_Weaver um, no? Because I have no idea why you would need one? Is the selected answer not correct? –  Mar 01 '13 at 04:03
  • I used `TargetNullValue={x:Static system:DBNull.Value}` and ``, and works great – marbel82 Jun 28 '20 at 13:26