Вложенное связывание и конвейерное преобразование - PullRequest
2 голосов
/ 02 апреля 2011

Чтобы иметь менее избыточную разметку XAML, я пытаюсь получить общий элемент выбора типа радиокнопки, то есть я использую ItemsControl с перечислением как ItemsSource и создаю шаблон данных, который показывает, какой элемент выбран, проверяя совпадает ли значение перечисления с текущим значением.

Само по себе это невозможно сделать с помощью простого конвертера или DataTrigger, поскольку необходимы две привязки, поэтому я создал общий MutliValueConverter для проверки на равенство:

<CheckBox.Visibility>
    <MultiBinding Converter="{StaticResource EqualityComparisonConv}">
        <Binding Path="Key"/>
        <Binding Path="DisplayMode_Current" Source="{x:Static local:App.Settings}"/>
    </MultiBinding>
</CheckBox.Visibility>
public class EqualityComparisonConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length < 2) throw new Exception("At least two inputs are needed for comparison");
        bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) => { return x1.Equals(x2); });
        return output;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    #endregion
}

Очевидная проблема здесь в том, что конвертер возвращает логическое значение, которое мне нужно было бы преобразовать в Visibility сначала.

Заключение MultiBinding в другую привязку только с помощью конвертера не работает, потому что свойства не являются свойствами зависимостей (следовательно, им не может быть назначена привязка). Я мог бы подумать о нескольких обходных путях, таких как сохранение bool в каком-либо свойстве Tag, чтобы я мог использовать его как новый источник привязки, но меня больше интересовало бы что-то вроде этого:

<CheckBox.Visibility>
    <local:PipeConverter Converter="{StaticResource BooleanToVisibilityConv}">
        <MultiBinding Converter="{StaticResource EqualityComparisonConv}">
            <Binding Path="Key"/>
            <Binding Path="DisplayMode_Current" Source="{x:Static local:App.Settings}"/>
        </MultiBinding>
    </local:PipeConverter>
</CheckBox.Visibility>

Этот класс должен будет обновлять свои выходные данные, когда происходят изменения в исходной привязке, и он должен иметь возможность выставлять свое выходное значение свойству Visibility, но я не знаю, как этого добиться. Одна проблема, с которой приходится сталкиваться, заключается в том, что существует необходимость в свойствах зависимостей, поэтому было бы неплохо наследовать от DependencyObject, но наследование от класса Binding также имело бы смысл, поскольку PipeConverter должен связываться и должен быть установлен как значение другой зависимости свойство.

Ответы [ 2 ]

3 голосов
/ 04 апреля 2011

Основываясь на идее (см. ответ publicgk , опция C ), можно создать конвертер, который содержит коллекцию конвертеров, которые внутренне используются в последовательности.что соответствует моим потребностям.то есть я могу использовать MultiValueConverter в начале и направить вывод в список обычных преобразователей:

[ContentProperty("Converters")]
public class GroupConverter : IValueConverter, IMultiValueConverter
{
    private IMultiValueConverter _multiValueConverter;
    public IMultiValueConverter MultiValueConverter
    {
        get { return _multiValueConverter; }
        set { _multiValueConverter = value; }
    }

    private List<IValueConverter> _converters = new List<IValueConverter>();
    public List<IValueConverter> Converters
    {
        get { return _converters; }
        set { _converters = value; }
    }

    #region IValueConverter Members
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return GroupConvert(value, Converters);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return GroupConvertBack(value, Converters.ToArray().Reverse());
    }

    private static object GroupConvert(object value, IEnumerable<IValueConverter> converters)
    {
        return converters.Aggregate(value, (acc, conv) => { return conv.Convert(acc, typeof(object), null, null); });
    }

    private static object GroupConvertBack(object value, IEnumerable<IValueConverter> converters)
    {
        return converters.Aggregate(value, (acc, conv) => { return conv.ConvertBack(acc, typeof(object), null, null); });
    }
    #endregion

    #region IMultiValueConverter Members
    private InvalidOperationException _multiValueConverterUnsetException =
        new InvalidOperationException("To use the converter as a MultiValueConverter the MultiValueConverter property needs to be set.");

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (MultiValueConverter == null) throw _multiValueConverterUnsetException;
        var firstConvertedValue = MultiValueConverter.Convert(values, targetType, parameter, culture);
        return GroupConvert(firstConvertedValue, Converters);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        if (MultiValueConverter == null) throw _multiValueConverterUnsetException;
        var tailConverted = GroupConvertBack(value, Converters.ToArray().Reverse());
        return MultiValueConverter.ConvertBack(tailConverted, targetTypes, parameter, culture);
    }

    #endregion
}

( Как вы можете видеть, я почти полностью игнорирую ConverterParameters, TargetTypesи CultureInfo параметров, далее методы ConvertBack не проверены, поэтому я никому не советую использовать это на самом деле. )

Использование XAML:

<vc:GroupConverter MultiValueConverter="{StaticResource EqualityComparisonConv}">
    <StaticResource ResourceKey="BoolToVisibilityConv"/>
</vc:GroupConverter>
2 голосов
/ 02 апреля 2011

Три варианта:

Вариант A : преобразование bool в видимость на вашем многозначном преобразователе (всего одна строка)

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    if (values.Length < 2)
        throw new Exception("At least two inputs are needed for comparison");
    bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) =>
         { return x1.Equals(x2); });
    return output ? Visibility.Visible : Visibility.Collapsed;
}


Вариант B : использовать существующий конвертер booltovisibility программно

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    if (values.Length < 2)
        throw new Exception("At least two inputs are needed for comparison");
    bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) =>
         { return x1.Equals(x2); });
    System.Windows.Controls.BooleanToVisibilityConverter booltovisibilityconverter = new System.Windows.Controls.BooleanToVisibilityConverter();
    return booltovisibilityconverter.Convert(output, System.Type.GetType("System.Boolean"), parameter, culture);
}

PS: Вы можете кэшировать конвертер booltovisibility вместо его создания каждый раз.

Опция C : конвейеры преобразователей
Конвертеры значений трубопроводов в WPF
PS: знайте, что эта статья довольно старая.

...