[C] bind to ValueTuples (#1317)
authorStephane Delcroix <stephane@delcroix.org>
Mon, 11 Dec 2017 21:32:46 +0000 (22:32 +0100)
committerGitHub <noreply@github.com>
Mon, 11 Dec 2017 21:32:46 +0000 (22:32 +0100)
* [C] bind to ValueTuples

* [C] allow indexing on ValueTuples

Xamarin.Forms.Core.UnitTests/BindingUnitTests.cs
Xamarin.Forms.Core/BindingExpression.cs

index 2dac4ed..2f0b057 100644 (file)
@@ -2158,5 +2158,24 @@ namespace Xamarin.Forms.Core.UnitTests
                        parent.Content = view;
                        Assert.That(model.Count, Is.EqualTo(1));
                }
+
+               class MockValueTupleContext
+               {
+                       public (string Foo, string Bar) Tuple { get; set; }
+               }
+
+               [Test]
+               public void ValueTupleAsBindingContext()
+               {
+                       var label = new Label {
+                               BindingContext = new MockValueTupleContext { Tuple = (Foo: "FOO", Bar: "BAR")},
+                       };
+
+                       label.SetBinding(Label.TextProperty, "Tuple.Foo");
+                       Assert.AreEqual("FOO", label.Text);
+                       label.SetBinding(Label.TextProperty, "Tuple[1]");
+                       Assert.AreEqual("BAR", label.Text);
+
+               }
        }
 }
\ No newline at end of file
index a4d3375..9c82cfb 100644 (file)
@@ -6,6 +6,7 @@ using System.Globalization;
 using System.Linq;
 using System.Reflection;
 using Xamarin.Forms.Internals;
+using System.Runtime.CompilerServices;
 
 namespace Xamarin.Forms
 {
@@ -290,8 +291,16 @@ namespace Xamarin.Forms
                                part.IndexerName = indexerName;
 
                                property = sourceType.GetDeclaredProperty(indexerName);
-                               if (property == null)
+                               if (property == null) //is the indexer defined on the base class?
                                        property = sourceType.BaseType.GetProperty(indexerName);
+                               if (property == null) //is the indexer defined on implemented interface ?
+                               {
+                                       foreach (var implementedInterface in sourceType.ImplementedInterfaces) {
+                                               property = implementedInterface.GetProperty(indexerName);
+                                               if (property != null)
+                                                       break;
+                                       }
+                               }
 
                                if (property != null)
                                {
@@ -354,6 +363,31 @@ namespace Xamarin.Forms
                                                }
                                        }
                                }
+
+                               TupleElementNamesAttribute tupleEltNames;
+                               if (   property != null
+                                   && part.NextPart != null
+                                       && property.PropertyType.IsGenericType
+                                       && (   property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,,>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,,,>)
+                                               || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,,,,>))
+                                       && (tupleEltNames = property.GetCustomAttribute(typeof(TupleElementNamesAttribute)) as TupleElementNamesAttribute) != null)
+                               {
+                                       //modify the nextPart to access the tuple item via the ITuple indexer
+                                       var nextPart = part.NextPart;
+                                       var name = nextPart.Content;
+                                       var index = tupleEltNames.TransformNames.IndexOf(name);
+                                       if (index >= 0)
+                                       {
+                                               nextPart.IsIndexer = true;
+                                               nextPart.Content = index.ToString();
+                                       }
+                               }
                        }
                }
 
@@ -502,13 +536,13 @@ namespace Xamarin.Forms
 
                        public object BindablePropertyField { get; set; }
 
-                       public string Content { get; }
+                       public string Content { get; internal set; }
 
                        public string IndexerName { get; set; }
 
                        public bool IsBindablePropertySetter { get; set; }
 
-                       public bool IsIndexer { get; }
+                       public bool IsIndexer { get; internal set; }
 
                        public bool IsSelf { get; }