Browse Source

Fix Discovering properties and indexers on parent interfaces of an interface type.

Fixes #4234
pull/4235/head
Jeremy Koritzinsky 6 years ago
parent
commit
5fb7fe3c32
  1. 41
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs
  2. 79
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

41
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs

@ -121,7 +121,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
else
{
var clrProperty = targetType.GetAllProperties().FirstOrDefault(p => p.Name == propName.PropertyName);
var clrProperty = GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName);
if (clrProperty is null)
{
@ -139,15 +139,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
IXamlProperty property = null;
for (var currentType = targetType; currentType != null; currentType = currentType.BaseType)
foreach (var currentType in TraverseTypeHierarchy(targetType))
{
var defaultMemberAttribute = currentType.CustomAttributes.FirstOrDefault(x => x.Type.Namespace == "System.Reflection" && x.Type.Name == "DefaultMemberAttribute");
if (defaultMemberAttribute != null)
{
property = targetType.GetAllProperties().FirstOrDefault(x => x.Name == (string)defaultMemberAttribute.Parameters[0]);
property = currentType.GetAllProperties().FirstOrDefault(x => x.Name == (string)defaultMemberAttribute.Parameters[0]);
break;
}
};
}
if (property is null)
{
throw new XamlX.XamlParseException($"The type '${targetType}' does not have an indexer.", lineInfo);
@ -252,6 +252,39 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return TypeReferenceResolver.ResolveType(context, $"{ns}:{name}", false,
lineInfo, true).GetClrType();
}
static IEnumerable<IXamlProperty> GetAllDefinedProperties(IXamlType type)
{
foreach (var t in TraverseTypeHierarchy(type))
{
foreach (var p in t.Properties)
{
yield return p;
}
}
}
static IEnumerable<IXamlType> TraverseTypeHierarchy(IXamlType type)
{
if (type.IsInterface)
{
yield return type;
foreach (var iface in type.Interfaces)
{
foreach (var h in TraverseTypeHierarchy(iface))
{
yield return h;
}
}
}
else
{
for (var currentType = type; currentType != null; currentType = currentType.BaseType)
{
yield return currentType;
}
}
}
}
class ScopeRegistrationFinder : IXamlAstVisitor

79
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -43,6 +43,33 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
[Fact]
public void ResolvesClrPropertyBasedOnDataContextType_InterfaceInheritance()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:IHasPropertyDerived'>
<TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
var dataContext = new TestDataContext
{
StringProperty = "foobar"
};
window.DataContext = dataContext;
Assert.Equal(dataContext.StringProperty, textBlock.Text);
}
}
[Fact]
public void ResolvesPathPassedByProperty()
{
@ -274,6 +301,36 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
[Fact]
public void ResolvesNonIntegerIndexerBindingFromParentInterfaceCorrectly()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding NonIntegerIndexerInterfaceProperty[Test]}' Name='textBlock' />
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
var dataContext = new TestDataContext();
dataContext.NonIntegerIndexerInterfaceProperty["Test"] = "Initial Value";
window.DataContext = dataContext;
Assert.Equal(dataContext.NonIntegerIndexerInterfaceProperty["Test"], textBlock.Text);
dataContext.NonIntegerIndexerInterfaceProperty["Test"] = "New Value";
Assert.Equal(dataContext.NonIntegerIndexerInterfaceProperty["Test"], textBlock.Text);
}
}
[Fact]
public void InfersDataTemplateTypeFromDataTypeProperty()
{
@ -584,7 +641,23 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
public class TestDataContext
public interface INonIntegerIndexer
{
string this[string key] {get; set;}
}
public interface INonIntegerIndexerDerived : INonIntegerIndexer
{}
public interface IHasProperty
{
string StringProperty {get; set; }
}
public interface IHasPropertyDerived : IHasProperty
{}
public class TestDataContext : IHasPropertyDerived
{
public string StringProperty { get; set; }
@ -600,7 +673,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public NonIntegerIndexer NonIntegerIndexerProperty { get; set; } = new NonIntegerIndexer();
public class NonIntegerIndexer : NotifyingBase
public INonIntegerIndexerDerived NonIntegerIndexerInterfaceProperty => NonIntegerIndexerProperty;
public class NonIntegerIndexer : NotifyingBase, INonIntegerIndexerDerived
{
private readonly Dictionary<string, string> _storage = new Dictionary<string, string>();

Loading…
Cancel
Save