Чи є спосіб ланцюга перетворювачів кількох значень у XAML?


123

У мене виникла ситуація, коли мені потрібно показати ціле значення, прив’язане до властивості в моєму контексті даних, після того, як перенести це через дві окремі конверсії:

  1. Зворотне значення в діапазоні (наприклад, діапазон від 1 до 100; значення в контексті даних - 90; користувач бачить значення 10)
  2. перетворити число в рядок

Я усвідомлюю, що можу зробити обидва кроки, створивши власний конвертер (який реалізує IValueConverter). Однак у мене вже є окремий перетворювач значень, який робить лише перший крок, а другий крок охоплюється Int32Converter.

Чи є спосіб я з'єднати ці два існуючі класи в XAML без створення нового класу, який їх об'єднує?

Якщо мені потрібно щось уточнити, будь ласка, повідомте мене про це. :)

Дякую.

Відповіді:


198

Я використовував цей метод Гарет Еванс у своєму проекті Silverlight.

Ось моя реалізація цього:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Що потім можна використовувати в XAML так:

<c:ValueConverterGroup x:Key="InvertAndVisibilitate">
   <c:BooleanInverterConverter/>
   <c:BooleanToVisibilityConverter/>
</c:ValueConverterGroup>

3
Найкраще для реалізації ConvertBack зробити копію колекції та повернути її назад, а потім об'єднати через це? Отже, ConvertBack був биreturn this.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, parameter, culture));
Нік Уделл

5
@DLeh Це не дуже елегантно, як це не працює. Він надає всім перетворювачам остаточний цільовий тип замість правильного цільового типу ...
Олександр Топлек

Як я можу використовувати це з MultiValueConverter як перший конвертер?
LightMonk

1
@Town Колега щойно знайшов це запитання, і це змусило мене переглянути його знову заради ностальгії. Тільки я щойно помітив, що ти не отримав заслугований тобі кредит (я прийняв власну відповідь!), Тому я позначив вашу відповідь прийнятою. Лише близько 9 років ...: facepalm:
Мал Росс

@MalRoss Ха-ха! Дякую! Приємно чути, що це все-таки корисно, я вже майже 8 років не торкався Silverlight, і все-таки це моя найпопулярніша відповідь :)
Місто

54

Виявив саме те, що я шукав, люб’язно Джош Сміт: Перетворювач цінності конвеєрів (посилання archive.org) .

Він визначає ValueConverterGroupклас, використання якого в XAML саме так, як я сподівався. Ось приклад:

<!-- Converts the Status attribute text to a SolidColorBrush used to draw 
     the output of statusDisplayNameGroup. -->
<local:ValueConverterGroup x:Key="statusForegroundGroup">
  <local:IntegerStringToProcessingStateConverter  />
  <local:ProcessingStateToColorConverter />
  <local:ColorToSolidColorBrushConverter />
</local:ValueConverterGroup> 

Чудові речі. Спасибі, Джош. :)


2
У цьому рішенні кожен перетворювач повинен мати справу лише з одним типом (він повинен бути оголошений в атрибуті Single-ValueConversion). Рішення @Town може впоратися і з мультиконвертерами.
Ю. Шохам

9
будь ласка, опублікуйте реалізацію; в іншому випадку, linkrot
Джейк Бергер

9

Реалізація міста з проекту Silverlight Гарета Еванса великий, проте він не підтримує різні параметри перетворювача.

Я змінив його, щоб ви могли надати параметри, розділені комами (якщо ви, звичайно, не уникаєте їх).

Конвертор:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    private string[] _parameters;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if(parameter != null)
            _parameters = Regex.Split(parameter.ToString(), @"(?<!\\),");

        return (this).Aggregate(value, (current, converter) => converter.Convert(current, targetType, GetParameter(converter), culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    private string GetParameter(IValueConverter converter)
    {
        if (_parameters == null)
            return null;

        var index = IndexOf(converter as IValueConverter);
        string parameter;

        try
        {
            parameter = _parameters[index];
        }

        catch (IndexOutOfRangeException ex)
        {
            parameter = null;
        }

        if (parameter != null)
            parameter = Regex.Unescape(parameter);

        return parameter;
    }
}

Примітка: ConvertBack тут не реалізований, див. Мою історію для повної версії.

Впровадження:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:converters="clr-namespace:ATXF.Converters;assembly=ATXF" x:Class="ATXF.TestPage">
  <ResourceDictionary>
    <converters:ValueConverterGroup x:Key="converters">
      <converters:ConverterOne />
      <converters:ConverterTwo />
    </converters:ValueConverterGroup>
  </ResourceDictionary>

  <Label Text="{Binding InitialValue, Converter={StaticResource converters}, ConverterParameter='Parameter1,Parameter2'}" />
</ContentPage>

6

Так, є способи ланцюгових перетворювачів, але це виглядає не дуже красиво, і тут вам це не потрібно. Якщо вам коли-небудь знадобиться це, запитайте себе, чи справді це шлях? Простий завжди працює краще, навіть якщо вам доведеться написати власний перетворювач.

У вашому конкретному випадку все, що вам потрібно зробити, - це відформатувати перетворене значення у рядок. StringFormatвласність на a Binding- це ваш друг тут.

 <TextBlock Text="{Binding Value,Converter={StaticResource myConverter},StringFormat=D}" />

5
Якщо ви активно використовуєте прив'язки, запис спеціальних перетворювачів на ланцюгові перетворювачі закінчується тоннами німих перетворювачів для всіляких конфігурацій. У цьому випадку прийнята відповідь - чудове рішення.
Яцек Горгонь

0

Ось невелике розширення відповіді міста на підтримку багатозв'язних:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter, IMultiValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return Convert(values as object, targetType, parameter, culture);
    }

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

    #endregion
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.