Подтвердить что ты не робот

Использование привязки для свойства Value условия DataTrigger

Я работаю над приложением WPF и борюсь с триггером данных. Я хотел бы привязать значение условия триггера к некоторому объекту, который у меня есть:

<DataTrigger Binding="{Binding Foo}" 
             Value="{Binding ElementName=AnotherElement, Path=Bar}">..

Однако мне не разрешено, так как не представляется возможным использовать привязки для свойства Value. Это? Могу ли я это достичь? Я получаю следующую ошибку:

"Связывание" не может быть установлено в свойстве "Значение" типа "DataTrigger". "Связывание" может быть установлено только в DependencyProperty объекта DependencyObject.

4b9b3361

Ответ 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}">..