DataTrigger, gdzie wartość NIE jest null?

162

Wiem, że mogę utworzyć metodę ustawiającą, która sprawdza, czy wartość jest równa NULL i coś zrobić. Przykład:

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

Ale jak mogę sprawdzić, czy występuje wartość „nie” ... na przykład „NIE NULL” lub „NIE = 3”? Czy to możliwe w XAML?

Wyniki: Dziękuję za odpowiedzi ... Wiedziałem, że mogę zrobić konwerter wartości (co oznacza, że ​​będę musiał wprowadzić kod, a to nie będzie czysty XAML, jak się spodziewałem). Jednak to odpowiada na pytanie, że w rzeczywistości „nie”, nie można tego zrobić w czystym XAML. Wybrana odpowiedź wskazuje jednak prawdopodobnie najlepszy sposób na stworzenie takiej funkcjonalności. Dobre znalezisko.

Timothy Khouri
źródło

Odpowiedzi:

42

Napotkałem podobne ograniczenie w przypadku DataTriggers i wydaje się, że możesz sprawdzić tylko równość. Najbliższą rzeczą, jaką widziałem, która może ci pomóc, jest technika wykonywania innych typów porównań, innych niż równość.

W tym poście na blogu opisano, jak wykonywać porównania, takie jak LT, GT itp. W DataTrigger.

To ograniczenie DataTrigger można do pewnego stopnia obejść, używając konwertera w celu przekształcenia danych w specjalną wartość, z którą można następnie porównać, jak zasugerowano w odpowiedzi Roberta Macnee.

J c
źródło
10
Co ciekawe, DataTrigger ma w rzeczywistości pole wewnętrzne, które kontroluje, czy testuje równość, czy nie. Niestety, aby dostać się do wymaganego pola, musisz trochę się zastanowić. Problem w tym, że w następnej wersji .net może się zepsuć.
Caleb Vear
154

Możesz do tego użyć IValueConverter:

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

Gdzie IsNullConverter jest zdefiniowany w innym miejscu (a conv jest ustawiony tak, aby odwoływał się do swojej przestrzeni nazw):

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

Bardziej ogólnym rozwiązaniem byłoby zaimplementowanie IValueConverter, który sprawdza równość z parametrem ConverterParameter, dzięki czemu można sprawdzić wszystko, a nie tylko wartość null.

Robert Macnee
źródło
6
Przypuszczam, że możesz uczynić konwerter nieco bardziej ogólnym i użyć parametru ConverterParameter do przekazania wartości do porównania (w celu obsługi obu porównań z NOT null i NOT 3.
J c
To zadziałało dla mnie nie lada gratką - używając Multiple Trigger, sprawia, że ​​jest ładny i potężny.
Bertie
149

To trochę oszustwo, ale po prostu ustawiam domyślny styl, a następnie zastępuję go za pomocą DataTrigger, jeśli wartość jest zerowa ...

  <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
źródło
1
To było najlepsze rozwiązanie dla mojego scenariusza! Dziękujemy za udzielenie tej odpowiedzi!
Scott
14
Nie sądzę, żeby to był hack, musisz to robić dużo czasu; i jest to najczystszy sposób na zrobienie tego.
akjoshi
3
Default Setter może być używany bez znacznika Style.Setter.
Naser Asadi
1
Tylko bilet! Wciąż ustawiałem domyślną kontrolkę, która jest właścicielem stylu, i nie mogłem zrozumieć, dlaczego zastępuje moje style :-) Dziękuję!
Riegardt Steyn
2
o wiele lepsza odpowiedź niż użycie konwertera ... proste i przejrzyste.
DasDas
27

Porównaj z null (jak powiedział Michael Noonan):

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

Porównaj z not null (bez konwertera):

<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
źródło
4
To zdecydowanie najprostsza odpowiedź. Lubię to!
TimothyP,
15

Używam tego, aby włączyć przycisk tylko wtedy, gdy wybrany jest element widoku listy (tj. Nie jest pusty):

<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
źródło
4
Czasami najprostsze rozwiązanie jest ukryte w widocznym miejscu. Uważam, że kod XAML jest interpretowany od góry do dołu. W ten sposób przycisk zostanie najpierw włączony, a następnie wyłączony, jeśli żaden element w widoku listy nie zostanie wybrany. Ale proszę mi powiedzieć, czy styl jest aktualizowany po wybraniu elementu w widoku listy?
froeschli
Przycisk jest aktywny po wybraniu elementu listy, tak.
SteveCav
14

Możesz użyć DataTriggerklasy w Microsoft.Expression.Interactions.dll, które są dostarczane z Expression Blend .

Przykład kodu:

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

Korzystając z tej metody, możesz wywołać przeciw GreaterThani LessThanrównież. Aby użyć tego kodu, należy odwołać się do dwóch bibliotek dll:

System.Windows.Interactivity.dll

Microsoft.Expression.Interactions.dll

yossharel
źródło
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>

Właśnie użyłem tu odwrotnej logiki ... ustawiając mój stackpanel na niewidoczny, gdy mój comboitem nie jest zapełniony, działa całkiem nieźle!

aromore
źródło
6

Zatrzymać! Bez konwertera! Nie chcę "sprzedawać" biblioteki tego gościa, ale nienawidziłem faktu robienia konwertera za każdym razem, gdy chciałem porównać rzeczy w XAML.

Więc z tą biblioteką: https://github.com/Alex141/CalcBinding

możesz to zrobić [i dużo więcej]:

Po pierwsze, w deklaracji windows / userControl:

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

następnie w bloku tekstowym

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

Magiczną częścią jest konw .: Binding „MYValue == null” . W rzeczywistości możesz ustawić dowolny warunek, jaki chcesz [spójrz na dokument].

zauważ, że nie jestem fanem osób trzecich. ale ta biblioteka jest darmowa i ma niewielki wpływ (wystarczy dodać 2 pliki .dll do projektu).

Szymon
źródło
5

Moje rozwiązanie znajduje się w wystąpieniu DataContext (lub ViewModel, jeśli używasz MVVM). Dodaję właściwość, która zwraca wartość true, jeśli spełniony jest warunek Not Null, którego chcę.

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

i powiąż DataTrigger z powyższą właściwością. Uwaga: w VB.NET pamiętaj, aby użyć operatora If i NOT funkcji IIf, która nie działa z obiektami Null. Wtedy XAML to:

    <DataTrigger Binding="{Binding IsSomeFieldNull}" Value="False">
      <Setter Property="TextBlock.Text" Value="It's NOT NULL Baby!" />
    </DataTrigger>
APaglia
źródło
3

Jeśli szukasz rozwiązania nie wykorzystującego IValueConverter, zawsze możesz skorzystać z poniższego mechanizmu

       <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
źródło
2

Przetwornik:

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

Wiążący:

Visibility="{Binding PropertyToBind, Converter={StaticResource nullableToVisibilityConverter}}"
abatishchev
źródło
2

Możesz użyć konwertera lub utworzyć nową właściwość w swoim ViewModel w następujący sposób:

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

i użyj go:

<DataTrigger Binding="{Binding SomeField}" Value="{Binding CanDoIt}">
Butsaty
źródło