public void Validation_Plugins_Send_Correct_Notifications()
        {
            var data = new IndeiTest();
            var observer = new ExpressionObserver(data, nameof(data.MustBePositive), true);
            var result = new List<object>();

            observer.Subscribe(x => result.Add(x));
            observer.SetValue(5);
            observer.SetValue(-5);
            observer.SetValue("foo");
            observer.SetValue(5);

            Assert.Equal(new[]
            {
                new BindingNotification(0),

                // Value is notified twice as ErrorsChanged is always called by IndeiTest.
                new BindingNotification(5),
                new BindingNotification(5),

                // Value is first signalled without an error as validation hasn't been updated.
                new BindingNotification(-5),
                new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, -5),

                // Exception is thrown by trying to set value to "foo".
                new BindingNotification(
                    new ArgumentException("Object of type 'System.String' cannot be converted to type 'System.Int32'."),
                    BindingErrorType.DataValidationError),

                // Value is set then validation is updated.
                new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5),
                new BindingNotification(5),
            }, result);
        }
Exemple #2
0
        public ExpressionObserver CreateExpressionObserver(
            IObservablePropertyBag instance, 
            PerspexProperty property)
        {
            IObservable<object> dataContext = null;

            if (property != Control.DataContextProperty)
            {
                dataContext = instance.GetObservable(Control.DataContextProperty);
            }
            else
            {
                var parent = instance.InheritanceParent as IObservablePropertyBag;

                if (parent != null)
                {
                    dataContext = parent.GetObservable(Control.DataContextProperty);
                }
            }

            if (dataContext != null)
            {
                var result = new ExpressionObserver(null, SourcePropertyPath);
                dataContext.Subscribe(x => result.Root = x);
                return result;
            }

            return null;
        }
        public void Should_Get_Simple_Property_Value_Type()
        {
            var data = new { Foo = "foo" };
            var target = new ExpressionObserver(data, "Foo");

            Assert.Equal(typeof(string), target.ResultType);
        }
        public void Should_Get_Simple_Property_Chain_Type()
        {
            var data = new { Foo = new { Bar = new { Baz = "baz" } } };
            var target = new ExpressionObserver(data, "Foo.Bar.Baz");

            Assert.Equal(typeof(string), target.ResultType);
        }
        public void Should_Have_Null_ResultType_For_Broken_Chain()
        {
            var data = new { Foo = new { Bar = 1 } };
            var target = new ExpressionObserver(data, "Foo.Bar.Baz");

            Assert.Null(target.ResultType);
        }
        public async void Should_Return_UnsetValue_For_String_Not_Convertible_To_Boolean()
        {
            var data = new { Foo = "foo" };
            var target = new ExpressionObserver(data, "!Foo");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Return_UnsetValue_For_Root_UnsetValue()
        {
            var data = new Class3 { Foo = "foo" };
            var target = new ExpressionObserver(AvaloniaProperty.UnsetValue, "Foo");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Get_Simple_Property_Value()
        {
            var data = new Class1();
            var target = new ExpressionObserver(data, "Foo");
            var result = await target.Take(1);

            Assert.Equal("foo", result);
        }
        public async void Should_Get_Simple_Property_From_Base_Class()
        {
            var data = new Class3 { Foo = "foo" };
            var target = new ExpressionObserver(data, "Foo");
            var result = await target.Take(1);

            Assert.Equal("foo", result);
        }
        public async void Should_Get_Simple_Property_Chain()
        {
            var data = new { Foo = new { Bar = new { Baz = "baz" } }  };
            var target = new ExpressionObserver(data, "Foo.Bar.Baz");
            var result = await target.Take(1);

            Assert.Equal("baz", result);
        }
        public async void Should_Get_Simple_Property_Value_Null()
        {
            var data = new { Foo = (string)null };
            var target = new ExpressionObserver(data, "Foo");
            var result = await target.Take(1);

            Assert.Null(result);
        }
        public async void Should_Not_Have_Value_For_Broken_Chain()
        {
            var data = new { Foo = new { Bar = 1 } };
            var target = new ExpressionObserver(data, "Foo.Bar.Baz");
            var result = await target.Take(1);

            Assert.Equal(PerspexProperty.UnsetValue, result);
        }
        public async void Should_Negate_0()
        {
            var data = new { Foo = 0 };
            var target = new ExpressionObserver(data, "!Foo");
            var result = await target.Take(1);

            Assert.Equal(true, result);
        }
        public async void Should_Get_UnsetValue_For_Invalid_Dictionary_Index()
        {
            var data = new { Foo = new Dictionary<int, string> { { 1, "foo" } } };
            var target = new ExpressionObserver(data, "Foo[invalid]");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Get_Array_Value()
        {
            var data = new { Foo = new [] { "foo", "bar" } };
            var target = new ExpressionObserver(data, "Foo[1]");
            var result = await target.Take(1);

            Assert.Equal("bar", result);
        }
        public async void Should_Negate_Boolean_Value()
        {
            var data = new { Foo = true };
            var target = new ExpressionObserver(data, "!Foo");
            var result = await target.Take(1);

            Assert.Equal(false, result);
        }
        public async void Should_Negate_True_String()
        {
            var data = new { Foo = "True" };
            var target = new ExpressionObserver(data, "!Foo");
            var result = await target.Take(1);

            Assert.Equal(false, result);
        }
        public async void Array_Out_Of_Bounds_Should_Return_UnsetValue()
        {
            var data = new { Foo = new[] { "foo", "bar" } };
            var target = new ExpressionObserver(data, "Foo[2]");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Return_Empty_For_Value_Not_Convertible_To_Boolean()
        {
            var data = new { Foo = new object() };
            var target = new ExpressionObserver(data, "!Foo");
            var result = await target.Take(1);

            Assert.Equal(PerspexProperty.UnsetValue, result);
        }
        public async void Array_With_Wrong_Dimensions_Should_Return_UnsetValue()
        {
            var data = new { Foo = new[] { "foo", "bar" } };
            var target = new ExpressionObserver(data, "Foo[1,2]");
            var result = await target.Take(1);

            Assert.Equal(PerspexProperty.UnsetValue, result);
        }
        public async void List_Out_Of_Bounds_Should_Return_UnsetValue()
        {
            var data = new { Foo = new List<string> { "foo", "bar" } };
            var target = new ExpressionObserver(data, "Foo[2]");
            var result = await target.Take(1);

            Assert.Equal(PerspexProperty.UnsetValue, result);
        }
        public async void Should_Get_UnsetValue_For_Invalid_Array_Index()
        {
            var data = new { Foo = new[] { "foo", "bar" } };
            var target = new ExpressionObserver(data, "Foo[invalid]");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Return_UnsetValue_For_Observable_Root_Null()
        {
            var data = new Class3 { Foo = "foo" };
            var target = new ExpressionObserver(Observable.Return(default(object)), "Foo");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Get_UnsetValue_For_Object_Without_Indexer()
        {
            var data = new { Foo = 5 };
            var target = new ExpressionObserver(data, "Foo[noindexer]");
            var result = await target.Take(1);

            Assert.Equal(AvaloniaProperty.UnsetValue, result);
        }
        public async void Should_Get_MultiDimensional_Array_Value()
        {
            var data = new { Foo = new[,] { { "foo", "bar" }, { "baz", "qux" } } };
            var target = new ExpressionObserver(data, "Foo[1, 1]");
            var result = await target.Take(1);

            Assert.Equal("qux", result);
        }
        public async void Should_Get_Value_For_String_Indexer()
        {
            var data = new { Foo = new Dictionary<string, string> { { "foo", "bar" }, { "baz", "qux" } } };
            var target = new ExpressionObserver(data, "Foo[foo]");
            var result = await target.Take(1);

            Assert.Equal("bar", result);
        }
        public async void Should_Get_Value_For_Non_String_Indexer()
        {
            var data = new { Foo = new Dictionary<double, string> { { 1.0, "bar" }, { 2.0, "qux" } } };
            var target = new ExpressionObserver(data, "Foo[1.0]");
            var result = await target.Take(1);

            Assert.Equal("bar", result);
        }
 public void Exception_Validation_Sends_ValidationUpdate()
 {
     var data = new ExceptionTest { MustBePositive = 5 };
     var observer = new ExpressionObserver(data, nameof(data.MustBePositive), false);
     var validationMessageFound = false;
     observer.Where(o => o is IValidationStatus).Subscribe(_ => validationMessageFound = true);
     observer.SetValue(-5);
     Assert.True(validationMessageFound);
 }
        public void Indei_Validation_Does_Not_Subscribe_When_DataValidatation_Not_Enabled()
        {
            var data = new IndeiTest { MustBePositive = 5 };
            var observer = new ExpressionObserver(data, nameof(data.MustBePositive), false);

            observer.Subscribe(_ => { });

            Assert.Equal(0, data.ErrorsChangedSubscriptionCount);
        }
        public void Disabled_Indei_Validation_Does_Not_Subscribe()
        {
            var data = new IndeiTest { MustBePositive = 5 };
            var observer = new ExpressionObserver(data, nameof(data.MustBePositive), false);

            observer.Subscribe(_ => { });

            Assert.Equal(0, data.SubscriptionCount);
        }
        public void Should_Get_Property_Value_From_Observable_With_DataValidation_Enabled()
        {
            using (var sync = UnitTestSynchronizationContext.Begin())
            {
                var data1  = new Class1();
                var data2  = new Class2("foo");
                var target = ExpressionObserver.Create(data1, o => o.Next.StreamBinding().Foo, true);
                var result = new List <object>();

                var sub = target.Subscribe(x => result.Add(x));
                data1.Next.OnNext(data2);
                sync.ExecutePostedCallbacks();

                Assert.Equal(new[] { new BindingNotification("foo") }, result);

                sub.Dispose();
                Assert.Equal(0, data1.PropertyChangedSubscriptionCount);

                GC.KeepAlive(data1);
                GC.KeepAlive(data2);
            }
        }
Exemple #32
0
        public void Should_Get_Property_Value_From_Observable()
        {
            using (var sync = UnitTestSynchronizationContext.Begin())
            {
                var data   = new Class1();
                var target = ExpressionObserver.Create(data, o => o.Next.StreamBinding().Foo);
                var result = new List <object>();

                var sub = target.Subscribe(x => result.Add(x));
                data.Next.OnNext(new Class2("foo"));
                sync.ExecutePostedCallbacks();

                Assert.Equal(new[] { "foo" }, result);

                sub.Dispose();
                // Forces WeakEvent compact
                Dispatcher.UIThread.RunJobs();
                Assert.Equal(0, data.PropertyChangedSubscriptionCount);

                GC.KeepAlive(data);
            }
        }
        public void Can_Replace_Root()
        {
            var first = new Class1 {
                Foo = "foo"
            };
            var second = new Class1 {
                Foo = "bar"
            };
            var root   = first;
            var target = new ExpressionObserver(() => root, "Foo");
            var result = new List <object>();
            var sub    = target.Subscribe(x => result.Add(x));

            root = second;
            target.UpdateRoot();
            root = null;
            target.UpdateRoot();

            Assert.Equal(new[] { "foo", "bar", PerspexProperty.UnsetValue }, result);

            Assert.Equal(0, first.SubscriptionCount);
            Assert.Equal(0, second.SubscriptionCount);
        }
        public void Should_Return_BindingNotification_Error_On_Task_Exception()
        {
            using (var sync = UnitTestSynchronizationContext.Begin())
            {
                var tcs    = new TaskCompletionSource <string>();
                var data   = new { Foo = tcs.Task };
                var target = new ExpressionObserver(data, "Foo");
                var result = new List <object>();

                var sub = target.Subscribe(x => result.Add(x));
                tcs.SetException(new NotSupportedException());
                sync.ExecutePostedCallbacks();

                Assert.Equal(
                    new[]
                {
                    new BindingNotification(
                        new AggregateException(new NotSupportedException()),
                        BindingErrorType.Error)
                },
                    result);
            }
        }
Exemple #35
0
        public void Should_Return_BindingNotification_If_Stream_Operator_Applied_To_Not_Supported_Type()
        {
            using (var sync = UnitTestSynchronizationContext.Begin())
            {
                var data   = new Class2("foo");
                var target = new ExpressionObserver(data, "Foo^", true);
                var result = new List <object>();

                var sub = target.Subscribe(x => result.Add(x));
                sync.ExecutePostedCallbacks();

                Assert.Equal(
                    new[]
                {
                    new BindingNotification(
                        new MarkupBindingChainException("Stream operator applied to unsupported type", "Foo^", "Foo^"),
                        BindingErrorType.Error)
                },
                    result);

                sub.Dispose();
            }
        }
        public void Should_Track_Simple_Property_Value()
        {
            var data = new Class1 {
                Foo = "foo"
            };
            var target = ExpressionObserver.Create(data, o => o.Foo);
            var result = new List <object>();

            var sub = target.Subscribe(x => result.Add(x));

            data.Foo = "bar";

            Assert.Equal(new[] { "foo", "bar" }, result);

            sub.Dispose();

            // Forces WeakEvent compact
            Dispatcher.UIThread.RunJobs();

            Assert.Equal(0, data.PropertyChangedSubscriptionCount);

            GC.KeepAlive(data);
        }
Exemple #37
0
        public void Should_Track_Chained_Attached_Value()
        {
            var data = new Class1
            {
                Next = new Class1
                {
                    [Owner.FooProperty] = "foo",
                }
            };

            var target = new ExpressionObserver(data, "Next.(Owner.Foo)");
            var result = new List <object>();

            var sub = target.Subscribe(x => result.Add(x));

            data.Next.SetValue(Owner.FooProperty, "bar");

            Assert.Equal(new[] { "foo", "bar" }, result);

            sub.Dispose();

            Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers());
        }
Exemple #38
0
        public async Task Should_Return_BindingNotification_For_Invalid_FallbackValue_With_Data_Validation()
        {
            var data = new Class1 {
                StringValue = "foo"
            };
            var target = new BindingExpression(
                ExpressionObserver.Create(data, o => o.StringValue, true),
                typeof(int),
                "bar",
                AvaloniaProperty.UnsetValue,
                DefaultValueConverter.Instance);
            var result = await target.Take(1);

            Assert.Equal(
                new BindingNotification(
                    new AggregateException(
                        new InvalidCastException("'foo' is not a valid number."),
                        new InvalidCastException("Could not convert FallbackValue 'bar' to 'System.Int32'")),
                    BindingErrorType.Error),
                result);

            GC.KeepAlive(data);
        }
Exemple #39
0
        public void Subscribing_Multiple_Times_Should_Return_Values_To_All()
        {
            var data = new Class1 {
                Foo = "foo"
            };
            var target  = new ExpressionObserver(data, "Foo");
            var result1 = new List <object>();
            var result2 = new List <object>();
            var result3 = new List <object>();

            target.Subscribe(x => result1.Add(x));
            target.Subscribe(x => result2.Add(x));

            data.Foo = "bar";

            target.Subscribe(x => result3.Add(x));

            Assert.Equal(new[] { "foo", "bar" }, result1);
            Assert.Equal(new[] { "foo", "bar" }, result2);
            Assert.Equal(new[] { "bar" }, result3);

            GC.KeepAlive(data);
        }
Exemple #40
0
        public void Should_Track_Property_Chain_Breaking_With_Missing_Member_Then_Mending()
        {
            var data = new Class1 {
                Next = new Class2 {
                    Bar = "bar"
                }
            };
            var target = new ExpressionObserver(data, "Next.Bar");
            var result = new List <object>();

            var sub      = target.Subscribe(x => result.Add(x));
            var old      = data.Next;
            var breaking = new WithoutBar();

            data.Next = breaking;
            data.Next = new Class2 {
                Bar = "baz"
            };

            Assert.Equal(
                new object[]
            {
                "bar",
                new BindingNotification(
                    new MissingMemberException("Could not find CLR property 'Bar' on 'Avalonia.Markup.UnitTests.Data.ExpressionObserverTests_Property+WithoutBar'"),
                    BindingErrorType.Error),
                "baz",
            },
                result);

            sub.Dispose();

            Assert.Equal(0, data.PropertyChangedSubscriptionCount);
            Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount);
            Assert.Equal(0, breaking.PropertyChangedSubscriptionCount);
            Assert.Equal(0, old.PropertyChangedSubscriptionCount);
        }
Exemple #41
0
        private ExpressionObserver CreateDataContextObserver(
            IAvaloniaObject target,
            string path,
            bool targetIsDataContext,
            object anchor,
            bool enableDataValidation)
        {
            Contract.Requires <ArgumentNullException>(target != null);

            if (!(target is IStyledElement))
            {
                target = anchor as IStyledElement;

                if (target == null)
                {
                    throw new InvalidOperationException("Cannot find a DataContext to bind to.");
                }
            }

            if (!targetIsDataContext)
            {
                var result = new ExpressionObserver(
                    () => target.GetValue(StyledElement.DataContextProperty),
                    path,
                    new UpdateSignal(target, StyledElement.DataContextProperty),
                    enableDataValidation);

                return(result);
            }
            else
            {
                return(new ExpressionObserver(
                           GetParentDataContext(target),
                           path,
                           enableDataValidation));
            }
        }
        public void Can_Replace_Root()
        {
            var first = new Class1 {
                Foo = "foo"
            };
            var second = new Class1 {
                Foo = "bar"
            };
            var root   = first;
            var update = new Subject <Unit>();
            var target = ExpressionObserver.Create(() => root, o => o.Foo, update);
            var result = new List <object>();
            var sub    = target.Subscribe(x => result.Add(x));

            root = second;
            update.OnNext(Unit.Default);
            root = null;
            update.OnNext(Unit.Default);

            Assert.Equal(
                new object[]
            {
                "foo",
                "bar",
                new BindingNotification(
                    new MarkupBindingChainException("Null value", "o => o.Foo", string.Empty),
                    BindingErrorType.Error,
                    AvaloniaProperty.UnsetValue)
            },
                result);

            Assert.Equal(0, first.PropertyChangedSubscriptionCount);
            Assert.Equal(0, second.PropertyChangedSubscriptionCount);

            GC.KeepAlive(first);
            GC.KeepAlive(second);
        }
        public void Sends_Correct_Notifications_With_Property_Chain()
        {
            var container = new Container();
            var inner     = new IndeiTest();

            var observer = new ExpressionObserver(
                container,
                $"{nameof(Container.Inner)}.{nameof(IndeiTest.MustBePositive)}",
                true);
            var result = new List <object>();

            observer.Subscribe(x => result.Add(x));

            Assert.Equal(new[]
            {
                new BindingNotification(
                    new MarkupBindingChainException("Null value", "Inner.MustBePositive", "Inner"),
                    BindingErrorType.Error,
                    AvaloniaProperty.UnsetValue),
            }, result);

            GC.KeepAlive(container);
            GC.KeepAlive(inner);
        }
Exemple #44
0
        public void Should_Track_End_Of_Property_Chain_Changing()
        {
            var data = new Class1 {
                Next = new Class2 {
                    Bar = "bar"
                }
            };
            var target = new ExpressionObserver(data, "Next.Bar");
            var result = new List <object>();

            var sub = target.Subscribe(x => result.Add(x));

            ((Class2)data.Next).Bar = "baz";
            ((Class2)data.Next).Bar = null;

            Assert.Equal(new[] { "bar", "baz", null }, result);

            sub.Dispose();

            Assert.Equal(0, data.PropertyChangedSubscriptionCount);
            Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount);

            GC.KeepAlive(data);
        }
        public void Should_Not_Keep_Source_Alive()
        {
            Func <Tuple <ExpressionObserver, WeakReference> > run = () =>
            {
                var source = new Class1 {
                    Foo = "foo"
                };
                var target = ExpressionObserver.Create(source, o => o.Foo);
                return(Tuple.Create(target, new WeakReference(source)));
            };

            var result = run();

            result.Item1.Subscribe(x => { });

            // Mono trickery
            GC.Collect(2);
            GC.WaitForPendingFinalizers();
            GC.WaitForPendingFinalizers();
            GC.Collect(2);


            Assert.Null(result.Item2.Target);
        }
        protected ExpressionObserver CreateDataContextObserver(
            IAvaloniaObject target,
            ExpressionNode node,
            bool targetIsDataContext,
            object anchor)
        {
            Contract.Requires <ArgumentNullException>(target != null);

            if (!(target is IDataContextProvider))
            {
                target = anchor as IDataContextProvider;

                if (target == null)
                {
                    throw new InvalidOperationException("Cannot find a DataContext to bind to.");
                }
            }

            if (!targetIsDataContext)
            {
                var result = new ExpressionObserver(
                    () => target.GetValue(StyledElement.DataContextProperty),
                    node,
                    new UpdateSignal(target, StyledElement.DataContextProperty),
                    null);

                return(result);
            }
            else
            {
                return(new ExpressionObserver(
                           GetParentDataContext(target),
                           node,
                           null));
            }
        }
        public void Can_Replace_Root()
        {
            var first = new Class1 {
                Foo = "foo"
            };
            var second = new Class1 {
                Foo = "bar"
            };
            var root   = first;
            var update = new Subject <Unit>();
            var target = new ExpressionObserver(() => root, "Foo", update);
            var result = new List <object>();
            var sub    = target.Subscribe(x => result.Add(x));

            root = second;
            update.OnNext(Unit.Default);
            root = null;
            update.OnNext(Unit.Default);

            Assert.Equal(new[] { "foo", "bar", AvaloniaProperty.UnsetValue }, result);

            Assert.Equal(0, first.SubscriptionCount);
            Assert.Equal(0, second.SubscriptionCount);
        }
Exemple #48
0
        public async Task Null_Value_Should_Use_TargetNullValue()
        {
            var data = new Class1 {
                StringValue = "foo"
            };

            var target = new BindingExpression(
                ExpressionObserver.Create(data, o => o.StringValue),
                typeof(string),
                AvaloniaProperty.UnsetValue,
                "bar",
                DefaultValueConverter.Instance);

            object result = null;

            target.Subscribe(x => result = x);

            Assert.Equal("foo", result);

            data.StringValue = null;
            Assert.Equal("bar", result);

            GC.KeepAlive(data);
        }
        public void Validation_Plugins_Send_Correct_Notifications()
        {
            var data     = new IndeiTest();
            var observer = new ExpressionObserver(data, nameof(data.MustBePositive), true);
            var result   = new List <object>();

            observer.Subscribe(x => result.Add(x));
            observer.SetValue(5);
            observer.SetValue(-5);
            observer.SetValue("foo");
            observer.SetValue(5);

            Assert.Equal(new[]
            {
                new BindingNotification(0),

                // Value is notified twice as ErrorsChanged is always called by IndeiTest.
                new BindingNotification(5),
                new BindingNotification(5),

                // Value is first signalled without an error as validation hasn't been updated.
                new BindingNotification(-5),
                new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, -5),

                // Exception is thrown by trying to set value to "foo".
                new BindingNotification(
                    new ArgumentException("Object of type 'System.String' cannot be converted to type 'System.Int32'."),
                    BindingErrorType.DataValidationError),

                // Value is set then validation is updated.
                new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5),
                new BindingNotification(5),
            }, result);

            GC.KeepAlive(data);
        }
Exemple #50
0
        /// <inheritdoc/>
        public InstancedBinding Initiate(
            IAvaloniaObject target,
            AvaloniaProperty targetProperty,
            object anchor             = null,
            bool enableDataValidation = false)
        {
            Contract.Requires <ArgumentNullException>(target != null);
            anchor = anchor ?? DefaultAnchor?.Target;

            enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;

            ExpressionObserver observer;

            var(node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver);

            if (ElementName != null)
            {
                observer = CreateElementObserver(
                    (target as IStyledElement) ?? (anchor as IStyledElement),
                    ElementName,
                    node);
            }
            else if (Source != null)
            {
                observer = CreateSourceObserver(Source, node);
            }
            else if (RelativeSource == null)
            {
                if (mode == SourceMode.Data)
                {
                    observer = CreateDataContextObserver(
                        target,
                        node,
                        targetProperty == StyledElement.DataContextProperty,
                        anchor);
                }
                else
                {
                    observer = new ExpressionObserver(
                        (target as IStyledElement) ?? (anchor as IStyledElement),
                        node);
                }
            }
            else if (RelativeSource.Mode == RelativeSourceMode.DataContext)
            {
                observer = CreateDataContextObserver(
                    target,
                    node,
                    targetProperty == StyledElement.DataContextProperty,
                    anchor);
            }
            else if (RelativeSource.Mode == RelativeSourceMode.Self)
            {
                observer = CreateSourceObserver(
                    (target as IStyledElement) ?? (anchor as IStyledElement),
                    node);
            }
            else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
            {
                observer = CreateTemplatedParentObserver(
                    (target as IStyledElement) ?? (anchor as IStyledElement),
                    node);
            }
            else if (RelativeSource.Mode == RelativeSourceMode.FindAncestor)
            {
                if (RelativeSource.Tree == TreeType.Visual && RelativeSource.AncestorType == null)
                {
                    throw new InvalidOperationException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.");
                }

                observer = CreateFindAncestorObserver(
                    (target as IStyledElement) ?? (anchor as IStyledElement),
                    RelativeSource,
                    node);
            }
            else
            {
                throw new NotSupportedException();
            }

            var fallback = FallbackValue;

            // If we're binding to DataContext and our fallback is UnsetValue then override
            // the fallback value to null, as broken bindings to DataContext must reset the
            // DataContext in order to not propagate incorrect DataContexts to child controls.
            // See Avalonia.Markup.UnitTests.Data.DataContext_Binding_Should_Produce_Correct_Results.
            if (targetProperty == StyledElement.DataContextProperty && fallback == AvaloniaProperty.UnsetValue)
            {
                fallback = null;
            }

            var converter  = Converter;
            var targetType = targetProperty?.PropertyType ?? typeof(object);

            // We only respect `StringFormat` if the type of the property we're assigning to will
            // accept a string. Note that this is slightly different to WPF in that WPF only applies
            // `StringFormat` for target type `string` (not `object`).
            if (!string.IsNullOrWhiteSpace(StringFormat) &&
                (targetType == typeof(string) || targetType == typeof(object)))
            {
                converter = new StringFormatValueConverter(StringFormat, converter);
            }

            var subject = new BindingExpression(
                observer,
                targetType,
                fallback,
                converter ?? DefaultValueConverter.Instance,
                ConverterParameter,
                Priority);

            return(new InstancedBinding(subject, Mode, Priority));
        }
Exemple #51
0
        public void ConditionalExpressionActivateAndInactivate()
        {
            var a = InitializeComplexTypeInstance();

            var result = string.Empty;
            var count  = 0;

            ExpressionObserver.Observes(
                () => a.BoolProp ? a.NestedProp.StringProp : a.StringProp,
                (value, exception) =>
            {
                Assert.Null(exception);
                result = value;
                count++;
            });

            var ifFalseTestCases          = new[] { "11", "22", "33" };
            var ifTrueTestCases           = new[] { "1", "2", "3", "4" };
            var ifTrueNestedPropTestCases = new[]
            {
                new ComplexType {
                    StringProp = "111"
                },
                new ComplexType {
                    StringProp = "222"
                },
                new ComplexType {
                    StringProp = "333"
                },
                new ComplexType {
                    StringProp = "444"
                },
                new ComplexType {
                    StringProp = "555"
                },
            };

            // Initial Test = false
            for (int i = 0; i < 5; i++)
            {
                count = 0;

                foreach (string testCase in ifFalseTestCases)
                {
                    a.StringProp = testCase;

                    Assert.Equal(a.BoolProp ? ifTrueNestedPropTestCases.Last().StringProp : testCase, result);
                }

                Assert.Equal(a.BoolProp ? 0 : ifFalseTestCases.Length, count);

                // -------------------------------------------------------

                count = 0;

                foreach (string testCase in ifTrueTestCases)
                {
                    a.NestedProp.StringProp = testCase;

                    Assert.Equal(a.BoolProp ? testCase : ifFalseTestCases.Last(), result);
                }

                Assert.Equal(a.BoolProp ? ifTrueTestCases.Length : 0, count);

                // -------------------------------------------------------

                count = 0;

                foreach (ComplexType testCase in ifTrueNestedPropTestCases)
                {
                    a.NestedProp = testCase;

                    Assert.Equal(a.BoolProp ? testCase.StringProp : ifFalseTestCases.Last(), result);
                }

                Assert.Equal(a.BoolProp ? ifTrueNestedPropTestCases.Length : 0, count);

                // -------------------------------------------------------

                a.BoolProp = !a.BoolProp;

                Assert.Equal(a.BoolProp ? ifTrueNestedPropTestCases.Last().StringProp : ifFalseTestCases.Last(), result);
            }
        }
            public InstancedBinding ItemsSelector(object item)
            {
                var obs = ExpressionObserver.Create(item, o => (o as Node).Children);

                return(InstancedBinding.OneWay(obs));
            }
Exemple #53
0
            public InstancedBinding ItemsSelector(object item)
            {
                var obs = new ExpressionObserver(item, nameof(Node.Children));

                return(InstancedBinding.OneWay(obs));
            }
Exemple #54
0
        public void Convert_Casts_Should_Error()
        {
            var test = 1;

            Assert.Throws <ExpressionParseException>(() => ExpressionObserver.Create(test, o => (double)o));
        }
Exemple #55
0
        public void ThreeLevelNestingConditionalExpressionInactivate()
        {
            var a = InitializeComplexTypeInstance();
            var b = InitializeComplexTypeInstance();
            var c = InitializeComplexTypeInstance();
            var d = InitializeComplexTypeInstance();

            var result = int.MinValue;
            var count  = 0;

            ExpressionObserver.Observes(() =>
                                        a.BoolProp
                    ? a.IntProp
                    : b.BoolProp
                        ? b.IntProp
                        : c.BoolProp
                            ? c.IntProp
                            : d.IntProp,
                                        (value, exception) =>
            {
                Assert.Null(exception);
                result = value;
                count++;
            });

            // 1. False-False-False -> d.IntProp
            Check(d, i =>
            {
                a.IntProp = b.IntProp = c.IntProp = i + 10086;
            });

            // 2. False-True-False -> b.IntProp
            b.BoolProp = true;
            Check(b, i =>
            {
                a.IntProp  = c.IntProp = d.IntProp = i + 10086;
                c.BoolProp = !c.BoolProp;
            });

            // 3. True-False-False -> a.IntProp
            b.BoolProp = false;
            a.BoolProp = true;
            Check(a, i =>
            {
                b.IntProp  = c.IntProp = d.IntProp = i + 10086;
                b.BoolProp = !b.BoolProp;
                c.BoolProp = !c.BoolProp;
            });

            void Check(ComplexType testObject, Action <int> uselessChange)
            {
                result = int.MinValue;
                count  = 0;

                testObject.IntProp = 666;
                Assert.Equal(testObject.IntProp, result);
                Assert.Equal(1, count);

                for (int i = 0; i < 5; i++)
                {
                    uselessChange(i);
                }
                Assert.Equal(testObject.IntProp, result);
                Assert.Equal(1, count);
            }
        }
Exemple #56
0
        public void TwoLevelNestingConditionalExpressionActivateAndInactivate()
        {
            var a = InitializeComplexTypeInstance();
            var complexTypeTestCases = new[]
            {
                new ComplexType {
                    IntProp = 111
                },
                new ComplexType {
                    IntProp = 222
                },
                new ComplexType {
                    IntProp = 333
                },
                new ComplexType {
                    IntProp = 444
                },
                new ComplexType {
                    IntProp = 555
                },
            };

            foreach (var testCase in complexTypeTestCases)
            {
                a.ComplexList.Add(testCase);
            }

            var result = int.MinValue;
            var count  = 0;

            ExpressionObserver.Observes(
                () => a.NestedProp.BoolProp
                    ? a.NestedProp.IntProp
                    : a.BoolProp
                        ? a.IntProp
                        : a.ComplexList[a.IntProp].IntProp,
                (value, exception) =>
            {
                Assert.Null(exception);
                result = value;
                count++;
            });

            // 1. False False: a.ComplexList[a.IntProp].IntProp
            for (int i = 1; i < a.ComplexList.Count; i++)
            {
                a.IntProp = i;
                Assert.Equal(a.ComplexList[i].IntProp, result);

                for (int j = 0; j < 5; j++)
                {
                    var expected = a.ComplexList[i].IntProp = j + 10086;
                    Assert.Equal(expected, result);
                }

                Assert.Equal(i * 6, count);
            }

            // 2. False True: a.IntProp
            result = int.MinValue;
            count  = 0;

            a.BoolProp = true;

            Assert.Equal(a.IntProp, result);
            Assert.Equal(1, count);

            for (int i = 0; i < 5; i++)
            {
                a.ComplexList.Last().IntProp = i + 10086;
                Assert.Equal(a.IntProp, result);
            }

            Assert.Equal(1, count);

            // 3. True True: a.NestedProp.IntProp
            result = int.MinValue;
            count  = 0;

            a.NestedProp.BoolProp = true;

            Assert.Equal(a.NestedProp.IntProp, result);
            Assert.Equal(1, count);

            for (int i = 0; i < 5; i++)
            {
                a.NestedProp.IntProp = i + 10086;
                Assert.Equal(a.NestedProp.IntProp, result);
            }

            Assert.Equal(6, count);

            // 4. True True/False: a.BoolProp ? a.IntProp : a.ComplexList[a.IntProp].IntProp
            result = int.MinValue;
            count  = 0;

            for (int i = 0; i < 5; i++)
            {
                a.BoolProp = !a.BoolProp;
                a.IntProp  = i + 10086;
                a.ComplexList.Last().IntProp = i + 10086;

                Assert.Equal(int.MinValue, result);
            }

            Assert.Equal(0, count);
        }