DataTrigger où la valeur n'est PAS nulle?

163

Je sais que je peux créer un setter qui vérifie si une valeur est NULL et faire quelque chose. Exemple:

<TextBlock>
  <TextBlock.Style>
    <Style>
      <Style.Triggers>
        <DataTrigger Binding="{Binding SomeField}" Value="{x:Null}">
          <Setter Property="TextBlock.Text" Value="It's NULL Baby!" />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </TextBlock.Style>
</TextBlock>

Mais comment puis-je vérifier une valeur "non" ... comme dans "NOT NULL" ou "NOT = 3"? Est-ce possible en XAML?

Résultats: Merci pour vos réponses ... Je savais que je pourrais faire un convertisseur de valeur (ce qui signifie que je devrais entrer dans le code, et ce ne serait pas du XAML pur comme je l'espérais). Cependant, cela répond à la question selon laquelle effectivement "non" vous ne pouvez pas le faire en XAML pur. La réponse choisie, cependant, montre probablement la meilleure façon de créer ce type de fonctionnalité. Bonne trouvaille.

Timothy Khouri
la source

Réponses:

42

J'ai rencontré une limitation similaire avec DataTriggers, et il semblerait que vous ne puissiez vérifier l'égalité. La chose la plus proche que j'ai vue qui pourrait vous aider est une technique pour faire d'autres types de comparaisons autres que l'égalité.

Cet article de blog décrit comment effectuer des comparaisons telles que LT, GT, etc. dans un DataTrigger.

Cette limitation du DataTrigger peut être contournée dans une certaine mesure en utilisant un convertisseur pour masser les données en une valeur spéciale que vous pouvez ensuite comparer, comme suggéré dans la réponse de Robert Macnee.

J c
la source
10
Fait intéressant, le DataTrigger a en fait un champ interne qui contrôle s'il teste l'égalité ou non. Malheureusement, vous devez faire une réflexion raisonnable pour accéder au champ requis. Le problème est qu'il peut se briser dans la prochaine version de .net.
Caleb Vear le
155

Vous pouvez utiliser un IValueConverter pour cela:

<TextBlock>
    <TextBlock.Resources>
        <conv:IsNullConverter x:Key="isNullConverter"/>
    </TextBlock.Resources>
    <TextBlock.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding SomeField, Converter={StaticResource isNullConverter}}" Value="False">
                    <Setter Property="TextBlock.Text" Value="It's NOT NULL Baby!"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

Où IsNullConverter est défini ailleurs (et conv est défini pour référencer son espace de noms):

public class IsNullConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value == null);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new InvalidOperationException("IsNullConverter can only be used OneWay.");
    }
}

Une solution plus générale serait d'implémenter un IValueConverter qui vérifie l'égalité avec le ConverterParameter, afin que vous puissiez vérifier quoi que ce soit, et pas seulement null.

Robert Macnee
la source
6
Je suppose que vous pourriez rendre le convertisseur un peu plus générique et utiliser ConverterParameter pour passer une valeur à comparer (afin de prendre en charge à la fois la comparaison avec NOT null et NOT 3.
J c
Cela a fonctionné un régal pour moi - en utilisant un déclencheur multiple, cela le rend agréable et puissant.
Bertie
150

C'est un peu une triche mais je viens de définir un style par défaut, puis je le remplace en utilisant un DataTrigger si la valeur est nulle ...

  <Style> 
      <!-- Highlight for Reviewed (Default) -->
      <Setter Property="Control.Background" Value="PaleGreen" /> 
      <Style.Triggers>
        <!-- Highlight for Not Reviewed -->
        <DataTrigger Binding="{Binding Path=REVIEWEDBY}" Value="{x:Null}">
          <Setter Property="Control.Background" Value="LightIndianRed" />
        </DataTrigger>
      </Style.Triggers>
  </Style>
Jamaxack
la source
1
C'était la meilleure solution pour mon scénario! Merci d'avoir fourni cette réponse!
Scott
14
Je ne pense pas que ce soit un hack, vous devez le faire beaucoup de temps; et c'est la manière la plus propre de le faire.
akjoshi
3
Le Setter par défaut peut être utilisé sans la balise Style.Setter.
Naser Asadi
1
Juste le ticket! J'ai continué à mettre la valeur par défaut dans le contrôle qui possède le style, et je ne pouvais pas comprendre pourquoi il continuait à remplacer mes styles :-) Merci!
Riegardt Steyn
2
réponse bien meilleure que l'utilisation d'un convertisseur ... simple et propre.
DasDas
27

Comparez avec nul (comme Michael Noonan l'a dit):

<Style>
    <Style.Triggers>
       <DataTrigger Binding="{Binding SomeProperty}" Value="{x:Null}">
           <Setter Property="Visibility" Value="Collapsed" />
        </DataTrigger>
     </Style.Triggers>
</Style>

Comparer avec non nul (sans convertisseur):

<Style>
    <Setter Property="Visibility" Value="Collapsed" />
    <Style.Triggers>
       <DataTrigger Binding="{Binding SomeProperty}" Value="{x:Null}">
           <Setter Property="Visibility" Value="Visible" />
        </DataTrigger>
     </Style.Triggers>
</Style>
JoanComasFdz
la source
4
C'est de loin la réponse la plus simple. Je l'aime!
TimothyP
15

J'utilise ceci pour activer uniquement un bouton si un élément de liste est sélectionné (c'est-à-dire non nul):

<Style TargetType="{x:Type Button}">
    <Setter Property="IsEnabled" Value="True"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding ElementName=lvMyList, Path=SelectedItem}" Value="{x:Null}">
            <Setter Property="IsEnabled" Value="False"/>
        </DataTrigger>
    </Style.Triggers>
</Style>
SteveCav
la source
4
Parfois, la solution la plus simple est cachée en vue claire. Je crois que le code XAML est interprété de haut en bas. De cette façon, le bouton sera d'abord activé, puis désactivé si aucun élément de la liste n'est sélectionné. Mais s'il vous plaît dites-moi, le style est-il mis à jour une fois qu'un élément est sélectionné dans la liste?
froeschli
Le bouton est activé lorsqu'un élément de liste est sélectionné, oui.
SteveCav
14

Vous pouvez utiliser la DataTriggerclasse dans Microsoft.Expression.Interactions.dll fournie avec Expression Blend .

Exemple de code:

<i:Interaction.Triggers>
    <i:DataTrigger Binding="{Binding YourProperty}" Value="{x:Null}" Comparison="NotEqual">
       <ie:ChangePropertyAction PropertyName="YourTargetPropertyName" Value="{Binding YourValue}"/>
    </i:DataTrigger
</i:Interaction.Triggers>

En utilisant cette méthode, vous pouvez déclencher contre GreaterThanet LessThanaussi. Pour utiliser ce code, vous devez référencer deux DLL:

System.Windows.Interactivity.dll

Microsoft.Expression.Interactions.dll

Yossharel
la source
6
<StackPanel.Style>
  <Style>
    <Setter Property="StackPanel.Visibility" Value="Visible"></Setter>
    <Style.Triggers>
      <DataTrigger  Binding="{Binding ElementName=ProfileSelectorComboBox, Path=SelectedItem.Tag}" Value="{x:Null}">
          <Setter Property="StackPanel.Visibility" Value="Collapsed"></Setter>
      </DataTrigger>
    </Style.Triggers>
  </Style>
</StackPanel.Style>

Je viens d'utiliser la logique inverse ici ... mettre mon stackpanel à invisible quand mon comboitem n'est pas peuplé, ça marche plutôt bien!

aromore
la source
6

Arrêtez! Pas de convertisseur! Je ne veux pas "vendre" la bibliothèque de ce type, mais je détestais le fait de faire du convertisseur à chaque fois que je voulais comparer des choses en XAML.

Donc avec cette bibliothèque: https://github.com/Alex141/CalcBinding

vous pouvez le faire [et bien plus encore]:

Tout d'abord, dans la déclaration de windows / userControl:

<Windows....
     xmlns:conv="clr-namespace:CalcBinding;assembly=CalcBinding"
>

puis, dans le bloc de texte

<TextBlock>
      <TextBlock.Style>
          <Style.Triggers>
          <DataTrigger Binding="{conv:Binding 'MyValue==null'}" Value="false">
             <Setter Property="Background" Value="#FF80C983"></Setter>
          </DataTrigger>
        </Style.Triggers>
      </TextBlock.Style>
    </TextBlock>

La partie magique est la conv: Binding 'MYValue == null' . En fait, vous pouvez définir n'importe quelle condition que vous vouliez [regardez le document].

notez que je ne suis pas fan de tiers. mais cette librairie est gratuite, et peu impactante (il suffit d'ajouter 2 .dll au projet).

Simon
la source
5

Ma solution est dans l'instance DataContext (ou ViewModel si vous utilisez MVVM). J'ajoute une propriété qui renvoie true si la condition Not Null que je veux est remplie.

    Public ReadOnly Property IsSomeFieldNull() As Boolean
        Get
            Return If(SomeField is Null, True, False)
        End Get
    End Property

et liez le DataTrigger à la propriété ci-dessus. Remarque: Dans VB.NET, veillez à utiliser l'opérateur If et PAS la fonction IIf, qui ne fonctionne pas avec les objets Null. Alors le XAML est:

    <DataTrigger Binding="{Binding IsSomeFieldNull}" Value="False">
      <Setter Property="TextBlock.Text" Value="It's NOT NULL Baby!" />
    </DataTrigger>
APaglia
la source
3

Si vous recherchez une solution qui n'utilise pas IValueConverter, vous pouvez toujours utiliser le mécanisme ci-dessous

       <StackPanel>
            <TextBlock Text="Border = Red when null value" />
            <Border x:Name="border_objectForNullValueTrigger" HorizontalAlignment="Stretch" Height="20"> 
                <Border.Style>
                    <Style TargetType="Border">
                        <Setter Property="Background" Value="Black" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ObjectForNullValueTrigger}" Value="{x:Null}">
                                <Setter Property="Background" Value="Red" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <TextBlock Text="Border = Green when not null value" />
            <Border HorizontalAlignment="Stretch" Height="20">
                <Border.Style>
                    <Style TargetType="Border">
                        <Setter Property="Background" Value="Green" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Background, ElementName=border_objectForNullValueTrigger}" Value="Red">
                                <Setter Property="Background" Value="Black" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <Button Content="Invert Object state" Click="Button_Click_1"/>
        </StackPanel>
Chaitanya Kadamati
la source
2

Convertisseur:

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

Contraignant:

Visibility="{Binding PropertyToBind, Converter={StaticResource nullableToVisibilityConverter}}"
abatishchev
la source
2

Vous pouvez utiliser un convertisseur ou créer une nouvelle propriété dans votre ViewModel comme ça:

public bool CanDoIt
{
    get
    {
        return !string.IsNullOrEmpty(SomeField);
    }
}

et utilisez-le:

<DataTrigger Binding="{Binding SomeField}" Value="{Binding CanDoIt}">
Butsaty
la source