Forráskód Böngészése

Add pattern-based support for streaming. Fix bugs in method bindings.

Jeremy Koritzinsky 7 éve
szülő
commit
c6c51dd36f

+ 2 - 2
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@@ -83,7 +83,7 @@ namespace Avalonia.Data.Core
             _root = new WeakReference(root);
         }
 
-        public static ExpressionObserver CreateFromExpression<T, U>(
+        public static ExpressionObserver Create<T, U>(
             T root,
             Expression<Func<T, U>> expression,
             bool enableDataValidation = false,
@@ -113,7 +113,7 @@ namespace Avalonia.Data.Core
             _finished = new Subject<Unit>();
         }
 
-        public static ExpressionObserver CreateFromExpression<T, U>(
+        public static ExpressionObserver Create<T, U>(
             IObservable<T> rootObservable,
             Expression<Func<T, U>> expression,
             bool enableDataValidation = false,

+ 14 - 0
src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs

@@ -175,6 +175,20 @@ namespace Avalonia.Data.Core.Parsers
                 return visited;
             }
 
+            if (node.Method.Name == StreamBindingExtensions.StreamBindingName || node.Method.Name.StartsWith(StreamBindingExtensions.StreamBindingName + '`'))
+            {
+                if (node.Method.IsStatic)
+                {
+                    Visit(node.Arguments[0]);
+                }
+                else
+                {
+                    Visit(node.Object);
+                }
+                Nodes.Add(new StreamNode());
+                return node;
+            }
+
             throw new ExpressionParseException(0, $"Invalid expression type in binding expression: {node.NodeType}.");
         }
 

+ 22 - 0
src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia
+{
+    public static class StreamBindingExtensions
+    {
+        internal static string StreamBindingName = "StreamBinding";
+
+        public static T StreamBinding<T>(this Task<T> @this)
+        {
+            throw new InvalidOperationException("This should be used only in a binding expression");
+        }
+
+        public static T StreamBinding<T>(this IObservable<T> @this)
+        {
+            throw new InvalidOperationException("This should be used only in a binding expression");
+        }
+    }
+}

+ 29 - 12
tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_ExpressionTree.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var target = new object();
 
-            var observer = ExpressionObserver.CreateFromExpression(target, o => o);
+            var observer = ExpressionObserver.Create(target, o => o);
 
             Assert.Equal(target, await observer.Take(1));
             GC.KeepAlive(target);
@@ -27,7 +27,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var target = new Class1();
 
-            var observer = ExpressionObserver.CreateFromExpression(target, o => o.Foo);
+            var observer = ExpressionObserver.Create(target, o => o.Foo);
 
             Assert.Null(await observer.Take(1));
 
@@ -45,7 +45,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         public void Property_Acccess_Expression_Can_Set_Property()
         {
             var data = new Class1();
-            var target = ExpressionObserver.CreateFromExpression(data, o => o.Foo);
+            var target = ExpressionObserver.Create(data, o => o.Foo);
 
             using (target.Subscribe(_ => { }))
             {
@@ -60,7 +60,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var data = new[] { 1, 2, 3, 4 };
 
-            var target = ExpressionObserver.CreateFromExpression(data, o => o[0]);
+            var target = ExpressionObserver.Create(data, o => o[0]);
 
             Assert.Equal(data[0], await target.Take(1));
             GC.KeepAlive(data);
@@ -71,7 +71,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var data = new List<int> { 1, 2, 3, 4 };
 
-            var target = ExpressionObserver.CreateFromExpression(data, o => o[0]);
+            var target = ExpressionObserver.Create(data, o => o[0]);
 
             Assert.Equal(data[0], await target.Take(1));
             GC.KeepAlive(data);
@@ -86,7 +86,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
 
             data.Add(key, new object());
 
-            var target = ExpressionObserver.CreateFromExpression(data, o => o[key]);
+            var target = ExpressionObserver.Create(data, o => o[key]);
 
             Assert.Equal(data[key], await target.Take(1));
 
@@ -98,7 +98,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var data = new[] { 1, 2, 3, 4 };
 
-            var target = ExpressionObserver.CreateFromExpression(data, o => o[0]);
+            var target = ExpressionObserver.Create(data, o => o[0]);
 
             using (target.Subscribe(_ => { }))
             {
@@ -113,7 +113,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             NotifyingBase test = new Class1 { Foo = "Test" };
 
-            var target = ExpressionObserver.CreateFromExpression(test, o => ((Class1)o).Foo);
+            var target = ExpressionObserver.Create(test, o => ((Class1)o).Foo);
 
             Assert.Equal("Test", await target.Take(1));
 
@@ -125,7 +125,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var test = 1;
 
-            Assert.Throws<ExpressionParseException>(() => ExpressionObserver.CreateFromExpression(test, o => (double)o));
+            Assert.Throws<ExpressionParseException>(() => ExpressionObserver.Create(test, o => (double)o));
         }
 
         [Fact]
@@ -133,7 +133,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             NotifyingBase test = new Class1 { Foo = "Test" };
 
-            var target = ExpressionObserver.CreateFromExpression(test, o => (o as Class1).Foo);
+            var target = ExpressionObserver.Create(test, o => (o as Class1).Foo);
 
             Assert.Equal("Test", await target.Take(1));
 
@@ -145,7 +145,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var test = new Class2();
 
-            var target = ExpressionObserver.CreateFromExpression(test, o => o[Class2.FooProperty]);
+            var target = ExpressionObserver.Create(test, o => o[Class2.FooProperty]);
 
             Assert.Equal("foo", await target.Take(1));
 
@@ -157,13 +157,30 @@ namespace Avalonia.Base.UnitTests.Data.Core
         {
             var test = new Class1 { Foo = "Test" };
 
-            var target = ExpressionObserver.CreateFromExpression(test, o => o.Foo.Length);
+            var target = ExpressionObserver.Create(test, o => o.Foo.Length);
 
             Assert.Equal(test.Foo.Length, await target.Take(1));
 
             GC.KeepAlive(test);
         }
 
+        [Fact]
+        public void Should_Get_Completed_Task_Value()
+        {
+            using (var sync = UnitTestSynchronizationContext.Begin())
+            {
+                var data = new { Foo = Task.FromResult("foo") };
+                var target = ExpressionObserver.Create(data, o => o.Foo.StreamBinding());
+                var result = new List<object>();
+
+                var sub = target.Subscribe(x => result.Add(x));
+
+                Assert.Equal(new[] { "foo" }, result);
+
+                GC.KeepAlive(data);
+            }
+        }
+
         private class Class1 : NotifyingBase
         {
             private string _foo;

+ 1 - 1
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@@ -515,7 +515,7 @@ namespace Avalonia.Controls.UnitTests
 
             public InstancedBinding ItemsSelector(object item)
             {
-                var obs = ExpressionObserver.CreateFromExpression(item, o => (o as Node).Children);
+                var obs = ExpressionObserver.Create(item, o => (o as Node).Children);
                 return InstancedBinding.OneWay(obs);
             }
 

+ 4 - 4
tests/Avalonia.LeakTests/ExpressionObserverTests.cs

@@ -24,7 +24,7 @@ namespace Avalonia.LeakTests
             Func<ExpressionObserver> run = () =>
             {
                 var source = new { Foo = new AvaloniaList<string> {"foo", "bar"} };
-                var target = ExpressionObserver.CreateFromExpression(source, o => o.Foo);
+                var target = ExpressionObserver.Create(source, o => o.Foo);
 
                 target.Subscribe(_ => { });
                 return target;
@@ -42,7 +42,7 @@ namespace Avalonia.LeakTests
             Func<ExpressionObserver> run = () =>
             {
                 var source = new { Foo = new AvaloniaList<string> { "foo", "bar" } };
-                var target = ExpressionObserver.CreateFromExpression(source, o => o.Foo, true);
+                var target = ExpressionObserver.Create(source, o => o.Foo, true);
 
                 target.Subscribe(_ => { });
                 return target;
@@ -60,7 +60,7 @@ namespace Avalonia.LeakTests
             Func<ExpressionObserver> run = () =>
             {
                 var source = new { Foo = new NonIntegerIndexer() };
-                var target = ExpressionObserver.CreateFromExpression(source, o => o.Foo);
+                var target = ExpressionObserver.Create(source, o => o.Foo);
 
                 target.Subscribe(_ => { });
                 return target;
@@ -78,7 +78,7 @@ namespace Avalonia.LeakTests
             Func<ExpressionObserver> run = () =>
             {
                 var source = new { Foo = new MethodBound() };
-                var target = ExpressionObserver.CreateFromExpression(source, o => (Action)o.Foo.A);
+                var target = ExpressionObserver.Create(source, o => (Action)o.Foo.A);
                 target.Subscribe(_ => { });
                 return target;
             };