Как выбрать нужный Converter в XAML, в зависимости от значения свойства во ViewModel?

У меня в приложении есть таблица цен, значения которой фактически представляют собой тип long, но для отображения в UI, необходимо делить эти значения на 100 с помощью конвертера:

class PenniesToRubleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int multiplier = 100;
        
        return (double) Math.Round(System.Convert.ToDouble(value), 0) / multiplier;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int multiplier = 100;
       
        try
        {
            var newValue = (value ?? 0f).ToString().Replace(".", ",");
            return System.Convert.ToSingle(newValue) * multiplier;
        }
        catch (Exception)
        {
            return string.Empty;
        }
    }
}

Код XAML из View, описывающий применение конвертера к конкретной колонке с ценами:

<DataGridTextColumn Binding="{Binding Path=OrderMaxPrice, Converter={StaticResource penniesToRubleConverter}}" IsReadOnly="True" Width="0.5*">
                <DataGridTextColumn.Header>
                    <Label Foreground="{DynamicResource White}" Content="Макс. ордер"/>
                </DataGridTextColumn.Header>
                <DataGridTextColumn.ElementStyle>
                    <Style TargetType="{x:Type TextBlock}">
                        <Setter Property="HorizontalAlignment" Value="Center" />
                        <Setter Property="VerticalAlignment" Value="Center" />
                    </Style>
                </DataGridTextColumn.ElementStyle>
            </DataGridTextColumn>

В чем суть проблемы:

В приложение добавилась поддержка валюты USD, значения которой имеют другую разрядность (тот же long, но на порядок больше - до десятых долей центов), и конвертер PenniesToRubleConverter не подходит для конвертации в подходящий для View формат. Делить теперь нужно на 1000.

Пробовал подставить ConverterParameter, но это не работает. Первая проблема этого подхода - ConverterParameter не принимает Binding в качестве параметра, и я не могу использовать выбранную валюту во ViewModel для передачи в конвертер.

Может есть способ назначать сам конвертер в зависимости от выбранной валюты во ViewModel? Например, если валюта окна - USD, то использовать CentsToDollarConverter, с делением на 1000, а если RUB - использовать существующий PenniesToRubleConverter?

Либо может есть другой способ обойти ограничение на привязку Binding к ConverterParameter?


Ответы (1 шт):

Автор решения: Николай Гончарик

Нашёл решение, которое может подойти в некоторых случаях лучше, чем MultiBinding и IMultiValueConverter (Спасибо @EvgeniyZ за помощь в поиске информации по вопросу).

Я решил проблему через proxy-объект, выступающий в качестве DataContext.

В VM я создал два свойства bool RubMode и bool UsdMode, сделал в XAML по две копии всех колонок, и отображаю их в зависимости от того, какая валюта задана в VM.

<DataGridTextColumn Visibility="{Binding Data.RubMode, Converter={StaticResource BooleanToVisible}, Source={StaticResource proxy}}" Binding="{Binding Path=MinPrice, Converter={StaticResource penniesToRubleConverter}}">
<DataGridTextColumn Visibility="{Binding Data.UsdMode, Converter={StaticResource BooleanToVisible}, Source={StaticResource proxy}}" Binding="{Binding Path=MinPrice, Converter={StaticResource centsToDollarsConverter}}"> 

В моей задаче никак не избежать нарушения принципа DRY. Либо мы сильно раздуваем разметку, описывая Multibinding, и одинакового кода у нас добавляется больше в 7-10 местах, и при описании конвертера нужно очень серьезно поработать над обратной конвертацией, т.к. в методе ConvertBack конвертера мы принимаем на входе один объект, который на входе не может хранить данные о валюте, только её "сконвертированное" значение.

Можно, конечно, распарсить string-представление числа 741.012, чтобы понять, что это USD цена, но когда число будет 741.00, начнутся проблемы. К тому же точность у USD и EUR валют одинаковая.

Минус моего решения говорит сам за себя - на деле у нас две копии колонок, но в данном конкретном примере я его считаю несущественным, можно отрефакторить при наличии времени и желания (вплоть до изменения используемой визуальной технологии вместо XAML).

→ Ссылка