denver
denver

Reputation: 3135

Specify target type for IValueConverter

I have a situation where I am using a multi-value converter. The values passed to it are themselves being converted.

<MenuItem>
    <MenuItem.IsEnabled>
        <MultiBinding Converter="{StaticResource BooleanAndConverter}">
            <Binding Path="Prop1" Converter="{StaticResource Converter1}" />
            <Binding Path="Prop2" Converter="{StaticResource Converter1}" />
        </MultiBinding>
    </MenuItem.IsEnabled>
</MenuItem

Converter1 contains some error checking to confirm that it is called with a valid target type. It throws an exception if not, as this is a developer error and the situation should be fixed.

The problem is that when Converter1 is used in this context the target type is System.Object. Now the BooleanAndConverter requires values of a certain type (Boolean), so how can I get that type passed as the target type of Converter1?

As requested here is the BooleanAndConverter code:

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        foreach (object value in values)
        {
            if (value.GetType() != typeof(bool))
            {
                throw new ArgumentException("BooleanAndConverter can only be used to convert booleans."); // developer error
            }
        }

        if (targetType != typeof(bool))
        {
            throw new ArgumentException("BooleanAndConverter can only convert to a boolean."); // developer error
        }

        foreach (object value in values)
        {
            if ((bool)value == false)
            {
                return false;
            }
        }
        return true;
    }

Let me restate the question as there seems to be some confusion. Converter1 knows what type it can convert from and to. It throws an exception when called with the wrong types. In this situation the targetType is not getting specified and an exception is being thrown. How do I get the targetType specified correctly? When not used in a multi-binding situation is does always get specified correctly based on what is being converted.

Upvotes: 1

Views: 10723

Answers (3)

Mike Marynowski
Mike Marynowski

Reputation: 3439

Value converter implementations should do an appropriate default conversion if object is the target type. You will get binding debug errors output if a bound converter is returning the wrong type and you can fix the problem accordingly. You also shouldn't be throwing errors in converters, you should output an informative message via Debug.WriteLine and return DependencyProperty.UnsetValue.

Because of situations like this it is generally not recommended to return more than one type of result based on the targetType parameter and it should be obvious what type of value the converter will return. You can either ignore targetType, or check that it is either the correct type or object. In practice it doesn't really make a difference.

Upvotes: 1

Frederick
Frederick

Reputation: 86

An possible alternative to some comments using a CommandParameter could be a MarkupExtension. Then you could write your MultiBinding like:

<MultiBinding Converter="{StaticResource BooleanAndConverter}">
  <Binding Path="Prop1" Converter="{conv:DebugTypeCheck, CheckType={x:Type sys:Boolean}" />
  <Binding Path="Prop2" Converter="{StaticResource DebugTypeCheckConverter}" CommandParameters="{x:Type sys:Boolean}" />
</MultiBinding>

On StackOverflow you can find several posts about Markupextension-Converters like here.

Sample implementation for both ways:

public class DebugTypeCheck : MarkupExtension, IValueConverter
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public Type CheckType { get; set; }

    [Conditional("DEBUG")]
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value.GetType() != CheckType)
        {
            throw new ArgumentException(); // developer error
        }

        return value;
    }  

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

public class DebugTypeCheckConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value.GetType() != (Type)parameter)
        {
            throw new ArgumentException(); // developer error
        }

        return value;
    }  

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

Upvotes: 1

MatrixManAtYrService
MatrixManAtYrService

Reputation: 9141

Seems to me that the code in the converter should be casting the object to whatever type it expects.

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var desired = value as desiredType;
    if (desired != null)
        //do stuff with 'desired' as a parameter
    else
        // error
 }

I do not know of a way get WPF to pass it in as a specific type. If you want the converter to behave differently for different types you could include the type as a converter parameter, or you could use a multivalueconverter to cast each parameter differently

Upvotes: 0

Related Questions