コード例 #1
0
        public void TestObjectPathExpressionArrayIndexer()
        {
            var exp  = ObjectPathExpression.Parse("[0]");
            var eval = exp.Evaluate(new int[] { 100, 200, 300 });

            Assert.AreEqual(100, eval);
        }
コード例 #2
0
        public void TestObjectPathExpressionStringIndexer()
        {
            var exp  = ObjectPathExpression.Parse("[0]");
            var eval = exp.Evaluate("ABCD");

            Assert.AreEqual('A', eval);
        }
コード例 #3
0
        public void TestObjectPathExpressionNestedProperty()
        {
            var exp  = ObjectPathExpression.Parse("Person.FirstName");
            var eval = exp.Evaluate(new { Person = new { FirstName = "John", LastName = "Smith" } });

            Assert.AreEqual("John", eval);
        }
コード例 #4
0
        public void TestObjectPathExpressionSingleProperty()
        {
            var exp  = ObjectPathExpression.Parse("Length");
            var eval = exp.Evaluate("1234");

            Assert.AreEqual(4, eval);
        }
コード例 #5
0
        public void TestOneLevel()
        {
            var testString = "Adam";
            var expression = ObjectPathExpression.Parse("Length");
            var eval       = expression.Evaluate(testString);

            Assert.AreEqual(testString.Length, eval);
        }
コード例 #6
0
        public void TestObjectPathExpressionDictionaryIndexerWithSpaces()
        {
            var exp  = ObjectPathExpression.Parse("[\"Meaning Of Life\"]");
            var eval = exp.Evaluate(new Dictionary <string, int>()
            {
                { "Meaning Of Life", 42 }
            });

            Assert.AreEqual(42, eval);
        }
コード例 #7
0
        public void TestObjectPathExpressionNestedPropertyAfterIndexerWithWhitespaceEverywhere()
        {
            var exp  = ObjectPathExpression.Parse("    [   0    ]     .          Person  .   FirstName   ");
            var eval = exp.Evaluate(new object[]
            {
                new { Person = new { FirstName = "John", LastName = "Smith" } },
                new { Person = new { FirstName = "Bob", LastName = "Smith" } }
            });

            Assert.AreEqual("John", eval);
        }
コード例 #8
0
        public void TestBackToBackIndexer()
        {
            var testObj = new string[1][];

            testObj[0]    = new string[1];
            testObj[0][0] = "Adam";

            var expression = ObjectPathExpression.Parse("[0][0]");
            var eval       = expression.Evaluate(testObj);

            Assert.AreEqual(testObj[0][0], eval);
        }
コード例 #9
0
        public ViewModelBinding(ConsoleControl control, PropertyInfo controlProperty, ObservableObject viewModel, string observablePath, Func <object, object> mapper, Func <object, object> reverseMapper)
        {
            this.mapper        = mapper;
            this.reverseMapper = reverseMapper;
            var exp              = ObjectPathExpression.Parse(observablePath);
            var trace            = exp.EvaluateAndTraceInfo(viewModel);
            var observableObject = trace[trace.Count - 2].Value as ObservableObject;

            var viewModelObservableProperty = trace.Last()?.MemberInfo as PropertyInfo;

            if (observableObject == null)
            {
                throw new InvalidOperationException($"ViewModel property '{viewModel.GetType().FullName}.{observablePath}' is not observable");
            }

            if (viewModelObservableProperty == null)
            {
                throw new InvalidOperationException($"Cannot resolve ViewModel property '{viewModel.GetType().FullName}.{observablePath}'");
            }

            if (mapper != null && reverseMapper != null)
            {
                if (viewModelObservableProperty.PropertyType != controlProperty.PropertyType &&
                    viewModelObservableProperty.PropertyType.IsSubclassOf(controlProperty.PropertyType) == false &&
                    viewModelObservableProperty.PropertyType.GetInterfaces().Contains(controlProperty.PropertyType) == false)
                {
                    throw new InvalidOperationException($"ViewModel type '{viewModel.GetType().FullName} property {observablePath}' of type {viewModelObservableProperty.PropertyType.FullName} is not compatible with control property '{controlProperty.DeclaringType.FullName}.{controlProperty.Name}' of type {controlProperty.PropertyType.FullName} ");
                }
            }

            observableObject.SynchronizeForLifetime(trace.Last().MemberInfo.Name, () =>
            {
                var newValue = viewModelObservableProperty.GetValue(observableObject);
                if (newValue == latestValue)
                {
                    return;
                }
                latestValue = newValue;
                controlProperty.SetValue(control, mapper != null ? mapper(newValue) : newValue);
            }, control.LifetimeManager);

            control.SubscribeForLifetime(controlProperty.Name, () =>
            {
                var newValue = controlProperty.GetValue(control);
                if (newValue == latestValue)
                {
                    return;
                }
                latestValue = newValue;
                viewModelObservableProperty.SetValue(observableObject, reverseMapper != null ? reverseMapper(newValue) : newValue);
            }, control.LifetimeManager);
        }
コード例 #10
0
        public void TestTwoLevels()
        {
            var testObj = new Customer()
            {
                Name = "Adam", Address = new Address()
                {
                    Line1 = "123 Main Street", Line2 = "Seattle, WA", ZipCode = "12345"
                }
            };
            var expression = ObjectPathExpression.Parse("Address.Line1");
            var eval       = expression.Evaluate(testObj);

            Assert.AreEqual(testObj.Address.Line1, eval);
        }
コード例 #11
0
        public void TestDeep()
        {
            var testObj = new Customer()
            {
                Name = "Adam", Address = new Address()
                {
                    Line1 = "123 Main Street", Line2 = "Seattle, WA", ZipCode = "12345"
                }
            };

            testObj.RelatedCustomers.Add(testObj);
            var expression = ObjectPathExpression.Parse("RelatedCustomers[0].RelatedCustomers[0].Name");
            var eval       = expression.Evaluate(testObj);

            Assert.AreEqual(testObj.RelatedCustomers[0].RelatedCustomers[0].Name, eval);
        }
コード例 #12
0
        private IDisposable DeepSubscribeInternal(string path, Action handler, Lifetime rootLifetime = null)
        {
            rootLifetime = rootLifetime ?? new Lifetime();
            var observableRoot = DeepObservableRoot ?? this;
            IObservableObject currentObservable = observableRoot;
            var pathExpression = ObjectPathExpression.Parse(path);
            List <IObjectPathElement> currentPath = new List <IObjectPathElement>();

            var anyChangeLifetime = new Lifetime();

            for (int i = 0; i < pathExpression.Elements.Count; i++)
            {
                var propertyElement = pathExpression.Elements[i] as PropertyPathElement;
                if (propertyElement == null)
                {
                    throw new NotSupportedException("Indexers are not supported for deep observability");
                }

                currentPath.Add(propertyElement);

                ObjectPathExpression.TraceNode eval;

                if (i == pathExpression.Elements.Count - 1 && propertyElement.PropertyName == ObservableObject.AnyProperty)
                {
                    eval = new ObjectPathExpression.TraceNode()
                    {
                        Value = null
                    };
                }
                else
                {
                    eval = new ObjectPathExpression(currentPath).EvaluateAndTraceInfo(observableRoot).Last();
                    if (i != pathExpression.Elements.Count - 1 && (eval.MemberInfo as PropertyInfo).PropertyType.GetInterfaces().Contains(typeof(IObservableObject)) == false)
                    {
                        throw new NotSupportedException($"element {eval.MemberInfo.Name} is not observable");
                    }
                }

                currentObservable.SubscribeForLifetime(propertyElement.PropertyName, () =>
                {
                    handler();
                    if (anyChangeLifetime.IsExpired == false)
                    {
                        anyChangeLifetime.Dispose();
                    }

                    if (rootLifetime.IsExpired == false)
                    {
                        DeepSubscribeInternal(path, handler, rootLifetime);
                    }
                }, EarliestOf(anyChangeLifetime, rootLifetime).LifetimeManager);

                currentObservable = eval.Value as IObservableObject;

                if (currentObservable == null)
                {
                    break;
                }
            }

            return(rootLifetime);
        }