Converter With Multiple Parameters
Asked Answered
W

8

53

How does one use a converter with Multiple parameters in a Windows Phone 7 Application?

Whatsoever answered 4/7, 2012 at 5:56 Comment(3)
Try to pass comma separated parameters in ConverterParameter property and get parameter values by splitting that values in Converter.Acaudal
@Hitesh Patel: You can't use a comma as that clashes with the Binding XAML syntax. Use some other character that XAML allows (maybe a pipe "|" character)?Nebulous
Is this what you looking for #55576468 ?Cipolin
N
77

Converters always implement IValueConverter. That means a call to Convert or ConvertBack passes a single additional parameter. That parameter is extracted from the XAML.

As Hitesh Patel suggests there is nothing to stop you putting more than one value into the parameter, so long as you have a delimiter to separate them out later, but you cannot use a comma as that delimits the XAML!

e.g.

XAML

<TextBlock Text="{Binding Path=ReleaseDate, Mode=OneWay,
                        Converter={StaticResource MyConverter}, 
                        ConverterParameter=Param1|Param2}" />

Converter

public object Convert(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
{
    string parameterString = parameter as string;
    if (!string.IsNullOrEmpty(parameterString))
    {
        string[] parameters = parameterString.Split(new char[]{'|'});
        // Now do something with the parameters
    }
}

Note, I have not checked it to see if a Pipe "|" character is valid in XAML there (should be), but if not just choose another character that does not clash.

Later versions of .Net do not require a character array for the simplest version of Split, so you can use this instead:

string[] parameters = parameterString.Split('|');

Addendum:

A trick eBay used to use in urls, years ago, was to delimit data in the URL with QQ. A double-Q does not naturally occur in text data. If you ever get stuck for a text delimiter that will avoid encoding issues just use QQ... This will not work with split though (which requires single characters, but nice to know) :)

Nebulous answered 4/7, 2012 at 8:43 Comment(7)
Regex.Split('QQ') would split at QQBummalo
@Simon_Weaver: Yes, but Regex is significantly slower than string methods (where they exist to do the same job), so Regex is best avoided, as of habit, for simple tasks. ThanksNebulous
Worth noting this parameter cannot be a binding.Funambulist
Ignore the extraneous '}' following "...Param2}}". Should be "...Param2}.Paleoecology
@BoiseBaked: Thanks. Have fixed.Nebulous
Worth noting that in italian we have a word that has that double q: 'soqquadro'Heartland
@MarkWalter that is a fascinating little fact I did not know :) They did use uppercased QQ so probably never happenNebulous
W
49

While the above answers may be feasible, they seem to be overly complicated. Simply use an IMultiValueConverter with an appropriate MultiBinding in the XAML code. Assuming that your ViewModel has the properties FirstValue, SecondValue, and ThirdValue, which are an int, a double, and a string, respectively, a valid multi converter might look like this:

C#

public class MyMultiValueConverter : IMultiValueConverter {
  public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
    int firstValue = (int)values[0];
    double secondValue = (double)values[1];
    string thirdValue = (string)values[2];

    return "You said " + thirdValue + ", but it's rather " + firstValue * secondValue;
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
    throw new NotImplementedException("Going back to what you had isn't supported.");
  }
}

XAML

<TextBlock.Text>
  <MultiBinding Converter="{StaticResource myNs:MyMultiValueConverter}">
    <Binding Path="FirstValue" />
    <Binding Path="SecondValue" />
    <Binding Path="ThirdValue" />
  </MultiBinding>
</TextBlock.Text>

Since it requires neither fumbling with the ProvideValue method required by MarkupExtension, nor the specification of a DependencyObject inside(!) a converter, I do believe that this is the most elegant solution.

Woodsum answered 28/12, 2018 at 22:49 Comment(2)
Although technically not passing multiple "parameters" and actually passing multiple values. Certainly just as effective (at least for my needs). much appreciated.Passionate
You provide wrong answer: task is to pass multiple PARAMETERS, not values! Value is already bound to smth.Chore
E
27

You can always derive from DependecyObject class and add as many DependencyProperty objects as you want. For example:

ExampleConverter.cs

public class ExampleConverter : DependencyObject, IValueConverter
{
    public string Example
    {
        get => GetValue(ExampleProperty).ToString();
        set => SetValue(ExampleProperty, value);
    }
    public static readonly DependencyProperty ExampleProperty =
        DependencyProperty.Register("Example", typeof(string), typeof(ExampleConverter), new PropertyMetadata(null));

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        //Do the convert
    }

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

And then in XAML:

ExampleView.xaml

<ResourceDictionary>
    <converters:ExampleConverter x:Key="ExampleConverter" Example="{Binding YourSecondParam}"/>
</ResourceDictionary>
...
<TextBlock Text="{Binding Path=ReleaseDate, Mode=OneWay,
                    Converter={StaticResource ExampleConverter}, 
                    ConverterParameter={Binding YourFirstParam}}" />
Eadwina answered 18/9, 2017 at 11:33 Comment(2)
This looks like an elegant solutions but my converter dependency property is never setGuess
It is hard to help you @Guess without any further details. I can just point you to the DependencyObject docs: learn.microsoft.com/pl-pl/dotnet/api/…Eadwina
C
24

This can be done using System.Windows.Markup.MarkupExtension (docs).

This will allow you pass values to the converter which could be used as arguments or return values, for example:

public class CustomNullToVisibilityConverter : MarkupExtension, IValueConverter
{
    public object NullValue { get; set; }
    public object NotNullValue { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return NullValue;

        return NotNullValue;
    }

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

Usage:

...
Visibility="{Binding Property, 
 Converter={cnv:CustomNullToVisibilityConverter NotNullValue=Visible, NullValue=Collapsed}}" 
/>
...

Be sure to reference the namespace of the converter in the .xaml.

Counterpane answered 4/12, 2018 at 11:58 Comment(1)
On UWP, MarkupExtension is in Windows.UI.Xaml.Markup, and the ProvideValue doesn't have any parameter.Exert
P
5

Similar to Kyle Olson answer, you can use a Specialized Collection like this:

XAML File:

xmlns:specialized="clr-namespace:System.Collections.Specialized;assembly=System"

<local:BoolToMessage x:Key="BoolToMessage"/>

<Label
    >
    <Label.Content>
        <Binding ElementName="mainWin" Path="HasSeedFile"
                FallbackValue="False" Converter="{StaticResource BoolToMessage}"
                Mode="OneWay">
            <Binding.ConverterParameter>
                <specialized:StringCollection>
                    <sys:String>param1</sys:String>
                    <sys:String>param2</sys:String>
                </specialized:StringCollection>
            </Binding.ConverterParameter>
        </Binding>
    </Label.Content>
</Label>

Converter:

using System.Collections.Specialized;

[ValueConversion(typeof(bool), typeof(string))]
public class BoolToMessage : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string[] p = new string[((StringCollection) parameter).Count];
        ((StringCollection) parameter).CopyTo(p,0);

        return (bool) value ? p[0] : p[1];
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

There are several Specialized Collection types that should meet most needs.

Pekingese answered 21/6, 2020 at 15:57 Comment(0)
C
4

If your input will not work with a string, and you have multiple parameters (not bindings). You can just pass an collection. Define one of whatever type is needed to avoid some UI Editor issues with arrays:

public class BrushCollection : Collection<Brush>
{
}

Then add the XAML using the collection

                <TextBox.Background >
                    <Binding Path="HasInitiativeChanged" Converter="{StaticResource changedToBrushConverter}">
                        <Binding.ConverterParameter>
                            <local:BrushCollection>
                                <SolidColorBrush Color="{DynamicResource ThemeTextBackground}"/>
                                <SolidColorBrush Color="{DynamicResource SecondaryColorBMedium}"/>
                            </local:BrushCollection>
                        </Binding.ConverterParameter>
                    </Binding>

                </TextBox.Background>

And then cast the result to an array of the appropriate type in the converter:

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

        BrushCollection brushes = (BrushCollection)parameter;
Chapman answered 23/3, 2019 at 16:53 Comment(0)
P
3

Solution for Xamarin:

public class BoolStateConverter : BindableObject, IValueConverter, IMarkupExtension
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var boolValue = (bool)value;
        return boolValue ? EnabledValue : DisabledValue;
    }

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

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public static BindableProperty EnabledValueProperty = BindableHelper.CreateProperty<string>(nameof(EnabledValue));
    public string EnabledValue
    {
        get => (string)GetValue(EnabledValueProperty);
        set => SetValue(EnabledValueProperty, value);
    }

    public static BindableProperty DisabledValueProperty = BindableHelper.CreateProperty<string>(nameof(DisabledValue));
    public string DisabledValue
    {
        get => (string)GetValue(DisabledValueProperty);
        set => SetValue(DisabledValueProperty, value);
    }
}

XAML Consumption:

<ContentPage.Resources>
    <ResourceDictionary>
        <converters:BoolStateConverter
            x:Key="BackwardButtonConverter"
            EnabledValue="{x:Static res:Images.IcActiveButton}"
            DisabledValue="{x:Static res:Images.IcInactiveButton}" />
    </ResourceDictionary>
</ContentPage.Resources>
Prager answered 23/10, 2019 at 9:38 Comment(0)
A
0

In addition to Jeff's answer. You can also pass different parameters types.

<Binding.ConverterParameter>
    <x:Array Type="{x:Type sys:Object}">
        <sys:Boolean>true</sys:Boolean>
        <sys:Double>70</sys:Double>
    </x:Array>
</Binding.ConverterParameter>
Avan answered 6/3, 2023 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.