From 5ed27d36d3642ea596579ce4e60969472434d518 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2016 19:58:42 -0600 Subject: [PATCH] Added code for listening for indexer updates via INotifyPropertyChanged. --- .../Data/CommonPropertyNames.cs | 13 +++++ src/Markup/Perspex.Markup/Data/IndexerNode.cs | 49 ++++++++++++++----- .../Perspex.Markup/Perspex.Markup.csproj | 1 + .../Data/ExpressionObserverTests_Indexer.cs | 35 +++++++++++++ 4 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs diff --git a/src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs b/src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs new file mode 100644 index 0000000000..5866539886 --- /dev/null +++ b/src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Markup.Data +{ + public static class CommonPropertyNames + { + public const string IndexerName = "Item"; + } +} diff --git a/src/Markup/Perspex.Markup/Data/IndexerNode.cs b/src/Markup/Perspex.Markup/Data/IndexerNode.cs index 97f3b688f9..46663074e3 100644 --- a/src/Markup/Perspex.Markup/Data/IndexerNode.cs +++ b/src/Markup/Perspex.Markup/Data/IndexerNode.cs @@ -6,6 +6,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; @@ -40,6 +41,22 @@ namespace Perspex.Markup.Data { incc.CollectionChanged += CollectionChanged; } + + var inpc = target as INotifyPropertyChanged; + + if(inpc != null) + { + inpc.PropertyChanged += IndexerPropertyChanged; + } + } + + private void IndexerPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var typeInfo = sender.GetType().GetTypeInfo(); + if (typeInfo.GetDeclaredProperty(e.PropertyName).GetIndexParameters().Any()) + { + CurrentValue = GetValue(sender); + } } protected override void Unsubscribe(object target) @@ -108,17 +125,8 @@ namespace Perspex.Markup.Data } else { - PropertyInfo indexerProperty = null; - ParameterInfo[] indexerParameters = null; - foreach (var property in typeInfo.DeclaredProperties) - { - var indexParams = property.GetIndexParameters(); - if (indexParams.Length > 0) - { - indexerProperty = property; - indexerParameters = indexParams; - } - } + var indexerProperty = GetIndexer(typeInfo); + var indexerParameters = indexerProperty?.GetIndexParameters(); if (indexerProperty != null && indexerParameters.Length == Arguments.Count) { var convertedObjectArray = new object[indexerParameters.Length]; @@ -138,6 +146,25 @@ namespace Perspex.Markup.Data return PerspexProperty.UnsetValue; } + private static PropertyInfo GetIndexer(TypeInfo typeInfo) + { + PropertyInfo indexer; + // Check for the default indexer name first to make this faster. + // This will only be false when a class in VB has a custom indexer name. + if ((indexer = typeInfo.GetDeclaredProperty(CommonPropertyNames.IndexerName)) != null) + { + return indexer; + } + foreach (var property in typeInfo.DeclaredProperties) + { + if (property.GetIndexParameters().Any()) + { + return property; + } + } + return null; + } + private bool InBounds(int[] args, Array array) { if (args.Length == array.Rank) diff --git a/src/Markup/Perspex.Markup/Perspex.Markup.csproj b/src/Markup/Perspex.Markup/Perspex.Markup.csproj index 535fec127f..ba6e7d9331 100644 --- a/src/Markup/Perspex.Markup/Perspex.Markup.csproj +++ b/src/Markup/Perspex.Markup/Perspex.Markup.csproj @@ -41,6 +41,7 @@ Properties\SharedAssemblyInfo.cs + diff --git a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs index 2f54ae3758..a233586a58 100644 --- a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs +++ b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs @@ -146,5 +146,40 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(new[] { "bar", PerspexProperty.UnsetValue }, result); } + + [Fact] + public void Should_Track_NonIntegerIndexer() + { + var data = new { Foo = new NonIntegerIndexer() }; + data.Foo["foo"] = "bar"; + data.Foo["baz"] = "qux"; + + var target = new ExpressionObserver(data, "Foo[foo]"); + var result = new List(); + + var sub = target.Subscribe(x => result.Add(x)); + data.Foo["foo"] = "bar2"; + + var expected = new[] { "bar", "bar2" }; + Assert.Equal(expected, result); + } + + private class NonIntegerIndexer : NotifyingBase + { + private Dictionary storage = new Dictionary(); + + public string this[string key] + { + get + { + return storage[key]; + } + set + { + storage[key] = value; + RaisePropertyChanged(CommonPropertyNames.IndexerName); + } + } + } } }