Issue in chaining IValueConvertes in WPF becauase of target type

北城以北 提交于 2019-12-25 00:34:25

问题


I was trying to chain converters as Town's answer in Is there a way to chain multiple value converters in XAML??

I like to make individual converters more strict by having targetType check as well :-

if (targetType != typeof(bool))
        throw new InvalidOperationException("The target must be a     
boolean");

But the chain fails as the end target type is different from the target at each stage.

I can remove the type check to make less strict as given in most of examples on SO, but I would prefer a chaining which respects each converter's type check as well. E.g. for better unit testing etc.

Also the interface IValueConverter doesn't expose the target type, I find it difficult to add that check myself.

 public class InverseBooleanConverter : IValueConverter
    {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
        if (targetType != typeof(bool))
            throw new InvalidOperationException("The target must be a boolean");

        if (!(value is bool))
            throw new ArgumentException("Argument 'value' must be of type bool");

        return !(bool)value;
        }
         ....
     }

[ValueConversion(typeof(bool), typeof(Visibility))]
public class VisibilityFromBoolConverter : IValueConverter
    {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
        if (targetType != typeof(Visibility))
            throw new InvalidOperationException("The target must be a Visibility");

        if (!(value is bool))
            throw new ArgumentException("Argument 'value' must be of type bool");

        var isVisible = (bool)value;
        return isVisible ? Visibility.Visible : Visibility.Collapsed;
        }
        ....
     }

And the composite is like :-

            <Converters:ValueConverterGroup x:Key="InvertAndVisible">
                  <Converters:InverseBooleanConverter />
                  <Converters:VisibilityFromBoolConverter />
            </Converters:ValueConverterGroup>

But I get exception "The target must be a boolean" from InverseBooleanConverter as it expects target to be bool instead of Visibility (the end target of chain).


回答1:


The original ValueConverterGroup code passes the final targetType into each stage, which is why your checks are failing. All you need to do is modify that behaviour to pass in the next converters targetType instead:

[ValueConversion(typeof(bool), typeof(Visibility))]
public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        for (int i = 0; i < this.Count(); i++)
        {
            var targ = (i == this.Count() - 1) 
                ? targetType 
                : (this[i + 1].GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false).First() as ValueConversionAttribute).SourceType;
            value = this[i].Convert(value, targ, parameter, culture);
        }
        if (value.GetType() != (this.GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false).First() as ValueConversionAttribute).TargetType)
            throw new InvalidOperationException("Last target must be of type " + targetType.Name);
        return value;
    }

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

    #endregion      
}



回答2:


I think you may have misunderstood the targetType parameter. According to the doco, it is the type of the target bound property.

This means that for your InverseBooleanConverter the target type will be a System.Visibility type.

For this sort of checking you should check the type of the incoming (bound) object:

if (value != null && value.GetType() != typeof(bool))
    throw new InvalidOperationException("The target must be a boolean");

But.... I would strongly suggest you don't throw exceptions from your converters - that can slow UI rendering down massively and they can be incredibly difficult to track down when you have a screenful of goodness (e.g. you have a grid with a couple of thousand rows in it, and one of the templated datagrid cells throws an exception - how are you going to identify it?). If you insist on throwing exceptions then at least surround it with an #if DEBUG define so that it isn't in your release code. Instead you should return DependencyProperty.UnsetValue if your converter cannot successfully convert a value. This ensures you don't get hard to track runtime exceptions, and it also ensures the binding subsystem can use things like the FallbackValue.



来源:https://stackoverflow.com/questions/32195401/issue-in-chaining-ivalueconvertes-in-wpf-becauase-of-target-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!