Display a default DataTemplate in a ContentControl when its content is null or empty?
Asked Answered
T

3

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?

Thessalonians answered 1/8, 2009 at 19:8 Comment(0)
D
61

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>
Decosta answered 2/8, 2009 at 19:4 Comment(8)
No problem it's a very common mistake people make when trying to do more complex things with styles and triggers.Decosta
Every time I come back for this I'm going to leave a comment. Thanks.Thessalonians
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...Thessalonians
It's the gift that keeps on giving, and the beauty of Stackoverflow.Decosta
Well, at least 100 rep this time.Thessalonians
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...Thessalonians
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...Sepulveda
@Sepulveda Name the Grid (or other top-level panel) PART_ContentHostDecosta
H
27

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>
Hierology answered 23/12, 2016 at 8:52 Comment(3)
this is an elegant solution with the TargetNullValue. Nice findPedo
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.Particular
Also have a look here: [#22316199 to learn about the FallbackValue attribute in case TargetNullValue alone does not do the trickNildanile
E
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}}" />
Escapee answered 1/8, 2009 at 19:16 Comment(5)
That's kinda weird... It was my understanding the preferred method was to return DependencyProperty.UnsetValue. I've done this in some type converters and it worked well...Thessalonians
@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...Escapee
@Will can you add an answer using DependencyProperty.UnsetValueZhao
@Zhao um, no? Because I have no idea why you would need one? Is the selected answer not correct?Thessalonians
I used TargetNullValue={x:Static system:DBNull.Value} and <DataTemplate DataType="{x:Type system:DBNull}">, and works greatImprecision

© 2022 - 2024 — McMap. All rights reserved.