Browse Source
Merge pull request #8221 from AvaloniaUI/validate-datatemplates
Validate DataTemplates on empty DataType
pull/8373/head
Max Katz
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with
57 additions and
9 deletions
src/Avalonia.Controls/Templates/DataTemplates.cs
src/Avalonia.Controls/Templates/ITypedDataTemplate.cs
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs
@ -1,3 +1,4 @@
using System ;
using Avalonia.Collections ;
namespace Avalonia.Controls.Templates
@ -13,6 +14,22 @@ namespace Avalonia.Controls.Templates
public DataTemplates ( )
{
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." ) ;
}
}
}
}
@ -0,0 +1,10 @@
using System ;
using Avalonia.Metadata ;
namespace Avalonia.Controls.Templates ;
public interface ITypedDataTemplate : IDataTemplate
{
[DataType]
Type ? DataType { get ; }
}
@ -5,7 +5,7 @@ using Avalonia.Metadata;
namespace Avalonia.Markup.Xaml.Templates
{
public class DataTemplate : IRecyclingDataTemplate
public class DataTemplate : IRecyclingDataTemplate , ITypedDataTemplate
{
[DataType]
public Type DataType { get ; set ; }
@ -9,7 +9,7 @@ using Avalonia.Metadata;
namespace Avalonia.Markup.Xaml.Templates
{
public class TreeDataTemplate : ITreeDataTemplate
public class TreeDataTemplate : ITreeDataTemplate , ITypedDataTemplate
{
[DataType]
public Type DataType { get ; set ; }
@ -413,11 +413,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
xmlns : local = ' clr - namespace : Avalonia . Markup . Xaml . UnitTests . MarkupExtensions ; assembly = Avalonia . Markup . Xaml . UnitTests '
x : DataType = ' local : TestDataContext ' >
< ItemsControl Items = ' { CompiledBinding ListProperty } ' Name = ' target ' >
< ItemsControl . DataTemplates >
< ItemsControl . ItemTemplate >
< DataTemplate >
< TextBlock Text = ' { CompiledBinding } ' Name = ' textBlock ' / >
< / DataTemplate >
< / ItemsControl . DataTemplates >
< / ItemsControl . ItemTemplate >
< / ItemsControl >
< / Window > ";
var window = ( Window ) AvaloniaRuntimeXamlLoader . Load ( xaml ) ;
@ -74,18 +74,18 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
< Window xmlns = ' https : //github.com/avaloniaui'>
< DockPanel >
< TabStrip Name = ' strip ' DockPanel . Dock = ' Top ' Items = ' { Binding Items } ' SelectedIndex = '0' >
< TabStrip . DataTemplates >
< TabStrip . ItemTemplate >
< DataTemplate >
< TextBlock Text = ' { Binding Header } ' / >
< / DataTemplate >
< / TabStrip . DataTemplates >
< / TabStrip . ItemTemplate >
< / TabStrip >
< Carousel Name = ' carousel ' Items = ' { Binding Items } ' SelectedIndex = ' { Binding # strip . SelectedIndex } ' >
< Carousel . DataTemplates >
< Carousel . ItemTemplate >
< DataTemplate >
< TextBlock Text = ' { Binding Detail } ' / >
< / DataTemplate >
< / Carousel . DataTemplates >
< / Carousel . ItemTemplate >
< / Carousel >
< / DockPanel >
< / Window > ";
@ -1,3 +1,4 @@
using System ;
using Avalonia.Controls ;
using Avalonia.Controls.Presenters ;
using Avalonia.UnitTests ;
@ -132,5 +133,25 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
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 ) ) ;
}
}
}
}
@ -14,7 +14,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
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 template = ( TreeDataTemplate ) ( templates . First ( ) ) ;