DataTrigger, где значение НЕ является нулевым?

163

Я знаю, что могу сделать установщик, который проверяет, является ли значение NULL, и что-то делает. Пример:

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

Но как я могу проверить значение "not" ... как в "NOT NULL" или "NOT = 3"? Это возможно в XAML?

Результаты: Спасибо за ваши ответы ... Я знал, что смогу сделать конвертер значений (что означает, что мне придется идти в коде, а это не будет чистый XAML, как я надеялся). Однако это отвечает на вопрос: «Нет», вы не можете сделать это в чистом XAML. Выбранный ответ, однако, показывает, вероятно, лучший способ создать такую ​​функциональность. Хорошая находка.

Тимоти Хоури
источник

Ответы:

42

Я столкнулся с аналогичным ограничением с DataTriggers, и казалось бы, что вы можете проверить только на равенство. Самая близкая вещь, которую я видел, может помочь вам - это метод для других сравнений, отличных от равенства.

В этом блоге рассказывается, как выполнять сравнения, такие как LT, GT и т. Д. В DataTrigger.

Это ограничение DataTrigger можно в некоторой степени обойти, используя Converter для преобразования данных в специальное значение, с которым вы затем можете сравнить, как это предлагается в ответе Роберта Макни.

J c
источник
10
Интересно, что DataTrigger фактически имеет внутреннее поле, которое контролирует, проверяет ли оно равенство или нет. К сожалению, вы должны сделать разумное количество размышлений, чтобы добраться до нужного поля. Проблема в том, что он может сломаться в следующей версии .net.
Калеб Вир
155

Вы можете использовать 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>

Где IsNullConverter определен в другом месте (а conv устанавливается для ссылки на его пространство имен):

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

Более общим решением было бы реализовать IValueConverter, который проверяет равенство с ConverterParameter, так что вы можете проверять что угодно, а не только ноль.

Роберт Макни
источник
6
Я полагаю, вы могли бы сделать конвертер немного более универсальным и использовать ConverterParameter для передачи значения, с которым сравнивать (для поддержки как сравнения с нулевым, так и с NOT 3.
J c
Это сработало для меня - используя Multiple Trigger, это делает его красивым и мощным.
Берти
150

Это немного обманывает, но я просто установил стиль по умолчанию, а затем переопределил его, используя DataTrigger, если значение равно нулю ...

  <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
источник
1
Это было лучшее решение для моего сценария! Спасибо за предоставленный ответ!
Скотт
14
Я не думаю, что это взлом, вам нужно делать это много времени; и это самый чистый способ сделать это.
Акёши
3
Установщик по умолчанию может использоваться без тега Style.Setter.
Насер Асади
1
Просто билет! Я продолжал помещать значение по умолчанию в элемент управления, которому принадлежит стиль, и не мог понять, почему он по-прежнему перекрывает мои стили :-) Спасибо!
Ригардт Стейн
2
способ лучше ответить, чем использовать конвертер ... просто и чисто.
DasDas
27

Сравните с нулем (как сказал Майкл Нунан):

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

Сравните с ненулевым (без конвертера):

<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
источник
4
Это, безусловно, самый прямой ответ. Мне это нравится!
TimothyP
15

Я использую это, чтобы включить кнопку, только если выбран элемент списка (то есть, не ноль):

<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
источник
4
Иногда самое простое решение скрыто в чистом виде. Я полагаю, код XAML интерпретируется сверху вниз. Таким образом, кнопка сначала будет включена, а затем отключена, если не выбран ни один элемент в списке. Но, пожалуйста, скажите, обновляется ли стиль после выбора элемента в списке?
froeschli
Кнопка активна, когда выбран элемент списка, да.
SteveCav
14

Вы можете использовать DataTriggerкласс в Microsoft.Expression.Interactions.dll, которые поставляются с Expression Blend .

Пример кода:

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

Используя этот метод, вы можете активировать против GreaterThanи LessThanтоже. Чтобы использовать этот код, вы должны ссылаться на две DLL:

System.Windows.Interactivity.dll

Microsoft.Expression.Interactions.dll

yossharel
источник
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>

Я просто использовал обратную логику здесь ... устанавливая мою стековую панель как невидимую, когда мой комбинированный элемент не заполнен, он работает довольно хорошо!

aromore
источник
6

Стоп! Нет конвертера! Я не хочу «продавать» библиотеку этого парня, но я ненавидел факт создания конвертера каждый раз, когда хотел сравнить материал в XAML.

Так и с этой библиотекой: https://github.com/Alex141/CalcBinding

Вы можете сделать это [и многое другое]:

Во-первых, в объявлении windows / userControl:

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

затем в текстовом блоке

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

Волшебная часть - это хит: привязка 'MYValue == null' . Фактически, вы можете установить любое условие, какое захотите [посмотрите на документ].

обратите внимание, что я не фанат третьей стороны. но эта библиотека бесплатна и мало влияет (просто добавьте 2 .dll в проект).

Саймон
источник
5

Мое решение в экземпляре DataContext (или ViewModel, если используется MVVM). Я добавляю свойство, которое возвращает true, если выполняется условие Not Null, которое я хочу.

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

и привязать DataTrigger к вышеуказанному свойству. Примечание. В VB.NET обязательно используйте оператор If и NOT функцию IIf, которая не работает с нулевыми объектами. Тогда XAML это:

    <DataTrigger Binding="{Binding IsSomeFieldNull}" Value="False">
      <Setter Property="TextBlock.Text" Value="It's NOT NULL Baby!" />
    </DataTrigger>
APaglia
источник
3

Если вы ищете решение, которое не использует IValueConverter, вы всегда можете использовать механизм ниже

       <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

Преобразователь:

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

Переплет:

Visibility="{Binding PropertyToBind, Converter={StaticResource nullableToVisibilityConverter}}"
abatishchev
источник
2

Вы можете использовать конвертер или создать новое свойство в вашей ViewModel следующим образом:

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

и использовать это:

<DataTrigger Binding="{Binding SomeField}" Value="{Binding CanDoIt}">
Butsaty
источник