16
votes

I want to tell WPF: "If TextBlock contains no data, then don't show it."

TRY #1 with a simple trigger produces the error "'Text' member is not valid because it does not have a qualifying type name.":

<StackPanel Margin="10">
    <TextBlock Padding="10" Background="Yellow" Text="{Binding MainMessage}">
        <TextBlock.Triggers>
            <Trigger Property="Text" Value="{x:Null}">
                <Setter Property="Visibility" Value="Collapsed"/>
            </Trigger>
        </TextBlock.Triggers>
    </TextBlock>
</StackPanel>

TRY #2 with a style trigger produces the error The type 'style' does not contain a public type-converter class:

<UserControl x:Class="TestItemsSource234.Views.SmartForm"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style x:Key="MainMessageStyle" TargetType="TextBlock">
            <Style.Triggers>
                <Trigger>
                    <Trigger Property="Text" Value="{x:Null}">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </Trigger>
                </Trigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <StackPanel Margin="10">        
        <TextBlock Style="MainMessageStyle" Padding="10" Background="Yellow" Text="{Binding MainMessage}"/>
    </StackPanel>
</UserControl>

TRY #3 with a style DataTrigger produces the same error The type 'style' does not contain a public type-converter class:

<UserControl x:Class="TestItemsSource234.Views.SmartForm"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style x:Key="MainMessageStyle" TargetType="TextBlock">
            <Style.Triggers>
                <Trigger>
                    <DataTrigger Binding="{Binding MainMessage}" Value="{x:Null}">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Trigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <StackPanel Margin="10">        
        <TextBlock Style="MainMessageStyle" Padding="10" Background="Yellow" Text="{Binding MainMessage}"/>
    </StackPanel>
</UserControl>

TRY #4: OK, that was a dumb oversight of mine, forgot the StaticResource, but even then both Try #2 and Try #3 get a new error The type System.Windows.Trigger in Style is unknown:

<UserControl x:Class="TestItemsSource234.Views.SmartForm"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style x:Key="MainMessageStyle" TargetType="TextBlock">
            <Style.Triggers>
                <Trigger>
                    <Trigger Property="Text" Value="">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </Trigger>
                </Trigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <StackPanel Margin="10">        
        <TextBlock Style="{StaticResource MainMessageStyle}" Padding="10" Background="Yellow" Text="{Binding MainMessage}"/>
    </StackPanel>
</UserControl>

So how do I do this?

ANSWER:

OK, so that was a maddening syntax hunt with a happy end, here's the version that works, hope it helps somebody, lessons learned:

  • if trigger, then style
  • if style, then StaticResource
  • if binding, then DataTrigger

code that works:

<UserControl x:Class="TestItemsSource234.Views.SmartForm"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style x:Key="MainMessageStyle" TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding MainMessage}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <StackPanel Margin="10">
        <ItemsControl
            ItemsSource="{Binding DataTypeViews}"/>
        <TextBlock Style="{StaticResource MainMessageStyle}" Padding="10" Background="Yellow" Text="{Binding MainMessage}"/>
    </StackPanel>
</UserControl>
3
ugh. why is wpf so awful?PeterAllenWebb
There is no excuse for the WPF team for why #4 does not just WORK! Why is WPF so cryptic?Martin Konicek

3 Answers

2
votes

Either Try #2 or Try #3 should be fine - the problem is in the line where you are referencing the style - you need to use either 'Style="{StaticResource [KeyName]}"' or 'Style="{DynamicResource [KeyName]}"'.

Try this (in Try #2):

<StackPanel Margin="10">        
    <TextBlock Style="{StaticResource MainMessageStyle}" Padding="10" Background="Yellow" Text="{Binding MainMessage}"/>
</StackPanel>

In Try 1 you reveal a limitation of current WPF versions: Triggers are not supported directly on elements.

5
votes

The easiest, simplest and quickest way are converters. So, why not KISS? (keep it simple, stupid)?

To implement converter classes only a few lines of code are necessary.

Converter:

public class StringToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return string.IsNullOrWhiteSpace((string)value) ? Visibility.Collapsed : Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var visiblity = (Visibility)value;
        return visiblity == Visibility.Visible;
    }
}

Usage:

Include namespace: xmlns:c="clr-namespace:Hfr.Windows.Controls"

Define resource: <c:StringToVisibilityConverter x:Key="StringToVisiblity"/>

Use it:

<TextBlock
    Text="{Binding SomeStringPropertyValue}"
    TextWrapping="Wrap"
    Visibility="{Binding SomeStringPropertyValue, Converter={StaticResource StringToVisiblity}}" />

Since you'll use your converters in multiple projects, implement the converter classes in a "common library".

1
votes

I think the simplest way to do this would be to define a Converter which converts string to visibility.

   ...

   return string.IsNullOrEmpty(s) ? Visibility.Collapsed : Visibility.Visible;
}

Then just

<TextBlock Visibility="{StaticResource StringToVisibilityConverter}"