diff --git a/Avalonia.sln b/Avalonia.sln
index ac678ba9ba..568a16ce0e 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.2027
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29102.190
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
@@ -197,7 +197,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "sam
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.FreeDesktop", "src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -1842,6 +1846,54 @@ Global
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.Build.0 = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.Build.0 = Release|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.Build.0 = Release|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/build/AndroidWorkarounds.props b/build/AndroidWorkarounds.props
index 8a5c18e1ae..67947296b3 100644
--- a/build/AndroidWorkarounds.props
+++ b/build/AndroidWorkarounds.props
@@ -5,4 +5,12 @@
+
+
+
+
+
+
+ false
+
diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index d989e643b8..3923bdeeda 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -13,6 +13,7 @@
+
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 6a40f7187d..7919c3ac5a 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -7,6 +7,7 @@
+
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 09d2612ac3..5aef0b5520 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -8,6 +8,9 @@ using Avalonia.Controls;
using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Skia;
using Avalonia.ReactiveUI;
+using Avalonia.Dialogs;
+using System.Collections.Generic;
+using System.Threading.Tasks;
namespace ControlCatalog.NetCore
{
@@ -51,21 +54,22 @@ namespace ControlCatalog.NetCore
else
return builder.StartWithClassicDesktopLifetime(args);
}
-
+
///
/// This method is needed for IDE previewer infrastructure
///
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
.UsePlatformDetect()
- .With(new X11PlatformOptions {EnableMultiTouch = true})
+ .With(new X11PlatformOptions { EnableMultiTouch = true })
.With(new Win32PlatformOptions
{
EnableMultitouch = true,
AllowEglInitialization = true
})
.UseSkia()
- .UseReactiveUI();
+ .UseReactiveUI()
+ .UseManagedSystemDialogs();
static void SilenceConsole()
{
@@ -74,7 +78,8 @@ namespace ControlCatalog.NetCore
Console.CursorVisible = false;
while (true)
Console.ReadKey(true);
- }) {IsBackground = true}.Start();
+ })
+ { IsBackground = true }.Start();
}
}
}
diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs
index 91d9f034a5..95c65ed92f 100644
--- a/samples/ControlCatalog/MainWindow.xaml.cs
+++ b/samples/ControlCatalog/MainWindow.xaml.cs
@@ -6,6 +6,7 @@ using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using ControlCatalog.ViewModels;
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace ControlCatalog
diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs
index 4d4a561b08..3c8d4ca7e6 100644
--- a/src/Avalonia.Base/Collections/AvaloniaList.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaList.cs
@@ -55,15 +55,15 @@ namespace Avalonia.Collections
///
public class AvaloniaList : IAvaloniaList, IList, INotifyCollectionChangedDebug
{
- private List _inner;
+ private readonly List _inner;
private NotifyCollectionChangedEventHandler _collectionChanged;
///
/// Initializes a new instance of the class.
///
public AvaloniaList()
- : this(Enumerable.Empty())
{
+ _inner = new List();
}
///
@@ -89,8 +89,8 @@ namespace Avalonia.Collections
///
public event NotifyCollectionChangedEventHandler CollectionChanged
{
- add { _collectionChanged += value; }
- remove { _collectionChanged -= value; }
+ add => _collectionChanged += value;
+ remove => _collectionChanged -= value;
}
///
@@ -150,7 +150,7 @@ namespace Avalonia.Collections
T old = _inner[index];
- if (!object.Equals(old, value))
+ if (!EqualityComparer.Default.Equals(old, value))
{
_inner[index] = value;
@@ -187,45 +187,38 @@ namespace Avalonia.Collections
Validate?.Invoke(item);
int index = _inner.Count;
_inner.Add(item);
- NotifyAdd(new[] { item }, index);
+ NotifyAdd(item, index);
}
///
/// Adds multiple items to the collection.
///
/// The items.
- public virtual void AddRange(IEnumerable items)
- {
- Contract.Requires(items != null);
-
- var list = (items as IList) ?? items.ToList();
-
- if (list.Count > 0)
- {
- if (Validate != null)
- {
- foreach (var item in list)
- {
- Validate((T)item);
- }
- }
-
- int index = _inner.Count;
- _inner.AddRange(items);
- NotifyAdd(list, index);
- }
- }
+ public virtual void AddRange(IEnumerable items) => InsertRange(_inner.Count, items);
///
/// Removes all items from the collection.
///
public virtual void Clear()
{
- if (this.Count > 0)
+ if (Count > 0)
{
- var old = _inner;
- _inner = new List();
- NotifyReset(old);
+ if (_collectionChanged != null)
+ {
+ var e = ResetBehavior == ResetBehavior.Reset ?
+ EventArgsCache.ResetCollectionChanged :
+ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _inner.ToList(), 0);
+
+ _inner.Clear();
+
+ _collectionChanged(this, e);
+ }
+ else
+ {
+ _inner.Clear();
+ }
+
+ NotifyCountChanged();
}
}
@@ -253,9 +246,20 @@ namespace Avalonia.Collections
/// Returns an enumerator that enumerates the items in the collection.
///
/// An .
- public IEnumerator GetEnumerator()
+ IEnumerator IEnumerable.GetEnumerator()
{
- return _inner.GetEnumerator();
+ return new Enumerator(_inner);
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return new Enumerator(_inner);
+ }
+
+ public Enumerator GetEnumerator()
+ {
+ return new Enumerator(_inner);
}
///
@@ -289,7 +293,7 @@ namespace Avalonia.Collections
{
Validate?.Invoke(item);
_inner.Insert(index, item);
- NotifyAdd(new[] { item }, index);
+ NotifyAdd(item, index);
}
///
@@ -301,20 +305,83 @@ namespace Avalonia.Collections
{
Contract.Requires(items != null);
- var list = (items as IList) ?? items.ToList();
+ bool willRaiseCollectionChanged = _collectionChanged != null;
+ bool hasValidation = Validate != null;
- if (list.Count > 0)
+ if (items is IList list)
{
- if (Validate != null)
+ if (list.Count > 0)
{
- foreach (var item in list)
+ if (list is ICollection collection)
{
- Validate((T)item);
+ if (hasValidation)
+ {
+ foreach (T item in collection)
+ {
+ Validate(item);
+ }
+ }
+
+ _inner.InsertRange(index, collection);
+ NotifyAdd(list, index);
+ }
+ else
+ {
+ using (IEnumerator en = items.GetEnumerator())
+ {
+ int insertIndex = index;
+
+ while (en.MoveNext())
+ {
+ T item = en.Current;
+
+ if (hasValidation)
+ {
+ Validate(item);
+ }
+
+ _inner.Insert(insertIndex++, item);
+ }
+ }
+
+ NotifyAdd(list, index);
}
}
+ }
+ else
+ {
+ using (IEnumerator en = items.GetEnumerator())
+ {
+ if (en.MoveNext())
+ {
+ // Avoid allocating list for collection notification if there is no event subscriptions.
+ List notificationItems = willRaiseCollectionChanged ?
+ new List() :
+ null;
+
+ int insertIndex = index;
+
+ do
+ {
+ T item = en.Current;
+
+ if (hasValidation)
+ {
+ Validate(item);
+ }
- _inner.InsertRange(index, items);
- NotifyAdd((items as IList) ?? items.ToList(), index);
+ _inner.Insert(insertIndex++, item);
+
+ if (willRaiseCollectionChanged)
+ {
+ notificationItems.Add(item);
+ }
+
+ } while (en.MoveNext());
+
+ NotifyAdd(notificationItems, index);
+ }
+ }
}
}
@@ -382,7 +449,7 @@ namespace Avalonia.Collections
if (index != -1)
{
_inner.RemoveAt(index);
- NotifyRemove(new[] { item }, index);
+ NotifyRemove(item , index);
return true;
}
@@ -412,7 +479,7 @@ namespace Avalonia.Collections
{
T item = _inner[index];
_inner.RemoveAt(index);
- NotifyRemove(new[] { item }, index);
+ NotifyRemove(item , index);
}
///
@@ -480,12 +547,6 @@ namespace Avalonia.Collections
_inner.CopyTo((T[])array, index);
}
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return _inner.GetEnumerator();
- }
-
///
Delegate[] INotifyCollectionChangedDebug.GetCollectionChangedSubscribers() => _collectionChanged?.GetInvocationList();
@@ -505,13 +566,29 @@ namespace Avalonia.Collections
NotifyCountChanged();
}
+ ///
+ /// Raises the event with a add action.
+ ///
+ /// The item that was added.
+ /// The starting index.
+ private void NotifyAdd(T item, int index)
+ {
+ if (_collectionChanged != null)
+ {
+ var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new[] { item }, index);
+ _collectionChanged(this, e);
+ }
+
+ NotifyCountChanged();
+ }
+
///
/// Raises the event when the property
/// changes.
///
private void NotifyCountChanged()
{
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
+ PropertyChanged?.Invoke(this, EventArgsCache.CountPropertyChanged);
}
///
@@ -531,23 +608,57 @@ namespace Avalonia.Collections
}
///
- /// Raises the event with a reset action.
+ /// Raises the event with a remove action.
///
- /// The items that were removed.
- private void NotifyReset(IList t)
+ /// The item that was removed.
+ /// The starting index.
+ private void NotifyRemove(T item, int index)
{
if (_collectionChanged != null)
{
- NotifyCollectionChangedEventArgs e;
-
- e = ResetBehavior == ResetBehavior.Reset ?
- new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) :
- new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, t, 0);
-
+ var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { item }, index);
_collectionChanged(this, e);
}
NotifyCountChanged();
}
+
+ ///
+ /// Enumerates the elements of a .
+ ///
+ public struct Enumerator : IEnumerator
+ {
+ private List.Enumerator _innerEnumerator;
+
+ public Enumerator(List inner)
+ {
+ _innerEnumerator = inner.GetEnumerator();
+ }
+
+ public bool MoveNext()
+ {
+ return _innerEnumerator.MoveNext();
+ }
+
+ void IEnumerator.Reset()
+ {
+ ((IEnumerator)_innerEnumerator).Reset();
+ }
+
+ public T Current => _innerEnumerator.Current;
+
+ object IEnumerator.Current => Current;
+
+ public void Dispose()
+ {
+ _innerEnumerator.Dispose();
+ }
+ }
+ }
+
+ internal static class EventArgsCache
+ {
+ internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs(nameof(AvaloniaList