瀏覽代碼

Merge branch 'master' into DisableButtonOnNullCommandBinding

Jeremy Koritzinsky 8 年之前
父節點
當前提交
6d32e6a36c
共有 71 個文件被更改,包括 572 次插入273 次删除
  1. 5 0
      .ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
  2. 5 0
      .ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
  3. 5 0
      .ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
  4. 5 0
      .ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
  5. 5 0
      .ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
  6. 5 0
      .ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
  7. 5 0
      .ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
  8. 5 0
      .ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
  9. 5 0
      .ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
  10. 5 0
      .ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
  11. 5 0
      .ncrunch/Direct3DInteropSample.v3.ncrunchproject
  12. 1 0
      Avalonia.sln.DotSettings
  13. 1 0
      appveyor.yml
  14. 40 3
      build.cake
  15. 0 5
      build/SkiaSharp.Desktop.props
  16. 2 1
      build/SkiaSharp.props
  17. 1 1
      docs/guidelines/build.md
  18. 1 1
      docs/index.md
  19. 1 1
      docs/tutorial/gettingstarted.md
  20. 15 9
      packages.cake
  21. 11 3
      parameters.cake
  22. 1 1
      readme.md
  23. 2 1
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  24. 1 1
      samples/interop/Direct3DInteropSample/MainWindow.cs
  25. 1 0
      samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
  26. 1 0
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  27. 1 0
      src/Android/Avalonia.Android/AndroidPlatform.cs
  28. 6 11
      src/Avalonia.Base/AvaloniaObject.cs
  29. 1 1
      src/Avalonia.Base/AvaloniaProperty.cs
  30. 9 16
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  31. 2 2
      src/Avalonia.Base/PriorityValue.cs
  32. 162 75
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  33. 20 4
      src/Avalonia.Controls/AppBuilderBase.cs
  34. 1 1
      src/Avalonia.Controls/Button.cs
  35. 1 1
      src/Avalonia.Controls/Control.cs
  36. 6 6
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  37. 1 1
      src/Avalonia.Controls/ItemsControl.cs
  38. 1 2
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  39. 3 4
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  40. 5 2
      src/Avalonia.Controls/TextBox.cs
  41. 19 16
      src/Avalonia.Controls/TopLevel.cs
  42. 17 11
      src/Avalonia.Controls/Window.cs
  43. 20 14
      src/Avalonia.Controls/WindowBase.cs
  44. 3 1
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  45. 7 1
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  46. 3 4
      src/Avalonia.Layout/Layoutable.cs
  47. 1 1
      src/Avalonia.Styling/Controls/NameScope.cs
  48. 7 0
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  49. 1 1
      src/Avalonia.Visuals/Rendering/ZIndexComparer.cs
  50. 1 1
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  51. 1 0
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  52. 3 3
      src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
  53. 3 3
      src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
  54. 1 0
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  55. 5 0
      src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
  56. 1 0
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  57. 1 0
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  58. 1 1
      src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs
  59. 2 2
      src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs
  60. 14 50
      src/Markup/Avalonia.Markup/DefaultValueConverter.cs
  61. 3 3
      src/Shared/SharedAssemblyInfo.cs
  62. 0 1
      src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj
  63. 1 1
      src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj
  64. 3 1
      src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
  65. 2 2
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  66. 1 1
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  67. 3 1
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  68. 1 0
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  69. 24 1
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  70. 53 0
      tests/Avalonia.Layout.UnitTests/MeasureTests.cs
  71. 17 0
      tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs

+ 5 - 0
.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Direct3DInteropSample.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
+  </Settings>
+</ProjectConfiguration>

+ 1 - 0
Avalonia.sln.DotSettings

@@ -1,4 +1,5 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String>
 	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
 	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String>

+ 1 - 0
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

+ 40 - 3
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<Exception>()
         .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
 ///////////////////////////////////////////////////////////////////////////////

+ 0 - 5
build/SkiaSharp.Desktop.props

@@ -1,5 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <PackageReference Include="Avalonia.Skia.Linux.Natives" Version="1.56.1.3" />
-  </ItemGroup>
-</Project>

+ 2 - 1
build/SkiaSharp.props

@@ -1,5 +1,6 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="SkiaSharp" Version="1.56.1-beta" />
+    <PackageReference Include="SkiaSharp" Version="1.57.1" />
+    <PackageReference Condition="$(TargetFramework.Trim('.').ToLower().StartsWith('netframework'))" Include="Avalonia.Skia.Linux.Natives" Version="1.57.1.3" />
   </ItemGroup>
 </Project>

+ 1 - 1
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
 

+ 1 - 1
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 :) ):
 

+ 1 - 1
docs/tutorial/gettingstarted.md

@@ -4,7 +4,7 @@
 
 ![](images/add-dialogs.png)
 
-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 :) ):

+ 15 - 9
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[]
                 {

+ 11 - 3
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
             {

+ 1 - 1
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

+ 2 - 1
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@@ -26,7 +26,7 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
+    <PlatformTarget>x86</PlatformTarget>
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>bin\Release\</OutputPath>
@@ -142,6 +142,7 @@
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\build\Serilog.props" />
+  <Import Project="..\..\build\SkiaSharp.props" />
   <Import Project="..\..\build\Serilog.Sinks.Trace.props" />
   <Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" />
 </Project>

+ 1 - 1
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

+ 1 - 0
samples/interop/GtkInteropDemo/GtkInteropDemo.csproj

@@ -149,6 +149,7 @@
       <Name>ControlCatalog</Name>
     </ProjectReference>
   </ItemGroup>
+  <Import Project="..\..\..\build\Rx.props" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
 </Project>

+ 1 - 0
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@@ -179,6 +179,7 @@
       <Generator>MSBuild:Compile</Generator>
     </Page>
   </ItemGroup>
+  <Import Project="..\..\..\build\Rx.props" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
 </Project>

+ 1 - 0
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -53,6 +53,7 @@ namespace Avalonia.Android
                 .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
                 .Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>()
                 .Bind<IPlatformSettings>().ToConstant(Instance)
+                .Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
                 .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
                 .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
                 .Bind<IWindowingPlatform>().ToConstant(Instance)

+ 6 - 11
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
         /// <returns>The default value.</returns>
         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());
         }
 
         /// <summary>
@@ -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})",

+ 1 - 1
src/Avalonia.Base/AvaloniaProperty.cs

@@ -476,7 +476,7 @@ namespace Avalonia
         /// <returns>True if the value is valid, otherwise false.</returns>
         public bool IsValidValue(object value)
         {
-            return TypeUtilities.TryCast(PropertyType, value, out value);
+            return TypeUtilities.TryConvertImplicit(PropertyType, value, out value);
         }
 
         /// <summary>

+ 9 - 16
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@@ -103,11 +103,9 @@ namespace Avalonia.Collections
 
             _inner = new Dictionary<TKey, TValue>();
 
-            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)
             {

+ 2 - 2
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());
             }
         }
     }

+ 162 - 75
src/Avalonia.Base/Utilities/TypeUtilities.cs

@@ -14,17 +14,61 @@ namespace Avalonia.Utilities
     /// </summary>
     public static class TypeUtilities
     {
-        private static readonly Dictionary<Type, List<Type>> Conversions = new Dictionary<Type, List<Type>>()
+        private static int[] Conversions =
         {
-            { typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
-            { typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
-            { typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
-            { typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
-            { typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
-            { typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } },
-            { typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
-            { typeof(ushort), new List<Type> { typeof(byte), typeof(char) } },
-            { typeof(short), new List<Type> { 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
         }
 
         /// <summary>
-        /// 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.
         /// </summary>
         /// <param name="to">The type to cast to.</param>
         /// <param name="value">The value to cast.</param>
+        /// <param name="culture">The culture to use.</param>
         /// <param name="result">If sucessful, contains the cast value.</param>
         /// <returns>True if the cast was sucessful, otherwise false.</returns>
-        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<ArgumentNullException>(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
         }
 
         /// <summary>
-        /// Try to convert a value to a type, using <see cref="System.Convert"/> if possible,
-        /// otherwise using <see cref="TryCast(Type, object, out object)"/>.
+        /// Try to convert a value to a type using the implicit conversions allowed by the C#
+        /// language.
         /// </summary>
         /// <param name="to">The type to cast to.</param>
         /// <param name="value">The value to cast.</param>
-        /// <param name="culture">The culture to use.</param>
         /// <param name="result">If sucessful, contains the cast value.</param>
         /// <returns>True if the cast was sucessful, otherwise false.</returns>
-        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
         }
 
         /// <summary>
-        /// 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.
         /// </summary>
         /// <param name="value">The value to cast.</param>
         /// <param name="type">The type to cast to..</param>
+        /// <param name="culture">The culture to use.</param>
         /// <returns>A value of <paramref name="type"/>.</returns>
-        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;
-            }
+        /// <summary>
+        /// 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.
+        /// </summary>
+        /// <param name="value">The value to cast.</param>
+        /// <param name="type">The type to cast to..</param>
+        /// <returns>A value of <paramref name="type"/>.</returns>
+        public static object ConvertImplicitOrDefault(object value, Type type)
+        {
+            return TryConvertImplicit(type, value, out object result) ? result : Default(type);
         }
 
         /// <summary>

+ 20 - 4
src/Avalonia.Controls/AppBuilderBase.cs

@@ -55,8 +55,7 @@ namespace Avalonia.Controls
         public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };
 
         /// <summary>
-        /// Gets or sets a method to call before <see cref="Start{TMainWindow}"/> is called on the
-        /// <see cref="Application"/>.
+        /// Gets or sets a method to call before Startis called on the <see cref="Application"/>.
         /// </summary>
         public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
 
@@ -94,8 +93,7 @@ namespace Avalonia.Controls
         protected TAppBuilder Self => (TAppBuilder) this;
 
         /// <summary>
-        /// Registers a callback to call before <see cref="Start{TMainWindow}"/> is called on the
-        /// <see cref="Application"/>.
+        /// Registers a callback to call before Start is called on the <see cref="Application"/>.
         /// </summary>
         /// <param name="callback">The callback.</param>
         /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
@@ -129,6 +127,24 @@ namespace Avalonia.Controls
             Instance.Run(window);
         }
 
+        /// <summary>
+        /// Starts the application with the provided instance of <typeparamref name="TMainWindow"/>.
+        /// </summary>
+        /// <typeparam name="TMainWindow">The window type.</typeparam>
+        /// <param name="mainWindow">Instance of type TMainWindow to use when starting the app</param>
+        /// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
+        public void Start<TMainWindow>(TMainWindow mainWindow, Func<object> dataContextProvider = null)
+            where TMainWindow : Window
+        {
+            Setup();
+            BeforeStartCallback(Self);
+
+            if (dataContextProvider != null)
+                mainWindow.DataContext = dataContextProvider();
+            mainWindow.Show();
+            Instance.Run(mainWindow);
+        }
+
         /// <summary>
         /// Sets up the platform-specific services for the application, but does not run it.
         /// </summary>

+ 1 - 1
src/Avalonia.Controls/Button.cs

@@ -291,7 +291,7 @@ namespace Avalonia.Controls
         {
             var button = e.Sender as Button;
             var isDefault = (bool)e.NewValue;
-            var inputRoot = button.VisualRoot as IInputElement;
+            var inputRoot = button?.VisualRoot as IInputElement;
 
             if (inputRoot != null)
             {

+ 1 - 1
src/Avalonia.Controls/Control.cs

@@ -645,7 +645,7 @@ namespace Avalonia.Controls
 
             if (_focusAdorner != null)
             {
-                var adornerLayer = _focusAdorner.Parent as Panel;
+                var adornerLayer = (IPanel)_focusAdorner.Parent;
                 adornerLayer.Children.Remove(_focusAdorner);
                 _focusAdorner = null;
             }

+ 6 - 6
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@@ -4,6 +4,7 @@ using Avalonia.Input;
 using Avalonia.Layout;
 using Avalonia.Platform;
 using Avalonia.Styling;
+using JetBrains.Annotations;
 
 namespace Avalonia.Controls.Embedding
 {
@@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding
         {
         }
 
+        [CanBeNull]
         public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
 
         public void Prepare()
@@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding
 
         protected override Size MeasureOverride(Size availableSize)
         {
-            base.MeasureOverride(PlatformImpl.ClientSize);
-            return PlatformImpl.ClientSize;
+            var cs = PlatformImpl?.ClientSize ?? default(Size);
+            base.MeasureOverride(cs);
+            return cs;
         }
 
         private readonly NameScope _nameScope = new NameScope();
@@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding
         public void Unregister(string name) => _nameScope.Unregister(name);
 
         Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
-        public void Dispose()
-        {
-            PlatformImpl.Dispose();
-        }
+        public void Dispose() => PlatformImpl?.Dispose();
     }
 }

+ 1 - 1
src/Avalonia.Controls/ItemsControl.cs

@@ -354,7 +354,7 @@ namespace Avalonia.Controls
             }
 
             var collection = sender as ICollection;
-            PseudoClasses.Set(":empty", collection.Count == 0);
+            PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
         }
 
         /// <summary>

+ 1 - 2
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@@ -155,8 +155,7 @@ namespace Avalonia.Controls.Presenters
                     case NotifyCollectionChangedAction.Add:
                         CreateAndRemoveContainers();
 
-                        if (e.NewStartingIndex >= FirstIndex &&
-                            e.NewStartingIndex < NextIndex)
+                        if (e.NewStartingIndex < NextIndex)
                         {
                             RecycleContainers();
                         }

+ 3 - 4
src/Avalonia.Controls/Primitives/PopupRoot.cs

@@ -9,6 +9,7 @@ using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.VisualTree;
+using JetBrains.Annotations;
 
 namespace Avalonia.Controls.Primitives
 {
@@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Gets the platform-specific window implementation.
         /// </summary>
+        [CanBeNull]
         public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
 
         /// <summary>
@@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives
         IVisual IHostedVisualTreeRoot.Host => Parent;
 
         /// <inheritdoc/>
-        public void Dispose()
-        {
-            this.PlatformImpl.Dispose();
-        }
+        public void Dispose() => PlatformImpl?.Dispose();
 
         /// <inheritdoc/>
         protected override void OnTemplateApplied(TemplateAppliedEventArgs e)

+ 5 - 2
src/Avalonia.Controls/TextBox.cs

@@ -720,7 +720,7 @@ namespace Avalonia.Controls
                         if (pos < text.Length)
                         {
                             --pos;
-                            if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n')
+                            if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n')
                             {
                                 --pos;
                             }
@@ -771,6 +771,9 @@ namespace Avalonia.Controls
 
         private string GetSelection()
         {
+            var text = Text;
+            if (string.IsNullOrEmpty(text))
+                return "";
             var selectionStart = SelectionStart;
             var selectionEnd = SelectionEnd;
             var start = Math.Min(selectionStart, selectionEnd);
@@ -779,7 +782,7 @@ namespace Avalonia.Controls
             {
                 return "";
             }
-            return Text.Substring(start, end - start);
+            return text.Substring(start, end - start);
         }
 
         private int GetLine(int caretIndex, IList<FormattedTextLine> lines)

+ 19 - 16
src/Avalonia.Controls/TopLevel.cs

@@ -14,6 +14,7 @@ using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Styling;
 using Avalonia.VisualTree;
+using JetBrains.Annotations;
 
 namespace Avalonia.Controls
 {
@@ -92,25 +93,25 @@ namespace Avalonia.Controls
             var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver);
             Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
 
-            PlatformImpl.SetInputRoot(this);
+            impl.SetInputRoot(this);
 
-            PlatformImpl.Closed = HandleClosed;
-            PlatformImpl.Input = HandleInput;
-            PlatformImpl.Paint = HandlePaint;
-            PlatformImpl.Resized = HandleResized;
-            PlatformImpl.ScalingChanged = HandleScalingChanged;
+            impl.Closed = HandleClosed;
+            impl.Input = HandleInput;
+            impl.Paint = HandlePaint;
+            impl.Resized = HandleResized;
+            impl.ScalingChanged = HandleScalingChanged;
 
 
             _keyboardNavigationHandler?.SetOwner(this);
             _accessKeyHandler?.SetOwner(this);
             styler?.ApplyStyles(this);
 
-            ClientSize = PlatformImpl.ClientSize;
+            ClientSize = impl.ClientSize;
             
             this.GetObservable(PointerOverElementProperty)
                 .Select(
                     x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
-                .Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor));
+                .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
 
             if (_applicationLifecycle != null)
             {
@@ -135,10 +136,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets the platform-specific window implementation.
         /// </summary>
-        public ITopLevelImpl PlatformImpl
-        {
-            get;
-        }
+        [CanBeNull]
+        public ITopLevelImpl PlatformImpl { get; private set; }
         
         /// <summary>
         /// Gets the renderer for the window.
@@ -177,7 +176,7 @@ namespace Avalonia.Controls
         Size ILayoutRoot.MaxClientSize => Size.Infinity;
 
         /// <inheritdoc/>
-        double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling;
+        double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
 
         IStyleHost IStyleHost.StylingParent
         {
@@ -189,25 +188,27 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         protected virtual IRenderTarget CreateRenderTarget()
         {
+            if(PlatformImpl == null)
+                throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)");
             return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
         }
 
         /// <inheritdoc/>
         void IRenderRoot.Invalidate(Rect rect)
         {
-            PlatformImpl.Invalidate(rect);
+            PlatformImpl?.Invalidate(rect);
         }
 
         /// <inheritdoc/>
         Point IRenderRoot.PointToClient(Point p)
         {
-            return PlatformImpl.PointToClient(p);
+            return PlatformImpl?.PointToClient(p) ?? default(Point);
         }
 
         /// <inheritdoc/>
         Point IRenderRoot.PointToScreen(Point p)
         {
-            return PlatformImpl.PointToScreen(p);
+            return PlatformImpl?.PointToScreen(p) ?? default(Point);
         }
 
         /// <summary>
@@ -224,6 +225,8 @@ namespace Avalonia.Controls
         /// </summary>
         protected virtual void HandleClosed()
         {
+            PlatformImpl = null;
+
             Closed?.Invoke(this, EventArgs.Empty);
             Renderer?.Dispose();
             Renderer = null;

+ 17 - 11
src/Avalonia.Controls/Window.cs

@@ -12,6 +12,7 @@ using Avalonia.Platform;
 using Avalonia.Styling;
 using System.Collections.Generic;
 using System.Linq;
+using JetBrains.Annotations;
 
 namespace Avalonia.Controls
 {
@@ -87,11 +88,11 @@ namespace Avalonia.Controls
         static Window()
         {
             BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
-            TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue));
+            TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
             HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
-                (s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue));
+                (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
 
-            IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
+            IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
         }
 
         /// <summary>
@@ -109,7 +110,7 @@ namespace Avalonia.Controls
         public Window(IWindowImpl impl)
             : base(impl)
         {
-            _maxPlatformClientSize = this.PlatformImpl.MaxClientSize;
+            _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
         }
 
         /// <inheritdoc/>
@@ -129,6 +130,7 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets the platform-specific window implementation.
         /// </summary>
+        [CanBeNull]
         public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
 
         /// <summary>
@@ -164,8 +166,12 @@ namespace Avalonia.Controls
         /// </summary>
         public WindowState WindowState
         {
-            get { return this.PlatformImpl.WindowState; }
-            set { this.PlatformImpl.WindowState = value; }
+            get { return PlatformImpl?.WindowState ?? WindowState.Normal; }
+            set
+            {
+                if (PlatformImpl != null)
+                    PlatformImpl.WindowState = value;
+            }
         }
 
         /// <summary>
@@ -189,7 +195,7 @@ namespace Avalonia.Controls
         public void Close()
         {
             s_windows.Remove(this);
-            PlatformImpl.Dispose();
+            PlatformImpl?.Dispose();
             IsVisible = false;
         }
 
@@ -221,7 +227,7 @@ namespace Avalonia.Controls
         {
             using (BeginAutoSizing())
             {
-                PlatformImpl.Hide();
+                PlatformImpl?.Hide();
             }
 
             IsVisible = false;
@@ -240,7 +246,7 @@ namespace Avalonia.Controls
 
             using (BeginAutoSizing())
             {
-                PlatformImpl.Show();
+                PlatformImpl?.Show();
             }
         }
 
@@ -278,7 +284,7 @@ namespace Avalonia.Controls
                 var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault();
                 SetIsEnabled(affectedWindows, false);
 
-                var modal = PlatformImpl.ShowDialog();
+                var modal = PlatformImpl?.ShowDialog();
                 var result = new TaskCompletionSource<TResult>();
 
                 Observable.FromEventPattern<EventHandler, EventArgs>(
@@ -287,7 +293,7 @@ namespace Avalonia.Controls
                     .Take(1)
                     .Subscribe(_ =>
                     {
-                        modal.Dispose();
+                        modal?.Dispose();
                         SetIsEnabled(affectedWindows, true);
                         activated?.Activate();
                         result.SetResult((TResult)_dialogResult);

+ 20 - 14
src/Avalonia.Controls/WindowBase.cs

@@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Layout;
 using Avalonia.Platform;
+using JetBrains.Annotations;
 
 namespace Avalonia.Controls
 {
@@ -43,10 +44,10 @@ namespace Avalonia.Controls
 
         public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver)
         {
-            PlatformImpl.Activated = HandleActivated;
-            PlatformImpl.Deactivated = HandleDeactivated;
-            PlatformImpl.PositionChanged = HandlePositionChanged;
-            this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x));
+            impl.Activated = HandleActivated;
+            impl.Deactivated = HandleDeactivated;
+            impl.PositionChanged = HandlePositionChanged;
+            this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x));
         }
 
         /// <summary>
@@ -64,6 +65,7 @@ namespace Avalonia.Controls
         /// </summary>
         public event EventHandler<PointEventArgs> PositionChanged;
 
+        [CanBeNull]
         public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
 
         /// <summary>
@@ -80,8 +82,12 @@ namespace Avalonia.Controls
         /// </summary>
         public Point Position
         {
-            get { return PlatformImpl.Position; }
-            set { PlatformImpl.Position = value; }
+            get { return PlatformImpl?.Position ?? default(Point); }
+            set
+            {
+                if (PlatformImpl is IWindowBaseImpl impl)
+                    impl.Position = value;
+            }
         }
 
         /// <summary>
@@ -98,7 +104,7 @@ namespace Avalonia.Controls
         /// </summary>
         public void Activate()
         {
-            PlatformImpl.Activate();
+            PlatformImpl?.Activate();
         }
 
         /// <summary>
@@ -110,7 +116,7 @@ namespace Avalonia.Controls
 
             try
             {
-                PlatformImpl.Hide();
+                PlatformImpl?.Hide();
                 IsVisible = false;
             }
             finally
@@ -131,7 +137,7 @@ namespace Avalonia.Controls
                 EnsureInitialized();
                 IsVisible = true;
                 LayoutManager.Instance.ExecuteInitialLayoutPass(this);
-                PlatformImpl.Show();
+                PlatformImpl?.Show();
             }
             finally
             {
@@ -163,10 +169,10 @@ namespace Avalonia.Controls
         {
             using (BeginAutoSizing())
             {
-                PlatformImpl.Resize(finalSize);
+                PlatformImpl?.Resize(finalSize);
             }
 
-            return base.ArrangeOverride(PlatformImpl.ClientSize);
+            return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size));
         }
 
         /// <summary>
@@ -174,7 +180,7 @@ namespace Avalonia.Controls
         /// </summary>
         protected void EnsureInitialized()
         {
-            if (!this.IsInitialized)
+            if (!IsInitialized)
             {
                 var init = (ISupportInitialize)this;
                 init.BeginInit();
@@ -268,12 +274,12 @@ namespace Avalonia.Controls
         /// <summary>
         /// Starts moving a window with left button being held. Should be called from left mouse button press event handler
         /// </summary>
-        public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag();
+        public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag();
 
         /// <summary>
         /// Starts resizing a window. This function is used if an application has window resizing controls. 
         /// Should be called from left mouse button press event handler
         /// </summary>
-        public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge);
+        public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge);
     }
 }

+ 3 - 1
src/Avalonia.DesignerSupport/DesignerAssist.cs

@@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport
         private static void SetScalingFactor(double factor)
         {
             PlatformManager.SetDesignerScalingFactor(factor);
-            s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize);
+            s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize);
         }
 
         static Window s_currentWindow;
@@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport
             s_currentWindow = window;
             window.Show();
             Design.ApplyDesignerProperties(window, control);
+            // ReSharper disable once PossibleNullReferenceException
+            // Always not null at this point
             Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle);
             Api.OnResize?.Invoke();
         }

+ 7 - 1
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@@ -82,7 +82,13 @@ namespace Avalonia
 
         private void LoadAssembliesInDirectory()
         {
-            foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll"))
+            var location = Assembly.GetEntryAssembly().Location;
+            if (string.IsNullOrWhiteSpace(location))
+                return;
+            var dir = new FileInfo(location).Directory;
+            if (dir == null)
+                return;
+            foreach (var file in dir.EnumerateFiles("*.dll"))
             {
                 try
                 {

+ 3 - 4
src/Avalonia.Layout/Layoutable.cs

@@ -456,10 +456,9 @@ namespace Avalonia.Layout
 
                 ApplyTemplate();
 
-                var constrained = LayoutHelper
-                    .ApplyLayoutConstraints(this, availableSize)
-                    .Deflate(margin);
-
+                var constrained = LayoutHelper.ApplyLayoutConstraints(
+                    this,
+                    availableSize.Deflate(margin));
                 var measured = MeasureOverride(constrained);
 
                 var width = measured.Width;

+ 1 - 1
src/Avalonia.Styling/Controls/NameScope.cs

@@ -50,7 +50,7 @@ namespace Avalonia.Controls
                     return result;
                 }
 
-                visual = (visual as ILogical).LogicalParent as Visual;
+                visual = (visual as ILogical)?.LogicalParent as Visual;
             }
 
             return null;

+ 7 - 0
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@@ -20,6 +20,11 @@ namespace Avalonia.Rendering
     /// </remarks>
     public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer
     {
+        class ImmediateRendererFactory : IRendererFactory
+        {
+            public IRenderer CreateRenderer(IRenderRoot root, IRenderLoop renderLoop) => new ImmediateRenderer(root);
+        }
+
         private readonly IVisual _root;
         private readonly IRenderRoot _renderRoot;
         private IRenderTarget _renderTarget;
@@ -36,6 +41,8 @@ namespace Avalonia.Rendering
             _renderRoot = root as IRenderRoot;
         }
 
+        public static IRendererFactory Factory { get; } = new ImmediateRendererFactory();
+
         /// <inheritdoc/>
         public bool DrawFps { get; set; }
 

+ 1 - 1
src/Avalonia.Visuals/Rendering/ZIndexComparer.cs

@@ -8,6 +8,6 @@ namespace Avalonia.Rendering
     {
         public static readonly ZIndexComparer Instance = new ZIndexComparer();
 
-        public int Compare(IVisual x, IVisual y) => x.ZIndex.CompareTo(y.ZIndex);
+        public int Compare(IVisual x, IVisual y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0);
     }
 }

+ 1 - 1
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@@ -189,7 +189,7 @@ namespace Avalonia.VisualTree
         {
             Contract.Requires<ArgumentNullException>(visual != null);
 
-            return visual.VisualRoot as IRenderRoot ?? visual.VisualRoot;
+            return visual as IRenderRoot ?? visual.VisualRoot;
         }
 
         /// <summary>

+ 1 - 0
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@@ -9,6 +9,7 @@ using Avalonia.Cairo.Media.Imaging;
 using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.Rendering;
+// ReSharper disable PossibleNullReferenceException
 
 namespace Avalonia.Cairo.Media
 {

+ 3 - 3
src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs

@@ -40,6 +40,6 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyVersion("1.0.0.0")]
 [assembly: AssemblyFileVersion("1.0.0.0")]
 
-[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
-[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
-[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]

+ 3 - 3
src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs

@@ -22,7 +22,7 @@ using System.Runtime.CompilerServices;
 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
 [assembly: AssemblyVersion("1.0.*")]
 
-[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
-[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
-[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
 

+ 1 - 0
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@@ -39,6 +39,7 @@ namespace Avalonia.Gtk3
                 .Bind<IPlatformThreadingInterface>().ToConstant(Instance)
                 .Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>()
                 .Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60))
+                .Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
                 .Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
 
         }

+ 5 - 0
src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs

@@ -2,6 +2,8 @@
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using Avalonia.Gtk3;
+using Avalonia.Platform;
 
 // General Information about an assembly is controlled through the following 
 // set of attributes. Change these attribute values to modify the information
@@ -28,3 +30,6 @@ using System.Runtime.InteropServices;
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion("1.0.0.0")]
 [assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]

+ 1 - 0
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@@ -28,6 +28,7 @@ namespace Avalonia.Gtk3
             List<IDisposable> disposables = null;
             Action dispose = () =>
             {
+                // ReSharper disable once PossibleNullReferenceException
                 foreach (var d in disposables)
                     d.Dispose();
                 disposables.Clear();

+ 1 - 0
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@@ -35,6 +35,7 @@ namespace Avalonia.LinuxFramebuffer
                 .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
                 .Bind<IMouseDevice>().ToConstant(MouseDevice)
                 .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
+                .Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
                 .Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
                 .Bind<IRenderLoop>().ToConstant(PlatformThreadingInterface.Instance);
         }

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs

@@ -68,7 +68,7 @@ namespace Avalonia.Markup.Xaml.Parsers
                 }
                 else if (property != null)
                 {
-                    var type = result.TargetType;
+                    var type = result?.TargetType;
 
                     if (type == null)
                     {

+ 2 - 2
src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs

@@ -36,7 +36,7 @@ namespace Avalonia.Markup.Data.Plugins
 
             if (task != null)
             {
-                var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result");
+                var resultProperty = task.GetType().GetRuntimeProperty("Result");
 
                 if (resultProperty != null)
                 {
@@ -61,7 +61,7 @@ namespace Avalonia.Markup.Data.Plugins
 
         protected IObservable<object> HandleCompleted(Task task)
         {
-            var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result");
+            var resultProperty = task.GetType().GetRuntimeProperty("Result");
             
             if (resultProperty != null)
             {

+ 14 - 50
src/Markup/Avalonia.Markup/DefaultValueConverter.cs

@@ -3,10 +3,7 @@
 
 using System;
 using System.Globalization;
-using System.Linq;
-using System.Reflection;
 using Avalonia.Data;
-using Avalonia.Logging;
 using Avalonia.Utilities;
 
 namespace Avalonia.Markup
@@ -32,32 +29,28 @@ namespace Avalonia.Markup
         /// <returns>The converted value.</returns>
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            object result;
-
-            if (value != null && 
-                (TypeUtilities.TryConvert(targetType, value, culture, out result) ||
-                 TryConvertEnum(value, targetType, culture, out result)))
+            if (value == null)
             {
-                return result;
+                return AvaloniaProperty.UnsetValue;
             }
 
-            if (value != null)
+            if (TypeUtilities.TryConvert(targetType, value, culture, out object result))
             {
-                string message;
+                return result;
+            }
 
-                if (TypeUtilities.IsNumeric(targetType))
-                {
-                    message = $"'{value}' is not a valid number.";
-                }
-                else
-                {
-                    message = $"Could not convert '{value}' to '{targetType.Name}'.";
-                }
+            string message;
 
-                return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
+            if (TypeUtilities.IsNumeric(targetType))
+            {
+                message = $"'{value}' is not a valid number.";
+            }
+            else
+            {
+                message = $"Could not convert '{value}' to '{targetType.Name}'.";
             }
 
-            return AvaloniaProperty.UnsetValue;
+            return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
         }
 
         /// <summary>
@@ -72,34 +65,5 @@ namespace Avalonia.Markup
         {
             return Convert(value, targetType, parameter, culture);
         }
-
-        private bool TryConvertEnum(object value, Type targetType, CultureInfo cultur, out object result)
-        {
-            var valueTypeInfo = value.GetType().GetTypeInfo();
-            var targetTypeInfo = targetType.GetTypeInfo();
-
-            if (valueTypeInfo.IsEnum && !targetTypeInfo.IsEnum)
-            {
-                var enumValue = (int)value;
-
-                if (TypeUtilities.TryCast(targetType, enumValue, out result))
-                {
-                    return true;
-                }
-            }
-            else if (!valueTypeInfo.IsEnum && targetTypeInfo.IsEnum)
-            {
-                object intValue;
-
-                if (TypeUtilities.TryCast(typeof(int), value, out intValue))
-                {
-                    result = Enum.ToObject(targetType, intValue);
-                    return true;
-                }
-            }
-
-            result = null;
-            return false;
-        }
     }
 }

+ 3 - 3
src/Shared/SharedAssemblyInfo.cs

@@ -13,6 +13,6 @@ using System.Resources;
 [assembly: AssemblyTrademark("")]
 [assembly: NeutralResourcesLanguage("en")]
 
-[assembly: AssemblyVersion("0.4.1")]
-[assembly: AssemblyFileVersion("0.4.1")]
-[assembly: AssemblyInformationalVersion("0.4.1")]
+[assembly: AssemblyVersion("0.5.1")]
+[assembly: AssemblyFileVersion("0.5.1")]
+[assembly: AssemblyInformationalVersion("0.5.1")]

+ 0 - 1
src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj

@@ -40,7 +40,6 @@
     <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
   </ItemGroup>
   <Import Project="..\..\..\build\SkiaSharp.props" />
-  <Import Project="..\..\..\build\SkiaSharp.Desktop.props" />
   <Import Project="..\Avalonia.Skia\Avalonia.Skia.projitems" Label="Shared" />
   <Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
 </Project>

+ 1 - 1
src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj

@@ -50,7 +50,7 @@
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <Optimize>true</Optimize>
     <DebugType>pdbonly</DebugType>
-    <PlatformTarget>x86</PlatformTarget>
+    <PlatformTarget>AnyCPU</PlatformTarget>
     <ErrorReport>prompt</ErrorReport>
     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>

+ 3 - 1
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))]

+ 2 - 2
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);

+ 1 - 1
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;

+ 3 - 1
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();
         }

+ 1 - 0
src/iOS/Avalonia.iOS/iOSPlatform.cs

@@ -58,6 +58,7 @@ namespace Avalonia.iOS
                 .Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
                 .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
                 .Bind<IMouseDevice>().ToConstant(MouseDevice)
+                .Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
                 .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
                 .Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
                 .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()

+ 24 - 1
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<string>)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<string>)target.Items;
             var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
 
             Assert.Equal(expected, actual);

+ 53 - 0
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;
+            }
+        }
     }
 }

+ 17 - 0
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]");
+        }
+    }
+}