Browse Source

Merge pull request #537 from donandren/memberselectorpath

member selector support complex member path
pull/541/head
Steven Kirk 10 years ago
parent
commit
f4c7cc3f15
  1. 58
      src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs
  2. 3
      src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs
  3. 3
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  4. 168
      tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs

58
src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs

@ -1,20 +1,64 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Reflection;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Markup.Data;
using System;
namespace Avalonia.Markup.Xaml.Templates
{
public class MemberSelector : IMemberSelector
{
public string MemberName { get; set; }
public string MemberName
{
get { return _memberName; }
set
{
if (_memberName != value)
{
_memberName = value;
_expressionNode = null;
_memberValueNode = null;
}
}
}
public object Select(object o)
{
// TODO: Handle nested property paths, changing values etc.
var property = o.GetType().GetRuntimeProperty(MemberName);
return property?.GetValue(o);
if (string.IsNullOrEmpty(MemberName))
{
return o;
}
if (_expressionNode == null)
{
_expressionNode = ExpressionNodeBuilder.Build(MemberName);
_memberValueNode = _expressionNode;
while (_memberValueNode.Next != null)
_memberValueNode = _memberValueNode.Next;
}
_expressionNode.Target = new WeakReference(o);
object result = _memberValueNode.CurrentValue.Target;
if (result == AvaloniaProperty.UnsetValue)
{
return null;
}
else if (result is BindingError)
{
return null;
}
return result;
}
private ExpressionNode _expressionNode;
private string _memberName;
private ExpressionNode _memberValueNode;
}
}
}

3
src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs

@ -7,4 +7,5 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Avalonia.Markup")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup")]
[assembly: InternalsVisibleTo("Avalonia.Markup.UnitTests")]
[assembly: InternalsVisibleTo("Avalonia.Markup.UnitTests")]
[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml")]

3
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" />
<PropertyGroup>
@ -103,6 +103,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SampleAvaloniaObject.cs" />
<Compile Include="StyleTests.cs" />
<Compile Include="Templates\MemberSelectorTests.cs" />
<Compile Include="Templates\DataTemplateTests.cs" />
<Compile Include="Xaml\BasicTests.cs" />
<Compile Include="Xaml\ControlBindingTests.cs" />

168
tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs

@ -0,0 +1,168 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Markup.Xaml.Templates;
using System;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Templates
{
public class MemberSelectorTests
{
[Fact]
public void Should_Not_Hold_Reference_To_Object()
{
WeakReference dataRef = null;
var selector = new MemberSelector() { MemberName = "Child.StringValue" };
Action run = () =>
{
var data = new Item()
{
Child = new Item() { StringValue = "Value1" }
};
Assert.Same("Value1", selector.Select(data));
dataRef = new WeakReference(data);
};
run();
GC.Collect();
Assert.False(dataRef.IsAlive);
}
[Fact]
public void Should_Select_Child_Property_Value()
{
var selector = new MemberSelector() { MemberName = "Child.StringValue" };
var data = new Item()
{
Child = new Item() { StringValue = "Value1" }
};
Assert.Same("Value1", selector.Select(data));
}
[Fact]
public void Should_Select_Child_Property_Value_In_Multiple_Items()
{
var selector = new MemberSelector() { MemberName = "Child.StringValue" };
var data = new Item[]
{
new Item() { Child = new Item() { StringValue = "Value1" } },
new Item() { Child = new Item() { StringValue = "Value2" } },
new Item() { Child = new Item() { StringValue = "Value3" } }
};
Assert.Same("Value1", selector.Select(data[0]));
Assert.Same("Value2", selector.Select(data[1]));
Assert.Same("Value3", selector.Select(data[2]));
}
[Fact]
public void Should_Select_MoreComplex_Property_Value()
{
var selector = new MemberSelector() { MemberName = "Child.Child.Child.StringValue" };
var data = new Item()
{
Child = new Item()
{
Child = new Item()
{
Child = new Item() { StringValue = "Value1" }
}
}
};
Assert.Same("Value1", selector.Select(data));
}
[Fact]
public void Should_Select_Null_Value_On_Null_Object()
{
var selector = new MemberSelector() { MemberName = "StringValue" };
Assert.Equal(null, selector.Select(null));
}
[Fact]
public void Should_Select_Null_Value_On_Wrong_MemberName()
{
var selector = new MemberSelector() { MemberName = "WrongProperty" };
var data = new Item() { StringValue = "Value1" };
Assert.Same(null, selector.Select(data));
}
[Fact]
public void Should_Select_Simple_Property_Value()
{
var selector = new MemberSelector() { MemberName = "StringValue" };
var data = new Item() { StringValue = "Value1" };
Assert.Same("Value1", selector.Select(data));
}
[Fact]
public void Should_Select_Simple_Property_Value_In_Multiple_Items()
{
var selector = new MemberSelector() { MemberName = "StringValue" };
var data = new Item[]
{
new Item() { StringValue = "Value1" },
new Item() { StringValue = "Value2" },
new Item() { StringValue = "Value3" }
};
Assert.Same("Value1", selector.Select(data[0]));
Assert.Same("Value2", selector.Select(data[1]));
Assert.Same("Value3", selector.Select(data[2]));
}
[Fact]
public void Should_Select_Target_On_Empty_MemberName()
{
var selector = new MemberSelector();
var data = new Item() { StringValue = "Value1" };
Assert.Same(data, selector.Select(data));
}
[Fact]
public void Should_Support_Change_Of_MemberName()
{
var selector = new MemberSelector() { MemberName = "StringValue" };
var data = new Item()
{
StringValue = "Value1",
IntValue = 1
};
Assert.Same("Value1", selector.Select(data));
selector.MemberName = "IntValue";
Assert.Equal(1, selector.Select(data));
}
private class Item
{
public Item Child { get; set; }
public int IntValue { get; set; }
public string StringValue { get; set; }
}
}
}
Loading…
Cancel
Save