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