Alex141 / CalcBinding

Advanced WPF Binding which supports expressions in Path property and other features
Apache License 2.0
638 stars 79 forks source link

Custom types with defined implicit cast to bool are unsupported by BoolToVisibility converter #53

Closed rstroilov closed 6 years ago

rstroilov commented 6 years ago

I faced in issue in my project. When using a type which has defined implicit cast to bool in a property, binding visibility to that property doesn't work and conversion fails with following message: System.InvalidCastException: Specified cast is not valid.

It happens because of explicit cast is called on value in BoolToVisibilityConverter.Convert: if ((bool)value) return Visibility.Visible;

this test reproduces issue:

        [Test]
        public void DoesNotFailConvertingWhenValueHasImplicitConversionToBool()
        {
            Assert.DoesNotThrow(() =>
                new BoolToVisibilityConverter()
                .Convert(new CastableToBoolean(true), typeof(Visibility), null, CultureInfo.CurrentCulture));
        }

        private sealed class CastableToBoolean
        {
            private bool value;

            public CastableToBoolean(bool value = false)
            {
                this.value = value;
            }

            public static implicit operator bool(CastableToBoolean obj)
            {
                return obj.value;
            }
        }
Alex141 commented 6 years ago

Hi, I agree with problem, but disagree with solution. I think it can be made shorter and more clear. How about using DLR?

     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
       var boolValue = (value is bool) ? (bool)value : (bool)(dynamic)value;
       ...
     }

If we remove cast to bool after dynamic cast, we handle implicit operators. If we leave bool cast, we can handle both explicit and implicit operators. What do you say?

rstroilov commented 6 years ago

Hi Alex,

your solution looks nice and beautiful, much better than my, but, unfortunately, it doesn't work for case specified.

... : (bool)(dynamic)value will cause same InvalidCastException ... : (dynamic)value will cause RuntimeBinderException saying that there is no implicit cast between object and bool.

Alex141 commented 6 years ago

Hi Roman,

Ok, I compared yours and my examples. The difference is only in access modifier of CastableToBoolean. I use 'public class' and you use 'private class'. Because of privacy of the class dlr can't acess to implicit operator. Changing access modifier on 'public' solves this problem.

Hmm, because the sample class models the class being used as the type of the source property, it must be public, as well as the property itself, otherwise the binding will simply not work, this is a general requirement for all properties (https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/binding-sources-overview#other-characteristics).

So the solution of this problem is in changing the access modifier of convertable class to public.

rstroilov commented 6 years ago

I was wondered and checked - yes, you are right. It looks a bit confusing, that access modifier is so important here, but it really works as it should.

Alex141 commented 6 years ago

Ok, I glad that we solved this problem by our efforts :) I will commit the changes and release a new version tomorrow

Alex141 commented 6 years ago

Version 2.3.0.1 with this fix has been released