Skip to content

Instantly share code, notes, and snippets.

@awatertrevi
Last active June 11, 2024 19:32
Show Gist options
  • Save awatertrevi/68924981bdea1800f5af162e4eb2b1f5 to your computer and use it in GitHub Desktop.
Save awatertrevi/68924981bdea1800f5af162e4eb2b1f5 to your computer and use it in GitHub Desktop.
ValueConverterGroup with parameters.
public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
private string[] _parameters;
private bool _shouldReverse = false;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ExtractParameters(parameter);
if (_shouldReverse)
{
Reverse();
_shouldReverse = false;
}
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)
{
ExtractParameters(parameter);
Reverse();
_shouldReverse = true;
return this.Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, GetParameter(converter), culture));
}
private void ExtractParameters(object parameter)
{
if (parameter != null)
_parameters = Regex.Split(parameter.ToString(), @"(?<!\\),");
}
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;
}
}
@awatertrevi
Copy link
Author

Hi @IGonza,

I think you are right. I shall try to update the snippet as soon as possible.

Thanks,

Trevi

@ADIX7
Copy link

ADIX7 commented Apr 27, 2021

ConvertBack has a bug, hasn't it? You always call Reverse(), so if ConvertBack is called twice the second time it will use the original order not the reversed. I would remove the _shouldReverse logic and simply user ((IEnumerable<IValueConverter>)this).Reverse(). It does not mutate the list.

@JMHeartley
Copy link

JMHeartley commented Jun 11, 2024

Here's it with @ADIX7's suggested edit and other refactorings

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

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ExtractParameters(parameter);

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

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ExtractParameters(parameter);

        return ((IEnumerable<IValueConverter>)this).Reverse() //use implicit cast to create new list so calling reverse won't mutate original
            .Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, GetParameter(converter), culture));
    }

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

        string parameter;
        try
        {
            var index = IndexOf(converter);
            parameter = _parameters[index];
        }
        catch (IndexOutOfRangeException)
        {
            return default;
        }

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

        return parameter;
    }

    private void ExtractParameters(object parameter)
    {
        if (parameter != null)
        {
            _parameters = Regex.Split(parameter.ToString(), @"(?<!\\),")
                    .Select(p => p.Trim())
                    .ToArray();
        }
    }
}

This is how I used it

<TextBlock Text="If you can see this, MyEnum is not Item1">
    <TextBlock.Visibility>
        <Binding Path="MyEnum"
                 RelativeSource="{RelativeSource AncestorType={x:Type local:MyUserControl}}"
                 ConverterParameter="CustomEnum.Item1, foo,bar">
            <!--parameters separated by comma, order corresponds with converter in same position-->
            <Binding.Converter>
                <converters:AggregateConverter>
                    <converters:EnumToBooleanConverter />
                    <!--this is how you use custom properties-->
                    <converters:NullableBooleanConverter TrueValue="False"
                                                          FalseValue="True" />
                    <converters:TruthyToVisibilityConverter />
                </converters:AggregateConverter>
            </Binding.Converter>
        </Binding>
    </TextBlock.Visibility>
</TextBlock>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment