diff --git a/Avalonia.sln b/Avalonia.sln
index 7cf2cf3b8a..88914fe188 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2024
+VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
@@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout", "src\Aval
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{B39A8919-9F95-48FE-AD7B-76E08B509888}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}"
EndProject
@@ -126,10 +126,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\Rende
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Avalonia.Win32.Shared", "src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.shproj", "{9DEFC6B7-845B-4D8F-AFC0-D32BF0032B8C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.NetStandard", "src\Windows\Avalonia.Win32.NetStandard\Avalonia.Win32.NetStandard.csproj", "{40759A76-D0F2-464E-8000-6FF0F5C4BD7C}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DotNetCoreRuntime", "src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj", "{7863EA94-F0FB-4386-BF8C-E5BFA761560A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
@@ -196,14 +192,11 @@ Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
- src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{40759a76-d0f2-464e-8000-6ff0f5c4bd7c}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7863ea94-f0fb-4386-bf8c-e5bfa761560a}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 4
- src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
- src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{9defc6b7-845b-4d8f-afc0-d32bf0032b8c}*SharedItemsImports = 13
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{dabfd304-d6a4-4752-8123-c2ccf7ac7831}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
EndGlobalSection
@@ -369,6 +362,7 @@ Global
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|NetCoreOnly.ActiveCfg = Debug|Any CPU
+ {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|NetCoreOnly.Build.0 = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|x86.ActiveCfg = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|x86.Build.0 = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -378,6 +372,7 @@ Global
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|NetCoreOnly.ActiveCfg = Release|Any CPU
+ {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|NetCoreOnly.Build.0 = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|x86.ActiveCfg = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|x86.Build.0 = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
@@ -1994,46 +1989,6 @@ Global
{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.ActiveCfg = Release|Any CPU
{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.Build.0 = Release|Any CPU
{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.Deploy.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|NetCoreOnly.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|NetCoreOnly.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Ad-Hoc|x86.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|iPhone.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|NetCoreOnly.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|NetCoreOnly.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|x86.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.AppStore|x86.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|iPhone.Build.0 = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|NetCoreOnly.ActiveCfg = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|NetCoreOnly.Build.0 = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Debug|x86.Build.0 = Debug|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|Any CPU.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|iPhone.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|iPhone.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|NetCoreOnly.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|NetCoreOnly.Build.0 = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|x86.ActiveCfg = Release|Any CPU
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C}.Release|x86.Build.0 = Release|Any CPU
{7863EA94-F0FB-4386-BF8C-E5BFA761560A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{7863EA94-F0FB-4386-BF8C-E5BFA761560A}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{7863EA94-F0FB-4386-BF8C-E5BFA761560A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -2623,8 +2578,6 @@ Global
{C7A69145-60B6-4882-97D6-A3921DD43978} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
- {9DEFC6B7-845B-4D8F-AFC0-D32BF0032B8C} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
- {40759A76-D0F2-464E-8000-6FF0F5C4BD7C} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {B9894058-278A-46B5-B6ED-AD613FCC03B3}
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
diff --git a/build/Rx.props b/build/Rx.props
index 323026f5e2..7078e31195 100644
--- a/build/Rx.props
+++ b/build/Rx.props
@@ -5,6 +5,5 @@
-
diff --git a/build/System.Drawing.Common.props b/build/System.Drawing.Common.props
new file mode 100644
index 0000000000..a568152bbd
--- /dev/null
+++ b/build/System.Drawing.Common.props
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/packages.cake b/packages.cake
index bc290fce22..17411aef4c 100644
--- a/packages.cake
+++ b/packages.cake
@@ -370,14 +370,13 @@ public class Packages
new NuGetPackSettings()
{
Id = "Avalonia.Win32",
- Dependencies = new []
+ Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }
- },
+ }.Deps(new string[]{null}, "System.Drawing.Common"),
Files = new []
{
- new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" },
- new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.Win32.dll", Target = "lib/netstandard2.0" }
+ new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.Win32.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Windows"),
OutputDirectory = parameters.NugetRoot
diff --git a/parameters.cake b/parameters.cake
index c727b3107f..e224cce151 100644
--- a/parameters.cake
+++ b/parameters.cake
@@ -97,7 +97,7 @@ public class Parameters
else
{
// Use AssemblyVersion with Build as version
- Version += "-build" + context.EnvironmentVariable("APPVEYOR_BUILD_NUMBER") + "-alpha";
+ Version += "-build" + context.EnvironmentVariable("APPVEYOR_BUILD_NUMBER") + "-beta";
}
}
diff --git a/readme.md b/readme.md
index f074faa2c4..eeee39dabe 100644
--- a/readme.md
+++ b/readme.md
@@ -2,9 +2,9 @@
# Avalonia
-| Gitter Chat | Windows Build Status | Linux/Mac Build Status |
-|---|---|---|
-| [](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [](https://travis-ci.org/AvaloniaUI/Avalonia) |
+| Gitter Chat | Windows Build Status | Linux/Mac Build Status | Open Collective |
+|---|---|---|---|
+| [](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [](https://travis-ci.org/AvaloniaUI/Avalonia) | [](#backers) [](#sponsors) |
## About
@@ -48,3 +48,33 @@ See the [build instructions here](http://avaloniaui.net/contributing/build).
## Contributing
Please read the [contribution guidelines](http://avaloniaui.net/contributing/contributing) before submitting a pull request.
+
+### Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing/contributing)].
+
+
+
+### Backers
+
+Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Avalonia#backer)]
+
+
+
+
+### Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/Avalonia#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
index 77f38d3bd7..c1c5cdcaf7 100644
--- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
+++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
@@ -170,15 +170,14 @@
Avalonia.Themes.Default
- {D2D3083-71DD-4CC9-8907-39A0D86FB322}
+ {7d2d3083-71dd-4cc9-8907-39a0d86fb322}
Avalonia.Skia
- false
- false
{d0a739b9-3c68-4ba6-a328-41606954b6bd}
ControlCatalog
+
\ No newline at end of file
diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
index 54cd132b95..b27b06a277 100644
--- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
@@ -34,14 +34,18 @@ namespace Avalonia.Collections
///
/// An action called when the collection is reset.
///
+ ///
+ /// Indicates if a weak subscription should be used to track changes to the collection.
+ ///
/// A disposable used to terminate the subscription.
public static IDisposable ForEachItem(
this IAvaloniaReadOnlyList collection,
Action added,
Action removed,
- Action reset)
+ Action reset,
+ bool weakSubscription = false)
{
- return collection.ForEachItem((_, i) => added(i), (_, i) => removed(i), reset);
+ return collection.ForEachItem((_, i) => added(i), (_, i) => removed(i), reset, weakSubscription);
}
///
@@ -63,12 +67,16 @@ namespace Avalonia.Collections
/// An action called when the collection is reset. This will be followed by calls to
/// for each item present in the collection after the reset.
///
+ ///
+ /// Indicates if a weak subscription should be used to track changes to the collection.
+ ///
/// A disposable used to terminate the subscription.
public static IDisposable ForEachItem(
this IAvaloniaReadOnlyList collection,
Action added,
Action removed,
- Action reset)
+ Action reset,
+ bool weakSubscription = false)
{
void Add(int index, IList items)
{
@@ -118,9 +126,17 @@ namespace Avalonia.Collections
};
Add(0, (IList)collection);
- collection.CollectionChanged += handler;
- return Disposable.Create(() => collection.CollectionChanged -= handler);
+ if (weakSubscription)
+ {
+ return collection.WeakSubscribe(handler);
+ }
+ else
+ {
+ collection.CollectionChanged += handler;
+
+ return Disposable.Create(() => collection.CollectionChanged -= handler);
+ }
}
public static IAvaloniaReadOnlyList CreateDerivedList(
diff --git a/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs b/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs
new file mode 100644
index 0000000000..d295cb91ce
--- /dev/null
+++ b/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs
@@ -0,0 +1,127 @@
+// 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;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
+using Avalonia.Utilities;
+
+namespace Avalonia.Collections
+{
+ public static class NotifyCollectionChangedExtensions
+ {
+ ///
+ /// Gets a weak observable for the CollectionChanged event.
+ ///
+ /// The collection.
+ /// An observable.
+ public static IObservable GetWeakCollectionChangedObservable(
+ this INotifyCollectionChanged collection)
+ {
+ Contract.Requires(collection != null);
+
+ return new WeakCollectionChangedObservable(new WeakReference(collection));
+ }
+
+ ///
+ /// Subcribes to the CollectionChanged event using a weak subscription.
+ ///
+ /// The collection.
+ ///
+ /// An action called when the collection event is raised.
+ ///
+ /// A disposable used to terminate the subscription.
+ public static IDisposable WeakSubscribe(
+ this INotifyCollectionChanged collection,
+ NotifyCollectionChangedEventHandler handler)
+ {
+ Contract.Requires(collection != null);
+ Contract.Requires(handler != null);
+
+ return
+ collection.GetWeakCollectionChangedObservable()
+ .Subscribe(e => handler.Invoke(collection, e));
+ }
+
+ ///
+ /// Subcribes to the CollectionChanged event using a weak subscription.
+ ///
+ /// The collection.
+ ///
+ /// An action called when the collection event is raised.
+ ///
+ /// A disposable used to terminate the subscription.
+ public static IDisposable WeakSubscribe(
+ this INotifyCollectionChanged collection,
+ Action handler)
+ {
+ Contract.Requires(collection != null);
+ Contract.Requires(handler != null);
+
+ return
+ collection.GetWeakCollectionChangedObservable()
+ .Subscribe(handler);
+ }
+
+ private class WeakCollectionChangedObservable : ObservableBase,
+ IWeakSubscriber
+ {
+ private WeakReference _sourceReference;
+ private readonly Subject _changed = new Subject();
+
+ private int _count;
+
+ public WeakCollectionChangedObservable(WeakReference source)
+ {
+ _sourceReference = source;
+ }
+
+ public void OnEvent(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ _changed.OnNext(e);
+ }
+
+ protected override IDisposable SubscribeCore(IObserver observer)
+ {
+ if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance))
+ {
+ if (_count++ == 0)
+ {
+ WeakSubscriptionManager.Subscribe(
+ instance,
+ nameof(instance.CollectionChanged),
+ this);
+ }
+
+ return Observable.Using(() => Disposable.Create(DecrementCount), _ => _changed)
+ .Subscribe(observer);
+ }
+ else
+ {
+ _changed.OnCompleted();
+ observer.OnCompleted();
+ return Disposable.Empty;
+ }
+ }
+
+ private void DecrementCount()
+ {
+ if (--_count == 0)
+ {
+ if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance))
+ {
+ WeakSubscriptionManager.Unsubscribe(
+ instance,
+ nameof(instance.CollectionChanged),
+ this);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index 7d29a4f969..cf7acb3e8a 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -81,12 +81,14 @@ namespace Avalonia.Threading
///
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
+ Contract.Requires(action != null);
return _jobRunner?.InvokeAsync(action, priority);
}
///
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
+ Contract.Requires(action != null);
_jobRunner?.Post(action, priority);
}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 4366de1cd6..6a26e29187 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -1,6 +1,7 @@
// 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;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -54,6 +55,7 @@ namespace Avalonia.Controls
private IEnumerable _items = new AvaloniaList