Browse Source

Inherit DataType from template without hardcoding on specific DataTemplate implementation

pull/8203/head
Max Katz 4 years ago
parent
commit
f1a2716858
  1. 9
      src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs
  2. 5
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
  3. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  4. 1
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  5. 1
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  6. 122
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

9
src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs

@ -0,0 +1,9 @@
using System;
namespace Avalonia.Metadata;
[AttributeUsage(AttributeTargets.Property)]
public class TemplateDataTypeAttribute : Attribute
{
}

5
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs

@ -49,6 +49,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
else if (child is XamlPropertyAssignmentNode pa)
{
var templateDataTypeAttribute = context.GetAvaloniaTypes().TemplateDataTypeAttribute;
if (pa.Property.Name == "DataContext"
&& pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement)
&& pa.Values[0] is XamlMarkupExtensionNode ext
@ -56,8 +58,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
inferredDataContextTypeNode = ParseDataContext(context, on, obj);
}
else if(context.GetAvaloniaTypes().DataTemplate.IsAssignableFrom(on.Type.GetClrType())
&& pa.Property.Name == "DataType"
else if(pa.Property.CustomAttributes.Any(a => a.Type == templateDataTypeAttribute)
&& pa.Values[0] is XamlTypeExtensionNode dataTypeNode)
{
inferredDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, dataTypeNode.Value.GetClrType());

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -26,6 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType Transitions { get; }
public IXamlType AssignBindingAttribute { get; }
public IXamlType DependsOnAttribute { get; }
public IXamlType TemplateDataTypeAttribute { get; }
public IXamlType UnsetValueType { get; }
public IXamlType StyledElement { get; }
public IXamlType IStyledElement { get; }
@ -112,6 +113,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions");
AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute");
TemplateDataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.TemplateDataTypeAttribute");
AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
AvaloniaProperty,
IBinding, cfg.WellKnownTypes.Object);

1
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@ -7,6 +7,7 @@ namespace Avalonia.Markup.Xaml.Templates
{
public class DataTemplate : IRecyclingDataTemplate
{
[TemplateDataType]
public Type DataType { get; set; }
[Content]

1
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@ -11,6 +11,7 @@ namespace Avalonia.Markup.Xaml.Templates
{
public class TreeDataTemplate : ITreeDataTemplate
{
[TemplateDataType]
public Type DataType { get; set; }
[Content]

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

@ -8,11 +8,14 @@ using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Avalonia.Input;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.UnitTests;
using XamlX;
using Xunit;
@ -455,7 +458,106 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml));
}
}
[Fact]
public void IgnoresDataTemplateTypeFromDataTypePropertyIfXDataTypeDefined()
{
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'>
<Window.DataTemplates>
<DataTemplate DataType='local:TestDataContextBaseClass' x:DataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
</DataTemplate>
</Window.DataTemplates>
<ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target");
var dataContext = new TestDataContext();
dataContext.StringProperty = "Initial Value";
window.DataContext = dataContext;
window.ApplyTemplate();
target.ApplyTemplate();
((ContentPresenter)target.Presenter).UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
}
}
[Fact]
public void InfersCustomDataTemplateBasedOnAttribute()
{
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'>
<Window.DataTemplates>
<local:CustomDataTemplate FancyDataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
</local:CustomDataTemplate>
</Window.DataTemplates>
<ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target");
var dataContext = new TestDataContext();
dataContext.StringProperty = "Initial Value";
window.DataContext = dataContext;
window.ApplyTemplate();
target.ApplyTemplate();
((ContentPresenter)target.Presenter).UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
}
}
[Fact]
public void InfersCustomDataTemplateBasedOnAttributeFromBaseClass()
{
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'>
<Window.DataTemplates>
<local:CustomDataTemplateInherit FancyDataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
</local:CustomDataTemplateInherit>
</Window.DataTemplates>
<ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target");
var dataContext = new TestDataContext();
dataContext.StringProperty = "Initial Value";
window.DataContext = dataContext;
window.ApplyTemplate();
target.ApplyTemplate();
((ContentPresenter)target.Presenter).UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text);
}
}
[Fact]
public void ResolvesElementNameBinding()
{
@ -1324,7 +1426,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public string StringProperty { get; set; }
}
public class TestDataContext : IHasPropertyDerived
public class TestDataContextBaseClass {}
public class TestDataContext : TestDataContextBaseClass, IHasPropertyDerived
{
public string StringProperty { get; set; }
@ -1413,4 +1517,20 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
return ReferenceEquals(null, parameter) == false;
}
}
public class CustomDataTemplate : IDataTemplate
{
[TemplateDataType]
public Type FancyDataType { get; set; }
[Content]
[TemplateContent]
public object Content { get; set; }
public bool Match(object data) => FancyDataType.IsInstanceOfType(data);
public IControl Build(object data) => TemplateContent.Load(Content)?.Control;
}
public class CustomDataTemplateInherit : CustomDataTemplate { }
}

Loading…
Cancel
Save