diff --git a/packages.cake b/packages.cake index d633230189..9defa3004c 100644 --- a/packages.cake +++ b/packages.cake @@ -303,7 +303,7 @@ public class Packages { new NuSpecContent { Source = "Avalonia.Android.dll", Target = "lib/MonoAndroid10" } }, - BasePath = context.Directory("./src/Android/Avalonia.Android/bin/" + parameters.DirSuffix + "/monoandroid44/"), + BasePath = context.Directory("./src/Android/Avalonia.Android/bin/" + parameters.DirSuffix + "/monoandroid44/MonoAndroid44/"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// diff --git a/readme.md b/readme.md index 345ad7fe9b..f345cbd9df 100644 --- a/readme.md +++ b/readme.md @@ -35,6 +35,9 @@ Install-Package Avalonia.Desktop Try out the latest build of Avalonia available for download here: https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master/artifacts +or use nightly build feeds as described here: +https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed + ## Documentation As mentioned above, Avalonia is still in beta and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/docs/quickstart/) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia). diff --git a/src/Avalonia.Controls/Mixins/ContentControlMixin.cs b/src/Avalonia.Controls/Mixins/ContentControlMixin.cs index 95193c0432..e4204bd27f 100644 --- a/src/Avalonia.Controls/Mixins/ContentControlMixin.cs +++ b/src/Avalonia.Controls/Mixins/ContentControlMixin.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Reactive.Disposables; using System.Runtime.CompilerServices; using Avalonia.Collections; using Avalonia.Controls.Presenters; @@ -75,6 +76,12 @@ namespace Avalonia.Controls.Mixins null, presenter.GetValue(ContentPresenter.ChildProperty)); + if (subscriptions.Value.TryGetValue(sender, out IDisposable previousSubscription)) + { + subscription = new CompositeDisposable(previousSubscription, subscription); + subscriptions.Value.Remove(sender); + } + subscriptions.Value.Add(sender, subscription); } } diff --git a/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs b/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs new file mode 100644 index 0000000000..f06553411c --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs @@ -0,0 +1,106 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Collections.Generic; +using System.Linq; +using Avalonia.Collections; +using Avalonia.Controls.Mixins; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.LogicalTree; +using Moq; +using Xunit; + +namespace Avalonia.Controls.UnitTests.Mixins +{ + public class ContentControlMixinTests + { + [Fact] + public void Multiple_Mixin_Usages_Should_Not_Throw() + { + var target = new TestControl() + { + Template = new FuncControlTemplate(_ => new Panel + { + Children = + { + new ContentPresenter { Name = "Content_1_Presenter" }, + new ContentPresenter { Name = "Content_2_Presenter" } + } + }) + }; + + var ex = Record.Exception(() => target.ApplyTemplate()); + + Assert.Null(ex); + } + + [Fact] + public void Replacing_Template_Releases_Events() + { + var p1 = new ContentPresenter { Name = "Content_1_Presenter" }; + var p2 = new ContentPresenter { Name = "Content_2_Presenter" }; + var target = new TestControl + { + Template = new FuncControlTemplate(_ => new Panel + { + Children = + { + p1, + p2 + } + }) + }; + target.ApplyTemplate(); + + Control tc; + + p1.Content = tc = new Control(); + p1.UpdateChild(); + Assert.Contains(tc, target.GetLogicalChildren()); + + p2.Content = tc = new Control(); + p2.UpdateChild(); + Assert.Contains(tc, target.GetLogicalChildren()); + + target.Template = null; + + p1.Content = tc = new Control(); + p1.UpdateChild(); + Assert.DoesNotContain(tc, target.GetLogicalChildren()); + + p2.Content = tc = new Control(); + p2.UpdateChild(); + Assert.DoesNotContain(tc, target.GetLogicalChildren()); + + } + + private class TestControl : TemplatedControl + { + public static readonly StyledProperty Content1Property = + AvaloniaProperty.Register(nameof(Content1)); + + public static readonly StyledProperty Content2Property = + AvaloniaProperty.Register(nameof(Content2)); + + static TestControl() + { + ContentControlMixin.Attach(Content1Property, x => x.LogicalChildren, "Content_1_Presenter"); + ContentControlMixin.Attach(Content2Property, x => x.LogicalChildren, "Content_2_Presenter"); + } + + public object Content1 + { + get { return GetValue(Content1Property); } + set { SetValue(Content1Property, value); } + } + + public object Content2 + { + get { return GetValue(Content2Property); } + set { SetValue(Content2Property, value); } + } + } + } +}