diff --git a/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Direct3DInteropSample.v3.ncrunchproject b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings
index ab21d6e50b..1fd6f8d092 100644
--- a/Avalonia.sln.DotSettings
+++ b/Avalonia.sln.DotSettings
@@ -1,4 +1,5 @@
+ ExplicitlyExcluded
ExplicitlyExcluded
ExplicitlyExcluded
HINT
diff --git a/appveyor.yml b/appveyor.yml
index 4bf7f7f157..6b63176a89 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -35,6 +35,7 @@ test: off
artifacts:
- path: artifacts\nuget\*.nupkg
- path: artifacts\zip\*.zip
+ - path: artifacts\inspectcode.xml
cache:
- gtk-sharp-2.12.26.msi
- dotnet-1.0.1.exe
diff --git a/build.cake b/build.cake
index 1e29b33fc1..6518431959 100644
--- a/build.cake
+++ b/build.cake
@@ -5,7 +5,8 @@
#addin "nuget:?package=Polly&version=4.2.0"
#addin "nuget:?package=NuGet.Core&version=2.12.0"
#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease"
-#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.1.20150828.125449"
+#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.3.20160517.113140"
+#tool "JetBrains.ReSharper.CommandLineTools"
///////////////////////////////////////////////////////////////////////////////
// TOOLS
///////////////////////////////////////////////////////////////////////////////
@@ -104,7 +105,7 @@ Task("Restore-NuGet-Packages")
.Does(() =>
{
var maxRetryCount = 5;
- var toolTimeout = 1d;
+ var toolTimeout = 2d;
Policy
.Handle()
.Retry(maxRetryCount, (exception, retryCount, context) => {
@@ -279,11 +280,15 @@ Task("Zip-Files")
Zip(parameters.ZipSourceControlCatalogDesktopDirs,
parameters.ZipTargetControlCatalogDesktopDirs,
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") +
+ GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") +
+ GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") +
+ GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe"));
});
Task("Create-NuGet-Packages")
.IsDependentOn("Run-Unit-Tests")
+ .IsDependentOn("Inspect")
.Does(() =>
{
foreach(var nuspec in packages.NuspecNuGetSettings)
@@ -331,7 +336,6 @@ Task("Publish-NuGet")
.WithCriteria(() => !parameters.IsLocalBuild)
.WithCriteria(() => !parameters.IsPullRequest)
.WithCriteria(() => parameters.IsMainRepo)
- .WithCriteria(() => parameters.IsMasterBranch)
.WithCriteria(() => parameters.IsNuGetRelease)
.Does(() =>
{
@@ -360,6 +364,39 @@ Task("Publish-NuGet")
Information("Publish-NuGet Task failed, but continuing with next Task...");
});
+Task("Inspect")
+ .WithCriteria(parameters.IsRunningOnWindows)
+ .IsDependentOn("Restore-NuGet-Packages")
+ .Does(() =>
+ {
+ var badIssues = new []{"PossibleNullReferenceException"};
+ var whitelist = new []{"tests", "src\\android", "src\\ios",
+ "src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"};
+ Information("Running code inspections");
+
+
+ StartProcess("tools\\JetBrains.ReSharper.CommandLineTools\\tools\\inspectcode.exe",
+ new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" });
+ Information("Analyzing report");
+ var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml"));
+ var failBuild = false;
+ foreach(var xml in doc.Descendants("Issue"))
+ {
+ var typeId = xml.Attribute("TypeId").Value.ToString();
+ if(badIssues.Contains(typeId))
+ {
+ var file = xml.Attribute("File").Value.ToString().ToLower();
+ if(whitelist.Any(wh => file.StartsWith(wh)))
+ continue;
+ var line = xml.Attribute("Line").Value.ToString();
+ Error(typeId + " - " + file + " on line " + line);
+ failBuild = true;
+ }
+ }
+ if(failBuild)
+ throw new Exception("Issues found");
+ });
+
///////////////////////////////////////////////////////////////////////////////
// TARGETS
///////////////////////////////////////////////////////////////////////////////
diff --git a/build/SkiaSharp.Desktop.props b/build/SkiaSharp.Desktop.props
deleted file mode 100644
index bb6b29cfa6..0000000000
--- a/build/SkiaSharp.Desktop.props
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index bd6b4ebc53..77407f9996 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,5 +1,6 @@
-
+
+
diff --git a/docs/guidelines/build.md b/docs/guidelines/build.md
index 12047ff357..822496814b 100644
--- a/docs/guidelines/build.md
+++ b/docs/guidelines/build.md
@@ -2,7 +2,7 @@
## Windows
-Avalonia requires at least Visual Studio 2015 to build on Windows.
+Avalonia requires at least Visual Studio 2017 to build on Windows.
### Install GTK Sharp
diff --git a/docs/index.md b/docs/index.md
index 99424e3f75..37d95b203d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -10,7 +10,7 @@ What does alpha mean? Well, it means that it's now at a stage where you can have
## How do I try it out
-The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/a4542e8a-b56c-4295-8df1-7e220178b873).
+The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
This will add a Avalonia project template and a Window template to the standard Visual Studo "Add" dialog (yes, icons still to come :) ):
diff --git a/docs/tutorial/gettingstarted.md b/docs/tutorial/gettingstarted.md
index 0972d26c48..5b7fe436a7 100644
--- a/docs/tutorial/gettingstarted.md
+++ b/docs/tutorial/gettingstarted.md
@@ -4,7 +4,7 @@

-The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213).
+The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
This will add a Avalonia project template and a Window template to the standard Visual Studo “Add”
dialog (yes, icons still to come :) ):
diff --git a/packages.cake b/packages.cake
index f030c73a06..1e8e356694 100644
--- a/packages.cake
+++ b/packages.cake
@@ -7,6 +7,7 @@ public class Packages
public FilePath[] BinFiles { get; private set; }
public string NugetPackagesDir {get; private set;}
public string SkiaSharpVersion {get; private set; }
+ public string SkiaSharpLinuxVersion {get; private set; }
public Packages(ICakeContext context, Parameters parameters)
{
// NUGET NUSPECS
@@ -75,6 +76,7 @@ public class Packages
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
+ SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1;
var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1;
var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1;
var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1;
@@ -85,6 +87,7 @@ public class Packages
context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
+ context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion);
context.Information("Package: SharpDX, version: {0}", SharpDXVersion);
context.Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version);
context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version);
@@ -425,10 +428,7 @@ public class Packages
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
- //netstandard1.3
- new NuSpecDependency() { Id = "Avalonia", TargetFramework = "netstandard1.3", Version = parameters.Version },
- new NuSpecDependency() { Id = "SkiaSharp", TargetFramework = "netstandard1.3", Version = SkiaSharpVersion },
- new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netstandard1.3", Version = "1.6.0" }
+ new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion }
},
Files = new []
{
@@ -446,11 +446,17 @@ public class Packages
Id = "Avalonia.Desktop",
Dependencies = new []
{
- new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Gtk", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Cairo", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version }
+ //Full .NET
+ new NuSpecDependency() { Id = "Avalonia.Direct2D1", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Gtk", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Cairo", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
+ //.NET Core
+ new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.1", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.1", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.1", Version = parameters.Version }
},
Files = new NuSpecContent[]
{
diff --git a/parameters.cake b/parameters.cake
index 4a7270011c..12f448303e 100644
--- a/parameters.cake
+++ b/parameters.cake
@@ -75,17 +75,25 @@ public class Parameters
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform)
&& StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable;
- IsNuGetRelease = IsTagged && IsReleasable;
+
// VERSION
Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion);
if (IsRunningOnAppVeyor)
{
+ string tagVersion = null;
if (IsTagged)
{
- // Use Tag Name as version
- Version = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
+ var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
+ var nugetReleasePrefix = "nuget-release-";
+ IsNuGetRelease = IsTagged && IsReleasable && tag.StartsWith(nugetReleasePrefix);
+ if(IsNuGetRelease)
+ tagVersion = tag.Substring(nugetReleasePrefix.Length);
+ }
+ if(tagVersion != null)
+ {
+ Version = tagVersion;
}
else
{
diff --git a/readme.md b/readme.md
index 2149aff386..621037e89e 100644
--- a/readme.md
+++ b/readme.md
@@ -42,7 +42,7 @@ using Direct2D and other operating systems using Gtk & Cairo.
Avalonia is now in alpha. What does "alpha" mean? Well, it means that it's now at a stage where you
can have a play and hopefully create simple applications. There's now a [Visual
-Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213)
+Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
containing project and item templates that will help you get started, and
there's an initial complement of controls. There's still a lot missing, and you
*will* find bugs, and the API *will* change, but this represents the first time
diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
index 3ca8640d40..e215105149 100644
--- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
+++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
@@ -26,7 +26,7 @@
4
- AnyCPU
+ x86
pdbonly
true
bin\Release\
@@ -142,6 +142,7 @@
+
\ No newline at end of file
diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs
index 1ff1e1938b..ad40e81895 100644
--- a/samples/interop/Direct3DInteropSample/MainWindow.cs
+++ b/samples/interop/Direct3DInteropSample/MainWindow.cs
@@ -58,7 +58,7 @@ namespace Direct3DInteropSample
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm),
IsWindowed = true,
- OutputHandle = PlatformImpl.Handle.Handle,
+ OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput
diff --git a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
index 044b96c865..d9a61064f1 100644
--- a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
+++ b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
@@ -149,6 +149,7 @@
ControlCatalog
+
\ No newline at end of file
diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
index c787ab76ee..28e5e274d0 100644
--- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
+++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
@@ -179,6 +179,7 @@
MSBuild:Compile
+
\ No newline at end of file
diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index 2089359c26..c779031e6c 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/src/Android/Avalonia.Android/AndroidPlatform.cs
@@ -53,6 +53,7 @@ namespace Avalonia.Android
.Bind().ToSingleton()
.Bind().ToSingleton()
.Bind().ToConstant(Instance)
+ .Bind().ToConstant(ImmediateRenderer.Factory)
.Bind().ToConstant(new AndroidThreadingInterface())
.Bind().ToTransient()
.Bind().ToConstant(Instance)
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 7359dceae1..1bdf1eb5e3 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -578,13 +578,13 @@ namespace Avalonia
if (notification == null)
{
- return TypeUtilities.CastOrDefault(value, type);
+ return TypeUtilities.ConvertImplicitOrDefault(value, type);
}
else
{
if (notification.HasValue)
{
- notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type));
+ notification.SetValue(TypeUtilities.ConvertImplicitOrDefault(notification.Value, type));
}
return notification;
@@ -622,14 +622,9 @@ namespace Avalonia
/// The default value.
private object GetDefaultValue(AvaloniaProperty property)
{
- if (property.Inherits && _inheritanceParent != null)
- {
- return (_inheritanceParent as AvaloniaObject).GetValueInternal(property);
- }
- else
- {
- return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType());
- }
+ if (property.Inherits && _inheritanceParent is AvaloniaObject aobj)
+ return aobj.GetValueInternal(property);
+ return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
}
///
@@ -735,7 +730,7 @@ namespace Avalonia
ThrowNotRegistered(property);
}
- if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
+ if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{
throw new ArgumentException(string.Format(
"Invalid value for Property '{0}': '{1}' ({2})",
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index 61006b1173..fb78e3b2a0 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -476,7 +476,7 @@ namespace Avalonia
/// True if the value is valid, otherwise false.
public bool IsValidValue(object value)
{
- return TypeUtilities.TryCast(PropertyType, value, out value);
+ return TypeUtilities.TryConvertImplicit(PropertyType, value, out value);
}
///
diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
index d63a5baf16..1aa239180c 100644
--- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
@@ -103,11 +103,9 @@ namespace Avalonia.Collections
_inner = new Dictionary();
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs("Count"));
- PropertyChanged(this, new PropertyChangedEventArgs($"Item[]"));
- }
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]"));
+
if (CollectionChanged != null)
{
@@ -144,12 +142,9 @@ namespace Avalonia.Collections
if (_inner.TryGetValue(key, out value))
{
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs("Count"));
- PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
- }
-
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
+
if (CollectionChanged != null)
{
var e = new NotifyCollectionChangedEventArgs(
@@ -199,11 +194,9 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value)
{
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs("Count"));
- PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
- }
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
+
if (CollectionChanged != null)
{
diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs
index 57e2854014..c33d50ff0e 100644
--- a/src/Avalonia.Base/PriorityValue.cs
+++ b/src/Avalonia.Base/PriorityValue.cs
@@ -249,7 +249,7 @@ namespace Avalonia
value = (notification.HasValue) ? notification.Value : null;
}
- if (TypeUtilities.TryCast(_valueType, value, out castValue))
+ if (TypeUtilities.TryConvertImplicit(_valueType, value, out castValue))
{
var old = _value;
@@ -285,7 +285,7 @@ namespace Avalonia
Property.Name,
_valueType,
value,
- value.GetType());
+ value?.GetType());
}
}
}
diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs
index 7295bfa7ab..dd93811498 100644
--- a/src/Avalonia.Base/Utilities/TypeUtilities.cs
+++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs
@@ -14,17 +14,61 @@ namespace Avalonia.Utilities
///
public static class TypeUtilities
{
- private static readonly Dictionary> Conversions = new Dictionary>()
+ private static int[] Conversions =
{
- { typeof(decimal), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
- { typeof(double), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
- { typeof(float), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
- { typeof(ulong), new List { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
- { typeof(long), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
- { typeof(uint), new List { typeof(byte), typeof(ushort), typeof(char) } },
- { typeof(int), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
- { typeof(ushort), new List { typeof(byte), typeof(char) } },
- { typeof(short), new List { typeof(byte) } }
+ 0b101111111111101, // Boolean
+ 0b100001111111110, // Char
+ 0b101111111111111, // SByte
+ 0b101111111111111, // Byte
+ 0b101111111111111, // Int16
+ 0b101111111111111, // UInt16
+ 0b101111111111111, // Int32
+ 0b101111111111111, // UInt32
+ 0b101111111111111, // Int64
+ 0b101111111111111, // UInt64
+ 0b101111111111101, // Single
+ 0b101111111111101, // Double
+ 0b101111111111101, // Decimal
+ 0b110000000000000, // DateTime
+ 0b111111111111111, // String
+ };
+
+ private static int[] ImplicitConversions =
+ {
+ 0b000000000000001, // Boolean
+ 0b001110111100010, // Char
+ 0b001110101010100, // SByte
+ 0b001111111111000, // Byte
+ 0b001110101010000, // Int16
+ 0b001111111100000, // UInt16
+ 0b001110101000000, // Int32
+ 0b001111110000000, // UInt32
+ 0b001110100000000, // Int64
+ 0b001111000000000, // UInt64
+ 0b000110000000000, // Single
+ 0b000100000000000, // Double
+ 0b001000000000000, // Decimal
+ 0b010000000000000, // DateTime
+ 0b100000000000000, // String
+ };
+
+ private static Type[] InbuiltTypes =
+ {
+ typeof(Boolean),
+ typeof(Char),
+ typeof(SByte),
+ typeof(Byte),
+ typeof(Int16),
+ typeof(UInt16),
+ typeof(Int32),
+ typeof(UInt32),
+ typeof(Int64),
+ typeof(UInt64),
+ typeof(Single),
+ typeof(Double),
+ typeof(Decimal),
+ typeof(DateTime),
+ typeof(String),
};
private static readonly Type[] NumericTypes = new[]
@@ -54,49 +98,104 @@ namespace Avalonia.Utilities
}
///
- /// Try to cast a value to a type, using implicit conversions if possible.
+ /// Try to convert a value to a type by any means possible.
///
/// The type to cast to.
/// The value to cast.
+ /// The culture to use.
/// If sucessful, contains the cast value.
/// True if the cast was sucessful, otherwise false.
- public static bool TryCast(Type to, object value, out object result)
+ public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
{
- Contract.Requires(to != null);
-
if (value == null)
{
result = null;
return AcceptsNull(to);
}
- var from = value.GetType();
-
if (value == AvaloniaProperty.UnsetValue)
{
result = value;
return true;
}
- else if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
+
+ var from = value.GetType();
+ var fromTypeInfo = from.GetTypeInfo();
+ var toTypeInfo = to.GetTypeInfo();
+
+ if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{
result = value;
return true;
}
- else if (Conversions.ContainsKey(to) && Conversions[to].Contains(from))
+
+ if (to == typeof(string))
{
- result = Convert.ChangeType(value, to);
+ result = Convert.ToString(value);
return true;
}
- else
+
+ if (toTypeInfo.IsEnum && from == typeof(string))
+ {
+ if (Enum.IsDefined(to, (string)value))
+ {
+ result = Enum.Parse(to, (string)value);
+ return true;
+ }
+ }
+
+ if (!fromTypeInfo.IsEnum && toTypeInfo.IsEnum)
{
- var cast = from.GetRuntimeMethods()
- .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
+ result = null;
+
+ if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue))
+ {
+ result = Enum.ToObject(to, enumValue);
+ return true;
+ }
+ }
- if (cast != null)
+ if (fromTypeInfo.IsEnum && IsNumeric(to))
+ {
+ try
{
- result = cast.Invoke(null, new[] { value });
+ result = Convert.ChangeType((int)value, to, culture);
return true;
}
+ catch
+ {
+ result = null;
+ return false;
+ }
+ }
+
+ var convertableFrom = Array.IndexOf(InbuiltTypes, from);
+ var convertableTo = Array.IndexOf(InbuiltTypes, to);
+
+ if (convertableFrom != -1 && convertableTo != -1)
+ {
+ if ((Conversions[convertableFrom] & 1 << convertableTo) != 0)
+ {
+ try
+ {
+ result = Convert.ChangeType(value, to, culture);
+ return true;
+ }
+ catch
+ {
+ result = null;
+ return false;
+ }
+ }
+ }
+
+ var cast = from.GetRuntimeMethods()
+ .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
+
+ if (cast != null)
+ {
+ result = cast.Invoke(null, new[] { value });
+ return true;
}
result = null;
@@ -104,15 +203,14 @@ namespace Avalonia.Utilities
}
///
- /// Try to convert a value to a type, using if possible,
- /// otherwise using .
+ /// Try to convert a value to a type using the implicit conversions allowed by the C#
+ /// language.
///
/// The type to cast to.
/// The value to cast.
- /// The culture to use.
/// If sucessful, contains the cast value.
/// True if the cast was sucessful, otherwise false.
- public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
+ public static bool TryConvertImplicit(Type to, object value, out object result)
{
if (value == null)
{
@@ -120,54 +218,44 @@ namespace Avalonia.Utilities
return AcceptsNull(to);
}
- var from = value.GetType();
-
if (value == AvaloniaProperty.UnsetValue)
{
result = value;
return true;
}
- if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
- {
- result = value;
- return true;
- }
+ var from = value.GetType();
+ var fromTypeInfo = from.GetTypeInfo();
+ var toTypeInfo = to.GetTypeInfo();
- if (to == typeof(string))
+ if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{
- result = Convert.ToString(value);
+ result = value;
return true;
}
- if (to.GetTypeInfo().IsEnum && from == typeof(string))
- {
- if (Enum.IsDefined(to, (string)value))
- {
- result = Enum.Parse(to, (string)value);
- return true;
- }
- }
-
- bool containsFrom = Conversions.ContainsKey(from);
- bool containsTo = Conversions.ContainsKey(to);
+ var convertableFrom = Array.IndexOf(InbuiltTypes, from);
+ var convertableTo = Array.IndexOf(InbuiltTypes, to);
- if ((containsFrom && containsTo) || (from == typeof(string) && containsTo))
+ if (convertableFrom != -1 && convertableTo != -1)
{
- try
- {
- result = Convert.ChangeType(value, to, culture);
- return true;
- }
- catch
+ if ((ImplicitConversions[convertableFrom] & 1 << convertableTo) != 0)
{
- result = null;
- return false;
+ try
+ {
+ result = Convert.ChangeType(value, to, CultureInfo.InvariantCulture);
+ return true;
+ }
+ catch
+ {
+ result = null;
+ return false;
+ }
}
}
var cast = from.GetRuntimeMethods()
- .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
+ .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
if (cast != null)
{
@@ -180,29 +268,28 @@ namespace Avalonia.Utilities
}
///
- /// Casts a value to a type, returning the default for that type if the value could not be
- /// cast.
+ /// Convert a value to a type by any means possible, returning the default for that type
+ /// if the value could not be converted.
///
/// The value to cast.
/// The type to cast to..
+ /// The culture to use.
/// A value of .
- public static object CastOrDefault(object value, Type type)
+ public static object ConvertOrDefault(object value, Type type, CultureInfo culture)
{
- var typeInfo = type.GetTypeInfo();
- object result;
+ return TryConvert(type, value, culture, out object result) ? result : Default(type);
+ }
- if (TypeUtilities.TryCast(type, value, out result))
- {
- return result;
- }
- else if (typeInfo.IsValueType)
- {
- return Activator.CreateInstance(type);
- }
- else
- {
- return null;
- }
+ ///
+ /// Convert a value to a type using the implicit conversions allowed by the C# language or
+ /// return the default for the type if the value could not be converted.
+ ///
+ /// The value to cast.
+ /// The type to cast to..
+ /// A value of .
+ public static object ConvertImplicitOrDefault(object value, Type type)
+ {
+ return TryConvertImplicit(type, value, out object result) ? result : Default(type);
}
///
diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index 97de093a59..1610d33f9b 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -55,8 +55,7 @@ namespace Avalonia.Controls
public Action AfterSetupCallback { get; private set; } = builder => { };
///
- /// Gets or sets a method to call before is called on the
- /// .
+ /// Gets or sets a method to call before Startis called on the .
///
public Action BeforeStartCallback { get; private set; } = builder => { };
@@ -94,8 +93,7 @@ namespace Avalonia.Controls
protected TAppBuilder Self => (TAppBuilder) this;
///
- /// Registers a callback to call before is called on the
- /// .
+ /// Registers a callback to call before Start is called on the .
///
/// The callback.
/// An instance.
@@ -129,6 +127,24 @@ namespace Avalonia.Controls
Instance.Run(window);
}
+ ///
+ /// Starts the application with the provided instance of .
+ ///
+ /// The window type.
+ /// Instance of type TMainWindow to use when starting the app
+ /// A delegate that will be called to create a data context for the window (optional).
+ public void Start(TMainWindow mainWindow, Func
diff --git a/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs b/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
index 69a3f34489..03ebc94a4c 100644
--- a/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
+++ b/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
@@ -37,4 +37,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 1, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index 1125863e72..4a9f2c6572 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -285,7 +285,7 @@ namespace Avalonia.Skia
paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter)
- paint.StrokeJoin = SKStrokeJoin.Mitter;
+ paint.StrokeJoin = SKStrokeJoin.Miter;
else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round;
else
@@ -397,7 +397,7 @@ namespace Avalonia.Skia
public void PopOpacityMask()
{
- Canvas.SaveLayer(new SKPaint { XferMode = SKXferMode.DstIn });
+ Canvas.SaveLayer(new SKPaint { BlendMode = SKBlendMode.DstIn });
using (var paintWrapper = maskStack.Pop())
{
Canvas.DrawPaint(paintWrapper.Paint);
diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
index 133d9cd789..8568c80c04 100644
--- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
+++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
@@ -42,7 +42,7 @@ namespace Avalonia.Skia
_paint.Typeface = skiaTypeface;
_paint.TextSize = (float)(typeface?.FontSize ?? 12);
_paint.TextAlign = textAlignment.ToSKTextAlign();
- _paint.XferMode = SKXferMode.Src;
+ _paint.BlendMode = SKBlendMode.Src;
_wrapping = wrapping;
_constraint = constraint;
diff --git a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
index bdee85c91e..a484d6c0d2 100644
--- a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
+++ b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding
{
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot();
- private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle;
+ private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero;
public WinFormsAvaloniaControlHost()
{
@@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding
if (_root.IsFocused)
FocusManager.Instance.Focus(null);
_root.GotFocus += RootGotFocus;
+ // ReSharper disable once PossibleNullReferenceException
+ // Always non-null at this point
_root.PlatformImpl.LostFocus += PlatformImpl_LostFocus;
FixPosition();
}
diff --git a/src/iOS/Avalonia.iOS/iOSPlatform.cs b/src/iOS/Avalonia.iOS/iOSPlatform.cs
index 165b7d44f9..6d6e1fab03 100644
--- a/src/iOS/Avalonia.iOS/iOSPlatform.cs
+++ b/src/iOS/Avalonia.iOS/iOSPlatform.cs
@@ -58,6 +58,7 @@ namespace Avalonia.iOS
.Bind().ToTransient()
.Bind().ToConstant(KeyboardDevice)
.Bind().ToConstant(MouseDevice)
+ .Bind().ToConstant(ImmediateRenderer.Factory)
.Bind().ToSingleton()
.Bind().ToConstant(PlatformThreadingInterface.Instance)
.Bind().ToSingleton()
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
index 2f98cccadf..1da9cfce76 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
@@ -206,6 +206,30 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(expected, actual);
}
+ [Fact]
+ public void Inserting_Items_Before_Visibile_Containers_Should_Update_Containers()
+ {
+ var target = CreateTarget();
+
+ target.ApplyTemplate();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ ((ILogicalScrollable)target).Offset = new Vector(0, 5);
+
+ var expected = Enumerable.Range(5, 10).Select(x => $"Item {x}").ToList();
+ var items = (ObservableCollection)target.Items;
+ var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
+
+ Assert.Equal(expected, actual);
+
+ items.Insert(0, "Inserted");
+
+ expected = Enumerable.Range(4, 10).Select(x => $"Item {x}").ToList();
+ actual = target.Panel.Children.Select(x => x.DataContext).ToList();
+ Assert.Equal(expected, actual);
+ }
+
[Fact]
public void Removing_First_Materialized_Item_Should_Update_Containers()
{
@@ -477,7 +501,6 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Arrange(new Rect(0, 0, 100, 100));
var expected = Enumerable.Range(0, 6).Select(x => $"Item {x}").ToList();
- var items = (ObservableCollection)target.Items;
var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);
diff --git a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs b/tests/Avalonia.Layout.UnitTests/MeasureTests.cs
index b5a3539da9..570628b5b7 100644
--- a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs
+++ b/tests/Avalonia.Layout.UnitTests/MeasureTests.cs
@@ -100,5 +100,58 @@ namespace Avalonia.Layout.UnitTests
Assert.Equal(0, target.DesiredSize.Height);
}
+
+ [Fact]
+ public void Margin_Should_Affect_AvailableSize()
+ {
+ MeasureTest target;
+
+ var outer = new Decorator
+ {
+ Width = 100,
+ Height = 100,
+ Child = target = new MeasureTest
+ {
+ Margin = new Thickness(10),
+ }
+ };
+
+ outer.Measure(Size.Infinity);
+
+ Assert.Equal(new Size(80, 80), target.AvailableSize);
+ }
+
+ [Fact]
+ public void Margin_Should_Be_Applied_Before_Width_Height()
+ {
+ MeasureTest target;
+
+ var outer = new Decorator
+ {
+ Width = 100,
+ Height = 100,
+ Child = target = new MeasureTest
+ {
+ Width = 80,
+ Height = 80,
+ Margin = new Thickness(10),
+ }
+ };
+
+ outer.Measure(Size.Infinity);
+
+ Assert.Equal(new Size(80, 80), target.AvailableSize);
+ }
+
+ class MeasureTest : Control
+ {
+ public Size? AvailableSize { get; private set; }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ AvailableSize = availableSize;
+ return availableSize;
+ }
+ }
}
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs
new file mode 100644
index 0000000000..8c0b043907
--- /dev/null
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs
@@ -0,0 +1,17 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml.Parsers;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests.Parsers
+{
+ public class SelectorParserTests
+ {
+ [Fact]
+ public void Parses_Boolean_Property_Selector()
+ {
+ var target = new SelectorParser((type, ns) => typeof(TextBlock));
+ var result = target.Parse("TextBlock[IsPointerOver=True]");
+ }
+ }
+}