DataTrigger wo Wert NICHT null ist?

162

Ich weiß, dass ich einen Setter erstellen kann, der prüft, ob ein Wert NULL ist, und etwas tun kann. Beispiel:

<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>

Aber wie kann ich nach einem "nicht" -Wert suchen ... wie in "NOT NULL" oder "NOT = 3"? Ist das in XAML möglich?

Ergebnisse: Vielen Dank für Ihre Antworten ... Ich wusste, dass ich einen Wertekonverter ausführen kann (was bedeutet, dass ich Code eingeben muss, und das wäre keine reine XAML, wie ich es mir erhofft habe). Dies beantwortet jedoch die Frage, dass Sie dies in reinem XAML nicht mit "Nein" tun können. Die ausgewählte Antwort zeigt jedoch wahrscheinlich den besten Weg, um diese Art von Funktionalität zu erstellen . Guter Fund.

Timothy Khouri
quelle

Antworten:

42

Ich bin bei DataTriggers auf eine ähnliche Einschränkung gestoßen, und es scheint, dass Sie nur die Gleichheit überprüfen können. Das Nächste, was ich gesehen habe, das Ihnen helfen könnte, ist eine Technik, mit der Sie andere Arten von Vergleichen als Gleichheit durchführen können.

In diesem Blogbeitrag wird beschrieben, wie Vergleiche wie LT, GT usw. in einem DataTrigger durchgeführt werden.

Diese Einschränkung des DataTrigger kann bis zu einem gewissen Grad umgangen werden, indem ein Konverter verwendet wird, um die Daten in einen speziellen Wert zu massieren, mit dem Sie vergleichen können, wie in der Antwort von Robert Macnee vorgeschlagen.

J c
quelle
10
Interessanterweise verfügt der DataTrigger tatsächlich über ein internes Feld, das steuert, ob er auf Gleichheit prüft oder nicht. Leider müssen Sie eine angemessene Menge an Reflexionen durchführen, um zum gewünschten Feld zu gelangen. Das Problem ist, dass es in der nächsten Version von .net möglicherweise kaputt geht.
Caleb Vear
154

Sie können hierfür einen IValueConverter verwenden:

<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>

Wo IsNullConverter an anderer Stelle definiert ist (und conv so eingestellt ist, dass es auf seinen Namespace verweist):

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.");
    }
}

Eine allgemeinere Lösung wäre die Implementierung eines IValueConverter, der die Gleichheit mit dem ConverterParameter überprüft, sodass Sie gegen alles und nicht nur gegen Null prüfen können.

Robert Macnee
quelle
6
Ich nehme an, Sie könnten den Konverter etwas allgemeiner gestalten und ConverterParameter verwenden, um einen Vergleichswert zu übergeben (um sowohl den Vergleich mit NOT null als auch NOT 3 zu unterstützen.
J c
Das hat mir sehr gut gefallen - mit einem Mehrfachauslöser ist es schön und kraftvoll.
Bertie
149

Dies ist ein kleiner Cheat, aber ich habe nur einen Standardstil festgelegt und ihn dann mit einem DataTrigger überschrieben, wenn der Wert null ist ...

  <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
quelle
1
Dies war die beste Lösung für mein Szenario! Vielen Dank für diese Antwort!
Scott
14
Ich denke nicht, dass dies ein Hack ist, Sie müssen dies viel Zeit tun; und dies ist der sauberste Weg, dies zu tun.
Akjoshi
3
Der Standard-Setter kann ohne das Style.Setter-Tag verwendet werden.
Naser Asadi
1
Nur das Ticket! Ich habe die Standardeinstellung in das Steuerelement übernommen, dem der Stil gehört, und konnte nicht herausfinden, warum es meine Stile immer wieder überschrieb :-) Danke!
Heliac
2
viel besser antworten als mit einem Konverter ... einfach und sauber.
DasDas
27

Vergleiche mit null (wie Michael Noonan sagte):

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

Vergleiche mit nicht null (ohne Konverter):

<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
quelle
4
Dies ist bei weitem die direkteste Antwort. Ich mag das!
TimothyP
15

Ich verwende dies, um eine Schaltfläche nur zu aktivieren, wenn ein Listenansichtselement ausgewählt ist (dh nicht null):

<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
quelle
4
Manchmal ist die einfachste Lösung in der Übersicht verborgen. Ich glaube, XAML-Code wird von oben nach unten interpretiert. Auf diese Weise wird die Schaltfläche zuerst aktiviert und dann deaktiviert, wenn kein Element in der Listenansicht ausgewählt ist. Aber bitte sagen Sie mir, wird der Stil aktualisiert, sobald ein Element in der Listenansicht ausgewählt wurde?
Froeschli
Die Schaltfläche ist aktiviert, wenn ein Listenelement ausgewählt ist, ja.
SteveCav
14

Sie können eine DataTriggerKlasse in Microsoft.Expression.Interactions.dll verwenden , die mit Expression Blend geliefert wird .

Codebeispiel:

<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>

Mit dieser Methode können Sie auch gegen GreaterThanund auslösen LessThan. Um diesen Code zu verwenden, sollten Sie auf zwei DLLs verweisen:

System.Windows.Interactivity.dll

Microsoft.Expression.Interactions.dll

Yossharel
quelle
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>

Ich habe hier nur die inverse Logik verwendet ... mein Stackpanel auf unsichtbar zu setzen, wenn mein Comboitem nicht gefüllt ist, es funktioniert ziemlich gut!

aromore
quelle
6

Halt! Kein Konverter! Ich möchte die Bibliothek dieses Typen nicht "verkaufen", aber ich hasste es, jedes Mal Konverter zu machen, wenn ich Dinge in XAML vergleichen wollte.

Also mit dieser Bibliothek: https://github.com/Alex141/CalcBinding

Sie können das tun [und vieles mehr]:

Erstens: In der Deklaration von windows / userControl:

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

dann im Textblock

<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>

Der magische Teil ist der Conv: Binding 'MYValue == null' . Tatsächlich können Sie jede gewünschte Bedingung festlegen [siehe Dokument].

Beachten Sie, dass ich kein Fan von Dritten bin. Diese Bibliothek ist jedoch kostenlos und hat nur geringe Auswirkungen (fügen Sie einfach 2 DLL zum Projekt hinzu).

Simon
quelle
5

Meine Lösung befindet sich in der DataContext-Instanz (oder in ViewModel, wenn MVVM verwendet wird). Ich füge eine Eigenschaft hinzu, die true zurückgibt, wenn die gewünschte Nicht-Null-Bedingung erfüllt ist.

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

und binden Sie den DataTrigger an die obige Eigenschaft. Hinweis: Verwenden Sie in VB.NET unbedingt den Operator If und NICHT die IIf-Funktion, die mit Null-Objekten nicht funktioniert. Dann ist die XAML:

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

Wenn Sie nach einer Lösung suchen, die IValueConverter nicht verwendet, können Sie immer den folgenden Mechanismus verwenden

       <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
quelle
2

Konverter:

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

Bindung:

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

Sie können einen Konverter verwenden oder eine neue Eigenschaft in Ihrem ViewModel erstellen:

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

und benutze es:

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