DataTrigger donde el valor NO es nulo?


163

Sé que puedo hacer un setter que verifique si un valor es NULL y haga algo. Ejemplo:

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

Pero, ¿cómo puedo verificar si hay un valor "no" ... como en "NOT NULL" o "NOT = 3"? ¿Es eso posible en XAML?

Resultados: Gracias por sus respuestas ... Sabía que podía hacer un convertidor de valor (lo que significa que tendría que ir en código, y eso no sería puro XAML como esperaba). Sin embargo, eso responde a la pregunta de que efectivamente "no" no puedes hacerlo en XAML puro. La respuesta seleccionada, sin embargo, muestra probablemente la mejor manera de crear ese tipo de funcionalidad. Buen descubrimiento.

Respuestas:


42

Me encontré con una limitación similar con DataTriggers, y parece que solo puedes verificar la igualdad. Lo más cercano que he visto que podría ayudarlo es una técnica para hacer otros tipos de comparaciones que no sean la igualdad.

Esta publicación de blog describe cómo hacer comparaciones como LT, GT, etc. en un DataTrigger.

Esta limitación del DataTrigger se puede solucionar hasta cierto punto mediante el uso de un convertidor para masajear los datos en un valor especial con el que luego se puede comparar, como se sugiere en la respuesta de Robert Macnee.


10
Curiosamente, el DataTrigger realmente tiene un campo interno que controla si prueba la igualdad o no la igualdad. Desafortunadamente, debe hacer una cantidad razonable de reflexión para llegar al campo requerido. El problema es que puede romperse en la próxima versión de .net.
Caleb Vear

155

Puede usar un IValueConverter para esto:

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

Donde IsNullConverter se define en otro lugar (y conv se establece para hacer referencia a su espacio de nombres):

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

Una solución más general sería implementar un IValueConverter que verifique la igualdad con el ConverterParameter, para que pueda verificar cualquier cosa, y no solo nulo.


66
Supongo que podría hacer que el convertidor sea un poco más genérico y usar ConverterParameter para pasar un valor para comparar (con el fin de admitir tanto la comparación como NO nula y NO 3.
J c

Esto funcionó de maravilla para mí: al usar un disparador múltiple, lo hace agradable y poderoso.
Bertie

150

Esto es un poco tramposo, pero solo configuré un estilo predeterminado y luego lo anulé usando un DataTrigger si el valor es nulo ...

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

1
¡Esta fue la mejor solución para mi escenario! ¡Gracias por dar esta respuesta!
Scott

14
No creo que esto sea un truco, debes hacerlo mucho tiempo; y esta es la forma más limpia de hacer esto.
akjoshi

3
Setter predeterminado se puede usar sin la etiqueta Style.Setter.
Naser Asadi

1
¡Solo el boleto! Seguí poniendo el valor predeterminado en el control que posee el Estilo, y no pude entender por qué siguió anulando mis estilos :-) ¡Gracias!
Riegardt Steyn

2
mejor respuesta que usando un convertidor ... simple y limpio.
DasDas

27

Comparar con nulo (como dijo Michael Noonan):

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

Comparar con no nulo (sin convertidor):

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

44
Esta es, con mucho, la respuesta más directa. ¡Me gusta!
TimothyP

15

Estoy usando esto para habilitar solo un botón si se selecciona un elemento de vista de lista (es decir, no es nulo):

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

44
A veces, la solución más simple está oculta a la vista. Creo que el código XAML se interpreta de arriba a abajo. De esa manera, el botón se habilitará primero y luego se deshabilitará si no se selecciona ningún elemento en la vista de lista. Pero por favor dígame, ¿se actualiza el estilo una vez que se selecciona un elemento en la vista de lista?
froeschli

El botón se habilita cuando se selecciona un elemento de la lista, sí.
SteveCav

14

Puede usar la DataTriggerclase en Microsoft.Expression.Interactions.dll que viene con Expression Blend .

Código de muestra:

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

Usando este método puedes disparar contra GreaterThany LessThantambién. Para utilizar este código, debe hacer referencia a dos dll:

System.Windows.Interactivity.dll

Microsoft.Expression.Interactions.dll


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>

Acabo de usar la lógica inversa aquí ... establecer mi panel de pila en invisible cuando mi comboitem no está lleno, ¡funciona bastante bien!


6

¡Detener! ¡Sin convertidor! No quiero "vender" la biblioteca de este tipo, pero odiaba el hecho de hacer un conversor cada vez que quería comparar cosas en XAML.

Entonces, con esta biblioteca: https://github.com/Alex141/CalcBinding

puedes hacer eso [y mucho más]:

Primero, en la declaración de windows / userControl:

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

entonces, en el bloque de texto

<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 parte mágica es la conv: Enlace 'MYValue == null' . De hecho, puedes establecer cualquier condición que desees [mira el documento].

Tenga en cuenta que no soy un fanático de terceros. pero esta biblioteca es gratuita y tiene poco impacto (solo agregue 2 .dll al proyecto).


5

Mi solución está en la instancia de DataContext (o ViewModel si usa MVVM). Agrego una propiedad que devuelve verdadero si se cumple la condición No nula que deseo.

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

y vincular el DataTrigger a la propiedad anterior. Nota: en VB.NET, asegúrese de utilizar el operador If y NOT la función IIf, que no funciona con objetos nulos. Entonces el XAML es:

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

3

Si está buscando una solución que no use IValueConverter, siempre puede ir con el siguiente mecanismo

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

2

Convertidor:

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

Unión:

Visibility="{Binding PropertyToBind, Converter={StaticResource nullableToVisibilityConverter}}"

2

Puede usar un convertidor o crear una nueva propiedad en su ViewModel así:

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

y úsalo:

<DataTrigger Binding="{Binding SomeField}" Value="{Binding CanDoIt}">
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.