Browse Source

Validate DataTemplates

pull/8221/head
Max Katz 4 years ago
parent
commit
1d9645f01f
  1. 17
      src/Avalonia.Controls/Templates/DataTemplates.cs
  2. 10
      src/Avalonia.Controls/Templates/ITypedDataTemplate.cs
  3. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  4. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  5. 4
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  6. 8
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs
  7. 21
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs
  8. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs

17
src/Avalonia.Controls/Templates/DataTemplates.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Collections; using Avalonia.Collections;
namespace Avalonia.Controls.Templates namespace Avalonia.Controls.Templates
@ -13,6 +14,22 @@ namespace Avalonia.Controls.Templates
public DataTemplates() public DataTemplates()
{ {
ResetBehavior = ResetBehavior.Remove; ResetBehavior = ResetBehavior.Remove;
Validate += ValidateDataTemplate;
}
private static void ValidateDataTemplate(IDataTemplate template)
{
var valid = template switch
{
ITypedDataTemplate typed => typed.DataType is not null,
_ => true
};
if (!valid)
{
throw new InvalidOperationException("DataTemplate inside of DataTemplates must have a DataType set. Set DataType property or use ItemTemplate with single template instead.");
}
} }
} }
} }

10
src/Avalonia.Controls/Templates/ITypedDataTemplate.cs

@ -0,0 +1,10 @@
using System;
using Avalonia.Metadata;
namespace Avalonia.Controls.Templates;
public interface ITypedDataTemplate : IDataTemplate
{
[DataType]
Type? DataType { get; }
}

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

@ -5,7 +5,7 @@ using Avalonia.Metadata;
namespace Avalonia.Markup.Xaml.Templates namespace Avalonia.Markup.Xaml.Templates
{ {
public class DataTemplate : IRecyclingDataTemplate public class DataTemplate : IRecyclingDataTemplate, ITypedDataTemplate
{ {
[DataType] [DataType]
public Type DataType { get; set; } public Type DataType { get; set; }

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

@ -9,7 +9,7 @@ using Avalonia.Metadata;
namespace Avalonia.Markup.Xaml.Templates namespace Avalonia.Markup.Xaml.Templates
{ {
public class TreeDataTemplate : ITreeDataTemplate public class TreeDataTemplate : ITreeDataTemplate, ITypedDataTemplate
{ {
[DataType] [DataType]
public Type DataType { get; set; } public Type DataType { get; set; }

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

@ -413,11 +413,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests' xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestDataContext'> x:DataType='local:TestDataContext'>
<ItemsControl Items='{CompiledBinding ListProperty}' Name='target'> <ItemsControl Items='{CompiledBinding ListProperty}' Name='target'>
<ItemsControl.DataTemplates> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text='{CompiledBinding}' Name='textBlock' /> <TextBlock Text='{CompiledBinding}' Name='textBlock' />
</DataTemplate> </DataTemplate>
</ItemsControl.DataTemplates> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);

8
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs

@ -74,18 +74,18 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
<Window xmlns='https://github.com/avaloniaui'> <Window xmlns='https://github.com/avaloniaui'>
<DockPanel> <DockPanel>
<TabStrip Name='strip' DockPanel.Dock='Top' Items='{Binding Items}' SelectedIndex='0'> <TabStrip Name='strip' DockPanel.Dock='Top' Items='{Binding Items}' SelectedIndex='0'>
<TabStrip.DataTemplates> <TabStrip.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text='{Binding Header}'/> <TextBlock Text='{Binding Header}'/>
</DataTemplate> </DataTemplate>
</TabStrip.DataTemplates> </TabStrip.ItemTemplate>
</TabStrip> </TabStrip>
<Carousel Name='carousel' Items='{Binding Items}' SelectedIndex='{Binding #strip.SelectedIndex}'> <Carousel Name='carousel' Items='{Binding Items}' SelectedIndex='{Binding #strip.SelectedIndex}'>
<Carousel.DataTemplates> <Carousel.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text='{Binding Detail}'/> <TextBlock Text='{Binding Detail}'/>
</DataTemplate> </DataTemplate>
</Carousel.DataTemplates> </Carousel.ItemTemplate>
</Carousel> </Carousel>
</DockPanel> </DockPanel>
</Window>"; </Window>";

21
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.UnitTests; using Avalonia.UnitTests;
@ -132,5 +133,25 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Same(viewModel.Child.Child, canvas.DataContext); Assert.Same(viewModel.Child.Child, canvas.DataContext);
} }
} }
[Fact]
public void DataTemplates_Without_Type_Should_Throw()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:sys='clr-namespace:System;assembly=netstandard'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.DataTemplates>
<DataTemplate>
<Canvas Name='foo'/>
</DataTemplate>
</Window.DataTemplates>
<ContentControl Name='target' Content='Foo'/>
</Window>";
Assert.Throws<InvalidOperationException>(() => (Window)AvaloniaRuntimeXamlLoader.Load(xaml));
}
}
} }
} }

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs

@ -14,7 +14,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{ {
using (UnitTestApplication.Start(TestServices.MockPlatformWrapper)) using (UnitTestApplication.Start(TestServices.MockPlatformWrapper))
{ {
var xaml = "<DataTemplates xmlns='https://github.com/avaloniaui'><TreeDataTemplate ItemsSource='{Binding}'/></DataTemplates>"; var xaml = "<DataTemplates xmlns='https://github.com/avaloniaui'><TreeDataTemplate DataType='Control' ItemsSource='{Binding}'/></DataTemplates>";
var templates = (DataTemplates)AvaloniaRuntimeXamlLoader.Load(xaml); var templates = (DataTemplates)AvaloniaRuntimeXamlLoader.Load(xaml);
var template = (TreeDataTemplate)(templates.First()); var template = (TreeDataTemplate)(templates.First());

Loading…
Cancel
Save