Преглед изворни кода

Merge branch 'master' into AndroidSkia

Wiesław Šoltés пре 9 година
родитељ
комит
c61d75484d
100 измењених фајлова са 4035 додато и 676 уклоњено
  1. 8 0
      .gitignore
  2. 1 10
      .travis.yml
  3. 128 5
      Avalonia.sln
  4. 7 0
      NuGet.Config
  5. 19 23
      appveyor.yml
  6. 776 0
      build.cake
  7. 201 0
      build.ps1
  8. 105 0
      build.sh
  9. 2 2
      docs/guidelines/build.md
  10. 86 0
      docs/tutorial/nuget.md
  11. 4 0
      docs/tutorial/toc.yml
  12. 0 2
      nuget/.gitignore
  13. 0 35
      nuget/build-appveyor.ps1
  14. 0 84
      nuget/build-version.ps1
  15. 0 1
      nuget/build.ps1
  16. 0 1
      nuget/include.ps1
  17. 0 27
      nuget/template/Avalonia.Android.nuspec
  18. 0 30
      nuget/template/Avalonia.Desktop.nuspec
  19. 0 20
      nuget/template/Avalonia.Skia.Desktop.nuspec
  20. 0 27
      nuget/template/Avalonia.iOS.nuspec
  21. 0 26
      nuget/template/Avalonia.nuspec
  22. 8 1
      samples/BindingTest/BindingTest.csproj
  23. 12 2
      samples/BindingTest/MainWindow.xaml
  24. 17 0
      samples/BindingTest/ViewModels/DataAnnotationsErrorViewModel.cs
  25. 2 2
      samples/BindingTest/ViewModels/ExceptionErrorViewModel.cs
  26. 73 0
      samples/BindingTest/ViewModels/IndeiErrorViewModel.cs
  27. 3 2
      samples/BindingTest/ViewModels/MainWindowViewModel.cs
  28. 4 4
      samples/ControlCatalog.Desktop/App.config
  29. 4 0
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  30. 11 1
      samples/ControlCatalog/ControlCatalog.csproj
  31. 24 0
      samples/ControlCatalog/MainView.xaml
  32. 19 0
      samples/ControlCatalog/MainView.xaml.cs
  33. 4 24
      samples/ControlCatalog/MainWindow.xaml
  34. 1 1
      samples/ControlCatalog/Pages/CanvasPage.xaml
  35. 4 0
      samples/TestApplication/TestApplication.csproj
  36. 4 4
      samples/VirtualizationTest/App.config
  37. 4 0
      samples/VirtualizationTest/VirtualizationTest.csproj
  38. 6 2
      samples/XamlTestApplication/XamlTestApplication.csproj
  39. 1 1
      samples/XamlTestApplication/packages.config
  40. 6 0
      samples/interop/GtkInteropDemo/App.config
  41. 160 0
      samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
  42. 26 0
      samples/interop/GtkInteropDemo/GtkInteropDemo.v2.ncrunchproject
  43. 30 0
      samples/interop/GtkInteropDemo/MainWindow.cs
  44. 24 0
      samples/interop/GtkInteropDemo/Program.cs
  45. 36 0
      samples/interop/GtkInteropDemo/Properties/AssemblyInfo.cs
  46. 71 0
      samples/interop/GtkInteropDemo/Properties/Resources.Designer.cs
  47. 117 0
      samples/interop/GtkInteropDemo/Properties/Resources.resx
  48. 30 0
      samples/interop/GtkInteropDemo/Properties/Settings.Designer.cs
  49. 7 0
      samples/interop/GtkInteropDemo/Properties/Settings.settings
  50. 6 0
      samples/interop/WindowsInteropTest/App.config
  51. 119 0
      samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.Designer.cs
  52. 23 0
      samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.cs
  53. 120 0
      samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.resx
  54. 21 0
      samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml
  55. 31 0
      samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs
  56. 22 0
      samples/interop/WindowsInteropTest/Program.cs
  57. 36 0
      samples/interop/WindowsInteropTest/Properties/AssemblyInfo.cs
  58. 71 0
      samples/interop/WindowsInteropTest/Properties/Resources.Designer.cs
  59. 117 0
      samples/interop/WindowsInteropTest/Properties/Resources.resx
  60. 30 0
      samples/interop/WindowsInteropTest/Properties/Settings.Designer.cs
  61. 7 0
      samples/interop/WindowsInteropTest/Properties/Settings.settings
  62. 76 0
      samples/interop/WindowsInteropTest/SelectorForm.Designer.cs
  63. 30 0
      samples/interop/WindowsInteropTest/SelectorForm.cs
  64. 120 0
      samples/interop/WindowsInteropTest/SelectorForm.resx
  65. 190 0
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  66. 26 0
      samples/interop/WindowsInteropTest/WindowsInteropTest.v2.ncrunchproject
  67. 2 2
      src/Android/Avalonia.Android/AndroidPlatform.cs
  68. 1 0
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  69. 2 0
      src/Android/Avalonia.Android/Platform/SkiaPlatform/WindowImpl.cs
  70. 18 0
      src/Android/Avalonia.Android/RuntimeInfo.cs
  71. 3 3
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  72. 2 2
      src/Android/Avalonia.AndroidTestApplication/packages.config
  73. 6 4
      src/Avalonia.Base/Avalonia.Base.csproj
  74. 1 1
      src/Avalonia.Base/AvaloniaDisposable.cs
  75. 134 133
      src/Avalonia.Base/AvaloniaObject.cs
  76. 9 2
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  77. 7 2
      src/Avalonia.Base/AvaloniaProperty.cs
  78. 85 0
      src/Avalonia.Base/Data/BindingChainNullException.cs
  79. 0 59
      src/Avalonia.Base/Data/BindingError.cs
  80. 282 0
      src/Avalonia.Base/Data/BindingNotification.cs
  81. 4 1
      src/Avalonia.Base/Data/BindingOperations.cs
  82. 3 1
      src/Avalonia.Base/Data/IBinding.cs
  83. 0 17
      src/Avalonia.Base/Data/IValidationStatus.cs
  84. 5 1
      src/Avalonia.Base/Data/IndexerBinding.cs
  85. 0 44
      src/Avalonia.Base/Data/ObjectValidationStatus.cs
  86. 9 2
      src/Avalonia.Base/DirectProperty.cs
  87. 18 2
      src/Avalonia.Base/DirectPropertyMetadata`1.cs
  88. 5 0
      src/Avalonia.Base/IDirectPropertyMetadata.cs
  89. 4 3
      src/Avalonia.Base/IPriorityValueOwner.cs
  90. 0 17
      src/Avalonia.Base/Platform/IPclPlatformWrapper.cs
  91. 39 0
      src/Avalonia.Base/Platform/IRuntimePlatform.cs
  92. 14 12
      src/Avalonia.Base/PriorityBindingEntry.cs
  93. 1 12
      src/Avalonia.Base/PriorityLevel.cs
  94. 25 16
      src/Avalonia.Base/PriorityValue.cs
  95. 2 1
      src/Avalonia.Base/PropertyMetadata.cs
  96. 2 2
      src/Avalonia.Base/Threading/SingleThreadDispatcher.cs
  97. 23 0
      src/Avalonia.Base/Utilities/ExceptionUtilities.cs
  98. 144 0
      src/Avalonia.Base/Utilities/SingleOrDictionary.cs
  99. 41 0
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  100. 54 0
      src/Avalonia.Base/Utilities/WeakObservable.cs

+ 8 - 0
.gitignore

@@ -152,3 +152,11 @@ $RECYCLE.BIN/
 #################
 #################
 *.userprefs
 *.userprefs
 *.nugetreferenceswitcher
 *.nugetreferenceswitcher
+
+#################
+## Cake
+#################
+tools/
+.nuget
+artifacts/
+nuget

+ 1 - 10
.travis.yml

@@ -4,17 +4,8 @@ os:
   - osx
   - osx
 mono:
 mono:
   - latest
   - latest
-solution: Avalonia.travis-mono.sln
-before_install:
-  - mkdir -p .nuget
-  - wget -O .nuget/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
-install:
-  - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y gtk-sharp2 ; fi
-  - mono .nuget/nuget.exe restore Avalonia.sln
-  - mono .nuget/nuget.exe install xunit.runner.console -Version 2.1.0 -OutputDirectory testrunner
 script:
 script:
-  - xbuild /p:Platform=Mono /p:Configuration=Release Avalonia.sln
-  - ./tests/run-tests.sh
+  - ./build.sh --target "Travis" --platform "Mono" --configuration "Release"
 notifications:
 notifications:
   email: false
   email: false
   webhooks:
   webhooks:

+ 128 - 5
Avalonia.sln

@@ -163,6 +163,14 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Avalonia.RenderTests", "tes
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualizationTest", "samples\VirtualizationTest\VirtualizationTest.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualizationTest", "samples\VirtualizationTest\VirtualizationTest.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}"
 EndProject
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC0258-D18C-4AB3-854F-7101680FC3F9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInteropTest", "samples\interop\WindowsInteropTest\WindowsInteropTest.csproj", "{C7A69145-60B6-4882-97D6-A3921DD43978}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GtkInteropDemo", "samples\interop\GtkInteropDemo\GtkInteropDemo.csproj", "{BD7F352C-6DC1-4740-BAF2-2D34A038728C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}"
+EndProject
 Global
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
@@ -172,11 +180,9 @@ Global
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
-		src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4
+		src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{78345174-5b52-4a14-b9fd-d5f2428137f0}*SharedItemsImports = 13
 		samples\TestApplicationShared\TestApplicationShared.projitems*{78345174-5b52-4a14-b9fd-d5f2428137f0}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
-		src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
-		src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{8c923867-8a8f-4f6b-8b80-47d9e8436166}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{8c923867-8a8f-4f6b-8b80-47d9e8436166}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
@@ -184,9 +190,7 @@ Global
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
 		src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{d35a9f3d-8bb0-496e-bf72-444038a7debb}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{d35a9f3d-8bb0-496e-bf72-444038a7debb}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{dabfd304-d6a4-4752-8123-c2ccf7ac7831}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{dabfd304-d6a4-4752-8123-c2ccf7ac7831}*SharedItemsImports = 4
-		src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{e106cf37-4066-4615-b684-172a6d30b058}*SharedItemsImports = 4
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{e106cf37-4066-4615-b684-172a6d30b058}*SharedItemsImports = 4
-		src\Shared\PlatformSupport\PlatformSupport.projitems*{e1aa3dbf-9056-4530-9376-18119a7a3ffe}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{e3a1060b-50d0-44e8-88b6-f44ef2e5bd72}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{e3a1060b-50d0-44e8-88b6-f44ef2e5bd72}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
@@ -2241,6 +2245,122 @@ Global
 		{FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|Mono.Build.0 = Release|Any CPU
 		{FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|Mono.Build.0 = Release|Any CPU
 		{FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|x86.ActiveCfg = Release|Any CPU
 		{FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|x86.ActiveCfg = Release|Any CPU
 		{FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|x86.Build.0 = Release|Any CPU
 		{FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|x86.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.AppStore|x86.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Debug|x86.Build.0 = Debug|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|iPhone.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|Mono.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|x86.ActiveCfg = Release|Any CPU
+		{C7A69145-60B6-4882-97D6-A3921DD43978}.Release|x86.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|Mono.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|Mono.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.AppStore|x86.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|Mono.Build.0 = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Debug|x86.Build.0 = Debug|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|iPhone.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|Mono.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|Mono.Build.0 = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|x86.ActiveCfg = Release|Any CPU
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C}.Release|x86.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|Mono.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|Mono.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.AppStore|x86.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|Mono.Build.0 = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|x86.Build.0 = Debug|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|iPhone.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|Mono.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|Mono.Build.0 = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|x86.ActiveCfg = Release|Any CPU
+		{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
@@ -2293,5 +2413,8 @@ Global
 		{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{48840EDD-24BF-495D-911E-2EB12AE75D3B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{48840EDD-24BF-495D-911E-2EB12AE75D3B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{A0CC0258-D18C-4AB3-854F-7101680FC3F9} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{C7A69145-60B6-4882-97D6-A3921DD43978} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
+		{BD7F352C-6DC1-4740-BAF2-2D34A038728C} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 	EndGlobalSection
 	EndGlobalSection
 EndGlobal
 EndGlobal

+ 7 - 0
NuGet.Config

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <packageSources>
+    <clear />
+    <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
+  </packageSources>
+</configuration>

+ 19 - 23
appveyor.yml

@@ -1,31 +1,27 @@
-version: 1.0.{build}
 os: Visual Studio 2015
 os: Visual Studio 2015
-before_build:
-- git submodule update --init
-- nuget restore Avalonia.sln
-
+platform:
+- Any CPU
+configuration:
+- Release
 environment:
 environment:
-  myget_key:
-    secure: XOgD5bJUKNOS2kDDgb+affS4pDcslxALh+xvvnr1Koy0PjXlhILsBdNhxRe0KcNm
-
+  NUGET_API_KEY:
+    secure: Xv89dlP2MSBZKhl1nrWSxqcDgCXB0HRhOd4SWQ+jRJ7QoLxQel5mLTipXM++J3G5
+  NUGET_API_URL: https://www.nuget.org/api/v2/package
+  MYGET_API_KEY:
+    secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK
+  MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package
 install:
 install:
   - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
   - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
   - msiexec /i gtk-sharp-2.12.26.msi /qn /norestart
   - msiexec /i gtk-sharp-2.12.26.msi /qn /norestart
   - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
   - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
-
-cache:
-  - gtk-sharp-2.12.26.msi
-  
-configuration:
-  - Release
-
-after_test:
+before_build:
+- git submodule update --init
+build_script:
+- ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration"
+after_build:
 - .\packages\JetBrains.dotMemoryUnit.2.1.20150828.125449\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode  --"tests\Avalonia.LeakTests\bin\Release\Avalonia.LeakTests.dll"
 - .\packages\JetBrains.dotMemoryUnit.2.1.20150828.125449\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode  --"tests\Avalonia.LeakTests\bin\Release\Avalonia.LeakTests.dll"
-- ps: nuget\build-appveyor.ps1
-
+test: off
 artifacts:
 artifacts:
-  - path: nuget\*.nupkg
-
-build:
-  project: Avalonia.sln
-  verbosity: minimal
+  - path: artifacts\nuget\*.nupkg
+cache:
+  - gtk-sharp-2.12.26.msi

+ 776 - 0
build.cake

@@ -0,0 +1,776 @@
+///////////////////////////////////////////////////////////////////////////////
+// ADDINS
+///////////////////////////////////////////////////////////////////////////////
+
+#addin "nuget:?package=Polly&version=4.2.0"
+#addin "nuget:?package=NuGet.Core&version=2.12.0"
+
+///////////////////////////////////////////////////////////////////////////////
+// TOOLS
+///////////////////////////////////////////////////////////////////////////////
+
+#tool "nuget:?package=xunit.runner.console&version=2.1.0"
+
+///////////////////////////////////////////////////////////////////////////////
+// USINGS
+///////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Polly;
+using NuGet;
+
+///////////////////////////////////////////////////////////////////////////////
+// ARGUMENTS
+///////////////////////////////////////////////////////////////////////////////
+
+var target = Argument("target", "Default");
+var platform = Argument("platform", "Any CPU");
+var configuration = Argument("configuration", "Release");
+var skipTests = HasArgument("skip-tests");
+///////////////////////////////////////////////////////////////////////////////
+// CONFIGURATION
+///////////////////////////////////////////////////////////////////////////////
+
+var MainRepo = "AvaloniaUI/Avalonia";
+var MasterBranch = "master";
+var AssemblyInfoPath = File("./src/Shared/SharedAssemblyInfo.cs");
+var ReleasePlatform = "Any CPU";
+var ReleaseConfiguration = "Release";
+var MSBuildSolution = "./Avalonia.sln";
+var XBuildSolution = "./Avalonia.sln";
+
+///////////////////////////////////////////////////////////////////////////////
+// PARAMETERS
+///////////////////////////////////////////////////////////////////////////////
+
+var isPlatformAnyCPU = StringComparer.OrdinalIgnoreCase.Equals(platform, "Any CPU");
+var isPlatformX86 = StringComparer.OrdinalIgnoreCase.Equals(platform, "x86");
+var isPlatformX64 = StringComparer.OrdinalIgnoreCase.Equals(platform, "x64");
+var isLocalBuild = BuildSystem.IsLocalBuild;
+var isRunningOnUnix = IsRunningOnUnix();
+var isRunningOnWindows = IsRunningOnWindows();
+var isRunningOnAppVeyor = BuildSystem.AppVeyor.IsRunningOnAppVeyor;
+var isPullRequest = BuildSystem.AppVeyor.Environment.PullRequest.IsPullRequest;
+var isMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, BuildSystem.AppVeyor.Environment.Repository.Name);
+var isMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, BuildSystem.AppVeyor.Environment.Repository.Branch);
+var isTagged = BuildSystem.AppVeyor.Environment.Repository.Tag.IsTag 
+               && !string.IsNullOrWhiteSpace(BuildSystem.AppVeyor.Environment.Repository.Tag.Name);
+var isReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, platform) 
+                   && StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, configuration);
+var isMyGetRelease = !isTagged && isReleasable;
+var isNuGetRelease = isTagged && isReleasable;
+
+///////////////////////////////////////////////////////////////////////////////
+// VERSION
+///////////////////////////////////////////////////////////////////////////////
+
+var version = ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion;
+
+if (isRunningOnAppVeyor)
+{
+    if (isTagged)
+    {
+        // Use Tag Name as version
+        version = BuildSystem.AppVeyor.Environment.Repository.Tag.Name;
+    }
+    else
+    {
+        // Use AssemblyVersion with Build as version
+        version += "-build" + EnvironmentVariable("APPVEYOR_BUILD_NUMBER") + "-alpha";
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DIRECTORIES
+///////////////////////////////////////////////////////////////////////////////
+
+var artifactsDir = (DirectoryPath)Directory("./artifacts");
+var nugetRoot = artifactsDir.Combine("nuget");
+
+var dirSuffix = configuration;
+var dirSuffixSkia = (isPlatformAnyCPU ? "x86" : platform) + "/" + configuration;
+var dirSuffixIOS = "iPhone" + "/" + configuration;
+
+var buildDirs = 
+    GetDirectories("./src/**/bin/" + dirSuffix) + 
+    GetDirectories("./src/**/obj/" + dirSuffix) + 
+    GetDirectories("./src/Markup/**/bin/" + dirSuffix) + 
+    GetDirectories("./src/Markup/**/obj/" + dirSuffix) + 
+    GetDirectories("./src/Android/**/bin/" + dirSuffix) + 
+    GetDirectories("./src/Android/**/obj/" + dirSuffix) + 
+    GetDirectories("./src/Gtk/**/bin/" + dirSuffix) + 
+    GetDirectories("./src/Gtk/**/obj/" + dirSuffix) + 
+    GetDirectories("./src/iOS/**/bin/" + dirSuffixIOS) + 
+    GetDirectories("./src/iOS/**/obj/" + dirSuffixIOS) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.Android/bin/" + dirSuffix) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.Android/obj/" + dirSuffix) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.Android.TestApp/bin/" + dirSuffix) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.Android.TestApp/obj/" + dirSuffix) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.Desktop/bin/" + dirSuffixSkia) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.Desktop/obj/" + dirSuffixSkia) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.iOS/bin/" + dirSuffixIOS) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.iOS/obj/" + dirSuffixIOS) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.iOS.TestApp/bin/" + dirSuffixIOS) + 
+    (DirectoryPath)Directory("./src/Skia/Avalonia.Skia.iOS.TestApp/obj/" + dirSuffixIOS) + 
+    GetDirectories("./src/Windows/**/bin/" + dirSuffix) + 
+    GetDirectories("./src/Windows/**/obj/" + dirSuffix) + 
+    GetDirectories("./tests/**/bin/" + dirSuffix) + 
+    GetDirectories("./tests/**/obj/" + dirSuffix) + 
+    GetDirectories("./Samples/**/bin/" + dirSuffix) + 
+    GetDirectories("./Samples/**/obj/" + dirSuffix);
+
+///////////////////////////////////////////////////////////////////////////////
+// NUGET NUSPECS
+///////////////////////////////////////////////////////////////////////////////
+
+Information("Getting git modules:");
+
+var ignoredSubModulesPaths = System.IO.File.ReadAllLines(".git/config").Where(m=>m.StartsWith("[submodule ")).Select(m => 
+{
+    var path = m.Split(' ')[1].Trim("\"[] \t".ToArray());
+    Information(path);
+    return ((DirectoryPath)Directory(path)).FullPath;
+}).ToList();
+
+var normalizePath = new Func<string, string>(
+    path => path.Replace(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar).ToUpperInvariant());
+
+// Key: Package Id
+// Value is Tuple where Item1: Package Version, Item2: The packages.config file path.
+var packageVersions = new Dictionary<string, IList<Tuple<string,string>>>();
+
+System.IO.Directory.EnumerateFiles(((DirectoryPath)Directory("./src")).FullPath, "packages.config", SearchOption.AllDirectories).ToList().ForEach(fileName =>
+{
+    if (!ignoredSubModulesPaths.Any(i => normalizePath(fileName).Contains(normalizePath(i))))
+    {
+        var file = new PackageReferenceFile(fileName);
+        foreach (PackageReference packageReference in file.GetPackageReferences())
+        {
+            IList<Tuple<string, string>> versions;
+            packageVersions.TryGetValue(packageReference.Id, out versions);
+            if (versions == null)
+            {
+                versions = new List<Tuple<string, string>>();
+                packageVersions[packageReference.Id] = versions;
+            }
+            versions.Add(Tuple.Create(packageReference.Version.ToString(), fileName));
+        }
+    }
+});
+
+Information("Checking installed NuGet package dependencies versions:");
+
+packageVersions.ToList().ForEach(package =>
+{
+    var packageVersion = package.Value.First().Item1;
+    bool isValidVersion = package.Value.All(x => x.Item1 == packageVersion);
+    if (!isValidVersion)
+    {
+        Information("Error: package {0} has multiple versions installed:", package.Key);
+        foreach (var v in package.Value)
+        {
+            Information("{0}, file: {1}", v.Item1, v.Item2);
+        }
+        throw new Exception("Detected multiple NuGet package version installed for different projects.");
+    }
+});
+
+Information("Setting NuGet package dependencies versions:");
+
+var SerilogVersion = packageVersions["Serilog"].FirstOrDefault().Item1;
+var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1;
+var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
+var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
+var SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
+var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1;
+var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1;
+var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1;
+
+Information("Package: Serilog, version: {0}", SerilogVersion);
+Information("Package: Splat, version: {0}", SplatVersion);
+Information("Package: Sprache, version: {0}", SpracheVersion);
+Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
+Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
+Information("Package: SharpDX, version: {0}", SharpDXVersion);
+Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version);
+Information("Package: SharpDX.DXGI, version: {0}", SharpDXDXGIVersion);
+
+var SetNuGetNuspecCommonProperties = new Action<NuGetPackSettings> ((nuspec) => {
+    nuspec.Version = version;
+    nuspec.Authors = new [] { "Avalonia Team" };
+    nuspec.Owners = new [] { "stevenk" };
+    nuspec.LicenseUrl = new Uri("http://opensource.org/licenses/MIT");
+    nuspec.ProjectUrl = new Uri("https://github.com/AvaloniaUI/Avalonia/");
+    nuspec.RequireLicenseAcceptance = false;
+    nuspec.Symbols = false;
+    nuspec.NoPackageAnalysis = true;
+    nuspec.Description = "The Avalonia UI framework";
+    nuspec.Copyright = "Copyright 2015";
+    nuspec.Tags = new [] { "Avalonia" };
+});
+
+var coreLibraries = new string[][]
+{
+    new [] { "./src/", "Avalonia.Animation", ".dll" },
+    new [] { "./src/", "Avalonia.Animation", ".xml" },
+    new [] { "./src/", "Avalonia.Base", ".dll" },
+    new [] { "./src/", "Avalonia.Base", ".xml" },
+    new [] { "./src/", "Avalonia.Controls", ".dll" },
+    new [] { "./src/", "Avalonia.Controls", ".xml" },
+    new [] { "./src/", "Avalonia.DesignerSupport", ".dll" },
+    new [] { "./src/", "Avalonia.DesignerSupport", ".xml" },
+    new [] { "./src/", "Avalonia.Diagnostics", ".dll" },
+    new [] { "./src/", "Avalonia.Diagnostics", ".xml" },
+    new [] { "./src/", "Avalonia.Input", ".dll" },
+    new [] { "./src/", "Avalonia.Input", ".xml" },
+    new [] { "./src/", "Avalonia.Interactivity", ".dll" },
+    new [] { "./src/", "Avalonia.Interactivity", ".xml" },
+    new [] { "./src/", "Avalonia.Layout", ".dll" },
+    new [] { "./src/", "Avalonia.Layout", ".xml" },
+    new [] { "./src/", "Avalonia.Logging.Serilog", ".dll" },
+    new [] { "./src/", "Avalonia.Logging.Serilog", ".xml" },
+    new [] { "./src/", "Avalonia.SceneGraph", ".dll" },
+    new [] { "./src/", "Avalonia.SceneGraph", ".xml" },
+    new [] { "./src/", "Avalonia.Styling", ".dll" },
+    new [] { "./src/", "Avalonia.Styling", ".xml" },
+    new [] { "./src/", "Avalonia.ReactiveUI", ".dll" },
+    new [] { "./src/", "Avalonia.Themes.Default", ".dll" },
+    new [] { "./src/", "Avalonia.Themes.Default", ".xml" },
+    new [] { "./src/Markup/", "Avalonia.Markup", ".dll" },
+    new [] { "./src/Markup/", "Avalonia.Markup", ".xml" },
+    new [] { "./src/Markup/", "Avalonia.Markup.Xaml", ".dll" },
+    new [] { "./src/Markup/", "Avalonia.Markup.Xaml", ".xml" }
+};
+
+var coreLibrariesFiles = coreLibraries.Select((lib) => {
+    return (FilePath)File(lib[0] + lib[1] + "/bin/" + dirSuffix + "/" + lib[1] + lib[2]);
+}).ToList();
+
+var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
+    return new NuSpecContent { 
+        Source = file.FullPath, Target = "lib/portable-windows8+net45" 
+    };
+});
+
+var win32CoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
+    return new NuSpecContent { 
+        Source = file.FullPath, Target = "lib/net45" 
+    };
+});
+
+var net45RuntimePlatformExtensions = new [] {".xml", ".dll"};
+var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => {
+    return new NuSpecContent {
+        Source = ((FilePath)File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + dirSuffix + "/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath, 
+        Target = "lib/net45" 
+    };
+});
+
+var nuspecNuGetSettingsCore = new []
+{
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion },
+            new NuSpecDependency() { Id = "Splat", Version = SplatVersion },
+            new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
+            new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion }
+        },
+        Files = coreLibrariesNuSpecContent.Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform).ToList(),
+        BasePath = Directory("./"),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.HtmlRenderer
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.HtmlRenderer",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/portable-windows8+net45" }
+        },
+        BasePath = Directory("./src/Avalonia.HtmlRenderer/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    }
+};
+
+var nuspecNuGetSettingsMobile = new []
+{
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Android
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Android",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.Skia.Android", Version = version }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Android.dll", Target = "lib/MonoAndroid10" }
+        },
+        BasePath = Directory("./src/Android/Avalonia.Android/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Skia.Android
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Skia.Android",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version },
+            new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Skia.Android.dll", Target = "lib/MonoAndroid10" }
+        },
+        BasePath = Directory("./src/Skia/Avalonia.Skia.Android/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.iOS
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.iOS",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.Skia.iOS", Version = version }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.iOS.dll", Target = "lib/Xamarin.iOS10" }
+        },
+        BasePath = Directory("./src/iOS/Avalonia.iOS/bin/" + dirSuffixIOS),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Skia.iOS
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Skia.iOS",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version },
+            new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Skia.iOS.dll", Target = "lib/Xamarin.iOS10" }
+        },
+        BasePath = Directory("./src/Skia/Avalonia.Skia.iOS/bin/" + dirSuffixIOS),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Mobile
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Mobile",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia.Android", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.iOS", Version = version }
+        },
+        Files = new NuSpecContent[]
+        {
+            new NuSpecContent { Source = "licence.md", Target = "" }
+        },
+        BasePath = Directory("./"),
+        OutputDirectory = nugetRoot
+    }
+};
+
+var nuspecNuGetSettingsDesktop = new []
+{
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Win32
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Win32",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Win32.dll", Target = "lib/net45" }
+        },
+        BasePath = Directory("./src/Windows/Avalonia.Win32/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Direct2D1
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Direct2D1",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version },
+            new NuSpecDependency() { Id = "SharpDX", Version = SharpDXVersion },
+            new NuSpecDependency() { Id = "SharpDX.Direct2D1", Version = SharpDXDirect2D1Version },
+            new NuSpecDependency() { Id = "SharpDX.DXGI", Version = SharpDXDXGIVersion }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/net45" }
+        },
+        BasePath = Directory("./src/Windows/Avalonia.Direct2D1/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Gtk
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Gtk",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Gtk.dll", Target = "lib/net45" }
+        },
+        BasePath = Directory("./src/Gtk/Avalonia.Gtk/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Cairo
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Cairo",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Cairo.dll", Target = "lib/net45" }
+        },
+        BasePath = Directory("./src/Gtk/Avalonia.Cairo/bin/" + dirSuffix),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Skia.Desktop
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Skia.Desktop",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia", Version = version },
+            new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }
+        },
+        Files = new []
+        {
+            new NuSpecContent { Source = "Avalonia.Skia.Desktop.dll", Target = "lib/net45" }
+        },
+        BasePath = Directory("./src/Skia/Avalonia.Skia.Desktop/bin/" + dirSuffixSkia),
+        OutputDirectory = nugetRoot
+    },
+    ///////////////////////////////////////////////////////////////////////////////
+    // Avalonia.Desktop
+    ///////////////////////////////////////////////////////////////////////////////
+    new NuGetPackSettings()
+    {
+        Id = "Avalonia.Desktop",
+        Dependencies = new []
+        {
+            new NuSpecDependency() { Id = "Avalonia.Win32", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.Gtk", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.Cairo", Version = version },
+            new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = version }
+        },
+        Files = new NuSpecContent[]
+        {
+            new NuSpecContent { Source = "licence.md", Target = "" }
+        },
+        BasePath = Directory("./"),
+        OutputDirectory = nugetRoot
+    }
+};
+
+var nuspecNuGetSettings = new List<NuGetPackSettings>();
+
+nuspecNuGetSettings.AddRange(nuspecNuGetSettingsCore);
+nuspecNuGetSettings.AddRange(nuspecNuGetSettingsDesktop);
+nuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile);
+
+nuspecNuGetSettings.ForEach((nuspec) => SetNuGetNuspecCommonProperties(nuspec));
+
+var nugetPackages = nuspecNuGetSettings.Select(nuspec => {
+    return nuspec.OutputDirectory.CombineWithFilePath(string.Concat(nuspec.Id, ".", nuspec.Version, ".nupkg"));
+}).ToArray();
+
+///////////////////////////////////////////////////////////////////////////////
+// INFORMATION
+///////////////////////////////////////////////////////////////////////////////
+
+Information("Building version {0} of Avalonia ({1}, {2}, {3}) using version {4} of Cake.", 
+    version,
+    platform,
+    configuration,
+    target,
+    typeof(ICakeContext).Assembly.GetName().Version.ToString());
+
+if (isRunningOnAppVeyor)
+{
+    Information("Repository Name: " + BuildSystem.AppVeyor.Environment.Repository.Name);
+    Information("Repository Branch: " + BuildSystem.AppVeyor.Environment.Repository.Branch);
+}
+
+Information("Target: " + target);
+Information("Platform: " + platform);
+Information("Configuration: " + configuration);
+Information("IsLocalBuild: " + isLocalBuild);
+Information("IsRunningOnUnix: " + isRunningOnUnix);
+Information("IsRunningOnWindows: " + isRunningOnWindows);
+Information("IsRunningOnAppVeyor: " + isRunningOnAppVeyor);
+Information("IsPullRequest: " + isPullRequest);
+Information("IsMainRepo: " + isMainRepo);
+Information("IsMasterBranch: " + isMasterBranch);
+Information("IsTagged: " + isTagged);
+Information("IsReleasable: " + isReleasable);
+Information("IsMyGetRelease: " + isMyGetRelease);
+Information("IsNuGetRelease: " + isNuGetRelease);
+
+///////////////////////////////////////////////////////////////////////////////
+// TASKS
+///////////////////////////////////////////////////////////////////////////////
+
+Task("Clean")
+    .Does(() =>
+{
+    CleanDirectories(buildDirs);
+    CleanDirectory(artifactsDir);
+    CleanDirectory(nugetRoot);
+});
+
+Task("Restore-NuGet-Packages")
+    .IsDependentOn("Clean")
+    .Does(() =>
+{
+    var maxRetryCount = 5;
+    var toolTimeout = 1d;
+    Policy
+        .Handle<Exception>()
+        .Retry(maxRetryCount, (exception, retryCount, context) => {
+            if (retryCount == maxRetryCount)
+            {
+                throw exception;
+            }
+            else
+            {
+                Verbose("{0}", exception);
+                toolTimeout+=0.5;
+            }})
+        .Execute(()=> {
+            if(isRunningOnWindows)
+            {
+                NuGetRestore(MSBuildSolution, new NuGetRestoreSettings {
+                    ToolTimeout = TimeSpan.FromMinutes(toolTimeout)
+                });
+            }
+            else
+            {
+                NuGetRestore(XBuildSolution, new NuGetRestoreSettings {
+                    ToolTimeout = TimeSpan.FromMinutes(toolTimeout)
+                });
+            }
+        });
+});
+
+Task("Build")
+    .IsDependentOn("Restore-NuGet-Packages")
+    .Does(() =>
+{
+    if(isRunningOnWindows)
+    {
+        MSBuild(MSBuildSolution, settings => {
+            settings.SetConfiguration(configuration);
+            settings.WithProperty("Platform", "\"" + platform + "\"");
+            settings.SetVerbosity(Verbosity.Minimal);
+            settings.WithProperty("Windows", "True");
+            settings.UseToolVersion(MSBuildToolVersion.VS2015);
+            settings.SetNodeReuse(false);
+        });
+    }
+    else
+    {
+        XBuild(XBuildSolution, settings => {
+            settings.SetConfiguration(configuration);
+            settings.WithProperty("Platform", "\"" + platform + "\"");
+            settings.SetVerbosity(Verbosity.Minimal);
+        });
+    }
+});
+
+Task("Run-Unit-Tests")
+    .IsDependentOn("Build")
+    .WithCriteria(() => !skipTests)
+    .Does(() =>
+{
+    var pattern = "./tests/Avalonia.*.UnitTests/bin/" + dirSuffix + "/Avalonia.*.UnitTests.dll";
+
+    Func<IFileSystemInfo, bool> ExcludeWindowsTests = i => {
+        return !(i.Path.FullPath.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0);
+    };
+
+    var unitTests = isRunningOnWindows ? GetFiles(pattern) : GetFiles(pattern, ExcludeWindowsTests);
+
+    if (isRunningOnWindows)
+    {
+        var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*Tests.dll") + 
+                           GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*Tests.dll") + 
+                           GetFiles("./tests/Avalonia.RenderTests/bin/" + dirSuffix + "/*Tests.dll");
+
+        unitTests += windowsTests;
+    }
+
+    var toolPath = (isPlatformAnyCPU || isPlatformX86) ? 
+        "./tools/xunit.runner.console/tools/xunit.console.x86.exe" :
+        "./tools/xunit.runner.console/tools/xunit.console.exe";
+
+    var settings = new XUnit2Settings 
+    { 
+        ToolPath = toolPath,
+        Parallelism = ParallelismOption.None 
+    };
+
+    if (isRunningOnWindows)
+    {
+        settings.NoAppDomain = false;
+    }
+
+    foreach (var file in unitTests)
+    {
+        Information("Running test " + file.GetFilenameWithoutExtension());
+        XUnit2(file.FullPath, settings);
+    }
+});
+
+Task("Create-NuGet-Packages")
+    .IsDependentOn("Run-Unit-Tests")
+    .Does(() =>
+{
+    foreach(var nuspec in nuspecNuGetSettings)
+    {
+        NuGetPack(nuspec);
+    }
+});
+
+Task("Publish-MyGet")
+    .IsDependentOn("Create-NuGet-Packages")
+    .WithCriteria(() => !isLocalBuild)
+    .WithCriteria(() => !isPullRequest)
+    .WithCriteria(() => isMainRepo)
+    .WithCriteria(() => isMasterBranch)
+    .WithCriteria(() => isMyGetRelease)
+    .Does(() =>
+{
+    var apiKey = EnvironmentVariable("MYGET_API_KEY");
+    if(string.IsNullOrEmpty(apiKey)) 
+    {
+        throw new InvalidOperationException("Could not resolve MyGet API key.");
+    }
+
+    var apiUrl = EnvironmentVariable("MYGET_API_URL");
+    if(string.IsNullOrEmpty(apiUrl)) 
+    {
+        throw new InvalidOperationException("Could not resolve MyGet API url.");
+    }
+
+    foreach(var nupkg in nugetPackages)
+    {
+        NuGetPush(nupkg, new NuGetPushSettings {
+            Source = apiUrl,
+            ApiKey = apiKey
+        });
+    }
+})
+.OnError(exception =>
+{
+    Information("Publish-MyGet Task failed, but continuing with next Task...");
+});
+
+Task("Publish-NuGet")
+    .IsDependentOn("Create-NuGet-Packages")
+    .WithCriteria(() => !isLocalBuild)
+    .WithCriteria(() => !isPullRequest)
+    .WithCriteria(() => isMainRepo)
+    .WithCriteria(() => isMasterBranch)
+    .WithCriteria(() => isNuGetRelease)
+    .Does(() =>
+{
+    var apiKey = EnvironmentVariable("NUGET_API_KEY");
+    if(string.IsNullOrEmpty(apiKey)) 
+    {
+        throw new InvalidOperationException("Could not resolve NuGet API key.");
+    }
+
+    var apiUrl = EnvironmentVariable("NUGET_API_URL");
+    if(string.IsNullOrEmpty(apiUrl)) 
+    {
+        throw new InvalidOperationException("Could not resolve NuGet API url.");
+    }
+
+    foreach(var nupkg in nugetPackages)
+    {
+        NuGetPush(nupkg, new NuGetPushSettings {
+            ApiKey = apiKey,
+            Source = apiUrl
+        });
+    }
+})
+.OnError(exception =>
+{
+    Information("Publish-NuGet Task failed, but continuing with next Task...");
+});
+
+///////////////////////////////////////////////////////////////////////////////
+// TARGETS
+///////////////////////////////////////////////////////////////////////////////
+
+Task("Package")
+  .IsDependentOn("Create-NuGet-Packages");
+
+Task("Default")
+  .IsDependentOn("Package");
+
+Task("AppVeyor")
+  .IsDependentOn("Publish-MyGet")
+  .IsDependentOn("Publish-NuGet");
+
+Task("Travis")
+  .IsDependentOn("Run-Unit-Tests");
+
+///////////////////////////////////////////////////////////////////////////////
+// EXECUTE
+///////////////////////////////////////////////////////////////////////////////
+
+RunTarget(target);

+ 201 - 0
build.ps1

@@ -0,0 +1,201 @@
+##########################################################################
+# This is the Cake bootstrapper script for PowerShell.
+# This file was downloaded from https://github.com/cake-build/resources
+# Feel free to change this file to fit your needs.
+##########################################################################
+
+<#
+
+.SYNOPSIS
+This is a Powershell script to bootstrap a Cake build.
+
+.DESCRIPTION
+This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
+and execute your Cake build script with the parameters you provide.
+
+.PARAMETER Script
+The build script to execute.
+.PARAMETER Target
+The build script target to run.
+.PARAMETER Platform
+The build platform to use.
+.PARAMETER Configuration
+The build configuration to use.
+.PARAMETER Verbosity
+Specifies the amount of information to be displayed.
+.PARAMETER Experimental
+Tells Cake to use the latest Roslyn release.
+.PARAMETER WhatIf
+Performs a dry run of the build script.
+No tasks will be executed.
+.PARAMETER Mono
+Tells Cake to use the Mono scripting engine.
+.PARAMETER SkipToolPackageRestore
+Skips restoring of packages.
+.PARAMETER SkipTests
+Skips unit tests
+.PARAMETER ScriptArgs
+Remaining arguments are added here.
+
+.LINK
+http://cakebuild.net
+
+#>
+
+[CmdletBinding()]
+Param(
+    [string]$Script = "build.cake",
+    [string]$Target = "Default",
+    [ValidateSet("Any CPU", "x86", "x64", "Mono", "iPhone")]
+    [string]$Platform = "Any CPU",
+    [ValidateSet("Release", "Debug")]
+    [string]$Configuration = "Release",
+    [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
+    [string]$Verbosity = "Verbose",
+    [switch]$Experimental,
+    [Alias("DryRun","Noop")]
+    [switch]$WhatIf,
+    [switch]$Mono,
+    [switch]$SkipToolPackageRestore,
+    [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
+    [string[]]$ScriptArgs
+)
+
+[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
+function MD5HashFile([string] $filePath)
+{
+    if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
+    {
+        return $null
+    }
+
+    [System.IO.Stream] $file = $null;
+    [System.Security.Cryptography.MD5] $md5 = $null;
+    try
+    {
+        $md5 = [System.Security.Cryptography.MD5]::Create()
+        $file = [System.IO.File]::OpenRead($filePath)
+        return [System.BitConverter]::ToString($md5.ComputeHash($file))
+    }
+    finally
+    {
+        if ($file -ne $null)
+        {
+            $file.Dispose()
+        }
+    }
+}
+
+Write-Host "Preparing to run build script..."
+
+if(!$PSScriptRoot){
+    $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
+}
+
+$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
+$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
+$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
+$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
+$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
+$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
+
+# Should we use mono?
+$UseMono = "";
+if($Mono.IsPresent) {
+    Write-Verbose -Message "Using the Mono based scripting engine."
+    $UseMono = "-mono"
+}
+
+# Should we use the new Roslyn?
+$UseExperimental = "";
+if($Experimental.IsPresent -and !($Mono.IsPresent)) {
+    Write-Verbose -Message "Using experimental version of Roslyn."
+    $UseExperimental = "-experimental"
+}
+
+# Is this a dry run?
+$UseDryRun = "";
+if($WhatIf.IsPresent) {
+    $UseDryRun = "-dryrun"
+}
+
+# Is this a dry run?
+$UseSkipTests = "";
+if($SkipTests.IsPresent) {
+    $UseSkipTests = "-skip-tests"
+}
+
+# Make sure tools folder exists
+if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
+    Write-Verbose -Message "Creating tools directory..."
+    New-Item -Path $TOOLS_DIR -Type directory | out-null
+}
+
+# Make sure that packages.config exist.
+if (!(Test-Path $PACKAGES_CONFIG)) {
+    Write-Verbose -Message "Downloading packages.config..."
+    try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
+        Throw "Could not download packages.config."
+    }
+}
+
+# Try find NuGet.exe in path if not exists
+if (!(Test-Path $NUGET_EXE)) {
+    Write-Verbose -Message "Trying to find nuget.exe in PATH..."
+    $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) }
+    $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
+    if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
+        Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
+        $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
+    }
+}
+
+# Try download NuGet.exe if not exists
+if (!(Test-Path $NUGET_EXE)) {
+    Write-Verbose -Message "Downloading NuGet.exe..."
+    try {
+        (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
+    } catch {
+        Throw "Could not download NuGet.exe."
+    }
+}
+
+# Save nuget.exe path to environment to be available to child processed
+$ENV:NUGET_EXE = $NUGET_EXE
+
+# Restore tools from NuGet?
+if(-Not $SkipToolPackageRestore.IsPresent) {
+    Push-Location
+    Set-Location $TOOLS_DIR
+
+    # Check for changes in packages.config and remove installed tools if true.
+    [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
+    if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
+      ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
+        Write-Verbose -Message "Missing or changed package.config hash..."
+        Remove-Item * -Recurse -Exclude packages.config,nuget.exe
+    }
+
+    Write-Verbose -Message "Restoring tools from NuGet..."
+    $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
+
+    if ($LASTEXITCODE -ne 0) {
+        Throw "An error occured while restoring NuGet tools."
+    }
+    else
+    {
+        $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
+    }
+    Write-Verbose -Message ($NuGetOutput | out-string)
+    Pop-Location
+}
+
+# Make sure that Cake has been installed.
+if (!(Test-Path $CAKE_EXE)) {
+    Throw "Could not find Cake.exe at $CAKE_EXE"
+}
+
+# Start Cake
+Write-Host "Running build script..."
+Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -platform=`"$Platform`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseSkipTests $UseMono $UseDryRun $UseExperimental $ScriptArgs"
+exit $LASTEXITCODE

+ 105 - 0
build.sh

@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+
+##########################################################################
+# This is the Cake bootstrapper script for Linux and OS X.
+# This file was downloaded from https://github.com/cake-build/resources
+# Feel free to change this file to fit your needs.
+##########################################################################
+
+# Define directories.
+SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+TOOLS_DIR=$SCRIPT_DIR/tools
+NUGET_EXE=$TOOLS_DIR/nuget.exe
+CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
+PACKAGES_CONFIG=$TOOLS_DIR/packages.config
+PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
+
+# Define md5sum or md5 depending on Linux/OSX
+MD5_EXE=
+if [[ "$(uname -s)" == "Darwin" ]]; then
+    MD5_EXE="md5 -r"
+else
+    MD5_EXE="md5sum"
+fi
+
+# Define default arguments.
+SCRIPT="build.cake"
+TARGET="Default"
+CONFIGURATION="Release"
+PLATFORM="Any CPU"
+VERBOSITY="verbose"
+DRYRUN=
+SKIP_TESTS=
+SHOW_VERSION=false
+SCRIPT_ARGUMENTS=()
+
+# Parse arguments.
+for i in "$@"; do
+    case $1 in
+        -s|--script) SCRIPT="$2"; shift ;;
+        -t|--target) TARGET="$2"; shift ;;
+        -p|--platform) PLATFORM="$2"; shift ;;
+        -c|--configuration) CONFIGURATION="$2"; shift ;;
+        --skip-tests) SKIP_TESTS="-skip-tests"; shift ;;
+        -v|--verbosity) VERBOSITY="$2"; shift ;;
+        -d|--dryrun) DRYRUN="-dryrun" ;;
+        --version) SHOW_VERSION=true ;;
+        --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;;
+        *) SCRIPT_ARGUMENTS+=("$1") ;;
+    esac
+    shift
+done
+
+# Make sure the tools folder exist.
+if [ ! -d "$TOOLS_DIR" ]; then
+  mkdir "$TOOLS_DIR"
+fi
+
+# Make sure that packages.config exist.
+if [ ! -f "$TOOLS_DIR/packages.config" ]; then
+    echo "Downloading packages.config..."
+    curl -Lsfo "$TOOLS_DIR/packages.config" http://cakebuild.net/download/bootstrapper/packages
+    if [ $? -ne 0 ]; then
+        echo "An error occured while downloading packages.config."
+        exit 1
+    fi
+fi
+
+# Download NuGet if it does not exist.
+if [ ! -f "$NUGET_EXE" ]; then
+    echo "Downloading NuGet..."
+    curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
+    if [ $? -ne 0 ]; then
+        echo "An error occured while downloading nuget.exe."
+        exit 1
+    fi
+fi
+
+# Restore tools from NuGet.
+pushd "$TOOLS_DIR" >/dev/null
+if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then
+    find . -type d ! -name . | xargs rm -rf
+fi
+
+mono "$NUGET_EXE" install -ExcludeVersion
+if [ $? -ne 0 ]; then
+    echo "Could not restore NuGet packages."
+    exit 1
+fi
+
+$MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' >| $PACKAGES_CONFIG_MD5
+
+popd >/dev/null
+
+# Make sure that Cake has been installed.
+if [ ! -f "$CAKE_EXE" ]; then
+    echo "Could not find Cake.exe at '$CAKE_EXE'."
+    exit 1
+fi
+
+# Start Cake
+if $SHOW_VERSION; then
+    exec mono "$CAKE_EXE" -version
+else
+    exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -platform="$PLATFORM" -configuration="$CONFIGURATION" -target=$TARGET $DRYRUN $SKIP_TESTS "${SCRIPT_ARGUMENTS[@]}"
+fi

+ 2 - 2
docs/guidelines/build.md

@@ -72,10 +72,10 @@ To build Avalonia in the `Debug` configuration:
 xbuild /p:Platform=Mono /p:Configuration=Debug Avalonia.sln
 xbuild /p:Platform=Mono /p:Configuration=Debug Avalonia.sln
 ```
 ```
 
 
-You should now be able to run the ControlCatalog sample:
+You should now be able to run the ControlCatalog.Desktop sample:
 
 
 ```
 ```
-mono ./samples/ControlCatalog/bin/Debug/ControlCatalog.exe
+mono ./samples/ControlCatalog.Desktop/bin/Debug/ControlCatalog.Desktop.exe
 ```
 ```
 
 
 ### Building Avalonia in MonoDevelop
 ### Building Avalonia in MonoDevelop

+ 86 - 0
docs/tutorial/nuget.md

@@ -0,0 +1,86 @@
+# Avalonia NuGet Packages
+
+Avalonia is divided into several `NuGet` packages. 
+
+* The `Avalonia` package contains core portable class libraries.
+* The `Dekstop` and `Mobile` packages contain platform specific windowing and rendering back-ends.
+* The `Avalonia.Desktop` package is intended to be used by the end users targeting multiple desktop platforms (`Windows`, `Linux` and `OSX`).
+* The `Avalonia.iOS` and `Avalonia.Android` packages are intended to be used by the end users targeting specific mobile platforms. 
+* The `Avalonia.Mobile` package is intended to be used by the end users targeting multiple mobile platforms (`Android` and `iOS`).
+
+## Core
+
+* Avalonia (.nupkg)
+  - Avalonia.Animation (.dll)
+  - Avalonia.Base (.dll)
+  - Avalonia.Controls (.dll)
+  - Avalonia.DesignerSupport (.dll)
+  - Avalonia.Diagnostics (.dll)
+  - Avalonia.Input (.dll)
+  - Avalonia.Interactivity (.dll)
+  - Avalonia.Layout (.dll)
+  - Avalonia.Logging.Serilog (.dll)
+  - Avalonia.SceneGraph (.dll)
+  - Avalonia.Styling (.dll)
+  - Avalonia.ReactiveUI (.dll)
+  - Avalonia.Themes.Default (.dll)
+  - Avalonia.Markup (.dll)
+  - Avalonia.Markup.Xaml (.dll)
+  - Serilog (.nupkg)
+  - Splat (.nupkg)
+  - Sprache (.nupkg)
+  - System.Reactive (.nupkg)
+
+* Avalonia.HtmlRenderer (.nupkg)
+  - Avalonia (.nupkg)
+
+## Desktop
+
+* Avalonia.Win32 (.nupkg)
+  - Avalonia.Win32 (.dll)
+  - Avalonia (.nupkg)
+
+* Avalonia.Direct2D1 (.nupkg)
+  - Avalonia.Direct2D1 (.dll)
+  - Avalonia (.nupkg)
+  - SharpDX (.nupkg)
+  - SharpDX.Direct2D1 (.nupkg)
+  - SharpDX.DXGI (.nupkg)
+
+* Avalonia.Gtk (.nupkg)
+  - Avalonia.Gtk (.dll)
+  - Avalonia (.nupkg)
+
+* Avalonia.Cairo (.nupkg)
+  - Avalonia.Cairo (.dll)
+  - Avalonia (.nupkg)
+
+* Avalonia.Skia.Desktop (.nupkg)
+  - Avalonia.Skia.Desktop (.dll)
+  - Avalonia (.nupkg)
+  - SkiaSharp (.nupkg)
+
+* Avalonia.Desktop (.nupkg)
+  - Avalonia.Win32 (.nupkg)
+  - Avalonia.Direct2D1 (.nupkg)
+  - Avalonia.Gtk (.nupkg)
+  - Avalonia.Cairo (.nupkg)
+  - Avalonia.Skia.Desktop (.nupkg)
+
+## Mobile
+
+* Avalonia.Android (.nupkg)
+  - Avalonia.Android (.dll)
+  - Avalonia.Skia.Android (.dll)
+  - Avalonia (.nupkg)
+  - SkiaSharp (.nupkg)
+
+* Avalonia.iOS (.nupkg)
+  - Avalonia.iOS (.dll)
+  - Avalonia.Skia.iOS (.dll)
+  - Avalonia (.nupkg)
+  - SkiaSharp (.nupkg)
+
+* Avalonia.Mobile (.nupkg)
+  - Avalonia.Android (.nupkg)
+  - Avalonia.iOS (.nupkg)

+ 4 - 0
docs/tutorial/toc.yml

@@ -1,2 +1,6 @@
 - name: Getting Started
 - name: Getting Started
   href: gettingstarted.md
   href: gettingstarted.md
+- name: Avalonia NuGet Packages
+  href: nuget.md
+- name: Avalonia for WPF Developers
+  href: from-wpf.md

+ 0 - 2
nuget/.gitignore

@@ -1,2 +0,0 @@
-Avalonia
-*.nupkg

+ 0 - 35
nuget/build-appveyor.ps1

@@ -1,35 +0,0 @@
-$ErrorActionPreference = "Stop"
-$scriptpath = $MyInvocation.MyCommand.Path
-$dir = Split-Path $scriptpath
-Push-Location $dir
-
-
-sv version $env:APPVEYOR_BUILD_NUMBER
-#sv version "1-debug"
-
-sv version 0.4.1-build$version-alpha
-sv key $env:myget_key
-
-. ".\include.ps1"
-.\build-version.ps1 $version
-
-sv reponame $env:APPVEYOR_REPO_NAME
-sv repobranch $env:APPVEYOR_REPO_BRANCH
-sv pullreq $env:APPVEYOR_PULL_REQUEST_NUMBER
-
-echo "Checking for publishing"
-echo "$reponame $repobranch $pullreq"
-if ([string]::IsNullOrWhiteSpace($pullreq))
-{
-    echo "Build is not a PR"
-    if($repobranch -eq "master")
-    {
-        echo "Repo branch matched"
-        foreach($pkg in $Packages)
-        {
-            nuget.exe push "$($pkg).$($version).nupkg" $key -Source https://www.myget.org/F/avalonia-ci/api/v2/package
-        }
-    }
-}
-
-

+ 0 - 84
nuget/build-version.ps1

@@ -1,84 +0,0 @@
-$ErrorActionPreference = "Stop"
-
-. ".\include.ps1"
-
-foreach($pkg in $Packages) 
-{
-    rm -Force -Recurse .\$pkg -ErrorAction SilentlyContinue
-}
-
-rm -Force -Recurse *.nupkg -ErrorAction SilentlyContinue
-Copy-Item template Avalonia -Recurse
-sv lib "Avalonia\lib\portable-windows8+net45"
-sv build "Avalonia.Desktop\lib\net45"
-
-sv skia_root "Avalonia.Skia.Desktop"
-sv skia_lib "Avalonia.Skia.Desktop\lib\net45"
-sv android "Avalonia.Android\lib\MonoAndroid10"
-sv ios "Avalonia.iOS\lib\Xamarin.iOS10"
-
-mkdir $lib -ErrorAction SilentlyContinue
-mkdir $build -ErrorAction SilentlyContinue
-mkdir $skia_lib
-mkdir $android
-mkdir $ios
-
-
-Copy-Item ..\src\Avalonia.Animation\bin\Release\Avalonia.Animation.dll $lib
-Copy-Item ..\src\Avalonia.Animation\bin\Release\Avalonia.Animation.xml $lib
-Copy-Item ..\src\Avalonia.Base\bin\Release\Avalonia.Base.dll $lib
-Copy-Item ..\src\Avalonia.Base\bin\Release\Avalonia.Base.xml $lib
-Copy-Item ..\src\Avalonia.Controls\bin\Release\Avalonia.Controls.dll $lib
-Copy-Item ..\src\Avalonia.Controls\bin\Release\Avalonia.Controls.xml $lib
-Copy-Item ..\src\Avalonia.DesignerSupport\bin\Release\Avalonia.DesignerSupport.dll $lib
-Copy-Item ..\src\Avalonia.DesignerSupport\bin\Release\Avalonia.DesignerSupport.xml $lib
-Copy-Item ..\src\Avalonia.Diagnostics\bin\Release\\Avalonia.Diagnostics.dll $lib
-Copy-Item ..\src\Avalonia.Diagnostics\bin\Release\\Avalonia.Diagnostics.xml $lib
-Copy-Item ..\src\Avalonia.Input\bin\Release\Avalonia.Input.dll $lib
-Copy-Item ..\src\Avalonia.Input\bin\Release\Avalonia.Input.xml $lib
-Copy-Item ..\src\Avalonia.Interactivity\bin\Release\Avalonia.Interactivity.dll $lib
-Copy-Item ..\src\Avalonia.Interactivity\bin\Release\Avalonia.Interactivity.xml $lib
-Copy-Item ..\src\Avalonia.Layout\bin\Release\Avalonia.Layout.dll $lib
-Copy-Item ..\src\Avalonia.Layout\bin\Release\Avalonia.Layout.xml $lib
-Copy-Item ..\src\Avalonia.Logging.Serilog\bin\Release\Avalonia.Logging.Serilog.dll $lib
-Copy-Item ..\src\Avalonia.Logging.Serilog\bin\Release\Avalonia.Logging.Serilog.xml $lib
-Copy-Item ..\src\Avalonia.SceneGraph\bin\Release\Avalonia.SceneGraph.dll $lib
-Copy-Item ..\src\Avalonia.SceneGraph\bin\Release\Avalonia.SceneGraph.xml $lib
-Copy-Item ..\src\Avalonia.Styling\bin\Release\Avalonia.Styling.dll $lib
-Copy-Item ..\src\Avalonia.Styling\bin\Release\Avalonia.Styling.xml $lib
-Copy-Item ..\src\Avalonia.Themes.Default\bin\Release\Avalonia.Themes.Default.dll $lib
-Copy-Item ..\src\Avalonia.Themes.Default\bin\Release\Avalonia.Themes.Default.xml $lib
-Copy-Item ..\src\Markup\Avalonia.Markup\bin\Release\Avalonia.Markup.dll $lib
-Copy-Item ..\src\Markup\Avalonia.Markup\bin\Release\Avalonia.Markup.xml $lib
-Copy-Item ..\src\Markup\Avalonia.Markup.Xaml\bin\Release\Avalonia.Markup.Xaml.dll $lib
-Copy-Item ..\src\Markup\Avalonia.Markup.Xaml\bin\Release\Avalonia.Markup.Xaml.xml $lib
-Copy-Item ..\src\Avalonia.HtmlRenderer\bin\Release\Avalonia.HtmlRenderer.dll $lib
-Copy-Item ..\src\Avalonia.ReactiveUI\bin\Release\Avalonia.ReactiveUI.dll $lib
-
-Copy-Item ..\src\Windows\Avalonia.Direct2D1\bin\Release\Avalonia.Direct2D1.dll $build
-Copy-Item ..\src\Windows\Avalonia.Win32\bin\Release\Avalonia.Win32.dll $build
-Copy-Item ..\src\Gtk\Avalonia.Gtk\bin\Release\Avalonia.Gtk.dll $build
-Copy-Item ..\src\Gtk\Avalonia.Cairo\bin\Release\Avalonia.Cairo.dll $build
-
-Copy-Item ..\src\Skia\Avalonia.Skia.Desktop\bin\x86\Release\Avalonia.Skia.Desktop.dll $skia_lib
-
-Copy-Item ..\src\Android\Avalonia.Android\bin\Release\Avalonia.Android.dll $android
-Copy-Item ..\src\Skia\Avalonia.Skia.Android\bin\Release\Avalonia.Skia.Android.dll $android
-
-Copy-Item ..\src\iOS\Avalonia.iOS\bin\iPhone\Release\Avalonia.iOS.dll $ios
-Copy-Item ..\src\Skia\Avalonia.Skia.iOS\bin\iPhone\Release\Avalonia.Skia.iOS.dll $ios
-
-foreach($pkg in $Packages)
-{
-    (gc Avalonia\$pkg.nuspec).replace('#VERSION#', $args[0]) | sc $pkg\$pkg.nuspec
-}
-
-foreach($pkg in $Packages)
-{
-    nuget.exe pack $pkg\$pkg.nuspec
-}
-
-foreach($pkg in $Packages)
-{
-    rm -Force -Recurse .\$pkg
-}

+ 0 - 1
nuget/build.ps1

@@ -1 +0,0 @@
-.\build-version.ps1 0.4.1

+ 0 - 1
nuget/include.ps1

@@ -1 +0,0 @@
-$Packages = @("Avalonia", "Avalonia.Desktop",  "Avalonia.Skia.Desktop", "Avalonia.Android", "Avalonia.iOS")

+ 0 - 27
nuget/template/Avalonia.Android.nuspec

@@ -1,27 +0,0 @@
-<?xml version="1.0"?>
-<package>
-  <metadata>
-    <id>Avalonia.Android</id>
-    <version>#VERSION#</version>
-    <authors>Avalonia Team</authors>
-    <owners>stevenk</owners>
-    <licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
-    <projectUrl>https://github.com/AvaloniaUI/Avalonia/</projectUrl>
-    <requireLicenseAcceptance>false</requireLicenseAcceptance>
-    <description>The Avalonia UI framework</description>
-    <releaseNotes></releaseNotes>
-    <copyright>Copyright 2015</copyright>
-    <tags>Avalonia</tags>
-    <dependencies>
-      <dependency id="Serilog" version="1.5.14" />
-      <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.50" />
-      <dependency id="System.Reactive" version="3.0.0" />
-      <dependency id="System.Reactive.Core" version="3.0.0" />
-      <dependency id="System.Reactive.Interfaces" version="3.0.0" />
-      <dependency id="System.Reactive.Linq" version="3.0.0" />
-      <dependency id="System.Reactive.PlatformServices" version="3.0.0" />
-      <dependency id="Avalonia" version="#VERSION#" />
-    </dependencies>
-  </metadata>
-</package>

+ 0 - 30
nuget/template/Avalonia.Desktop.nuspec

@@ -1,30 +0,0 @@
-<?xml version="1.0"?>
-<package>
-  <metadata>
-    <id>Avalonia.Desktop</id>
-    <version>#VERSION#</version>
-    <authors>Avalonia Team</authors>
-    <owners>stevenk</owners>
-    <licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
-    <projectUrl>https://github.com/AvaloniaUI/Avalonia/</projectUrl>
-    <requireLicenseAcceptance>false</requireLicenseAcceptance>
-    <description>The Avalonia UI framework</description>
-    <releaseNotes></releaseNotes>
-    <copyright>Copyright 2015</copyright>
-    <tags>Avalonia</tags>
-    <dependencies>
-      <dependency id="Serilog" version="1.5.14" />
-      <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.50" />
-      <dependency id="System.Reactive" version="3.0.0" />
-      <dependency id="System.Reactive.Core" version="3.0.0" />
-      <dependency id="System.Reactive.Interfaces" version="3.0.0" />
-      <dependency id="System.Reactive.Linq" version="3.0.0" />
-      <dependency id="System.Reactive.PlatformServices" version="3.0.0" />
-      <dependency id="SharpDX" version="3.0.2"/>
-      <dependency id="SharpDX.Direct2D1" version="3.0.2"/>
-      <dependency id="SharpDX.DXGI" version="3.0.2"/>
-      <dependency id="Avalonia" version="#VERSION#" />
-    </dependencies>
-  </metadata>
-</package>

+ 0 - 20
nuget/template/Avalonia.Skia.Desktop.nuspec

@@ -1,20 +0,0 @@
-<?xml version="1.0"?>
-<package>
-  <metadata>
-    <id>Avalonia.Skia.Desktop</id>
-    <version>#VERSION#</version>
-    <authors>Avalonia Team</authors>
-    <owners>stevenk</owners>
-    <licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
-    <projectUrl>https://github.com/AvaloniaUI/Avalonia/</projectUrl>
-    <requireLicenseAcceptance>false</requireLicenseAcceptance>
-    <description>The Avalonia UI framework</description>
-    <releaseNotes></releaseNotes>
-    <copyright>Copyright 2015</copyright>
-    <tags>Avalonia</tags>
-    <dependencies>
-      <dependency id="SkiaSharp" version="1.53.0"/>
-      <dependency id="Avalonia" version="#VERSION#" />
-    </dependencies>
-  </metadata>
-</package>

+ 0 - 27
nuget/template/Avalonia.iOS.nuspec

@@ -1,27 +0,0 @@
-<?xml version="1.0"?>
-<package>
-  <metadata>
-    <id>Avalonia.iOS</id>
-    <version>#VERSION#</version>
-    <authors>Avalonia Team</authors>
-    <owners>stevenk</owners>
-    <licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
-    <projectUrl>https://github.com/AvaloniaUI/Avalonia/</projectUrl>
-    <requireLicenseAcceptance>false</requireLicenseAcceptance>
-    <description>The Avalonia UI framework</description>
-    <releaseNotes></releaseNotes>
-    <copyright>Copyright 2015</copyright>
-    <tags>Avalonia</tags>
-    <dependencies>
-      <dependency id="Serilog" version="1.5.14" />
-      <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.50" />
-      <dependency id="System.Reactive" version="3.0.0" />
-      <dependency id="System.Reactive.Core" version="3.0.0" />
-      <dependency id="System.Reactive.Interfaces" version="3.0.0" />
-      <dependency id="System.Reactive.Linq" version="3.0.0" />
-      <dependency id="System.Reactive.PlatformServices" version="3.0.0" />
-      <dependency id="Avalonia" version="#VERSION#" />
-    </dependencies>
-  </metadata>
-</package>

+ 0 - 26
nuget/template/Avalonia.nuspec

@@ -1,26 +0,0 @@
-<?xml version="1.0"?>
-<package>
-  <metadata>
-    <id>Avalonia</id>
-    <version>#VERSION#</version>
-    <authors>Avalonia Team</authors>
-    <owners>stevenk</owners>
-    <licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
-    <projectUrl>https://github.com/AvaloniaUI/Avalonia/</projectUrl>
-    <requireLicenseAcceptance>false</requireLicenseAcceptance>
-    <description>The Avalonia UI framework</description>
-    <releaseNotes>Initial alpha release.</releaseNotes>
-    <copyright>Copyright 2015</copyright>
-    <tags>Avalonia</tags>
-    <dependencies>
-      <dependency id="Serilog" version="1.5.14" />
-      <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.50" />
-      <dependency id="System.Reactive" version="3.0.0" />
-      <dependency id="System.Reactive.Core" version="3.0.0" />
-      <dependency id="System.Reactive.Interfaces" version="3.0.0" />
-      <dependency id="System.Reactive.Linq" version="3.0.0" />
-      <dependency id="System.Reactive.PlatformServices" version="3.0.0" />
-    </dependencies>
-  </metadata>
-</package>

+ 8 - 1
samples/BindingTest/BindingTest.csproj

@@ -50,6 +50,7 @@
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System" />
+    <Reference Include="System.ComponentModel.DataAnnotations" />
     <Reference Include="System.Core" />
     <Reference Include="System.Core" />
     <Reference Include="System.Reactive.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
     <Reference Include="System.Reactive.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
       <HintPath>..\..\packages\System.Reactive.Core.3.0.0\lib\net45\System.Reactive.Core.dll</HintPath>
       <HintPath>..\..\packages\System.Reactive.Core.3.0.0\lib\net45\System.Reactive.Core.dll</HintPath>
@@ -80,7 +81,9 @@
     <Compile Include="TestItemView.xaml.cs">
     <Compile Include="TestItemView.xaml.cs">
       <DependentUpon>TestItemView.xaml</DependentUpon>
       <DependentUpon>TestItemView.xaml</DependentUpon>
     </Compile>
     </Compile>
-    <Compile Include="ViewModels\ExceptionPropertyErrorViewModel.cs" />
+    <Compile Include="ViewModels\DataAnnotationsErrorViewModel.cs" />
+    <Compile Include="ViewModels\IndeiErrorViewModel.cs" />
+    <Compile Include="ViewModels\ExceptionErrorViewModel.cs" />
     <Compile Include="ViewModels\MainWindowViewModel.cs" />
     <Compile Include="ViewModels\MainWindowViewModel.cs" />
     <Compile Include="ViewModels\TestItem.cs" />
     <Compile Include="ViewModels\TestItem.cs" />
   </ItemGroup>
   </ItemGroup>
@@ -93,6 +96,10 @@
     <EmbeddedResource Include="TestItemView.xaml" />
     <EmbeddedResource Include="TestItemView.xaml" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
       <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
       <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
       <Name>Avalonia.Markup.Xaml</Name>
       <Name>Avalonia.Markup.Xaml</Name>

+ 12 - 2
samples/BindingTest/MainWindow.xaml

@@ -70,9 +70,19 @@
     </TabItem>
     </TabItem>
     <TabItem Header="Property Validation">
     <TabItem Header="Property Validation">
       <StackPanel Orientation="Horizontal">
       <StackPanel Orientation="Horizontal">
-        <StackPanel Margin="18" Gap="4" Width="200" DataContext="{Binding ExceptionPropertyValidation}">
+        <StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding ExceptionDataValidation}">
           <TextBlock FontSize="16" Text="Exception Validation"/>
           <TextBlock FontSize="16" Text="Exception Validation"/>
-          <TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10, EnableValidation=True}"/>
+          <TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
+        </StackPanel>
+        <StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding IndeiDataValidation}">
+          <TextBlock FontSize="16" Text="INotifyDataErrorInfo Validation"/>
+          <TextBox Watermark="Maximum" UseFloatingWatermark="True" Text="{Binding Path=Maximum}"/>
+          <TextBox Watermark="Value" UseFloatingWatermark="True" Text="{Binding Path=Value}"/>
+        </StackPanel>
+        <StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding DataAnnotationsValidation}">
+          <TextBlock FontSize="16" Text="Data Annotations Validation"/>
+          <TextBox Watermark="Phone #" UseFloatingWatermark="True" Text="{Binding PhoneNumber}"/>
+          <TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
         </StackPanel>
         </StackPanel>
       </StackPanel>
       </StackPanel>
     </TabItem>
     </TabItem>

+ 17 - 0
samples/BindingTest/ViewModels/DataAnnotationsErrorViewModel.cs

@@ -0,0 +1,17 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System.ComponentModel.DataAnnotations;
+
+namespace BindingTest.ViewModels
+{
+    public class DataAnnotationsErrorViewModel
+    {
+        [Phone]
+        [MaxLength(10)]
+        public string PhoneNumber { get; set; }
+
+        [Range(0, 9)]
+        public int LessThan10 { get; set; }
+    }
+}

+ 2 - 2
samples/BindingTest/ViewModels/ExceptionPropertyErrorViewModel.cs → samples/BindingTest/ViewModels/ExceptionErrorViewModel.cs

@@ -6,7 +6,7 @@ using System;
 
 
 namespace BindingTest.ViewModels
 namespace BindingTest.ViewModels
 {
 {
-    public class ExceptionPropertyErrorViewModel : ReactiveObject
+    public class ExceptionErrorViewModel : ReactiveObject
     {
     {
         private int _lessThan10;
         private int _lessThan10;
 
 
@@ -21,7 +21,7 @@ namespace BindingTest.ViewModels
                 }
                 }
                 else
                 else
                 {
                 {
-                    throw new InvalidOperationException("Value must be less than 10.");
+                    throw new ArgumentOutOfRangeException("Value must be less than 10.");
                 }
                 }
             }
             }
         }
         }

+ 73 - 0
samples/BindingTest/ViewModels/IndeiErrorViewModel.cs

@@ -0,0 +1,73 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using ReactiveUI;
+using System;
+using System.ComponentModel;
+using System.Collections;
+
+namespace BindingTest.ViewModels
+{
+    public class IndeiErrorViewModel : ReactiveObject, INotifyDataErrorInfo
+    {
+        private int _maximum = 10;
+        private int _value;
+        private string _valueError;
+
+        public IndeiErrorViewModel()
+        {
+            this.WhenAnyValue(x => x.Maximum, x => x.Value)
+                .Subscribe(_ => UpdateErrors());
+        }
+
+        public bool HasErrors
+        {
+            get { throw new NotImplementedException(); }
+        }
+
+        public int Maximum
+        {
+            get { return _maximum; }
+            set { this.RaiseAndSetIfChanged(ref _maximum, value); }
+        }
+
+        public int Value
+        {
+            get { return _value; }
+            set { this.RaiseAndSetIfChanged(ref _value, value); }
+        }
+
+        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
+
+        public IEnumerable GetErrors(string propertyName)
+        {
+            switch (propertyName)
+            {
+                case nameof(Value):
+                    return new[] { _valueError };
+                default:
+                    return null;
+            }
+        }
+
+        private void UpdateErrors()
+        {
+            if (Value <= Maximum)
+            {
+                if (_valueError != null)
+                {
+                    _valueError = null;
+                    ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Value)));
+                }
+            }
+            else
+            {
+                if (_valueError == null)
+                {
+                    _valueError = "Value must be less than Maximum";
+                    ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Value)));
+                }
+            }
+        }
+    }
+}

+ 3 - 2
samples/BindingTest/ViewModels/MainWindowViewModel.cs

@@ -69,7 +69,8 @@ namespace BindingTest.ViewModels
 
 
         public ReactiveCommand<object> StringValueCommand { get; }
         public ReactiveCommand<object> StringValueCommand { get; }
 
 
-        public ExceptionPropertyErrorViewModel ExceptionPropertyValidation { get; }
-            = new ExceptionPropertyErrorViewModel();
+        public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel();
+        public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel();
+        public IndeiErrorViewModel IndeiDataValidation { get; } = new IndeiErrorViewModel();
     }
     }
 }
 }

+ 4 - 4
samples/ControlCatalog.Desktop/App.config

@@ -1,13 +1,13 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
 <configuration>
     <startup> 
     <startup> 
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
     </startup>
     </startup>
   <runtime>
   <runtime>
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
       <dependentAssembly>
-        <assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral"/>
-        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
+        <assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
       </dependentAssembly>
       </dependentAssembly>
     </assemblyBinding>
     </assemblyBinding>
   </runtime>
   </runtime>

+ 4 - 0
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@@ -64,6 +64,10 @@
       <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
       <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
       <Name>Avalonia.DesignerSupport</Name>
       <Name>Avalonia.DesignerSupport</Name>
     </ProjectReference>
     </ProjectReference>
+    <ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Gtk\Avalonia.Cairo\Avalonia.Cairo.csproj">
     <ProjectReference Include="..\..\src\Gtk\Avalonia.Cairo\Avalonia.Cairo.csproj">
       <Project>{FB05AC90-89BA-4F2F-A924-F37875FB547C}</Project>
       <Project>{FB05AC90-89BA-4F2F-A924-F37875FB547C}</Project>
       <Name>Avalonia.Cairo</Name>
       <Name>Avalonia.Cairo</Name>

+ 11 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -38,7 +38,7 @@
     <EmbeddedResource Include="App.xaml">
     <EmbeddedResource Include="App.xaml">
       <SubType>Designer</SubType>
       <SubType>Designer</SubType>
     </EmbeddedResource>
     </EmbeddedResource>
-    <EmbeddedResource Include="MainWindow.xaml">
+    <EmbeddedResource Include="MainView.xaml">
       <SubType>Designer</SubType>
       <SubType>Designer</SubType>
     </EmbeddedResource>
     </EmbeddedResource>
     <EmbeddedResource Include="Pages\BorderPage.xaml">
     <EmbeddedResource Include="Pages\BorderPage.xaml">
@@ -88,6 +88,9 @@
     <Compile Include="App.xaml.cs">
     <Compile Include="App.xaml.cs">
       <DependentUpon>App.xaml</DependentUpon>
       <DependentUpon>App.xaml</DependentUpon>
     </Compile>
     </Compile>
+    <Compile Include="MainView.xaml.cs">
+      <DependentUpon>MainView.xaml</DependentUpon>
+    </Compile>
     <Compile Include="MainWindow.xaml.cs">
     <Compile Include="MainWindow.xaml.cs">
       <DependentUpon>MainWindow.xaml</DependentUpon>
       <DependentUpon>MainWindow.xaml</DependentUpon>
     </Compile>
     </Compile>
@@ -215,6 +218,13 @@
       <SubType>Designer</SubType>
       <SubType>Designer</SubType>
     </EmbeddedResource>
     </EmbeddedResource>
   </ItemGroup>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>
+      </SubType>
+    </EmbeddedResource>
+  </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
        Other similar extension points exist, see Microsoft.Common.targets.

+ 24 - 0
samples/ControlCatalog/MainView.xaml

@@ -0,0 +1,24 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+        xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <TabControl Classes="sidebar">
+    <TabControl.Transition>
+      <CrossFade Duration="0.25"/>
+    </TabControl.Transition>
+    <TabItem Header="Border"><pages:BorderPage/></TabItem>
+    <TabItem Header="Button"><pages:ButtonPage/></TabItem>
+    <TabItem Header="Canvas"><pages:CanvasPage/></TabItem>
+    <TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
+    <TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>
+    <TabItem Header="DropDown"><pages:DropDownPage/></TabItem>
+    <TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
+    <TabItem Header="Image"><pages:ImagePage/></TabItem>
+    <TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
+    <TabItem Header="Menu"><pages:MenuPage/></TabItem>
+    <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
+    <TabItem Header="Slider"><pages:SliderPage/></TabItem>
+    <TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
+    <TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
+    <TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
+  </TabControl>
+</UserControl>

+ 19 - 0
samples/ControlCatalog/MainView.xaml.cs

@@ -0,0 +1,19 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog
+{
+    public class MainView : UserControl
+    {
+        public MainView()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+}

+ 4 - 24
samples/ControlCatalog/MainWindow.xaml

@@ -1,26 +1,6 @@
-<Window xmlns="https://github.com/avaloniaui"
-        xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
         Title="Avalonia Control Gallery"
         Title="Avalonia Control Gallery"
-        Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog">
-  <TabControl Classes="sidebar">
-    <TabControl.Transition>
-      <CrossFade Duration="0.25"/>
-    </TabControl.Transition>
-    <TabItem Header="Border"><pages:BorderPage/></TabItem>
-    <TabItem Header="Button"><pages:ButtonPage/></TabItem>
-    <TabItem Header="Canvas"><pages:CanvasPage/></TabItem>
-    <TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
-    <TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>
-    <TabItem Header="DropDown"><pages:DropDownPage/></TabItem>
-    <TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
-    <TabItem Header="Image"><pages:ImagePage/></TabItem>
-    <TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
-    <TabItem Header="Menu"><pages:MenuPage/></TabItem>
-    <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
-    <TabItem Header="Slider"><pages:SliderPage/></TabItem>
-    <TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
-    <TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
-    <TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
-  </TabControl>
+        Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
+        xmlns:local="clr-namespace:ControlCatalog;assembly=ControlCatalog">
+    <local:MainView/>
 </Window>
 </Window>

+ 1 - 1
samples/ControlCatalog/Pages/CanvasPage.xaml

@@ -13,7 +13,7 @@
           </LinearGradientBrush>
           </LinearGradientBrush>
         </Rectangle.OpacityMask>      </Rectangle>
         </Rectangle.OpacityMask>      </Rectangle>
       <Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/>
       <Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/>
-      <Path Fill="Orange" Data="M 0,0 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
+      <Path Fill="Orange" Data="M 0,0 c 0,0 50,0 50,-50 c 0,0 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
       <Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">
       <Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">
         <Path.Data>
         <Path.Data>
           <PathGeometry>
           <PathGeometry>

+ 4 - 0
samples/TestApplication/TestApplication.csproj

@@ -83,6 +83,10 @@
     <None Include="packages.config" />
     <None Include="packages.config" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Gtk\Avalonia.Cairo\Avalonia.Cairo.csproj">
     <ProjectReference Include="..\..\src\Gtk\Avalonia.Cairo\Avalonia.Cairo.csproj">
       <Project>{FB05AC90-89BA-4F2F-A924-F37875FB547C}</Project>
       <Project>{FB05AC90-89BA-4F2F-A924-F37875FB547C}</Project>
       <Name>Avalonia.Cairo</Name>
       <Name>Avalonia.Cairo</Name>

+ 4 - 4
samples/VirtualizationTest/App.config

@@ -1,13 +1,13 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
 <configuration>
     <startup> 
     <startup> 
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
     </startup>
     </startup>
   <runtime>
   <runtime>
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
       <dependentAssembly>
-        <assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral"/>
-        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
+        <assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
       </dependentAssembly>
       </dependentAssembly>
     </assemblyBinding>
     </assemblyBinding>
   </runtime>
   </runtime>

+ 4 - 0
samples/VirtualizationTest/VirtualizationTest.csproj

@@ -103,6 +103,10 @@
       <Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
       <Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
       <Name>Avalonia.Diagnostics</Name>
       <Name>Avalonia.Diagnostics</Name>
     </ProjectReference>
     </ProjectReference>
+    <ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
     <ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
       <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
       <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
       <Name>Avalonia.Input</Name>
       <Name>Avalonia.Input</Name>

+ 6 - 2
samples/XamlTestApplication/XamlTestApplication.csproj

@@ -48,8 +48,8 @@
       <HintPath>..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll</HintPath>
       <HintPath>..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
-    <Reference Include="Sprache, Version=2.0.0.50, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\..\packages\Sprache.2.0.0.50\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid+Xamarin.iOS10+MonoTouch\Sprache.dll</HintPath>
+    <Reference Include="Sprache, Version=2.0.0.51, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Sprache.2.0.0.51\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid+Xamarin.iOS10+MonoTouch\Sprache.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System" />
@@ -91,6 +91,10 @@
     <None Include="App.config" />
     <None Include="App.config" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
       <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
       <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
       <Name>Avalonia.Markup</Name>
       <Name>Avalonia.Markup</Name>

+ 1 - 1
samples/XamlTestApplication/packages.config

@@ -2,7 +2,7 @@
 <packages>
 <packages>
   <package id="Serilog" version="1.5.14" targetFramework="net45" />
   <package id="Serilog" version="1.5.14" targetFramework="net45" />
   <package id="Splat" version="1.6.2" targetFramework="net45" />
   <package id="Splat" version="1.6.2" targetFramework="net45" />
-  <package id="Sprache" version="2.0.0.50" targetFramework="net45" />
+  <package id="Sprache" version="2.0.0.51" targetFramework="net45" />
   <package id="System.Reactive" version="3.0.0" targetFramework="net45" />
   <package id="System.Reactive" version="3.0.0" targetFramework="net45" />
   <package id="System.Reactive.Core" version="3.0.0" targetFramework="net45" />
   <package id="System.Reactive.Core" version="3.0.0" targetFramework="net45" />
   <package id="System.Reactive.Interfaces" version="3.0.0" targetFramework="net45" />
   <package id="System.Reactive.Interfaces" version="3.0.0" targetFramework="net45" />

+ 6 - 0
samples/interop/GtkInteropDemo/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
+    </startup>
+</configuration>

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

@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{BD7F352C-6DC1-4740-BAF2-2D34A038728C}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>GtkInteropDemo</RootNamespace>
+    <AssemblyName>GtkInteropDemo</AssemblyName>
+    <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f, processorArchitecture=MSIL" />
+    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f, processorArchitecture=MSIL" />
+    <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f, processorArchitecture=MSIL" />
+    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f, processorArchitecture=MSIL" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MainWindow.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
+      <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
+      <Name>Avalonia.Animation</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Base\Avalonia.Base.csproj">
+      <Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
+      <Name>Avalonia.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
+      <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
+      <Name>Avalonia.Controls</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
+      <Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
+      <Name>Avalonia.Diagnostics</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Input\Avalonia.Input.csproj">
+      <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
+      <Name>Avalonia.Input</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
+      <Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
+      <Name>Avalonia.Interactivity</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
+      <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
+      <Name>Avalonia.Layout</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj">
+      <Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
+      <Name>Avalonia.ReactiveUI</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.SceneGraph\Avalonia.SceneGraph.csproj">
+      <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
+      <Name>Avalonia.SceneGraph</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
+      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
+      <Name>Avalonia.Styling</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
+      <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
+      <Name>Avalonia.Themes.Default</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Gtk\Avalonia.Cairo\Avalonia.Cairo.csproj">
+      <Project>{fb05ac90-89ba-4f2f-a924-f37875fb547c}</Project>
+      <Name>Avalonia.Cairo</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Gtk\Avalonia.Gtk\Avalonia.Gtk.csproj">
+      <Project>{54f237d5-a70a-4752-9656-0c70b1a7b047}</Project>
+      <Name>Avalonia.Gtk</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
+      <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
+      <Name>Avalonia.Markup.Xaml</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
+      <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
+      <Name>Avalonia.Markup</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Skia\Avalonia.Skia.Desktop\Avalonia.Skia.Desktop.csproj">
+      <Project>{925dd807-b651-475f-9f7c-cbeb974ce43d}</Project>
+      <Name>Avalonia.Skia.Desktop</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\ControlCatalog\ControlCatalog.csproj">
+      <Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
+      <Name>ControlCatalog</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 26 - 0
samples/interop/GtkInteropDemo/GtkInteropDemo.v2.ncrunchproject

@@ -0,0 +1,26 @@
+<ProjectConfiguration>
+  <AutoDetectNugetBuildDependencies>true</AutoDetectNugetBuildDependencies>
+  <BuildPriority>1000</BuildPriority>
+  <CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
+  <ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
+  <PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
+  <AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
+  <AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
+  <AllowCodeAnalysis>false</AllowCodeAnalysis>
+  <IgnoreThisComponentCompletely>true</IgnoreThisComponentCompletely>
+  <RunPreBuildEvents>false</RunPreBuildEvents>
+  <RunPostBuildEvents>false</RunPostBuildEvents>
+  <PreviouslyBuiltSuccessfully>false</PreviouslyBuiltSuccessfully>
+  <InstrumentAssembly>true</InstrumentAssembly>
+  <PreventSigningOfAssembly>false</PreventSigningOfAssembly>
+  <AnalyseExecutionTimes>true</AnalyseExecutionTimes>
+  <DetectStackOverflow>true</DetectStackOverflow>
+  <IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
+  <DefaultTestTimeout>60000</DefaultTestTimeout>
+  <UseBuildConfiguration />
+  <UseBuildPlatform />
+  <ProxyProcessPath />
+  <UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
+  <MSTestThreadApartmentState>STA</MSTestThreadApartmentState>
+  <BuildProcessArchitecture>x86</BuildProcessArchitecture>
+</ProjectConfiguration>

+ 30 - 0
samples/interop/GtkInteropDemo/MainWindow.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Diagnostics;
+using Avalonia.Gtk.Embedding;
+using ControlCatalog;
+using Gtk;
+
+namespace GtkInteropDemo
+{
+    class MainWindow : Window
+    {
+        public MainWindow() : base("Gtk Embedding Demo")
+        {
+            var root = new HBox();
+            var left  = new VBox();
+            left.Add(new Button("I'm GTK button"));
+            left.Add(new Calendar());
+            root.PackEnd(left, false, false, 0);
+            var host = new GtkAvaloniaControlHost() {Content = new MainView()};
+            host.SetSizeRequest(600, 600);
+            root.PackStart(host, true, true, 0);
+            Add(root);
+            
+            ShowAll();
+        }
+    }
+}

+ 24 - 0
samples/interop/GtkInteropDemo/Program.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia;
+using Avalonia.Controls;
+using ControlCatalog;
+
+namespace GtkInteropDemo
+{
+    static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            AppBuilder.Configure<App>().UseGtk().UseCairo().SetupWithoutStarting();
+            new MainWindow().Show();
+            Gtk.Application.Run();
+        }
+    }
+}

+ 36 - 0
samples/interop/GtkInteropDemo/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("GtkInteropDemo")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("GtkInteropDemo")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("bd7f352c-6dc1-4740-baf2-2d34a038728c")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 71 - 0
samples/interop/GtkInteropDemo/Properties/Resources.Designer.cs

@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GtkInteropDemo.Properties
+{
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GtkInteropDemo.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
samples/interop/GtkInteropDemo/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 30 - 0
samples/interop/GtkInteropDemo/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GtkInteropDemo.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
samples/interop/GtkInteropDemo/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 6 - 0
samples/interop/WindowsInteropTest/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
+    </startup>
+</configuration>

+ 119 - 0
samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.Designer.cs

@@ -0,0 +1,119 @@
+using Avalonia.Win32.Embedding;
+
+namespace WindowsInteropTest
+{
+    partial class EmbedToWinFormsDemo
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.button1 = new System.Windows.Forms.Button();
+            this.monthCalendar1 = new System.Windows.Forms.MonthCalendar();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.avaloniaHost = new WinFormsAvaloniaControlHost();
+            this.groupBox1.SuspendLayout();
+            this.groupBox2.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // button1
+            // 
+            this.button1.Location = new System.Drawing.Point(28, 29);
+            this.button1.Name = "button1";
+            this.button1.Size = new System.Drawing.Size(164, 73);
+            this.button1.TabIndex = 0;
+            this.button1.Text = "button1";
+            this.button1.UseVisualStyleBackColor = true;
+            // 
+            // monthCalendar1
+            // 
+            this.monthCalendar1.Location = new System.Drawing.Point(28, 114);
+            this.monthCalendar1.Name = "monthCalendar1";
+            this.monthCalendar1.TabIndex = 1;
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+            this.groupBox1.Controls.Add(this.button1);
+            this.groupBox1.Controls.Add(this.monthCalendar1);
+            this.groupBox1.Location = new System.Drawing.Point(12, 12);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(227, 418);
+            this.groupBox1.TabIndex = 2;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "WinForms";
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.groupBox2.Controls.Add(this.avaloniaHost);
+            this.groupBox2.Location = new System.Drawing.Point(245, 12);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(501, 418);
+            this.groupBox2.TabIndex = 3;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "Avalonia";
+            // 
+            // avaloniaHost
+            // 
+            this.avaloniaHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.avaloniaHost.Content = null;
+            this.avaloniaHost.Location = new System.Drawing.Point(6, 19);
+            this.avaloniaHost.Name = "avaloniaHost";
+            this.avaloniaHost.Size = new System.Drawing.Size(489, 393);
+            this.avaloniaHost.TabIndex = 0;
+            this.avaloniaHost.Text = "avaloniaHost";
+            // 
+            // EmbedToWinFormsDemo
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(758, 442);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.groupBox1);
+            this.MinimumSize = new System.Drawing.Size(600, 400);
+            this.Name = "EmbedToWinFormsDemo";
+            this.Text = "EmbedToWinFormsDemo";
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox2.ResumeLayout(false);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Button button1;
+        private System.Windows.Forms.MonthCalendar monthCalendar1;
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private WinFormsAvaloniaControlHost avaloniaHost;
+    }
+}

+ 23 - 0
samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Avalonia.Controls;
+using ControlCatalog;
+
+namespace WindowsInteropTest
+{
+    public partial class EmbedToWinFormsDemo : Form
+    {
+        public EmbedToWinFormsDemo()
+        {
+            InitializeComponent();
+            avaloniaHost.Content = new MainView();
+        }
+    }
+}

+ 120 - 0
samples/interop/WindowsInteropTest/EmbedToWinFormsDemo.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 21 - 0
samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml

@@ -0,0 +1,21 @@
+<Window x:Class="WindowsInteropTest.EmbedToWpfDemo"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:WindowsInteropTest"
+             xmlns:embedding="clr-namespace:Avalonia.Win32.Embedding;assembly=Avalonia.Win32"
+             mc:Ignorable="d" 
+             d:DesignHeight="400" d:DesignWidth="400" MinWidth="500" MinHeight="400">
+    <DockPanel>
+        <GroupBox DockPanel.Dock="Left" Header="WPF">
+            <StackPanel>
+                <Slider/>
+                <Calendar/>
+            </StackPanel>
+        </GroupBox>
+        <GroupBox Header="Avalonia">
+            <embedding:WpfAvaloniaControlHost x:Name="Host"/>
+        </GroupBox>
+    </DockPanel>
+</Window>

+ 31 - 0
samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Avalonia.Controls;
+using ControlCatalog;
+using Window = System.Windows.Window;
+
+namespace WindowsInteropTest
+{
+    /// <summary>
+    /// Interaction logic for EmbedToWpfDemo.xaml
+    /// </summary>
+    public partial class EmbedToWpfDemo : Window
+    {
+        public EmbedToWpfDemo()
+        {
+            InitializeComponent();
+            Host.Content =  new MainView();
+        }
+    }
+}

+ 22 - 0
samples/interop/WindowsInteropTest/Program.cs

@@ -0,0 +1,22 @@
+using System;
+using Avalonia.Controls;
+using ControlCatalog;
+using Avalonia;
+
+namespace WindowsInteropTest
+{
+    static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            System.Windows.Forms.Application.EnableVisualStyles();
+            System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
+            AppBuilder.Configure<App>().UseWin32().UseSkia().SetupWithoutStarting();
+            System.Windows.Forms.Application.Run(new SelectorForm());
+        }
+    }
+}

+ 36 - 0
samples/interop/WindowsInteropTest/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("WindowsInteropTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WindowsInteropTest")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c7a69145-60b6-4882-97d6-a3921dd43978")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 71 - 0
samples/interop/WindowsInteropTest/Properties/Resources.Designer.cs

@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace WindowsInteropTest.Properties
+{
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowsInteropTest.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
samples/interop/WindowsInteropTest/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 30 - 0
samples/interop/WindowsInteropTest/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace WindowsInteropTest.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
samples/interop/WindowsInteropTest/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 76 - 0
samples/interop/WindowsInteropTest/SelectorForm.Designer.cs

@@ -0,0 +1,76 @@
+namespace WindowsInteropTest
+{
+    partial class SelectorForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.btnEmbedToWinForms = new System.Windows.Forms.Button();
+            this.btnEmbedToWpf = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // btnEmbedToWinForms
+            // 
+            this.btnEmbedToWinForms.Location = new System.Drawing.Point(12, 12);
+            this.btnEmbedToWinForms.Name = "btnEmbedToWinForms";
+            this.btnEmbedToWinForms.Size = new System.Drawing.Size(201, 86);
+            this.btnEmbedToWinForms.TabIndex = 0;
+            this.btnEmbedToWinForms.Text = "Embed to WinForms";
+            this.btnEmbedToWinForms.UseVisualStyleBackColor = true;
+            this.btnEmbedToWinForms.Click += new System.EventHandler(this.btnEmbedToWinForms_Click);
+            // 
+            // btnEmbedToWpf
+            // 
+            this.btnEmbedToWpf.Location = new System.Drawing.Point(219, 12);
+            this.btnEmbedToWpf.Name = "btnEmbedToWpf";
+            this.btnEmbedToWpf.Size = new System.Drawing.Size(201, 86);
+            this.btnEmbedToWpf.TabIndex = 1;
+            this.btnEmbedToWpf.Text = "Embed to WPF";
+            this.btnEmbedToWpf.UseVisualStyleBackColor = true;
+            this.btnEmbedToWpf.Click += new System.EventHandler(this.btnEmbedToWpf_Click);
+            // 
+            // SelectorForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(432, 284);
+            this.Controls.Add(this.btnEmbedToWpf);
+            this.Controls.Add(this.btnEmbedToWinForms);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+            this.MaximizeBox = false;
+            this.Name = "SelectorForm";
+            this.Text = "Interop";
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Button btnEmbedToWinForms;
+        private System.Windows.Forms.Button btnEmbedToWpf;
+    }
+}
+

+ 30 - 0
samples/interop/WindowsInteropTest/SelectorForm.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace WindowsInteropTest
+{
+    public partial class SelectorForm : Form
+    {
+        public SelectorForm()
+        {
+            InitializeComponent();
+        }
+
+        private void btnEmbedToWinForms_Click(object sender, EventArgs e)
+        {
+            new EmbedToWinFormsDemo().ShowDialog(this);
+        }
+
+        private void btnEmbedToWpf_Click(object sender, EventArgs e)
+        {
+            new EmbedToWpfDemo().ShowDialog();
+        }
+    }
+}

+ 120 - 0
samples/interop/WindowsInteropTest/SelectorForm.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

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

@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{C7A69145-60B6-4882-97D6-A3921DD43978}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>WindowsInteropTest</RootNamespace>
+    <AssemblyName>WindowsInteropTest</AssemblyName>
+    <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xaml" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+    <Reference Include="WindowsBase" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="EmbedToWinFormsDemo.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="EmbedToWinFormsDemo.Designer.cs">
+      <DependentUpon>EmbedToWinFormsDemo.cs</DependentUpon>
+    </Compile>
+    <Compile Include="EmbedToWpfDemo.xaml.cs">
+      <DependentUpon>EmbedToWpfDemo.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="SelectorForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="SelectorForm.Designer.cs">
+      <DependentUpon>SelectorForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <EmbeddedResource Include="EmbedToWinFormsDemo.resx">
+      <DependentUpon>EmbedToWinFormsDemo.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <EmbeddedResource Include="SelectorForm.resx">
+      <DependentUpon>SelectorForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
+      <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
+      <Name>Avalonia.Animation</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Base\Avalonia.Base.csproj">
+      <Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
+      <Name>Avalonia.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
+      <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
+      <Name>Avalonia.Controls</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj">
+      <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
+      <Name>Avalonia.DesignerSupport</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
+      <Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
+      <Name>Avalonia.Diagnostics</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Input\Avalonia.Input.csproj">
+      <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
+      <Name>Avalonia.Input</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
+      <Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
+      <Name>Avalonia.Interactivity</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
+      <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
+      <Name>Avalonia.Layout</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj">
+      <Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
+      <Name>Avalonia.ReactiveUI</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.SceneGraph\Avalonia.SceneGraph.csproj">
+      <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
+      <Name>Avalonia.SceneGraph</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
+      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
+      <Name>Avalonia.Styling</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
+      <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
+      <Name>Avalonia.Themes.Default</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
+      <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
+      <Name>Avalonia.Markup.Xaml</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
+      <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
+      <Name>Avalonia.Markup</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Skia\Avalonia.Skia.Desktop\Avalonia.Skia.Desktop.csproj">
+      <Project>{925dd807-b651-475f-9f7c-cbeb974ce43d}</Project>
+      <Name>Avalonia.Skia.Desktop</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj">
+      <Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project>
+      <Name>Avalonia.Direct2D1</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj">
+      <Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project>
+      <Name>Avalonia.Win32</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\ControlCatalog\ControlCatalog.csproj">
+      <Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
+      <Name>ControlCatalog</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="EmbedToWpfDemo.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 26 - 0
samples/interop/WindowsInteropTest/WindowsInteropTest.v2.ncrunchproject

@@ -0,0 +1,26 @@
+<ProjectConfiguration>
+  <AutoDetectNugetBuildDependencies>true</AutoDetectNugetBuildDependencies>
+  <BuildPriority>1000</BuildPriority>
+  <CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
+  <ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
+  <PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
+  <AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
+  <AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
+  <AllowCodeAnalysis>false</AllowCodeAnalysis>
+  <IgnoreThisComponentCompletely>true</IgnoreThisComponentCompletely>
+  <RunPreBuildEvents>false</RunPreBuildEvents>
+  <RunPostBuildEvents>false</RunPostBuildEvents>
+  <PreviouslyBuiltSuccessfully>false</PreviouslyBuiltSuccessfully>
+  <InstrumentAssembly>true</InstrumentAssembly>
+  <PreventSigningOfAssembly>false</PreventSigningOfAssembly>
+  <AnalyseExecutionTimes>true</AnalyseExecutionTimes>
+  <DetectStackOverflow>true</DetectStackOverflow>
+  <IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
+  <DefaultTestTimeout>60000</DefaultTestTimeout>
+  <UseBuildConfiguration />
+  <UseBuildPlatform />
+  <ProxyProcessPath />
+  <UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
+  <MSTestThreadApartmentState>STA</MSTestThreadApartmentState>
+  <BuildProcessArchitecture>x86</BuildProcessArchitecture>
+</ProjectConfiguration>

+ 2 - 2
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -56,7 +56,7 @@ namespace Avalonia.Android
 
 
         public void Init(Type applicationType)
         public void Init(Type applicationType)
         {
         {
-            SharedPlatform.Register(applicationType.Assembly);
+            StandardRuntimePlatformServices.Register(applicationType.Assembly);
         }
         }
 
 
         public IWindowImpl CreateWindow()
         public IWindowImpl CreateWindow()
@@ -64,7 +64,7 @@ namespace Avalonia.Android
             return new WindowImpl();
             return new WindowImpl();
         }
         }
 
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
         {
             throw new NotImplementedException();
             throw new NotImplementedException();
         }
         }

+ 1 - 0
src/Android/Avalonia.Android/Avalonia.Android.csproj

@@ -77,6 +77,7 @@
     <Compile Include="Platform\AndroidTopLevelRenderer.cs" />
     <Compile Include="Platform\AndroidTopLevelRenderer.cs" />
     <Compile Include="Resources\Resource.Designer.cs" />
     <Compile Include="Resources\Resource.Designer.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="RuntimeInfo.cs" />
     <Compile Include="SystemDialogImpl.cs" />
     <Compile Include="SystemDialogImpl.cs" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>

+ 2 - 0
src/Android/Avalonia.Android/Platform/SkiaPlatform/WindowImpl.cs

@@ -87,6 +87,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
 
         public Action<double> ScalingChanged { get; set; }
         public Action<double> ScalingChanged { get; set; }
 
 
+        public Action<Point> PositionChanged { get; set; }
+
         public View View => this;
         public View View => this;
 
 
         Action ITopLevelImpl.Activated { get; set; }
         Action ITopLevelImpl.Activated { get; set; }

+ 18 - 0
src/Android/Avalonia.Android/RuntimeInfo.cs

@@ -0,0 +1,18 @@
+using Avalonia.Platform;
+
+namespace Avalonia.Shared.PlatformSupport
+{
+    internal partial class StandardRuntimePlatform
+    {
+        public RuntimePlatformInfo GetRuntimeInfo() => new RuntimePlatformInfo
+        {
+            IsCoreClr = false,
+            IsDesktop = false,
+            IsMobile = true,
+            IsDotNetFramework = false,
+            IsMono = true,
+            IsUnix = true,
+            OperatingSystem = OperatingSystemType.Android
+        };
+    }
+}

+ 3 - 3
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@@ -65,15 +65,15 @@
     <Reference Include="Mono.Android" />
     <Reference Include="Mono.Android" />
     <Reference Include="mscorlib" />
     <Reference Include="mscorlib" />
     <Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
     <Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
-      <HintPath>..\..\..\packages\Serilog.1.5.9\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll</HintPath>
+      <HintPath>..\..\..\packages\Serilog.1.5.14\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll</HintPath>
       <HintPath>..\..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
-    <Reference Include="Sprache, Version=2.0.0.47, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\..\..\packages\Sprache.2.0.0.47\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid1+MonoTouch1\Sprache.dll</HintPath>
+    <Reference Include="Sprache, Version=2.0.0.51, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\..\..\packages\Sprache.2.0.0.51\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid+Xamarin.iOS10+MonoTouch\Sprache.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System" />

+ 2 - 2
src/Android/Avalonia.AndroidTestApplication/packages.config

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
 <packages>
-  <package id="Serilog" version="1.5.9" targetFramework="monoandroid51" />
+  <package id="Serilog" version="1.5.14" targetFramework="monoandroid44" />
   <package id="Splat" version="1.6.2" targetFramework="monoandroid51" />
   <package id="Splat" version="1.6.2" targetFramework="monoandroid51" />
-  <package id="Sprache" version="2.0.0.47" targetFramework="monoandroid51" />
+  <package id="Sprache" version="2.0.0.51" targetFramework="monoandroid44" />
   <package id="System.Collections" version="4.0.11" targetFramework="monoandroid44" />
   <package id="System.Collections" version="4.0.11" targetFramework="monoandroid44" />
   <package id="System.Collections.Concurrent" version="4.0.12" targetFramework="monoandroid44" />
   <package id="System.Collections.Concurrent" version="4.0.12" targetFramework="monoandroid44" />
   <package id="System.ComponentModel" version="4.0.1" targetFramework="monoandroid44" />
   <package id="System.ComponentModel" version="4.0.1" targetFramework="monoandroid44" />

+ 6 - 4
src/Avalonia.Base/Avalonia.Base.csproj

@@ -43,10 +43,9 @@
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">
       <Link>Properties\SharedAssemblyInfo.cs</Link>
       <Link>Properties\SharedAssemblyInfo.cs</Link>
     </Compile>
     </Compile>
-    <Compile Include="Data\BindingError.cs" />
+    <Compile Include="Data\BindingChainNullException.cs" />
+    <Compile Include="Data\BindingNotification.cs" />
     <Compile Include="Data\IndexerBinding.cs" />
     <Compile Include="Data\IndexerBinding.cs" />
-    <Compile Include="Data\IValidationStatus.cs" />
-    <Compile Include="Data\ObjectValidationStatus.cs" />
     <Compile Include="Diagnostics\INotifyCollectionChangedDebug.cs" />
     <Compile Include="Diagnostics\INotifyCollectionChangedDebug.cs" />
     <Compile Include="Data\AssignBindingAttribute.cs" />
     <Compile Include="Data\AssignBindingAttribute.cs" />
     <Compile Include="Data\BindingOperations.cs" />
     <Compile Include="Data\BindingOperations.cs" />
@@ -85,7 +84,7 @@
     <Compile Include="AvaloniaPropertyRegistry.cs" />
     <Compile Include="AvaloniaPropertyRegistry.cs" />
     <Compile Include="AvaloniaProperty`1.cs" />
     <Compile Include="AvaloniaProperty`1.cs" />
     <Compile Include="Platform\IAssetLoader.cs" />
     <Compile Include="Platform\IAssetLoader.cs" />
-    <Compile Include="Platform\IPclPlatformWrapper.cs" />
+    <Compile Include="Platform\IRuntimePlatform.cs" />
     <Compile Include="Data\BindingPriority.cs" />
     <Compile Include="Data\BindingPriority.cs" />
     <Compile Include="PriorityBindingEntry.cs" />
     <Compile Include="PriorityBindingEntry.cs" />
     <Compile Include="Collections\IAvaloniaList.cs" />
     <Compile Include="Collections\IAvaloniaList.cs" />
@@ -116,9 +115,12 @@
     <Compile Include="Threading\AvaloniaScheduler.cs" />
     <Compile Include="Threading\AvaloniaScheduler.cs" />
     <Compile Include="Threading\AvaloniaSynchronizationContext.cs" />
     <Compile Include="Threading\AvaloniaSynchronizationContext.cs" />
     <Compile Include="Threading\SingleThreadDispatcher.cs" />
     <Compile Include="Threading\SingleThreadDispatcher.cs" />
+    <Compile Include="Utilities\ExceptionUtilities.cs" />
     <Compile Include="Utilities\IWeakSubscriber.cs" />
     <Compile Include="Utilities\IWeakSubscriber.cs" />
     <Compile Include="Utilities\MathUtilities.cs" />
     <Compile Include="Utilities\MathUtilities.cs" />
+    <Compile Include="Utilities\SingleOrDictionary.cs" />
     <Compile Include="Utilities\TypeUtilities.cs" />
     <Compile Include="Utilities\TypeUtilities.cs" />
+    <Compile Include="Utilities\WeakObservable.cs" />
     <Compile Include="Utilities\WeakSubscriptionManager.cs" />
     <Compile Include="Utilities\WeakSubscriptionManager.cs" />
     <Compile Include="Utilities\WeakTimer.cs" />
     <Compile Include="Utilities\WeakTimer.cs" />
   </ItemGroup>
   </ItemGroup>

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

@@ -20,7 +20,7 @@ namespace Avalonia
         {
         {
             IsDisposed = true;
             IsDisposed = true;
 #if DEBUG_DISPOSE
 #if DEBUG_DISPOSE
-            DisposedAt = AvaloniaLocator.Current.GetService<IPclPlatformWrapper>().GetStackTrace();
+            DisposedAt = AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetStackTrace();
 #endif
 #endif
             DoDispose();
             DoDispose();
         }
         }

+ 134 - 133
src/Avalonia.Base/AvaloniaObject.cs

@@ -50,29 +50,6 @@ namespace Avalonia
         /// </summary>
         /// </summary>
         private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
         private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
 
 
-        /// <summary>
-        /// Defines the <see cref="ValidationStatus"/> property.
-        /// </summary>
-        public static readonly DirectProperty<AvaloniaObject, ObjectValidationStatus> ValidationStatusProperty =
-            AvaloniaProperty.RegisterDirect<AvaloniaObject, ObjectValidationStatus>(nameof(ValidationStatus), c => c.ValidationStatus);
-
-        private ObjectValidationStatus validationStatus;
-
-        /// <summary>
-        /// The current validation status of the control.
-        /// </summary>
-        public ObjectValidationStatus ValidationStatus
-        {
-            get
-            {
-                return validationStatus;
-            }
-            private set
-            {
-                SetAndRaise(ValidationStatusProperty, ref validationStatus, value);
-            }
-        }
-
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="AvaloniaObject"/> class.
         /// Initializes a new instance of the <see cref="AvaloniaObject"/> class.
         /// </summary>
         /// </summary>
@@ -251,6 +228,10 @@ namespace Avalonia
         /// </summary>
         /// </summary>
         /// <param name="property">The property.</param>
         /// <param name="property">The property.</param>
         /// <returns>True if the property is set, otherwise false.</returns>
         /// <returns>True if the property is set, otherwise false.</returns>
+        /// <remarks>
+        /// Checks whether a value is assigned to the property, or that there is a binding to the
+        /// property that is producing a value other than <see cref="AvaloniaProperty.UnsetValue"/>.
+        /// </remarks>
         public bool IsSet(AvaloniaProperty property)
         public bool IsSet(AvaloniaProperty property)
         {
         {
             Contract.Requires<ArgumentNullException>(property != null);
             Contract.Requires<ArgumentNullException>(property != null);
@@ -281,42 +262,11 @@ namespace Avalonia
 
 
             if (property.IsDirect)
             if (property.IsDirect)
             {
             {
-                var accessor = (IDirectPropertyAccessor)GetRegistered(property);
-                LogPropertySet(property, value, priority);
-                accessor.SetValue(this, DirectUnsetToDefault(value, property));
+                SetDirectValue(property, value);
             }
             }
             else
             else
             {
             {
-                PriorityValue v;
-                var originalValue = value;
-
-                if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
-                {
-                    ThrowNotRegistered(property);
-                }
-
-                if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
-                {
-                    throw new ArgumentException(string.Format(
-                        "Invalid value for Property '{0}': '{1}' ({2})",
-                        property.Name,
-                        originalValue,
-                        originalValue?.GetType().FullName ?? "(null)"));
-                }
-
-                if (!_values.TryGetValue(property, out v))
-                {
-                    if (value == AvaloniaProperty.UnsetValue)
-                    {
-                        return;
-                    }
-
-                    v = CreatePriorityValue(property);
-                    _values.Add(property, v);
-                }
-
-                LogPropertySet(property, value, priority);
-                v.SetValue(value, (int)priority);
+                SetStyledValue(property, value, priority);
             }
             }
         }
         }
 
 
@@ -371,7 +321,6 @@ namespace Avalonia
                     GetDescription(source));
                     GetDescription(source));
 
 
                 IDisposable subscription = null;
                 IDisposable subscription = null;
-                IDisposable validationSubcription = null;
 
 
                 if (_directBindings == null)
                 if (_directBindings == null)
                 {
                 {
@@ -379,19 +328,14 @@ namespace Avalonia
                 }
                 }
 
 
                 subscription = source
                 subscription = source
-                    .Where(x =>  !(x is IValidationStatus))
                     .Select(x => CastOrDefault(x, property.PropertyType))
                     .Select(x => CastOrDefault(x, property.PropertyType))
                     .Do(_ => { }, () => _directBindings.Remove(subscription))
                     .Do(_ => { }, () => _directBindings.Remove(subscription))
-                    .Subscribe(x => DirectBindingSet(property, x));
-                validationSubcription = source
-                    .OfType<IValidationStatus>()
-                    .Subscribe(x => DataValidationChanged(property, x));
+                    .Subscribe(x => SetDirectValue(property, x));
 
 
                 _directBindings.Add(subscription);
                 _directBindings.Add(subscription);
 
 
                 return Disposable.Create(() =>
                 return Disposable.Create(() =>
                 {
                 {
-                    validationSubcription.Dispose();
                     subscription.Dispose();
                     subscription.Dispose();
                     _directBindings.Remove(subscription);
                     _directBindings.Remove(subscription);
                 });
                 });
@@ -487,28 +431,9 @@ namespace Avalonia
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        void IPriorityValueOwner.DataValidationChanged(PriorityValue sender, IValidationStatus status)
+        void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification)
         {
         {
-            var property = sender.Property;
-            DataValidationChanged(property, status);
-        }
-
-        /// <summary>
-        /// Called when the validation state on a tracked property is changed.
-        /// </summary>
-        /// <param name="property">The property whose validation state changed.</param>
-        /// <param name="status">The new validation state.</param>
-        protected virtual void DataValidationChanged(AvaloniaProperty property, IValidationStatus status)
-        {
-        }
-
-        /// <summary>
-        /// Updates the validation status of the current object.
-        /// </summary>
-        /// <param name="status">The new validation status.</param>
-        protected void UpdateValidationState(IValidationStatus status)
-        {
-            ValidationStatus = ValidationStatus.UpdateValidationStatus(status);
+            UpdateDataValidation(sender.Property, notification);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -542,6 +467,18 @@ namespace Avalonia
             });
             });
         }
         }
 
 
+        /// <summary>
+        /// Called to update the validation state for properties for which data validation is
+        /// enabled.
+        /// </summary>
+        /// <param name="property">The property.</param>
+        /// <param name="status">The new validation status.</param>
+        protected virtual void UpdateDataValidation(
+            AvaloniaProperty property,
+            BindingNotification status)
+        {
+        }
+
         /// <summary>
         /// <summary>
         /// Called when a avalonia property changes on the object.
         /// Called when a avalonia property changes on the object.
         /// </summary>
         /// </summary>
@@ -623,22 +560,27 @@ namespace Avalonia
 
 
         /// <summary>
         /// <summary>
         /// Tries to cast a value to a type, taking into account that the value may be a
         /// Tries to cast a value to a type, taking into account that the value may be a
-        /// <see cref="BindingError"/>.
+        /// <see cref="BindingNotification"/>.
         /// </summary>
         /// </summary>
         /// <param name="value">The value.</param>
         /// <param name="value">The value.</param>
         /// <param name="type">The type.</param>
         /// <param name="type">The type.</param>
-        /// <returns>The cast value, or a <see cref="BindingError"/>.</returns>
+        /// <returns>The cast value, or a <see cref="BindingNotification"/>.</returns>
         private static object CastOrDefault(object value, Type type)
         private static object CastOrDefault(object value, Type type)
         {
         {
-            var error = value as BindingError;
+            var notification = value as BindingNotification;
 
 
-            if (error == null)
+            if (notification == null)
             {
             {
                 return TypeUtilities.CastOrDefault(value, type);
                 return TypeUtilities.CastOrDefault(value, type);
             }
             }
             else
             else
             {
             {
-                return error;
+                if (notification.HasValue)
+                {
+                    notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type));
+                }
+
+                return notification;
             }
             }
         }
         }
 
 
@@ -666,50 +608,6 @@ namespace Avalonia
             return result;
             return result;
         }
         }
 
 
-        /// <summary>
-        /// Sets a property value for a direct property binding.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        /// <param name="value">The value.</param>
-        /// <returns></returns>
-        private void DirectBindingSet(AvaloniaProperty property, object value)
-        {
-            var error = value as BindingError;
-
-            if (error == null)
-            {
-                SetValue(property, value);
-            }
-            else
-            {
-                if (error.UseFallbackValue)
-                {
-                    SetValue(property, error.FallbackValue);
-                }
-
-                Logger.Error(
-                    LogArea.Binding,
-                    this,
-                    "Error binding to {Target}.{Property}: {Message}",
-                    this,
-                    property,
-                    error.Exception.Message);
-            }
-        }
-
-        /// <summary>
-        /// Converts an unset value to the default value for a direct property.
-        /// </summary>
-        /// <param name="value">The value.</param>
-        /// <param name="property">The property.</param>
-        /// <returns>The value.</returns>
-        private object DirectUnsetToDefault(object value, AvaloniaProperty property)
-        {
-            return value == AvaloniaProperty.UnsetValue ?
-                ((IDirectPropertyMetadata)property.GetMetadata(GetType())).UnsetValue :
-                value;
-        }
-
         /// <summary>
         /// <summary>
         /// Gets the default value for a property.
         /// Gets the default value for a property.
         /// </summary>
         /// </summary>
@@ -753,6 +651,109 @@ namespace Avalonia
             return result;
             return result;
         }
         }
 
 
+        /// <summary>
+        /// Sets the value of a direct property.
+        /// </summary>
+        /// <param name="property">The property.</param>
+        /// <param name="value">The value.</param>
+        private void SetDirectValue(AvaloniaProperty property, object value)
+        {
+            var notification = value as BindingNotification;
+
+            if (notification != null)
+            {
+                if (notification.ErrorType == BindingErrorType.Error)
+                {
+                    Logger.Error(
+                        LogArea.Binding,
+                        this,
+                        "Error in binding to {Target}.{Property}: {Message}",
+                        this,
+                        property,
+                        ExceptionUtilities.GetMessage(notification.Error));
+                }
+
+                if (notification.HasValue)
+                {
+                    value = notification.Value;
+                }
+            }
+
+            if (notification == null || notification.HasValue)
+            {
+                var metadata = (IDirectPropertyMetadata)property.GetMetadata(GetType());
+                var accessor = (IDirectPropertyAccessor)GetRegistered(property);
+                var finalValue = value == AvaloniaProperty.UnsetValue ? 
+                    metadata.UnsetValue : value;
+
+                LogPropertySet(property, value, BindingPriority.LocalValue);
+
+                accessor.SetValue(this, finalValue);
+            }
+
+            if (notification != null)
+            {
+                UpdateDataValidation(property, notification);
+            }
+        }
+
+        /// <summary>
+        /// Sets the value of a styled property.
+        /// </summary>
+        /// <param name="property">The property.</param>
+        /// <param name="value">The value.</param>
+        /// <param name="priority">The priority of the value.</param>
+        private void SetStyledValue(AvaloniaProperty property, object value, BindingPriority priority)
+        {
+            var notification = value as BindingNotification;
+
+            // We currently accept BindingNotifications for non-direct properties but we just
+            // strip them to their underlying value.
+            if (notification != null)
+            {
+                if (!notification.HasValue)
+                {
+                    return;
+                }
+                else
+                {
+                    value = notification.Value;
+                }
+            }
+
+            var originalValue = value;
+
+            if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
+            {
+                ThrowNotRegistered(property);
+            }
+
+            if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
+            {
+                throw new ArgumentException(string.Format(
+                    "Invalid value for Property '{0}': '{1}' ({2})",
+                    property.Name,
+                    originalValue,
+                    originalValue?.GetType().FullName ?? "(null)"));
+            }
+
+            PriorityValue v;
+
+            if (!_values.TryGetValue(property, out v))
+            {
+                if (value == AvaloniaProperty.UnsetValue)
+                {
+                    return;
+                }
+
+                v = CreatePriorityValue(property);
+                _values.Add(property, v);
+            }
+
+            LogPropertySet(property, value, priority);
+            v.SetValue(value, (int)priority);
+        }
+
         /// <summary>
         /// <summary>
         /// Given a <see cref="AvaloniaProperty"/> returns a registered avalonia property that is
         /// Given a <see cref="AvaloniaProperty"/> returns a registered avalonia property that is
         /// equal or throws if not found.
         /// equal or throws if not found.

+ 9 - 2
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@@ -216,7 +216,13 @@ namespace Avalonia
             Contract.Requires<ArgumentNullException>(property != null);
             Contract.Requires<ArgumentNullException>(property != null);
             Contract.Requires<ArgumentNullException>(binding != null);
             Contract.Requires<ArgumentNullException>(binding != null);
 
 
-            var result = binding.Initiate(target, property, anchor);
+            var metadata = property.GetMetadata(target.GetType()) as IDirectPropertyMetadata;
+
+            var result = binding.Initiate(
+                target,
+                property,
+                anchor, 
+                metadata?.EnableDataValidation ?? false);
 
 
             if (result != null)
             if (result != null)
             {
             {
@@ -311,7 +317,8 @@ namespace Avalonia
             public InstancedBinding Initiate(
             public InstancedBinding Initiate(
                 IAvaloniaObject target,
                 IAvaloniaObject target,
                 AvaloniaProperty targetProperty,
                 AvaloniaProperty targetProperty,
-                object anchor = null)
+                object anchor = null,
+                bool enableDataValidation = false)
             {
             {
                 return new InstancedBinding(_source);
                 return new InstancedBinding(_source);
             }
             }

+ 7 - 2
src/Avalonia.Base/AvaloniaProperty.cs

@@ -360,20 +360,25 @@ namespace Avalonia
         /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
         /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
         /// </param>
         /// </param>
         /// <param name="defaultBindingMode">The default binding mode for the property.</param>
         /// <param name="defaultBindingMode">The default binding mode for the property.</param>
+        /// <param name="enableDataValidation">
+        /// Whether the property is interested in data validation.
+        /// </param>
         /// <returns>A <see cref="AvaloniaProperty{TValue}"/></returns>
         /// <returns>A <see cref="AvaloniaProperty{TValue}"/></returns>
         public static DirectProperty<TOwner, TValue> RegisterDirect<TOwner, TValue>(
         public static DirectProperty<TOwner, TValue> RegisterDirect<TOwner, TValue>(
             string name,
             string name,
             Func<TOwner, TValue> getter,
             Func<TOwner, TValue> getter,
             Action<TOwner, TValue> setter = null,
             Action<TOwner, TValue> setter = null,
             TValue unsetValue = default(TValue),
             TValue unsetValue = default(TValue),
-            BindingMode defaultBindingMode = BindingMode.OneWay)
+            BindingMode defaultBindingMode = BindingMode.OneWay,
+            bool enableDataValidation = false)
                 where TOwner : IAvaloniaObject
                 where TOwner : IAvaloniaObject
         {
         {
             Contract.Requires<ArgumentNullException>(name != null);
             Contract.Requires<ArgumentNullException>(name != null);
 
 
             var metadata = new DirectPropertyMetadata<TValue>(
             var metadata = new DirectPropertyMetadata<TValue>(
                 unsetValue: unsetValue,
                 unsetValue: unsetValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                enableDataValidation: enableDataValidation);
 
 
             var result = new DirectProperty<TOwner, TValue>(name, getter, setter, metadata);
             var result = new DirectProperty<TOwner, TValue>(name, getter, setter, metadata);
             AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
             AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);

+ 85 - 0
src/Avalonia.Base/Data/BindingChainNullException.cs

@@ -0,0 +1,85 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Avalonia.Data
+{
+    /// <summary>
+    /// An exception returned through <see cref="BindingNotification"/> signalling that a
+    /// requested binding expression could not be evaluated because of a null in one of the links
+    /// of the binding chain.
+    /// </summary>
+    public class BindingChainNullException : Exception
+    {
+        private string _message;
+
+        /// <summary>
+        /// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
+        /// </summary>
+        public BindingChainNullException()
+        {
+        }
+
+        /// <summary>
+        /// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
+        /// </summary>
+        public BindingChainNullException(string message)
+        {
+            _message = message;
+        }
+
+        /// <summary>
+        /// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
+        /// </summary>
+        /// <param name="expression">The expression.</param>
+        /// <param name="expressionNullPoint">
+        /// The point in the expression at which the null was encountered.
+        /// </param>
+        public BindingChainNullException(string expression, string expressionNullPoint)
+        {
+            Expression = expression;
+            ExpressionNullPoint = expressionNullPoint;
+        }
+
+        /// <summary>
+        /// Gets the expression that could not be evaluated.
+        /// </summary>
+        public string Expression { get; protected set; }
+
+        /// <summary>
+        /// Gets the point in the expression at which the null was encountered.
+        /// </summary>
+        public string ExpressionNullPoint { get; protected set; }
+
+        /// <inheritdoc/>
+        public override string Message
+        {
+            get
+            {
+                if (_message == null)
+                {
+                    _message = BuildMessage();
+                }
+
+                return _message;
+            }
+        }
+
+        private string BuildMessage()
+        {
+            if (Expression != null && ExpressionNullPoint != null)
+            {
+                return $"'{ExpressionNullPoint}' is null in expression '{Expression}'.";
+            }
+            else if (ExpressionNullPoint != null)
+            {
+                return $"'{ExpressionNullPoint}' is null in expression.";
+            }
+            else
+            {
+                return "Null encountered in binding expression.";
+            }
+        }
+    }
+}

+ 0 - 59
src/Avalonia.Base/Data/BindingError.cs

@@ -1,59 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-
-namespace Avalonia.Data
-{
-    /// <summary>
-    /// Represents a recoverable binding error.
-    /// </summary>
-    /// <remarks>
-    /// When produced by a binding source observable, informs the binding system that an error
-    /// occurred. It can also provide an optional fallback value to be pushed to the binding
-    /// target. 
-    /// 
-    /// Instead of using <see cref="BindingError"/>, one could simply not push a value (in the
-    /// case of a no fallback value) or push a fallback value, but BindingError also causes an
-    /// error to be logged with the correct binding target.
-    /// </remarks>
-    public class BindingError
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BindingError"/> class.
-        /// </summary>
-        /// <param name="exception">An exception describing the binding error.</param>
-        public BindingError(Exception exception)
-        {
-            Exception = exception;
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BindingError"/> class.
-        /// </summary>
-        /// <param name="exception">An exception describing the binding error.</param>
-        /// <param name="fallbackValue">The fallback value.</param>
-        public BindingError(Exception exception, object fallbackValue)
-        {
-            Exception = exception;
-            FallbackValue = fallbackValue;
-            UseFallbackValue = true;
-        }
-
-        /// <summary>
-        /// Gets the exception describing the binding error.
-        /// </summary>
-        public Exception Exception { get; }
-
-        /// <summary>
-        /// Get the fallback value.
-        /// </summary>
-        public object FallbackValue { get; }
-
-        /// <summary>
-        /// Get a value indicating whether the fallback value should be pushed to the binding
-        /// target.
-        /// </summary>
-        public bool UseFallbackValue { get; }
-    }
-}

+ 282 - 0
src/Avalonia.Base/Data/BindingNotification.cs

@@ -0,0 +1,282 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Avalonia.Data
+{
+    /// <summary>
+    /// Defines the types of binding errors for a <see cref="BindingNotification"/>.
+    /// </summary>
+    public enum BindingErrorType
+    {
+        /// <summary>
+        /// There was no error.
+        /// </summary>
+        None,
+
+        /// <summary>
+        /// There was a binding error.
+        /// </summary>
+        Error,
+
+        /// <summary>
+        /// There was a data validation error.
+        /// </summary>
+        DataValidationError,
+    }
+
+    /// <summary>
+    /// Represents a binding notification that can be a valid binding value, or a binding or
+    /// data validation error.
+    /// </summary>
+    public class BindingNotification
+    {
+        /// <summary>
+        /// A binding notification representing the null value.
+        /// </summary>
+        public static readonly BindingNotification Null =
+            new BindingNotification(null);
+
+        /// <summary>
+        /// A binding notification representing <see cref="AvaloniaProperty.UnsetValue"/>.
+        /// </summary>
+        public static readonly BindingNotification UnsetValue =
+            new BindingNotification(AvaloniaProperty.UnsetValue);
+
+        // Null cannot be held in WeakReference as it's indistinguishable from an expired value so
+        // use this value in its place.
+        private static readonly object NullValue = new object();
+
+        private WeakReference<object> _value;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BindingNotification"/> class.
+        /// </summary>
+        /// <param name="value">The binding value.</param>
+        public BindingNotification(object value)
+        {
+            _value = new WeakReference<object>(value ?? NullValue);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BindingNotification"/> class.
+        /// </summary>
+        /// <param name="error">The binding error.</param>
+        /// <param name="errorType">The type of the binding error.</param>
+        public BindingNotification(Exception error, BindingErrorType errorType)
+        {
+            if (errorType == BindingErrorType.None)
+            {
+                throw new ArgumentException($"'errorType' may not be None");
+            }
+
+            Error = error;
+            ErrorType = errorType;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BindingNotification"/> class.
+        /// </summary>
+        /// <param name="error">The binding error.</param>
+        /// <param name="errorType">The type of the binding error.</param>
+        /// <param name="fallbackValue">The fallback value.</param>
+        public BindingNotification(Exception error, BindingErrorType errorType, object fallbackValue)
+            : this(error, errorType)
+        {
+            _value = new WeakReference<object>(fallbackValue ?? NullValue);
+        }
+
+        /// <summary>
+        /// Gets the value that should be passed to the target when <see cref="HasValue"/>
+        /// is true.
+        /// </summary>
+        /// <remarks>
+        /// If this property is read when <see cref="HasValue"/> is false then it will return
+        /// <see cref="AvaloniaProperty.UnsetValue"/>.
+        /// </remarks>
+        public object Value
+        {
+            get
+            {
+                if (_value != null)
+                {
+                    object result;
+
+                    if (_value.TryGetTarget(out result))
+                    {
+                        return result == NullValue ? null : result;
+                    }
+                }
+
+                // There's the possibility of a race condition in that HasValue can return true,
+                // and then the value is GC'd before Value is read. We should be ok though as
+                // we return UnsetValue which should be a safe alternative.
+                return AvaloniaProperty.UnsetValue;
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether <see cref="Value"/> should be pushed to the target.
+        /// </summary>
+        public bool HasValue => _value != null;
+
+        /// <summary>
+        /// Gets the error that occurred on the source, if any.
+        /// </summary>
+        public Exception Error { get; set; }
+
+        /// <summary>
+        /// Gets the type of error that <see cref="Error"/> represents, if any.
+        /// </summary>
+        public BindingErrorType ErrorType { get; set; }
+
+        /// <summary>
+        /// Compares two instances of <see cref="BindingNotification"/> for equality.
+        /// </summary>
+        /// <param name="a">The first instance.</param>
+        /// <param name="b">The second instance.</param>
+        /// <returns>true if the two instances are equal; otherwise false.</returns>
+        public static bool operator ==(BindingNotification a, BindingNotification b)
+        {
+            if (object.ReferenceEquals(a, b))
+            {
+                return true;
+            }
+
+            if ((object)a == null || (object)b == null)
+            {
+                return false;
+            }
+
+            return a.HasValue == b.HasValue &&
+                   a.ErrorType == b.ErrorType &&
+                   (!a.HasValue || object.Equals(a.Value, b.Value)) &&
+                   (a.ErrorType == BindingErrorType.None || ExceptionEquals(a.Error, b.Error));
+        }
+
+        /// <summary>
+        /// Compares two instances of <see cref="BindingNotification"/> for inequality.
+        /// </summary>
+        /// <param name="a">The first instance.</param>
+        /// <param name="b">The second instance.</param>
+        /// <returns>true if the two instances are unequal; otherwise false.</returns>
+        public static bool operator !=(BindingNotification a, BindingNotification b)
+        {
+            return !(a == b);
+        }
+
+        /// <summary>
+        /// Gets a value from an object that may be a <see cref="BindingNotification"/>.
+        /// </summary>
+        /// <param name="o">The object.</param>
+        /// <returns>The value.</returns>
+        /// <remarks>
+        /// If <paramref name="o"/> is a <see cref="BindingNotification"/> then returns the binding
+        /// notification's <see cref="Value"/>. If not, returns the object unchanged.
+        /// </remarks>
+        public static object ExtractValue(object o)
+        {
+            var notification = o as BindingNotification;
+            return notification != null ? notification.Value : o;
+        }
+
+        /// <summary>
+        /// Gets an exception from an object that may be a <see cref="BindingNotification"/>.
+        /// </summary>
+        /// <param name="o">The object.</param>
+        /// <returns>The value.</returns>
+        /// <remarks>
+        /// If <paramref name="o"/> is a <see cref="BindingNotification"/> then returns the binding
+        /// notification's <see cref="Error"/>. If not, returns the object unchanged.
+        /// </remarks>
+        public static object ExtractError(object o)
+        {
+            var notification = o as BindingNotification;
+            return notification != null ? notification.Error : o;
+        }
+
+        /// <summary>
+        /// Compares an object to an instance of <see cref="BindingNotification"/> for equality.
+        /// </summary>
+        /// <param name="obj">The object to compare.</param>
+        /// <returns>true if the two instances are equal; otherwise false.</returns>
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as BindingNotification);
+        }
+
+        /// <summary>
+        /// Compares a value to an instance of <see cref="BindingNotification"/> for equality.
+        /// </summary>
+        /// <param name="other">The value to compare.</param>
+        /// <returns>true if the two instances are equal; otherwise false.</returns>
+        public bool Equals(BindingNotification other)
+        {
+            return this == other;
+        }
+
+        /// <summary>
+        /// Gets the hash code for this instance of <see cref="BindingNotification"/>. 
+        /// </summary>
+        /// <returns>A hash code.</returns>
+        public override int GetHashCode()
+        {
+            return base.GetHashCode();
+        }
+
+        /// <summary>
+        /// Adds an error to the <see cref="BindingNotification"/>.
+        /// </summary>
+        /// <param name="e">The error to add.</param>
+        /// <param name="type">The error type.</param>
+        public void AddError(Exception e, BindingErrorType type)
+        {
+            Contract.Requires<ArgumentNullException>(e != null);
+            Contract.Requires<ArgumentException>(type != BindingErrorType.None);
+
+            Error = Error != null ? new AggregateException(Error, e) : e;
+
+            if (type == BindingErrorType.Error || ErrorType == BindingErrorType.Error)
+            {
+                ErrorType = BindingErrorType.Error;
+            }
+        }
+
+        /// <summary>
+        /// Removes the <see cref="Value"/> and makes <see cref="HasValue"/> return null.
+        /// </summary>
+        public void ClearValue()
+        {
+            _value = null;
+        }
+
+        /// <summary>
+        /// Sets the <see cref="Value"/>.
+        /// </summary>
+        public void SetValue(object value)
+        {
+            _value = new WeakReference<object>(value ?? NullValue);
+        }
+
+        /// <inheritdoc/>
+        public override string ToString()
+        {
+            switch (ErrorType)
+            {
+                case BindingErrorType.None:
+                    return $"{{Value: {Value}}}";
+                default:
+                    return HasValue ?
+                        $"{{{ErrorType}: {Error}, Fallback: {Value}}}" : 
+                        $"{{{ErrorType}: {Error}}}";
+            }
+        }
+
+        private static bool ExceptionEquals(Exception a, Exception b)
+        {
+            return a?.GetType() == b?.GetType() &&
+                   a?.Message == b?.Message;
+        }
+    }
+}

+ 4 - 1
src/Avalonia.Base/Data/BindingOperations.cs

@@ -54,7 +54,10 @@ namespace Avalonia.Data
 
 
                     if (source != null)
                     if (source != null)
                     {
                     {
-                        return source.Take(1).Subscribe(x => target.SetValue(property, x, binding.Priority));
+                        return source
+                            .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
+                            .Take(1)
+                            .Subscribe(x => target.SetValue(property, x, binding.Priority));
                     }
                     }
                     else
                     else
                     {
                     {

+ 3 - 1
src/Avalonia.Base/Data/IBinding.cs

@@ -19,12 +19,14 @@ namespace Avalonia.Data
         /// order to locate named controls or resources. The <paramref name="anchor"/> parameter 
         /// order to locate named controls or resources. The <paramref name="anchor"/> parameter 
         /// can be used to provice this context.
         /// can be used to provice this context.
         /// </param>
         /// </param>
+        /// <param name="enableDataValidation">Whether data validation should be enabled.</param>
         /// <returns>
         /// <returns>
         /// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
         /// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
         /// </returns>
         /// </returns>
         InstancedBinding Initiate(
         InstancedBinding Initiate(
             IAvaloniaObject target, 
             IAvaloniaObject target, 
             AvaloniaProperty targetProperty,
             AvaloniaProperty targetProperty,
-            object anchor = null);
+            object anchor = null,
+            bool enableDataValidation = false);
     }
     }
 }
 }

+ 0 - 17
src/Avalonia.Base/Data/IValidationStatus.cs

@@ -1,17 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-namespace Avalonia.Data
-{
-    /// <summary>
-    /// Contains information on if the current object passed validation.
-    /// Subclasses of this class contain additional information depending on the method of validation checking. 
-    /// </summary>
-    public interface IValidationStatus
-    {
-        /// <summary>
-        /// True when the data passes validation; otherwise, false.
-        /// </summary>
-        bool IsValid { get; }
-    }
-}

+ 5 - 1
src/Avalonia.Base/Data/IndexerBinding.cs

@@ -21,7 +21,11 @@ namespace Avalonia.Data
         public AvaloniaProperty Property { get; }
         public AvaloniaProperty Property { get; }
         private BindingMode Mode { get; }
         private BindingMode Mode { get; }
 
 
-        public InstancedBinding Initiate(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor = null)
+        public InstancedBinding Initiate(
+            IAvaloniaObject target,
+            AvaloniaProperty targetProperty,
+            object anchor = null,
+            bool enableDataValidation = false)
         {
         {
             var mode = Mode == BindingMode.Default ?
             var mode = Mode == BindingMode.Default ?
                 targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
                 targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :

+ 0 - 44
src/Avalonia.Base/Data/ObjectValidationStatus.cs

@@ -1,44 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Avalonia.Data
-{
-    /// <summary>
-    /// An immutable struct that contains validation information for a <see cref="AvaloniaObject"/> that validates a single property.
-    /// </summary>
-    public struct ObjectValidationStatus : IValidationStatus
-    {
-        private Dictionary<Type, IValidationStatus> currentValidationStatus;
-
-        public bool IsValid => currentValidationStatus?.Values.All(status => status.IsValid) ?? true;
-
-        /// <summary>
-        /// Constructs the structure with the given validation information.
-        /// </summary>
-        /// <param name="validations">The validation information</param>
-        public ObjectValidationStatus(Dictionary<Type, IValidationStatus> validations)
-            :this()
-        {
-            currentValidationStatus = validations;
-        }
-
-        /// <summary>
-        /// Creates a new status with the updated information.
-        /// </summary>
-        /// <param name="status">The updated status information.</param>
-        /// <returns>The new validation status.</returns>
-        public ObjectValidationStatus UpdateValidationStatus(IValidationStatus status)
-        {
-            var newStatus = new Dictionary<Type, IValidationStatus>(currentValidationStatus ??
-                                                                new Dictionary<Type, IValidationStatus>());
-            newStatus[status.GetType()] = status;
-            return new ObjectValidationStatus(newStatus);
-        }
-
-        public IEnumerable<IValidationStatus> StatusInformation => currentValidationStatus.Values;
-    }
-}

+ 9 - 2
src/Avalonia.Base/DirectProperty.cs

@@ -85,19 +85,26 @@ namespace Avalonia
         /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
         /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
         /// </param>
         /// </param>
         /// <param name="defaultBindingMode">The default binding mode for the property.</param>
         /// <param name="defaultBindingMode">The default binding mode for the property.</param>
+        /// <param name="enableDataValidation">
+        /// Whether the property is interested in data validation.
+        /// </param>
         /// <returns>The property.</returns>
         /// <returns>The property.</returns>
         public DirectProperty<TNewOwner, TValue> AddOwner<TNewOwner>(
         public DirectProperty<TNewOwner, TValue> AddOwner<TNewOwner>(
             Func<TNewOwner, TValue> getter,
             Func<TNewOwner, TValue> getter,
             Action<TNewOwner, TValue> setter = null,
             Action<TNewOwner, TValue> setter = null,
             TValue unsetValue = default(TValue),
             TValue unsetValue = default(TValue),
-            BindingMode defaultBindingMode = BindingMode.OneWay)
+            BindingMode defaultBindingMode = BindingMode.OneWay,
+            bool enableDataValidation = false)
                 where TNewOwner : AvaloniaObject
                 where TNewOwner : AvaloniaObject
         {
         {
             var result = new DirectProperty<TNewOwner, TValue>(
             var result = new DirectProperty<TNewOwner, TValue>(
                 this,
                 this,
                 getter,
                 getter,
                 setter,
                 setter,
-                new DirectPropertyMetadata<TValue>(unsetValue, defaultBindingMode));
+                new DirectPropertyMetadata<TValue>(
+                    unsetValue: unsetValue,
+                    defaultBindingMode: defaultBindingMode,
+                    enableDataValidation: enableDataValidation));
 
 
             AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
             AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
             return result;
             return result;

+ 18 - 2
src/Avalonia.Base/DirectPropertyMetadata`1.cs

@@ -17,19 +17,35 @@ namespace Avalonia
         /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
         /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
         /// </param>
         /// </param>
         /// <param name="defaultBindingMode">The default binding mode.</param>
         /// <param name="defaultBindingMode">The default binding mode.</param>
+        /// <param name="enableDataValidation">
+        /// Whether the property is interested in data validation.
+        /// </param>
         public DirectPropertyMetadata(
         public DirectPropertyMetadata(
             TValue unsetValue = default(TValue),
             TValue unsetValue = default(TValue),
-            BindingMode defaultBindingMode = BindingMode.Default)
+            BindingMode defaultBindingMode = BindingMode.Default,
+            bool enableDataValidation = false)
                 : base(defaultBindingMode)
                 : base(defaultBindingMode)
         {
         {
             UnsetValue = unsetValue;
             UnsetValue = unsetValue;
+            EnableDataValidation = enableDataValidation;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>.
+        /// Gets the value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>.
         /// </summary>
         /// </summary>
         public TValue UnsetValue { get; private set; }
         public TValue UnsetValue { get; private set; }
 
 
+        /// <summary>
+        /// Gets a value indicating whether the property is interested in data validation.
+        /// </summary>
+        /// <remarks>
+        /// Data validation is validation performed at the target of a binding, for example in a
+        /// view model using the INotifyDataErrorInfo interface. Only certain properties on a
+        /// control (such as a TextBox's Text property) will be interested in recieving data
+        /// validation messages so this feature must be explicitly enabled by setting this flag.
+        /// </remarks>
+        public bool EnableDataValidation { get; }
+
         /// <inheritdoc/>
         /// <inheritdoc/>
         object IDirectPropertyMetadata.UnsetValue => UnsetValue;
         object IDirectPropertyMetadata.UnsetValue => UnsetValue;
 
 

+ 5 - 0
src/Avalonia.Base/IDirectPropertyMetadata.cs

@@ -12,5 +12,10 @@ namespace Avalonia
         /// Gets the to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>.
         /// Gets the to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>.
         /// </summary>
         /// </summary>
         object UnsetValue { get; }
         object UnsetValue { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether the property is interested in data validation.
+        /// </summary>
+        bool EnableDataValidation { get; }
     }
     }
 }
 }

+ 4 - 3
src/Avalonia.Base/IPriorityValueOwner.cs

@@ -19,10 +19,11 @@ namespace Avalonia
         void Changed(PriorityValue sender, object oldValue, object newValue);
         void Changed(PriorityValue sender, object oldValue, object newValue);
 
 
         /// <summary>
         /// <summary>
-        /// Called when the validation state of a <see cref="PriorityValue"/> changes.
+        /// Called when a <see cref="BindingNotification"/> is received by a 
+        /// <see cref="PriorityValue"/>.
         /// </summary>
         /// </summary>
         /// <param name="sender">The source of the change.</param>
         /// <param name="sender">The source of the change.</param>
-        /// <param name="status">The validation status.</param>
-        void DataValidationChanged(PriorityValue sender, IValidationStatus status);
+        /// <param name="notification">The notification.</param>
+        void BindingNotificationReceived(PriorityValue sender, BindingNotification notification);
     }
     }
 }
 }

+ 0 - 17
src/Avalonia.Base/Platform/IPclPlatformWrapper.cs

@@ -1,17 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Avalonia.Platform
-{
-    public interface IPclPlatformWrapper
-    {
-        Assembly[] GetLoadedAssemblies();
-        void PostThreadPoolItem(Action cb);
-        IDisposable StartSystemTimer(TimeSpan interval, Action tick);
-        string GetStackTrace();
-    }
-}

+ 39 - 0
src/Avalonia.Base/Platform/IRuntimePlatform.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Platform
+{
+    public interface IRuntimePlatform
+    {
+        Assembly[] GetLoadedAssemblies();
+        void PostThreadPoolItem(Action cb);
+        IDisposable StartSystemTimer(TimeSpan interval, Action tick);
+        string GetStackTrace();
+        RuntimePlatformInfo GetRuntimeInfo();
+    }
+
+    public struct RuntimePlatformInfo
+    {
+        public OperatingSystemType OperatingSystem { get; set; }
+        public bool IsDesktop { get; set; }
+        public bool IsMobile { get; set; }
+        public bool IsCoreClr { get; set; }
+        public bool IsMono { get; set; }
+        public bool IsDotNetFramework { get; set; }
+        public bool IsUnix { get; set; }
+    }
+
+    public enum OperatingSystemType
+    {
+        Unknown,
+        WinNT,
+        Linux,
+        OSX,
+        Android,
+        iOS
+    }
+}

+ 14 - 12
src/Avalonia.Base/PriorityBindingEntry.cs

@@ -93,22 +93,24 @@ namespace Avalonia
 
 
         private void ValueChanged(object value)
         private void ValueChanged(object value)
         {
         {
-            var bindingError = value as BindingError;
+            var notification = value as BindingNotification;
 
 
-            if (bindingError != null)
+            if (notification != null)
             {
             {
-                _owner.Error(this, bindingError);
+                if (notification.HasValue)
+                {
+                    Value = notification.Value;
+                    _owner.Changed(this);
+                }
+
+                if (notification.ErrorType != BindingErrorType.None)
+                {
+                    _owner.Error(this, notification);
+                }
             }
             }
-
-            var validationStatus = value as IValidationStatus;
-
-            if (validationStatus != null)
-            {
-                _owner.Validation(this, validationStatus);
-            }
-            else if (bindingError == null || bindingError.UseFallbackValue)
+            else
             {
             {
-                Value = bindingError == null ? value : bindingError.FallbackValue;
+                Value = value;
                 _owner.Changed(this);
                 _owner.Changed(this);
             }
             }
         }
         }

+ 1 - 12
src/Avalonia.Base/PriorityLevel.cs

@@ -159,22 +159,11 @@ namespace Avalonia
         /// </summary>
         /// </summary>
         /// <param name="entry">The entry that completed.</param>
         /// <param name="entry">The entry that completed.</param>
         /// <param name="error">The error.</param>
         /// <param name="error">The error.</param>
-        public void Error(PriorityBindingEntry entry, BindingError error)
+        public void Error(PriorityBindingEntry entry, BindingNotification error)
         {
         {
             _owner.LevelError(this, error);
             _owner.LevelError(this, error);
         }
         }
 
 
-        /// <summary>
-        /// Invoked when an entry in <see cref="Bindings"/> reports validation status.
-        /// </summary>
-        /// <param name="entry">The entry that completed.</param>
-        /// <param name="validationStatus">The validation status.</param>
-        public void Validation(PriorityBindingEntry entry, IValidationStatus validationStatus)
-        {
-            _owner.LevelValidation(this, validationStatus);
-        }
-
-
         /// <summary>
         /// <summary>
         /// Activates the first binding that has a value.
         /// Activates the first binding that has a value.
         /// </summary>
         /// </summary>

+ 25 - 16
src/Avalonia.Base/PriorityValue.cs

@@ -28,7 +28,7 @@ namespace Avalonia
     {
     {
         private readonly IPriorityValueOwner _owner;
         private readonly IPriorityValueOwner _owner;
         private readonly Type _valueType;
         private readonly Type _valueType;
-        private readonly Dictionary<int, PriorityLevel> _levels = new Dictionary<int, PriorityLevel>();
+        private readonly SingleOrDictionary<int, PriorityLevel> _levels = new SingleOrDictionary<int, PriorityLevel>();
         private object _value;
         private object _value;
         private readonly Func<object, object> _validate;
         private readonly Func<object, object> _validate;
 
 
@@ -77,7 +77,6 @@ namespace Avalonia
         /// </summary>
         /// </summary>
         /// <param name="binding">The binding.</param>
         /// <param name="binding">The binding.</param>
         /// <param name="priority">The binding priority.</param>
         /// <param name="priority">The binding priority.</param>
-        /// <param name="validation">Validation settings for the binding.</param>
         /// <returns>
         /// <returns>
         /// A disposable that will remove the binding.
         /// A disposable that will remove the binding.
         /// </returns>
         /// </returns>
@@ -179,31 +178,21 @@ namespace Avalonia
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Called whenever a priority level validation state changes.
-        /// </summary>
-        /// <param name="priorityLevel">The priority level of the changed entry.</param>
-        /// <param name="validationStatus">The validation status.</param>
-        public void LevelValidation(PriorityLevel priorityLevel, IValidationStatus validationStatus)
-        {
-            _owner.DataValidationChanged(this, validationStatus);
-        }
-
         /// <summary>
         /// <summary>
         /// Called when a priority level encounters an error.
         /// Called when a priority level encounters an error.
         /// </summary>
         /// </summary>
         /// <param name="level">The priority level of the changed entry.</param>
         /// <param name="level">The priority level of the changed entry.</param>
         /// <param name="error">The binding error.</param>
         /// <param name="error">The binding error.</param>
-        public void LevelError(PriorityLevel level, BindingError error)
+        public void LevelError(PriorityLevel level, BindingNotification error)
         {
         {
             Logger.Log(
             Logger.Log(
                 LogEventLevel.Error,
                 LogEventLevel.Error,
                 LogArea.Binding,
                 LogArea.Binding,
                 _owner,
                 _owner,
-                "Error binding to {Target}.{Property}: {Message}",
+                "Error in binding to {Target}.{Property}: {Message}",
                 _owner,
                 _owner,
                 Property,
                 Property,
-                error.Exception.Message);
+                error.Error.Message);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -248,8 +237,14 @@ namespace Avalonia
         /// <param name="priority">The priority level that the value came from.</param>
         /// <param name="priority">The priority level that the value came from.</param>
         private void UpdateValue(object value, int priority)
         private void UpdateValue(object value, int priority)
         {
         {
+            var notification = value as BindingNotification;
             object castValue;
             object castValue;
 
 
+            if (notification != null)
+            {
+                value = (notification.HasValue) ? notification.Value : null;
+            }
+
             if (TypeUtilities.TryCast(_valueType, value, out castValue))
             if (TypeUtilities.TryCast(_valueType, value, out castValue))
             {
             {
                 var old = _value;
                 var old = _value;
@@ -261,7 +256,21 @@ namespace Avalonia
 
 
                 ValuePriority = priority;
                 ValuePriority = priority;
                 _value = castValue;
                 _value = castValue;
-                _owner?.Changed(this, old, _value);
+
+                if (notification?.HasValue == true)
+                {
+                    notification.SetValue(castValue);
+                }
+
+                if (notification == null || notification.HasValue)
+                {
+                    _owner?.Changed(this, old, _value);
+                }
+
+                if (notification != null)
+                {
+                    _owner?.BindingNotificationReceived(this, notification);
+                }
             }
             }
             else
             else
             {
             {

+ 2 - 1
src/Avalonia.Base/PropertyMetadata.cs

@@ -17,7 +17,8 @@ namespace Avalonia
         /// Initializes a new instance of the <see cref="PropertyMetadata"/> class.
         /// Initializes a new instance of the <see cref="PropertyMetadata"/> class.
         /// </summary>
         /// </summary>
         /// <param name="defaultBindingMode">The default binding mode.</param>
         /// <param name="defaultBindingMode">The default binding mode.</param>
-        public PropertyMetadata(BindingMode defaultBindingMode = BindingMode.Default)
+        public PropertyMetadata(
+            BindingMode defaultBindingMode = BindingMode.Default)
         {
         {
             _defaultBindingMode = defaultBindingMode;
             _defaultBindingMode = defaultBindingMode;
         }
         }

+ 2 - 2
src/Avalonia.Base/Threading/SingleThreadDispatcher.cs

@@ -33,7 +33,7 @@ namespace Avalonia.Threading
             }
             }
 
 
             public IDisposable StartTimer(TimeSpan interval, Action tick)
             public IDisposable StartTimer(TimeSpan interval, Action tick)
-                => AvaloniaLocator.Current.GetService<IPclPlatformWrapper>().StartSystemTimer(interval,
+                => AvaloniaLocator.Current.GetService<IRuntimePlatform>().StartSystemTimer(interval,
                     () => _timerJobRunner.Post(tick, DispatcherPriority.Normal));
                     () => _timerJobRunner.Post(tick, DispatcherPriority.Normal));
 
 
             public void Signal() => _evnt.Set();
             public void Signal() => _evnt.Set();
@@ -50,7 +50,7 @@ namespace Avalonia.Threading
         public static Dispatcher StartNew(CancellationToken token)
         public static Dispatcher StartNew(CancellationToken token)
         {
         {
             var dispatcher = new SingleThreadDispatcher();
             var dispatcher = new SingleThreadDispatcher();
-            AvaloniaLocator.Current.GetService<IPclPlatformWrapper>().PostThreadPoolItem(() =>
+            AvaloniaLocator.Current.GetService<IRuntimePlatform>().PostThreadPoolItem(() =>
             {
             {
                 dispatcher.MainLoop(token);
                 dispatcher.MainLoop(token);
             });
             });

+ 23 - 0
src/Avalonia.Base/Utilities/ExceptionUtilities.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Utilities
+{
+    internal static class ExceptionUtilities
+    {
+        public static string GetMessage(Exception e)
+        {
+            var aggregate = e as AggregateException;
+
+            if (aggregate != null)
+            {
+                return string.Join(" | ", aggregate.InnerExceptions.Select(x => x.Message));
+            }
+
+            return e.Message;
+        }
+    }
+}

+ 144 - 0
src/Avalonia.Base/Utilities/SingleOrDictionary.cs

@@ -0,0 +1,144 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Utilities
+{
+    /// <summary>
+    /// Stores either a single key value pair or constructs a dictionary when more than one value is stored.
+    /// </summary>
+    /// <typeparam name="TKey">The type of the key.</typeparam>
+    /// <typeparam name="TValue">The type of the value.</typeparam>
+    public class SingleOrDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
+    {
+        private KeyValuePair<TKey, TValue>? _singleValue;
+        private Dictionary<TKey, TValue> dictionary;
+
+        public void Add(TKey key, TValue value)
+        {
+            if (_singleValue != null)
+            {
+                dictionary = new Dictionary<TKey, TValue>();
+                ((ICollection<KeyValuePair<TKey, TValue>>)dictionary).Add(_singleValue.Value);
+                _singleValue = null;
+            }
+
+            if (dictionary != null)
+            {
+                dictionary.Add(key, value);
+            }
+            else
+            {
+                _singleValue = new KeyValuePair<TKey, TValue>(key, value);
+            }
+        }
+
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            if (dictionary == null)
+            {
+                if (!_singleValue.HasValue || !_singleValue.Value.Key.Equals(key))
+                {
+                    value = default(TValue);
+                    return false;
+                }
+                else
+                {
+                    value = _singleValue.Value.Value;
+                    return true;
+                }
+            }
+            else
+            {
+                return dictionary.TryGetValue(key, out value);
+            }
+        }
+
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+        {
+            if (dictionary == null)
+            {
+                if (_singleValue.HasValue)
+                {
+                    return new SingleEnumerator<KeyValuePair<TKey, TValue>>(_singleValue.Value);
+                }
+            }
+            else
+            {
+                return dictionary.GetEnumerator();
+            }
+            return Enumerable.Empty<KeyValuePair<TKey, TValue>>().GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        public IEnumerable<TValue> Values
+        {
+            get
+            {
+                if(dictionary == null)
+                {
+                    if (_singleValue.HasValue)
+                    {
+                        return new[] { _singleValue.Value.Value };
+                    }
+                }
+                else
+                {
+                    return dictionary.Values;
+                }
+                return Enumerable.Empty<TValue>();
+            }
+        }
+
+        private class SingleEnumerator<T> : IEnumerator<T>
+        {
+            private T value;
+            private int index = -1;
+
+            public SingleEnumerator(T value)
+            {
+                this.value = value;
+            }
+
+            public T Current
+            {
+                get
+                {
+                    if (index == 0)
+                    {
+                        return value;
+                    }
+                    else
+                    {
+                        throw new InvalidOperationException();
+                    }
+                }
+            }
+
+            object IEnumerator.Current => Current;
+
+            public void Dispose()
+            {
+            }
+
+            public bool MoveNext()
+            {
+                index++;
+                return index < 1;
+            }
+
+            public void Reset()
+            {
+                index = -1;
+            }
+        }
+
+    }
+}

+ 41 - 0
src/Avalonia.Base/Utilities/TypeUtilities.cs

@@ -27,6 +27,21 @@ namespace Avalonia.Utilities
             { typeof(short), new List<Type> { typeof(byte) } }
             { typeof(short), new List<Type> { typeof(byte) } }
         };
         };
 
 
+        private static readonly Type[] NumericTypes = new[]
+        {
+            typeof(Byte),
+            typeof(Decimal),
+            typeof(Double),
+            typeof(Int16),
+            typeof(Int32),
+            typeof(Int64),
+            typeof(SByte),
+            typeof(Single),
+            typeof(UInt16),
+            typeof(UInt32),
+            typeof(UInt64),
+        };
+
         /// <summary>
         /// <summary>
         /// Returns a value indicating whether null can be assigned to the specified type.
         /// Returns a value indicating whether null can be assigned to the specified type.
         /// </summary>
         /// </summary>
@@ -208,5 +223,31 @@ namespace Avalonia.Utilities
                 return null;
                 return null;
             }
             }
         }
         }
+
+        /// <summary>
+        /// Determines if a type is numeric.  Nullable numeric types are considered numeric.
+        /// </summary>
+        /// <returns>
+        /// True if the type is numberic; otherwise false.
+        /// </returns>
+        /// <remarks>
+        /// Boolean is not considered numeric.
+        /// </remarks>
+        public static bool IsNumeric(Type type)
+        {
+            if (type == null)
+            {
+                return false;
+            }
+
+            if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+            {
+                return IsNumeric(Nullable.GetUnderlyingType(type));
+            }
+            else
+            {
+                return NumericTypes.Contains(type);
+            }
+        }
     }
     }
 }
 }

+ 54 - 0
src/Avalonia.Base/Utilities/WeakObservable.cs

@@ -0,0 +1,54 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Reactive;
+using System.Reactive.Linq;
+
+namespace Avalonia.Utilities
+{
+    /// <summary>
+    /// Provides extension methods for working with weak event handlers.
+    /// </summary>
+    public static class WeakObservable
+    {
+        /// <summary>
+        /// Converts a .NET event conforming to the standard .NET event pattern into an observable
+        /// sequence, subscribing weakly.
+        /// </summary>
+        /// <typeparam name="TEventArgs">The type of the event args.</typeparam>
+        /// <param name="target">Object instance that exposes the event to convert.</param>
+        /// <param name="eventName">Name of the event to convert.</param>
+        /// <returns></returns>
+        public static IObservable<EventPattern<object, TEventArgs>> FromEventPattern<TEventArgs>(
+            object target, 
+            string eventName)
+            where TEventArgs : EventArgs
+        {
+            Contract.Requires<ArgumentNullException>(target != null);
+            Contract.Requires<ArgumentNullException>(eventName != null);
+
+            return Observable.Create<EventPattern<object, TEventArgs>>(observer =>
+            {
+                var handler = new Handler<TEventArgs>(observer);
+                WeakSubscriptionManager.Subscribe(target, eventName, handler);
+                return () => WeakSubscriptionManager.Unsubscribe(target, eventName, handler);
+            }).Publish().RefCount();
+        }
+
+        private class Handler<TEventArgs> : IWeakSubscriber<TEventArgs> where TEventArgs : EventArgs
+        {
+            private IObserver<EventPattern<object, TEventArgs>> _observer;
+
+            public Handler(IObserver<EventPattern<object, TEventArgs>> observer)
+            {
+                _observer = observer;
+            }
+
+            public void OnEvent(object sender, TEventArgs e)
+            {
+                _observer.OnNext(new EventPattern<object, TEventArgs>(sender, e));
+            }
+        }
+    }
+}

Неке датотеке нису приказане због велике количине промена