Использование привязки для свойства Value условия DataTrigger
Я работаю над приложением WPF и борюсь с триггером данных. Я хотел бы привязать значение условия триггера к некоторому объекту, который у меня есть:
<DataTrigger Binding="{Binding Foo}"
Value="{Binding ElementName=AnotherElement, Path=Bar}">..
Однако мне не разрешено, так как не представляется возможным использовать привязки для свойства Value. Это? Могу ли я это достичь? Я получаю следующую ошибку:
"Связывание" не может быть установлено в свойстве "Значение" типа "DataTrigger". "Связывание" может быть установлено только в DependencyProperty объекта DependencyObject.
Ответы
Ответ 1
Нет, это невозможно. Как сказано в сообщении об ошибке, только свойства зависимостей могут быть объектами привязок WPF, а DataTrigger.Value не является свойством зависимостей. Поэтому вам нужно будет присвоить фактическое значение.
Обходной путь заключается в использовании MultiBinding, дочерние привязки которого являются двумя связями, которые вы хотите сравнить, с IMultiValueConverter, который возвращает true, если два входа равны и ложны, если они неравны. Затем DataTrigger может использовать этот MultiBinding и значение True.
Ответ 2
Чтобы подробнее ответить на этот вопрос, здесь является примером того, как это делается.
Ответ 3
Вот пример с конвертером IMultiValueConverter
.
В этом XAML есть три флажка. Первые два связаны со свойствами Foo и Bar (оба являются логическими). Третий использует множественное связывание с IMultiValueConverter. Проверяется, когда Foo и Bar имеют одинаковые значения.
<!-- It expected that the DataContext of this StackPanel has boolean Bar and Foo properties.. -->
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<!-- local contains the MultiValueEqualityConverter class implementation -->
<local:MultiValueEqualityConverter x:Key="multiValueEqualityConverter"/>
</StackPanel.Resources>
<CheckBox IsChecked="{Binding Foo}">Foo</CheckBox>
<CheckBox IsChecked="{Binding Bar}">Bar</CheckBox>
<CheckBox IsEnabled="False" Content="Are the same">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Setters>
<Setter Property="IsChecked" Value="False"/>
</Style.Setters>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource multiValueEqualityConverter}">
<Binding RelativeSource="{RelativeSource self}" Path="DataContext.Foo" Mode="OneWay" />
<Binding RelativeSource="{RelativeSource self}" Path="DataContext.Bar" Mode="OneWay"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</StackPanel>
Простая реализация IMultiValueConverter
для односторонней привязки:
public class MultiValueEqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values?.All(o => o?.Equals(values[0]) == true) == true || values?.All(o => o == null) == true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Ответ 4
Я использую MVVM. Поскольку я несколько раз возвращался к этому ответу, стоит упомянуть, что каждый раз я получаю один и тот же результат:
Сделайте модель для каждого элемента, а не сравнивайте ее.
Например, у меня обычно есть стек строк в ItemsControl.DataTemplate
. Я пытаюсь установить IsEnabled
(или что-то еще), используя DataTrigger
, сравнивая с динамическим Value={Binding}
.
Примерно в то же время мой кодовый живот проваливается, и я направляюсь в СЦ, и в конечном итоге здесь.
Затем я почти всегда решаю вести список моделей строк во ViewModel, которые обрабатывают свои собственные IsEnabled
и уведомляют пользовательский интерфейс соответствующим образом.
Я использую их для ItemsControl.Source
, потом удивляюсь, почему я не просто сделал это с самого начала.
Ответ 5
Свойство Value объекта DataTrigger не является DependencyProperty
, которое можно связать. Поэтому мы RegisterAttached
имеем свойство зависимостей, которое может связывать и устанавливать значение свойства DataTrigger Value каждый раз, когда устанавливается значение свойства присоединенной зависимости.
Вот пример класса DataTriggerAssists
public class DataTriggerAssists
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.RegisterAttached(
"Value",
typeof(object),
typeof(DataTriggerAssists),
new FrameworkPropertyMetadata(null, OnValueChanged));
public static object GetValue(DependencyObject d)
{
return d.GetValue(ValueProperty);
}
public static void SetValue(DependencyObject d, object value)
{
d.SetValue(ValueProperty, value);
}
public static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
if (d is DataTrigger trigger)
trigger.Value = args.NewValue;
}
}
Объявите префикс с именем as
с пространством имен класса DataTriggerAssists
.
Тогда вы можете использовать как это.
<DataTrigger Binding="{Binding Foo}"
as:DataTriggerAssists.Value="{Binding ElementName=AnotherElement, Path=Bar}">..