DataTrigger em que o valor NÃO é nulo?

162

Eu sei que posso fazer um setter que verifica se um valor é NULL e faz alguma coisa. Exemplo:

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

Mas como posso verificar um valor "not" ... como em "NOT NULL" ou "NOT = 3"? Isso é possível no XAML?

Resultados: Obrigado por suas respostas ... Eu sabia que poderia fazer um conversor de valor (o que significa que eu precisaria digitar o código e isso não seria puro XAML como eu esperava). No entanto, isso responde à pergunta de que efetivamente "não" não é possível em XAML puro. A resposta selecionada, no entanto, mostra provavelmente a melhor maneira de criar esse tipo de funcionalidade. Boa descoberta.

Timothy Khouri
fonte

Respostas:

42

Eu encontrei uma limitação semelhante com o DataTriggers, e parece que você só pode verificar a igualdade. A coisa mais próxima que vi que pode ajudá-lo é uma técnica para fazer outros tipos de comparação além da igualdade.

Esta postagem do blog descreve como fazer comparações como LT, GT, etc. em um DataTrigger.

Essa limitação do DataTrigger pode ser contornada em certa medida, usando um Conversor para massagear os dados em um valor especial com o qual você pode comparar, conforme sugerido na resposta de Robert Macnee.

J c
fonte
10
Curiosamente, o DataTrigger realmente tem um campo interno que controla se ele testa a igualdade ou não. Infelizmente você precisa refletir bastante para chegar ao campo necessário. O problema é que ele pode ser interrompido na próxima versão do .net.
Caleb Vear 27/02/09
154

Você pode usar um IValueConverter para isso:

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

Onde IsNullConverter é definido em outro lugar (e conv é definido para referenciar seu espaço para nome):

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

Uma solução mais geral seria implementar um IValueConverter que verifique a igualdade com o ConverterParameter, para que você possa verificar qualquer coisa, e não apenas nulo.

Robert Macnee
fonte
6
Eu suponho que você poderia fazer o conversor de um pouco mais genérico e utilização ConverterParameter para passar em um valor para comparar com (a fim de apoiar tanto em comparação com não nulo e não 3.
J c
Isso funcionou muito bem para mim - usar um Multiple Trigger, o torna agradável e poderoso.
Bertie
149

Isso é meio trapaceiro, mas eu apenas defini um estilo padrão e o substituí usando um DataTrigger se o valor for 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>
Jamaxack
fonte
1
Esta foi a melhor solução para o meu cenário! Obrigado por fornecer esta resposta!
24410 Scott
14
Eu não acho que isso seja um hack, você precisa fazer isso muito tempo; e esta é a maneira mais limpa de fazer isso.
akjoshi
3
O Setter padrão pode ser usado sem a etiqueta Style.Setter.
Naser Asadi
1
Apenas o ingresso! Continuei colocando o padrão no controle que possui o estilo e não conseguia descobrir por que ele continuava substituindo meus estilos :-) Obrigado!
Riegardt Steyn
2
resposta muito melhor do que usar um conversor ... simples e limpo.
DasDas 03/09/2015
27

Compare com null (como disse Michael Noonan):

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

Compare com not null (sem um conversor):

<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
fonte
4
Esta é de longe a resposta mais direta. Eu gosto disso!
precisa saber é o seguinte
15

Estou usando isso para ativar apenas um botão se um item de exibição de lista estiver selecionado (ou seja, não 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>
SteveCav
fonte
4
Às vezes, a solução mais simples fica oculta. Eu acredito que o código XAML é interpretado de cima para baixo. Dessa forma, o botão será ativado e desativado primeiro se nenhum elemento na exibição em lista estiver selecionado. Mas, por favor, diga-me, o estilo é atualizado depois que um item é selecionado na lista?
froeschli
O botão é ativado quando um item da lista é selecionado, sim.
SteveCav
14

Você pode usar a DataTriggerclasse no Microsoft.Expression.Interactions.dll que acompanha o Expression Blend .

Exemplo de código:

<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 esse método, você pode acionar contra GreaterThane LessThantambém. Para usar esse código, você deve referenciar duas DLLs:

System.Windows.Interactivity.dll

Microsoft.Expression.Interactions.dll

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

Acabei de usar a lógica inversa aqui ... configurando meu stackpanel para invisível quando meu comboitem não está preenchido, funciona muito bem!

mais
fonte
6

Pare! Sem conversor! Eu não quero "vender" a biblioteca desse cara, mas eu odiava o fato de fazer conversor toda vez que queria comparar coisas em XAML.

Portanto, com esta biblioteca: https://github.com/Alex141/CalcBinding

você pode fazer isso [e muito mais]:

Primeiro, na declaração do windows / userControl:

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

então, no bloco 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>

A parte mágica é a conv: Vinculação 'MYValue == null' . De fato, você pode definir qualquer condição que desejar [consulte o documento].

note que eu não sou fã de terceiros. mas essa biblioteca é gratuita e tem pouco impacto (basta adicionar 2 .dll ao projeto).

Simon
fonte
5

Minha solução está na instância DataContext (ou ViewModel, se estiver usando MVVM). Eu adiciono uma propriedade que retorna true se a condição Não Nula que eu quero for atendida.

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

e vincule o DataTrigger à propriedade acima. Nota: No VB.NET, certifique-se de usar o operador If e NOT a função IIf, que não funciona com objetos nulos. Então o XAML é:

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

Se você está procurando uma solução que não use IValueConverter, sempre pode usar o mecanismo abaixo

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

Conversor:

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

Obrigatório:

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

Você pode usar um conversor ou criar uma nova propriedade no seu ViewModel assim:

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

e use-o:

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