diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index f74ab07e31..c3b136d41d 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/src/Avalonia.Build.Tasks/DeterministicIdGenerator.cs b/src/Avalonia.Build.Tasks/DeterministicIdGenerator.cs new file mode 100644 index 0000000000..f207b558a3 --- /dev/null +++ b/src/Avalonia.Build.Tasks/DeterministicIdGenerator.cs @@ -0,0 +1,12 @@ +using System; +using XamlX.Transform; + +namespace Avalonia.Build.Tasks +{ + public class DeterministicIdGenerator : IXamlIdentifierGenerator + { + private int _nextId = 1; + + public string GenerateIdentifierPart() => (_nextId++).ToString(); + } +} diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 6ef8a98fae..508045dccb 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -22,7 +22,6 @@ using XamlX.IL; namespace Avalonia.Build.Tasks { - public static partial class XamlCompilerTaskExecutor { static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml") @@ -99,7 +98,8 @@ namespace Avalonia.Build.Tasks XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage), AvaloniaXamlIlLanguage.CustomValueConverter, new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)), - new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure))); + new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)), + new DeterministicIdGenerator()); var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext", diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index e02efc2bd2..6419981fb1 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -341,7 +341,9 @@ namespace Avalonia.Controls var pointNum = orient ? x.Position.X : x.Position.Y; var logicalPos = MathUtilities.Clamp(pointNum / pointDen, 0.0d, 1.0d); - var invert = orient ? 0 : 1; + var invert = orient ? + IsDirectionReversed ? 1 : 0 : + IsDirectionReversed ? 0 : 1; var calcVal = Math.Abs(invert - logicalPos); var range = Maximum - Minimum; var finalValue = calcVal * range + Minimum; diff --git a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs index 6771d3e179..359da3d7c2 100644 --- a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs +++ b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs @@ -9,18 +9,22 @@ namespace Avalonia.ReactiveUI { /// /// Initializes ReactiveUI framework to use with Avalonia. Registers Avalonia - /// scheduler and Avalonia activation for view fetcher. Always remember to - /// call this method if you are using ReactiveUI in your application. + /// scheduler, an activation for view fetcher, a template binding hook. Remember + /// to call this method if you are using ReactiveUI in your application. /// public static TAppBuilder UseReactiveUI(this TAppBuilder builder) - where TAppBuilder : AppBuilderBase, new() - { - return builder.AfterPlatformServicesSetup(_ => + where TAppBuilder : AppBuilderBase, new() => + builder.AfterPlatformServicesSetup(_ => Locator.RegisterResolverCallbackChanged(() => { + if (Locator.CurrentMutable is null) + { + return; + } + + PlatformRegistrationManager.SetRegistrationNamespaces(RegistrationNamespace.Avalonia); RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); Locator.CurrentMutable.RegisterConstant(new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook)); - }); - } + })); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs index 0c0dcb1634..f6f47dce0d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs @@ -14,8 +14,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions XamlXmlnsMappings xmlnsMappings, XamlValueConverter customValueConverter, XamlIlClrPropertyInfoEmitter clrPropertyEmitter, - XamlIlPropertyInfoAccessorFactoryEmitter accessorFactoryEmitter) - : base(typeSystem, defaultAssembly, typeMappings, xmlnsMappings, customValueConverter) + XamlIlPropertyInfoAccessorFactoryEmitter accessorFactoryEmitter, + IXamlIdentifierGenerator identifierGenerator = null) + : base(typeSystem, defaultAssembly, typeMappings, xmlnsMappings, customValueConverter, identifierGenerator) { ClrPropertyEmitter = clrPropertyEmitter; AccessorFactoryEmitter = accessorFactoryEmitter; diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs index 07c5451135..650534b347 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs @@ -30,7 +30,7 @@ namespace Avalonia.Markup.Xaml.Templates public IControl Build(object data, IControl existing) { - return existing ?? TemplateContent.Load(Content).Control; + return existing ?? TemplateContent.Load(Content)?.Control; } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs index c8843a3176..c096ed7ed7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs @@ -10,8 +10,7 @@ namespace Avalonia.Markup.Xaml.Templates [TemplateContent] public object Content { get; set; } - public IPanel Build() - => (IPanel)TemplateContent.Load(Content).Control; + public IPanel Build() => (IPanel)TemplateContent.Load(Content)?.Control; object ITemplate.Build() => Build(); } diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs index 65323ae665..45fae9cb28 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs @@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Templates [TemplateContent] public object Content { get; set; } - public IControl Build() => TemplateContent.Load(Content).Control; + public IControl Build() => TemplateContent.Load(Content)?.Control; object ITemplate.Build() => Build(); } diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs index 96f25668fb..483a1a5d06 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs @@ -1,6 +1,4 @@ using System; -using Avalonia.Controls; -using System.Collections.Generic; using Avalonia.Controls.Templates; namespace Avalonia.Markup.Xaml.Templates @@ -14,6 +12,12 @@ namespace Avalonia.Markup.Xaml.Templates { return (ControlTemplateResult)direct(null); } + + if (templateContent is null) + { + return null; + } + throw new ArgumentException(nameof(templateContent)); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index d785ac4ac0..7b065c7f47 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs @@ -51,8 +51,12 @@ namespace Avalonia.Markup.Xaml.Templates public IControl Build(object data) { - var visualTreeForItem = TemplateContent.Load(Content).Control; - visualTreeForItem.DataContext = data; + var visualTreeForItem = TemplateContent.Load(Content)?.Control; + if (visualTreeForItem != null) + { + visualTreeForItem.DataContext = data; + } + return visualTreeForItem; } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs index 033b670bf4..53881467e7 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs @@ -7,6 +7,31 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml { public class DataTemplateTests : XamlTestBase { + [Fact] + public void DataTemplate_Can_Be_Empty() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Null(target.Presenter.Child); + } + } + [Fact] public void DataTemplate_Can_Contain_Name() {