9 changed files with 334 additions and 29 deletions
@ -0,0 +1,107 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace Perspex.Markup.Binding |
||||
|
{ |
||||
|
internal class ElementAccessorNode : ExpressionNode |
||||
|
{ |
||||
|
private int[] _intArgs; |
||||
|
|
||||
|
public ElementAccessorNode(ExpressionNode next, IList<object> arguments) |
||||
|
: base(next) |
||||
|
{ |
||||
|
Arguments = arguments; |
||||
|
|
||||
|
var intArgs = Arguments.OfType<int>().ToArray(); |
||||
|
|
||||
|
if (intArgs.Length == arguments.Count) |
||||
|
{ |
||||
|
_intArgs = intArgs; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IList<object> Arguments { get; } |
||||
|
|
||||
|
protected override void SubscribeAndUpdate(object target) |
||||
|
{ |
||||
|
CurrentValue = GetValue(target); |
||||
|
|
||||
|
var incc = target as INotifyCollectionChanged; |
||||
|
|
||||
|
if (incc != null) |
||||
|
{ |
||||
|
incc.CollectionChanged += CollectionChanged; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void Unsubscribe(object target) |
||||
|
{ |
||||
|
var incc = target as INotifyCollectionChanged; |
||||
|
|
||||
|
if (incc != null) |
||||
|
{ |
||||
|
incc.CollectionChanged -= CollectionChanged; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
||||
|
{ |
||||
|
bool update = false; |
||||
|
|
||||
|
switch (e.Action) |
||||
|
{ |
||||
|
case NotifyCollectionChangedAction.Add: |
||||
|
update = _intArgs[0] >= e.NewStartingIndex; |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Remove: |
||||
|
update = _intArgs[0] >= e.OldStartingIndex; |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Replace: |
||||
|
update = _intArgs[0] >= e.NewStartingIndex && |
||||
|
_intArgs[0] < e.NewStartingIndex + e.NewItems.Count; |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Move: |
||||
|
update = (_intArgs[0] >= e.NewStartingIndex && |
||||
|
_intArgs[0] < e.NewStartingIndex + e.NewItems.Count) || |
||||
|
(_intArgs[0] >= e.OldStartingIndex && |
||||
|
_intArgs[0] < e.OldStartingIndex + e.OldItems.Count); |
||||
|
break; |
||||
|
case NotifyCollectionChangedAction.Reset: |
||||
|
update = true; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (update) |
||||
|
{ |
||||
|
CurrentValue = GetValue(sender); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ExpressionValue GetValue(object target) |
||||
|
{ |
||||
|
var typeInfo = target.GetType().GetTypeInfo(); |
||||
|
var list = target as IList; |
||||
|
|
||||
|
if (typeInfo.IsArray && _intArgs != null) |
||||
|
{ |
||||
|
return new ExpressionValue(((Array)target).GetValue(_intArgs)); |
||||
|
} |
||||
|
else if (target is IList && _intArgs?.Length == 1) |
||||
|
{ |
||||
|
if (_intArgs[0] < list.Count) |
||||
|
{ |
||||
|
return new ExpressionValue(list[_intArgs[0]]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ExpressionValue.None; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,113 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.ObjectModel; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Markup.Binding; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.UnitTests.Binding |
||||
|
{ |
||||
|
public class ExpressionObserverTests_Indexer |
||||
|
{ |
||||
|
[Fact] |
||||
|
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.True(result.HasValue); |
||||
|
Assert.Equal("bar", result.Value); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
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.True(result.HasValue); |
||||
|
Assert.Equal("qux", result.Value); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async void Should_Get_List_Value() |
||||
|
{ |
||||
|
var data = new { Foo = new List<string> { "foo", "bar" } }; |
||||
|
var target = new ExpressionObserver(data, "Foo[1]"); |
||||
|
var result = await target.Take(1); |
||||
|
|
||||
|
Assert.True(result.HasValue); |
||||
|
Assert.Equal("bar", result.Value); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Track_INCC_Add() |
||||
|
{ |
||||
|
var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; |
||||
|
var target = new ExpressionObserver(data, "Foo[2]"); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
var sub = target.Subscribe(x => result.Add(x.Value)); |
||||
|
data.Foo.Add("baz"); |
||||
|
|
||||
|
Assert.Equal(new[] { null, "baz" }, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Track_INCC_Remove() |
||||
|
{ |
||||
|
var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; |
||||
|
var target = new ExpressionObserver(data, "Foo[0]"); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
var sub = target.Subscribe(x => result.Add(x.Value)); |
||||
|
data.Foo.RemoveAt(0); |
||||
|
|
||||
|
Assert.Equal(new[] { "foo", "bar" }, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Track_INCC_Replace() |
||||
|
{ |
||||
|
var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; |
||||
|
var target = new ExpressionObserver(data, "Foo[1]"); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
var sub = target.Subscribe(x => result.Add(x.Value)); |
||||
|
data.Foo[1] = "baz"; |
||||
|
|
||||
|
Assert.Equal(new[] { "bar", "baz" }, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Track_INCC_Move() |
||||
|
{ |
||||
|
var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; |
||||
|
var target = new ExpressionObserver(data, "Foo[1]"); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
var sub = target.Subscribe(x => result.Add(x.Value)); |
||||
|
data.Foo.Move(0, 1); |
||||
|
|
||||
|
Assert.Equal(new[] { "bar", "foo" }, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Track_INCC_Reset() |
||||
|
{ |
||||
|
var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; |
||||
|
var target = new ExpressionObserver(data, "Foo[1]"); |
||||
|
var result = new List<object>(); |
||||
|
|
||||
|
var sub = target.Subscribe(x => result.Add(x.Value)); |
||||
|
data.Foo.Clear(); |
||||
|
|
||||
|
Assert.Equal(new[] { "bar", null }, result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue