Ver Fonte

Merge branch 'master' into render-loop

 Conflicts:
	src/Android/Avalonia.Android/AndroidPlatform.cs
	src/Avalonia.Controls/TopLevel.cs
Steven Kirk há 9 anos atrás
pai
commit
b602e8caba
100 ficheiros alterados com 1507 adições e 470 exclusões
  1. 68 11
      Avalonia.sln
  2. 10 15
      build.cake
  3. 156 0
      docs/spec/binding-from-code.md
  4. 99 0
      docs/spec/binding-from-xaml.md
  5. 4 0
      docs/spec/toc.yml
  6. 2 0
      docs/spec/working-with-properties.md
  7. 2 18
      docs/tutorial/from-wpf.md
  8. 19 0
      samples/ControlCatalog.Android/Assets/AboutAssets.txt
  9. 169 0
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  10. 26 0
      samples/ControlCatalog.Android/ControlCatalog.Android.v2.ncrunchproject
  11. 4 0
      samples/ControlCatalog.Android/GettingStarted.Xamarin
  12. 46 0
      samples/ControlCatalog.Android/MainActivity.cs
  13. 5 0
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  14. 30 0
      samples/ControlCatalog.Android/Properties/AssemblyInfo.cs
  15. 44 0
      samples/ControlCatalog.Android/Resources/AboutResources.txt
  16. 114 0
      samples/ControlCatalog.Android/Resources/Resource.Designer.cs
  17. BIN
      samples/ControlCatalog.Android/Resources/drawable/Icon.png
  18. 13 0
      samples/ControlCatalog.Android/Resources/layout/Main.axml
  19. 5 0
      samples/ControlCatalog.Android/Resources/values/Strings.xml
  20. 21 8
      src/Android/Avalonia.Android/AndroidPlatform.cs
  21. 1 1
      src/Android/Avalonia.Android/Platform/SkiaPlatform/MainWindowImpl.cs
  22. 5 8
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  23. 70 12
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  24. 2 0
      src/Avalonia.Animation/Avalonia.Animation.csproj
  25. 2 1
      src/Avalonia.Base/Avalonia.Base.csproj
  26. 0 2
      src/Avalonia.Base/AvaloniaObject.cs
  27. 7 1
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  28. 2 3
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  29. 0 1
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  30. 0 2
      src/Avalonia.Base/Collections/AvaloniaList.cs
  31. 25 32
      src/Avalonia.Base/Data/BindingChainException.cs
  32. 1 1
      src/Avalonia.Base/Data/InstancedBinding.cs
  33. 0 3
      src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
  34. 0 1
      src/Avalonia.Base/PriorityBindingEntry.cs
  35. 4 2
      src/Avalonia.Controls/AppBuilderBase.cs
  36. 1 0
      src/Avalonia.Controls/Avalonia.Controls.csproj
  37. 4 4
      src/Avalonia.Controls/Canvas.cs
  38. 0 1
      src/Avalonia.Controls/Classes.cs
  39. 0 5
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  40. 2 1
      src/Avalonia.Controls/HotkeyManager.cs
  41. 0 2
      src/Avalonia.Controls/Platform/PlatformManager.cs
  42. 3 2
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  43. 61 6
      src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
  44. 50 3
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  45. 6 15
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  46. 1 1
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  47. 1 1
      src/Avalonia.Controls/ScrollViewer.cs
  48. 60 19
      src/Avalonia.Controls/TextBox.cs
  49. 0 1
      src/Avalonia.Controls/TopLevel.cs
  50. 24 94
      src/Avalonia.Controls/Utils/StringUtils.cs
  51. 31 28
      src/Avalonia.Controls/Utils/UndoRedoHelper.cs
  52. 1 0
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  53. 0 3
      src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
  54. 4 4
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  55. 17 5
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  56. 1 0
      src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
  57. 25 0
      src/Avalonia.Input/AccessKeyHandler.cs
  58. 1 0
      src/Avalonia.Input/Avalonia.Input.csproj
  59. 0 1
      src/Avalonia.Input/Navigation/DirectionalNavigation.cs
  60. 1 0
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  61. 1 0
      src/Avalonia.Layout/Avalonia.Layout.csproj
  62. 1 1
      src/Avalonia.Layout/LayoutManager.cs
  63. 2 1
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  64. 12 0
      src/Avalonia.Logging.Serilog/SerilogLogger.cs
  65. 1 0
      src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj
  66. 21 3
      src/Avalonia.SceneGraph/Media/DrawingContext.cs
  67. 10 1
      src/Avalonia.SceneGraph/Media/FormattedText.cs
  68. 1 0
      src/Avalonia.SceneGraph/Media/Imaging/RenderTargetBitmap.cs
  69. 1 0
      src/Avalonia.SceneGraph/Media/MatrixTransform.cs
  70. 2 6
      src/Avalonia.SceneGraph/Media/PathMarkupParser.cs
  71. 1 0
      src/Avalonia.SceneGraph/Media/RotateTransform.cs
  72. 1 0
      src/Avalonia.SceneGraph/Media/ScaleTransform.cs
  73. 1 1
      src/Avalonia.SceneGraph/Media/StreamGeometry.cs
  74. 1 0
      src/Avalonia.SceneGraph/Media/Transform.cs
  75. 1 0
      src/Avalonia.SceneGraph/Media/TranslateTransform.cs
  76. 1 0
      src/Avalonia.Styling/Avalonia.Styling.csproj
  77. 3 3
      src/Avalonia.Styling/Styling/SelectorMatch.cs
  78. 0 1
      src/Avalonia.Styling/Styling/Style.cs
  79. 1 0
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  80. 4 1
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  81. 2 0
      src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs
  82. 3 1
      src/Gtk/Avalonia.Gtk/WindowImplBase.cs
  83. 6 6
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  84. 1 1
      src/Markup/Avalonia.Markup.Xaml/OmniXAML
  85. 6 4
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  86. 14 43
      src/Markup/Avalonia.Markup/Data/ExpressionNode.cs
  87. 7 7
      src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs
  88. 42 0
      src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs
  89. 0 33
      src/Markup/Avalonia.Markup/Data/MarkupBindingChainNullException.cs
  90. 10 0
      src/Markup/Avalonia.Markup/Data/Parsers/ExpressionParser.cs
  91. 2 2
      src/Markup/Avalonia.Markup/Data/Plugins/IStreamPlugin.cs
  92. 3 15
      src/Markup/Avalonia.Markup/Data/Plugins/ObservableStreamPlugin.cs
  93. 2 2
      src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs
  94. 31 0
      src/Markup/Avalonia.Markup/Data/StreamNode.cs
  95. 10 2
      src/Shared/PlatformSupport/AssetLoader.cs
  96. 7 0
      src/Skia/Avalonia.Skia.Android.TestApp/App.cs
  97. 24 8
      src/Skia/Avalonia.Skia.Android.TestApp/Avalonia.Skia.Android.TestApp.csproj
  98. 16 9
      src/Skia/Avalonia.Skia.Android.TestApp/MainActivity.cs
  99. 2 0
      src/Skia/Avalonia.Skia.Android.TestApp/Resources/Resource.Designer.cs
  100. 3 1
      src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj

+ 68 - 11
Avalonia.sln

@@ -172,6 +172,7 @@ EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}"
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\RenderTest\RenderTest.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\RenderTest\RenderTest.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
 EndProject
 EndProject
 Global
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -196,7 +197,6 @@ Global
 		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
-		samples\TestApplicationShared\TestApplicationShared.projitems*{ff69b927-c545-49ae-8e16-3d14d621aa12}*SharedItemsImports = 4
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
 		Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -2120,24 +2120,24 @@ Global
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|Mono.ActiveCfg = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|Mono.ActiveCfg = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|x86.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|x86.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|x86.Build.0 = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|x86.Build.0 = Release|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.ActiveCfg = Debug|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.Build.0 = Debug|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.ActiveCfg = Debug|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.Build.0 = Debug|x86
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Mono.ActiveCfg = Debug|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Mono.ActiveCfg = Debug|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.Build.0 = Debug|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.ActiveCfg = Debug|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.Build.0 = Debug|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.ActiveCfg = Release|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.Build.0 = Release|x86
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhone.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhone.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhone.Build.0 = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhone.Build.0 = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Mono.ActiveCfg = Release|Any CPU
 		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Mono.ActiveCfg = Release|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.ActiveCfg = Release|Any CPU
-		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.Build.0 = Release|Any CPU
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.ActiveCfg = Release|x86
+		{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.Build.0 = Release|x86
 		{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
 		{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
 		{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
 		{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
 		{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
 		{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -2404,6 +2404,62 @@ Global
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|Mono.Build.0 = Release|Any CPU
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|Mono.Build.0 = Release|Any CPU
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|x86.ActiveCfg = Release|Any CPU
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|x86.ActiveCfg = Release|Any CPU
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|x86.Build.0 = Release|Any CPU
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|x86.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Mono.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Mono.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|x86.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Mono.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Mono.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|x86.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|x86.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.Deploy.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|x86.Build.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|x86.Deploy.0 = Debug|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Mono.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.ActiveCfg = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.Build.0 = Release|Any CPU
+		{29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.Deploy.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
@@ -2460,5 +2516,6 @@ Global
 		{C7A69145-60B6-4882-97D6-A3921DD43978} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{C7A69145-60B6-4882-97D6-A3921DD43978} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{BD7F352C-6DC1-4740-BAF2-2D34A038728C} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{BD7F352C-6DC1-4740-BAF2-2D34A038728C} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 	EndGlobalSection
 	EndGlobalSection
 EndGlobal
 EndGlobal

+ 10 - 15
build.cake

@@ -635,21 +635,19 @@ Task("Run-Unit-Tests")
     .WithCriteria(() => !skipTests)
     .WithCriteria(() => !skipTests)
     .Does(() =>
     .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);
+    var unitTests = GetDirectories("./tests/Avalonia.*.UnitTests")
+        .Select(dir => System.IO.Path.GetFileName(dir.FullPath))
+        .Where(name => isRunningOnWindows ? true : !(name.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0))
+        .Select(name => MakeAbsolute(File("./tests/" + name + "/bin/" + dirSuffix + "/" + name + ".dll")))
+        .ToList();
 
 
     if (isRunningOnWindows)
     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");
+        var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*.Tests.dll") + 
+                           GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*.LeakTests.dll") + 
+                           GetFiles("./tests/Avalonia.RenderTests/bin/" + dirSuffix + "/*.RenderTests.dll");
 
 
-        unitTests += windowsTests;
+        unitTests.AddRange(windowsTests);
     }
     }
 
 
     var toolPath = (isPlatformAnyCPU || isPlatformX86) ? 
     var toolPath = (isPlatformAnyCPU || isPlatformX86) ? 
@@ -662,10 +660,7 @@ Task("Run-Unit-Tests")
         Parallelism = ParallelismOption.None 
         Parallelism = ParallelismOption.None 
     };
     };
 
 
-    if (isRunningOnWindows)
-    {
-        settings.NoAppDomain = false;
-    }
+    settings.NoAppDomain = !isRunningOnWindows;
 
 
     foreach (var file in unitTests)
     foreach (var file in unitTests)
     {
     {

+ 156 - 0
docs/spec/binding-from-code.md

@@ -0,0 +1,156 @@
+# Binding from Code
+
+Avalonia binding from code works somewhat differently to WPF/UWP. At the low level, Avalonia's
+binding system is based on Reactive Extensions' `IObservable` which is then built upon by XAML
+bindings (which can also be instantiated in code).
+
+## Binding to an observable
+
+You can bind a property to an observable using the `AvaloniaObject.Bind` method:
+
+```csharp
+// We use an Rx Subject here so we can push new values using OnNext
+var source = new Subject<string>();
+var textBlock = new TextBlock();
+
+// Bind TextBlock.Text to source
+textBlock.Bind(TextBlock.TextProperty, source);
+
+// Set textBlock.Text to "hello"
+source.OnNext("hello");
+// Set textBlock.Text to "world!"
+source.OnNext("world!");
+```
+
+## Binding priorities
+
+You can also pass a priority to a binding. *Note: Priorities only apply to styled properties: they*
+*are ignored for direct properties.*
+
+The priority is passed using the `BindingPriority` enum, which looks like this:
+
+```csharp
+/// <summary>
+/// The priority of a binding.
+/// </summary>
+public enum BindingPriority
+{
+    /// <summary>
+    /// A value that comes from an animation.
+    /// </summary>
+    Animation = -1,
+
+    /// <summary>
+    /// A local value: this is the default.
+    /// </summary>
+    LocalValue = 0,
+
+    /// <summary>
+    /// A triggered style binding.
+    /// </summary>
+    /// <remarks>
+    /// A style trigger is a selector such as .class which overrides a
+    /// <see cref="TemplatedParent"/> binding. In this way, a basic control can have
+    /// for example a Background from the templated parent which changes when the
+    /// control has the :pointerover class.
+    /// </remarks>
+    StyleTrigger,
+
+    /// <summary>
+    /// A binding to a property on the templated parent.
+    /// </summary>
+    TemplatedParent,
+
+    /// <summary>
+    /// A style binding.
+    /// </summary>
+    Style,
+
+    /// <summary>
+    /// The binding is uninitialized.
+    /// </summary>
+    Unset = int.MaxValue,
+}
+```
+
+Bindings with a priority with a smaller number take precedence over bindings with a higher value
+priority, and bindings added more recently take precedence over other bindings with the same
+priority. Whenever the binding produces `AvaloniaProperty.UnsetValue` then the next binding in the
+priority order is selected.
+
+## Setting a binding in an object initializer
+
+It is often useful to set up bindings in object initializers. You can do this using the indexer:
+
+```csharp
+var source = new Subject<string>();
+var textBlock = new TextBlock
+{
+    Foreground = Brushes.Red,
+    MaxWidth = 200,
+    [!TextBlock.TextProperty] = source.ToBinding(),
+};
+```
+
+Using this method you can also easily bind a property on one control to a property on another:
+
+```csharp
+var textBlock1 = new TextBlock();
+var textBlock2 = new TextBlock
+{
+    Foreground = Brushes.Red,
+    MaxWidth = 200,
+    [!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty],
+};
+```
+
+Of course the indexer can be used outside object initializers too:
+
+```csharp
+textBlock2[!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty];
+```
+
+# Transforming binding values
+
+Because we're working with observables, we can easily transform the values we're binding!
+
+```csharp
+var source = new Subject<string>();
+var textBlock = new TextBlock
+{
+    Foreground = Brushes.Red,
+    MaxWidth = 200,
+    [!TextBlock.TextProperty] = source.Select(x => "Hello " + x).ToBinding(),
+};
+```
+
+# Using XAML bindings from code
+
+Sometimes when you want the additional features that XAML bindings provide, it's easier to use XAML bindings from code. For example, using only observables you could bind to a property on `DataContext` like this:
+
+```csharp
+var textBlock = new TextBlock();
+var viewModelProperty = textBlock.GetObservable(TextBlock.DataContext)
+    .OfType<MyViewModel>()
+    .Select(x => x?.Name);
+textBlock.Bind(TextBlock, viewModelProperty);
+```
+
+However, it might be preferable to use a XAML binding in this case:
+
+```csharp
+var textBlock = new TextBlock
+{
+    [!TextBlock.TextProperty] = new Binding("Name")
+};
+```
+
+By using XAML binding objects, you get access to binding to named controls and [all the other features that XAML bindings bring](binding-from.xaml.md):
+
+```csharp
+var textBlock = new TextBlock
+{
+    [!TextBlock.TextProperty] = new Binding("Text") { ElementName = "other" }
+};
+```
+

+ 99 - 0
docs/spec/binding-from-xaml.md

@@ -0,0 +1,99 @@
+# Binding from XAML
+
+Binding from XAML works on the whole the same as in other XAML frameworks: you use the `{Binding}`
+markup extension. Avalonia does have some extra syntacic niceties however. Here's an overview of
+what you can currently do in Avalonia:
+
+## Binding to a property on the DataContext
+
+By default a binding binds to a property on the `DataContext`, e.g.:
+
+```xml
+<!-- Binds to the tb.DataContext.Name property -->
+<TextBlock Name="tb" Text="{Binding Name}"/>
+<!-- Which is the same as ('Path' is optional) -->
+<TextBlock Name="tb" Text="{Binding Path=Name}"/>
+```
+
+An empty binding binds to DataContext itself
+
+```xml
+<!-- Binds to the tb.DataContext property -->
+<TextBlock Name="tb" Text="{Binding}"/>
+<!-- Which is the same as -->
+<TextBlock Name="tb" Text="{Binding .}"/>
+```
+
+This usage is identical to WPF/UWP etc.
+
+## Two way bindings and more
+
+You can also specify a binding `Mode`:
+
+```xml
+<!-- Bind two-way to the property (although this is actually the default binding mode for
+     TextBox.Text) so strictly speaking it's unnecessary here) -->
+<TextBox Name="tb" Text="{Binding Name, Mode=TwoWay}"/>
+```
+
+This usage is identical to WPF/UWP etc.
+
+## Binding to a property on the templated parent
+
+When you're creating a control template and you want to bind to the templated parent you can use:
+
+```xml
+<TextBlock Name="tb" Text="{TemplateBinding Caption}"/>
+<!-- Which is short for -->
+<TextBlock Name="tb" Text="{Binding Caption, RelativeSource={RelativeSource TemplatedParent}}"/>
+```
+
+This usage is identical to WPF/UWP etc.
+
+## Binding to a named control
+
+If you want to bind to a property on another (named) control, you can use `ElementName` as in
+WPF/UWP:
+
+```xml
+<!-- Binds to the Tag property of a control named "other" -->
+<TextBlock Text="{Binding Tag, ElementName=other}"/>
+```
+
+However Avalonia also introduces a shorthand syntax for this:
+
+```xml
+<TextBlock Text="{Binding #other.Tag}"/>
+```
+
+## Negating bindings
+
+You can also negate the value of a binding using the `!` operator:
+
+```xml
+<TextBox IsEnabled="{Binding !HasErrors}"/>
+```
+
+Here, the `TextBox` will only be enabled when the view model signals that it has no errors. Behind
+the scenes, Avalonia tries to convert the incoming value to a boolean, and if it can be converted
+it negates the value. If the incoming value cannot be converted to a boolean then no value will be
+pushed to the binding target.
+
+This syntax is specific to Avalonia.
+
+## Binding to tasks and observables
+
+You can subscribe to the result of a task or an observable by using the `^` stream binding operator.
+
+```xml
+<!-- If DataContext.Name is an IObservable<string> then this will bind to the length of each
+     string produced by the observable as each value is produced -->
+<TextBlock Text="{Binding Name^.Length}"/>
+```
+
+This syntax is specific to Avalonia.
+
+*Note: the stream operator is actually extensible, see
+[here](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup/Data/Plugins/IStreamPlugin.cs)
+for the interface to implement and [here](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs#L47)
+for the registration.*

+ 4 - 0
docs/spec/toc.yml

@@ -8,3 +8,7 @@
   href: working-with-properties.md
   href: working-with-properties.md
 - name: Logging
 - name: Logging
   href: logging.md
   href: logging.md
+- name: Binding from XAML
+  href: binding-from-xaml.md
+- name: Binding from Code
+  href: binding-from-code.md

+ 2 - 0
docs/spec/working-with-properties.md

@@ -71,6 +71,8 @@ property to the first:
   Console.WriteLine(textBlock2.Text);
   Console.WriteLine(textBlock2.Text);
 ```
 ```
 
 
+To read more about creating bindings from code, see [Binding from Code](binding-from-code.md).
+
 # Subscribing to a Property on Any Object
 # Subscribing to a Property on Any Object
 
 
 The `GetObservable` method returns an observable that tracks changes to a
 The `GetObservable` method returns an observable that tracks changes to a

+ 2 - 18
docs/tutorial/from-wpf.md

@@ -40,17 +40,6 @@ placed in a `DataTemplates` collection on each control (and on `Application`):
         <ContentControl Content="{Binding Foo}"/>
         <ContentControl Content="{Binding Foo}"/>
     <UserControl>    
     <UserControl>    
 
 
-`ItemsControl`s don't currently have an `ItemTemplate` property: instead just
-place the template for your items into the control's `DataTemplates`, e.g.
-
-    <ListBox Items="ItemsSource">
-        <ListBox.DataTemplates>
-            <DataTemplate>
-                <TextBlock Text="{Binding Caption}"/>
-            </DataTemplate>
-        </ListBox.DataTemplates>
-    </ListBox>
-
 Data templates in Avalonia can also target interfaces and derived classes (which
 Data templates in Avalonia can also target interfaces and derived classes (which
 cannot be done in WPF) and so the order of `DataTemplate`s can be important:
 cannot be done in WPF) and so the order of `DataTemplate`s can be important:
 `DataTemplate`s  within the same collection are evaluated in declaration order
 `DataTemplate`s  within the same collection are evaluated in declaration order
@@ -92,13 +81,8 @@ referred to using the `{StyleResource}` markup extension both inside and outside
 styles.
 styles.
 
 
 For non-style-related resources, we suggest defining them in code and referring
 For non-style-related resources, we suggest defining them in code and referring
-to them in markup using the `{Static}` markup extension. There are [various
-reasons](http://www.codemag.com/article/1501091) for this, but briefly:
-
-- Resources have to be parsed
-- The tree has to be traversed to find them
-- XAML doesn't handle immutable objects
-- XAML syntax can be long-winded compared to C#
+to them in markup using the `{Static}` markup extension. To read more about the reasoning for this,
+see [this issue comment](https://github.com/AvaloniaUI/Avalonia/issues/462#issuecomment-191849723).
 
 
 ## Grid
 ## Grid
 
 

+ 19 - 0
samples/ControlCatalog.Android/Assets/AboutAssets.txt

@@ -0,0 +1,19 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories) and given a Build Action of "AndroidAsset".
+
+These files will be deployed with you package and will be accessible using Android's
+AssetManager, like this:
+
+public class ReadAsset : Activity
+{
+	protected override void OnCreate (Bundle bundle)
+	{
+		base.OnCreate (bundle);
+
+		InputStream input = Assets.Open ("my_asset.txt");
+	}
+}
+
+Additionally, some Android functions will automatically load asset files:
+
+Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

+ 169 - 0
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{29132311-1848-4FD6-AE0C-4FF841151BD3}</ProjectGuid>
+    <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ControlCatalog.Android</RootNamespace>
+    <AssemblyName>ControlCatalog.Android</AssemblyName>
+    <FileAlignment>512</FileAlignment>
+    <AndroidApplication>true</AndroidApplication>
+    <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+    <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
+    <TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
+    <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>True</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
+    <AndroidLinkMode>None</AndroidLinkMode>
+    <EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
+    <BundleAssemblies>False</BundleAssemblies>
+    <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
+    <AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
+    <Debugger>Xamarin</Debugger>
+    <AndroidEnableMultiDex>False</AndroidEnableMultiDex>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
+    <AndroidLinkMode>Full</AndroidLinkMode>
+    <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
+    <BundleAssemblies>False</BundleAssemblies>
+    <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
+    <AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
+    <Debugger>Xamarin</Debugger>
+    <AotAssemblies>False</AotAssemblies>
+    <EnableLLVM>False</EnableLLVM>
+    <AndroidEnableMultiDex>False</AndroidEnableMultiDex>
+    <EnableProguard>False</EnableProguard>
+    <DebugSymbols>False</DebugSymbols>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Mono.Android" />
+    <Reference Include="mscorlib" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MainActivity.cs" />
+    <Compile Include="Resources\Resource.Designer.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="GettingStarted.Xamarin" />
+    <None Include="Resources\AboutResources.txt" />
+    <None Include="Assets\AboutAssets.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <AndroidResource Include="Resources\layout\Main.axml">
+      <SubType>Designer</SubType>
+    </AndroidResource>
+  </ItemGroup>
+  <ItemGroup>
+    <AndroidResource Include="Resources\values\Strings.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <AndroidResource Include="Resources\drawable\Icon.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Properties\AndroidManifest.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
+      <Project>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</Project>
+      <Name>Avalonia.Android</Name>
+    </ProjectReference>
+    <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.HtmlRenderer\Avalonia.HtmlRenderer.csproj">
+      <Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
+      <Name>Avalonia.HtmlRenderer</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.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.Android\Avalonia.Skia.Android.csproj">
+      <Project>{bd43f7c0-396b-4aa1-bad9-dfde54d51298}</Project>
+      <Name>Avalonia.Skia.Android</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj">
+      <Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
+      <Name>ControlCatalog</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.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/ControlCatalog.Android/ControlCatalog.Android.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>

+ 4 - 0
samples/ControlCatalog.Android/GettingStarted.Xamarin

@@ -0,0 +1,4 @@
+<GettingStarted>
+	<LocalContent>GS\Android\CS\AndroidApp\GettingStarted.html</LocalContent>
+	<EmbeddedNavigation>false</EmbeddedNavigation>
+</GettingStarted>

+ 46 - 0
samples/ControlCatalog.Android/MainActivity.cs

@@ -0,0 +1,46 @@
+using System;
+using Android.App;
+
+using Android.OS;
+using Android.Content.PM;
+using Avalonia.Android.Platform.Specific;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Avalonia.Styling;
+using Avalonia.Themes.Default;
+using Avalonia;
+
+namespace ControlCatalog.Android
+{
+    [Activity(Label = "ControlCatalog.Android", MainLauncher = true, Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)]
+    public class MainActivity : AvaloniaActivity
+    {
+        public MainActivity() : base(typeof (App))
+        {
+
+        }
+
+        protected override void OnCreate(Bundle savedInstanceState)
+        {
+            base.OnCreate(savedInstanceState);
+
+            App app;
+            if (Avalonia.Application.Current != null)
+                app = (App)Avalonia.Application.Current;
+            else
+            {
+                app = new App();
+                AppBuilder.Configure(app)
+                    .UseAndroid()
+                    .UseSkia()
+                    .SetupWithoutStarting();
+            }
+
+            var mainWindow = new MainWindow();
+            app.Run(mainWindow);
+        }
+    }
+}
+

+ 5 - 0
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ControlCatalog.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
+	<uses-sdk />
+	<application android:label="ControlCatalog.Android"></application>
+</manifest>

+ 30 - 0
samples/ControlCatalog.Android/Properties/AssemblyInfo.cs

@@ -0,0 +1,30 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Android.App;
+
+// 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("ControlCatalog.Android")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ControlCatalog.Android")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+
+// 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")]

+ 44 - 0
samples/ControlCatalog.Android/Resources/AboutResources.txt

@@ -0,0 +1,44 @@
+Images, layout descriptions, binary blobs and string dictionaries can be included 
+in your application as resource files.  Various Android APIs are designed to 
+operate on the resource IDs instead of dealing with images, strings or binary blobs 
+directly.
+
+For example, a sample Android app that contains a user interface layout (main.axml),
+an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 
+would keep its resources in the "Resources" directory of the application:
+
+Resources/
+    drawable/
+        icon.png
+
+    layout/
+        main.axml
+
+    values/
+        strings.xml
+
+In order to get the build system to recognize Android resources, set the build action to
+"AndroidResource".  The native Android APIs do not operate directly with filenames, but 
+instead operate on resource IDs.  When you compile an Android application that uses resources, 
+the build system will package the resources for distribution and generate a class called "R" 
+(this is an Android convention) that contains the tokens for each one of the resources 
+included. For example, for the above Resources layout, this is what the R class would expose:
+
+public class R {
+    public class drawable {
+        public const int icon = 0x123;
+    }
+
+    public class layout {
+        public const int main = 0x456;
+    }
+
+    public class strings {
+        public const int first_string = 0xabc;
+        public const int second_string = 0xbcd;
+    }
+}
+
+You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 
+to reference the layout/main.axml file, or R.strings.first_string to reference the first 
+string in the dictionary file values/strings.xml.

+ 114 - 0
samples/ControlCatalog.Android/Resources/Resource.Designer.cs

@@ -0,0 +1,114 @@
+#pragma warning disable 1591
+//------------------------------------------------------------------------------
+// <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>
+//------------------------------------------------------------------------------
+
+[assembly: global::Android.Runtime.ResourceDesignerAttribute("ControlCatalog.Android.Resource", IsApplication=true)]
+
+namespace ControlCatalog.Android
+{
+	
+	
+	[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
+	public partial class Resource
+	{
+		
+		static Resource()
+		{
+			global::Android.Runtime.ResourceIdManager.UpdateIdValues();
+		}
+		
+		public static void UpdateIdValues()
+		{
+			global::Avalonia.Android.Resource.String.ApplicationName = global::ControlCatalog.Android.Resource.String.ApplicationName;
+			global::Avalonia.Android.Resource.String.Hello = global::ControlCatalog.Android.Resource.String.Hello;
+		}
+		
+		public partial class Attribute
+		{
+			
+			static Attribute()
+			{
+				global::Android.Runtime.ResourceIdManager.UpdateIdValues();
+			}
+			
+			private Attribute()
+			{
+			}
+		}
+		
+		public partial class Drawable
+		{
+			
+			// aapt resource value: 0x7f020000
+			public const int Icon = 2130837504;
+			
+			static Drawable()
+			{
+				global::Android.Runtime.ResourceIdManager.UpdateIdValues();
+			}
+			
+			private Drawable()
+			{
+			}
+		}
+		
+		public partial class Id
+		{
+			
+			// aapt resource value: 0x7f050000
+			public const int MyButton = 2131034112;
+			
+			static Id()
+			{
+				global::Android.Runtime.ResourceIdManager.UpdateIdValues();
+			}
+			
+			private Id()
+			{
+			}
+		}
+		
+		public partial class Layout
+		{
+			
+			// aapt resource value: 0x7f030000
+			public const int Main = 2130903040;
+			
+			static Layout()
+			{
+				global::Android.Runtime.ResourceIdManager.UpdateIdValues();
+			}
+			
+			private Layout()
+			{
+			}
+		}
+		
+		public partial class String
+		{
+			
+			// aapt resource value: 0x7f040001
+			public const int ApplicationName = 2130968577;
+			
+			// aapt resource value: 0x7f040000
+			public const int Hello = 2130968576;
+			
+			static String()
+			{
+				global::Android.Runtime.ResourceIdManager.UpdateIdValues();
+			}
+			
+			private String()
+			{
+			}
+		}
+	}
+}
+#pragma warning restore 1591

BIN
samples/ControlCatalog.Android/Resources/drawable/Icon.png


+ 13 - 0
samples/ControlCatalog.Android/Resources/layout/Main.axml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+<Button  
+    android:id="@+id/MyButton"
+    android:layout_width="match_parent" 
+    android:layout_height="wrap_content" 
+    android:text="@string/Hello"
+    />
+</LinearLayout>

+ 5 - 0
samples/ControlCatalog.Android/Resources/values/Strings.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="Hello">Hello World, Click Me!</string>
+    <string name="ApplicationName">ControlCatalog.Android</string>
+</resources>

+ 21 - 8
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -1,16 +1,24 @@
 using Avalonia.Android.Platform;
 using Avalonia.Android.Platform;
 using Avalonia.Android.Platform.Input;
 using Avalonia.Android.Platform.Input;
-using Avalonia.Android.Platform.Specific;
+using Avalonia.Android.Platform.SkiaPlatform;
+using Avalonia.Controls;
 using Avalonia.Controls.Platform;
 using Avalonia.Controls.Platform;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Input.Platform;
 using Avalonia.Platform;
 using Avalonia.Platform;
 using Avalonia.Shared.PlatformSupport;
 using Avalonia.Shared.PlatformSupport;
-using Avalonia.Skia;
-using System;
-using System.Collections.Generic;
-using Avalonia.Android.Platform.SkiaPlatform;
-using System.IO;
+
+namespace Avalonia
+{
+    public static class AndroidApplicationExtensions
+    {
+        public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
+        {
+            builder.UseWindowingSubsystem(Android.AndroidPlatform.Initialize, "Android");
+            return builder;
+        }
+    }
+}
 
 
 namespace Avalonia.Android
 namespace Avalonia.Android
 {
 {
@@ -24,14 +32,19 @@ namespace Avalonia.Android
 
 
         private readonly double _scalingFactor = 1;
         private readonly double _scalingFactor = 1;
 
 
-        AndroidPlatform()
+        public AndroidPlatform()
+        {
+            _scalingFactor = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity;
+        }
+
+        public static void Initialize()
         {
         {
             AvaloniaLocator.CurrentMutable
             AvaloniaLocator.CurrentMutable
                 .Bind<IClipboard>().ToTransient<ClipboardImpl>()
                 .Bind<IClipboard>().ToTransient<ClipboardImpl>()
                 .Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
                 .Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
                 .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
                 .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
                 .Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>()
                 .Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>()
-                .Bind<IPlatformSettings>().ToConstant(this)
+                .Bind<IPlatformSettings>().ToConstant(Instance)
                 .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
                 .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
                 .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
                 .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
                 .Bind<IWindowingPlatform>().ToConstant(this);
                 .Bind<IWindowingPlatform>().ToConstant(this);

+ 1 - 1
src/Android/Avalonia.Android/Platform/SkiaPlatform/MainWindowImpl.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         {
         {
         }
         }
 
 
-        public WindowState WindowState
+        public new WindowState WindowState
         {
         {
             get { return WindowState.Normal; }
             get { return WindowState.Normal; }
             set { }
             set { }

+ 5 - 8
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@@ -10,7 +10,7 @@
     <OutputType>Library</OutputType>
     <OutputType>Library</OutputType>
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>Avalonia.AndroidTestApplication</RootNamespace>
     <RootNamespace>Avalonia.AndroidTestApplication</RootNamespace>
-    <AssemblyName>Avalonia Test Application</AssemblyName>
+    <AssemblyName>Avalonia.AndroidTestApplication</AssemblyName>
     <FileAlignment>512</FileAlignment>
     <FileAlignment>512</FileAlignment>
     <AndroidApplication>true</AndroidApplication>
     <AndroidApplication>true</AndroidApplication>
     <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
     <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
@@ -77,11 +77,7 @@
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System" />
-    <Reference Include="System.Collections" />
     <Reference Include="System.Core" />
     <Reference Include="System.Core" />
-    <Reference Include="System.IO" />
-    <Reference Include="System.Linq.Expressions" />
-    <Reference Include="System.ObjectModel" />
     <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\netstandard1.3\System.Reactive.Core.dll</HintPath>
       <HintPath>..\..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.3\System.Reactive.Core.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
@@ -98,8 +94,6 @@
       <HintPath>..\..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.3\System.Reactive.PlatformServices.dll</HintPath>
       <HintPath>..\..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.3\System.Reactive.PlatformServices.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
-    <Reference Include="System.Runtime" />
-    <Reference Include="System.Runtime.Extensions" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Xml" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   </ItemGroup>
@@ -178,6 +172,10 @@
       <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
       <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
       <Name>Avalonia.Themes.Default</Name>
       <Name>Avalonia.Themes.Default</Name>
     </ProjectReference>
     </ProjectReference>
+    <ProjectReference Include="..\..\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\Avalonia.HtmlRenderer\Avalonia.HtmlRenderer.csproj">
     <ProjectReference Include="..\..\Avalonia.HtmlRenderer\Avalonia.HtmlRenderer.csproj">
       <Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
       <Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
       <Name>Avalonia.HtmlRenderer</Name>
       <Name>Avalonia.HtmlRenderer</Name>
@@ -187,7 +185,6 @@
       <Name>Avalonia.Skia.Android</Name>
       <Name>Avalonia.Skia.Android</Name>
     </ProjectReference>
     </ProjectReference>
   </ItemGroup>
   </ItemGroup>
-  <Import Project="..\..\..\samples\TestApplicationShared\TestApplicationShared.projitems" Label="Shared" />
   <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.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.

+ 70 - 12
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@@ -1,12 +1,14 @@
+using System;
 using Android.App;
 using Android.App;
 using Android.Content.PM;
 using Android.Content.PM;
 using Android.OS;
 using Android.OS;
-using Avalonia.Android;
 using Avalonia.Android.Platform.Specific;
 using Avalonia.Android.Platform.Specific;
-using Avalonia.Android.Platform.Specific.Helpers;
-using Avalonia.Controls.Platform;
-using Avalonia.Platform;
-using TestApplication;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Avalonia.Styling;
+using Avalonia.Themes.Default;
 
 
 namespace Avalonia.AndroidTestApplication
 namespace Avalonia.AndroidTestApplication
 {
 {
@@ -30,15 +32,71 @@ namespace Avalonia.AndroidTestApplication
             if (Avalonia.Application.Current != null)
             if (Avalonia.Application.Current != null)
                 app = (App)Avalonia.Application.Current;
                 app = (App)Avalonia.Application.Current;
             else
             else
+            {
                 app = new App();
                 app = new App();
-           
+                AppBuilder.Configure(app)
+                    .UseAndroid()
+                    .UseSkia()
+                    .SetupWithoutStarting();
+            }
 
 
-            MainWindow.RootNamespace = "Avalonia.AndroidTestApplication";
-            var window = MainWindow.Create();
+            app.Run();
+        }
+    }
+
+    public class App : Application
+    {
+        public void Run()
+        {
+            Styles.Add(new DefaultTheme());
+
+            var loader = new AvaloniaXamlLoader();
+            var baseLight = (IStyle)loader.Load(
+                new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"));
+            Styles.Add(baseLight);
+
+            var wnd = App.CreateSimpleWindow();
+            wnd.AttachDevTools();
+
+            Run(wnd);
+        }
+
+        // This provides a simple UI tree for testing input handling, drawing, etc
+        public static Window CreateSimpleWindow()
+        {
+            Window window = new Window
+            {
+                Title = "Avalonia Test Application",
+                Background = Brushes.Red,
+                Content = new StackPanel
+                {
+                    Margin = new Thickness(30),
+                    Background = Brushes.Yellow,
+                    Children = new Avalonia.Controls.Controls
+                    {
+                        new TextBlock
+                        {
+                            Text = "TEXT BLOCK",
+                            Width = 300,
+                            Height = 40,
+                            Background = Brushes.White,
+                            Foreground = Brushes.Black
+                        },
+
+                        new Button
+                        {
+                            Content = "BUTTON",
+                            Width = 150,
+                            Height = 40,
+                            Background = Brushes.LightGreen,
+                            Foreground = Brushes.Black
+                        }
+
+                    }
+                }
+            };
 
 
-            window.Show();
-            app.Run(window);
+            return window;
         }
         }
-        
     }
     }
-}
+}

+ 2 - 0
src/Avalonia.Animation/Avalonia.Animation.csproj

@@ -25,6 +25,7 @@
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Debug\Avalonia.Animation.XML</DocumentationFile>
     <DocumentationFile>bin\Debug\Avalonia.Animation.XML</DocumentationFile>
+    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
     <DebugType>pdbonly</DebugType>
@@ -35,6 +36,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Animation.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Animation.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

+ 2 - 1
src/Avalonia.Base/Avalonia.Base.csproj

@@ -38,12 +38,13 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Base.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Base.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <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\BindingChainNullException.cs" />
+    <Compile Include="Data\BindingChainException.cs" />
     <Compile Include="Data\BindingNotification.cs" />
     <Compile Include="Data\BindingNotification.cs" />
     <Compile Include="Data\IndexerBinding.cs" />
     <Compile Include="Data\IndexerBinding.cs" />
     <Compile Include="Diagnostics\INotifyCollectionChangedDebug.cs" />
     <Compile Include="Diagnostics\INotifyCollectionChangedDebug.cs" />

+ 0 - 2
src/Avalonia.Base/AvaloniaObject.cs

@@ -164,9 +164,7 @@ namespace Avalonia
 
 
             set
             set
             {
             {
-                var metadata = binding.Property.GetMetadata(GetType());
                 var sourceBinding = value as IBinding;
                 var sourceBinding = value as IBinding;
-
                 this.Bind(binding.Property, sourceBinding);
                 this.Bind(binding.Property, sourceBinding);
             }
             }
         }
         }

+ 7 - 1
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@@ -16,7 +16,13 @@ namespace Avalonia
     /// </summary>
     /// </summary>
     public static class AvaloniaObjectExtensions
     public static class AvaloniaObjectExtensions
     {
     {
-        public static IBinding AsBinding<T>(this IObservable<T> source)
+        /// <summary>
+        /// Converts an <see cref="IObservable{T}"/> to an <see cref="IBinding"/>.
+        /// </summary>
+        /// <typeparam name="T">The type produced by the observable.</typeparam>
+        /// <param name="source">The observable</param>
+        /// <returns>An <see cref="IBinding"/>.</returns>
+        public static IBinding ToBinding<T>(this IObservable<T> source)
         {
         {
             return new BindingAdaptor(source.Select(x => (object)x));
             return new BindingAdaptor(source.Select(x => (object)x));
         }
         }

+ 2 - 3
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@@ -64,8 +64,6 @@ namespace Avalonia
         {
         {
             Contract.Requires<ArgumentNullException>(type != null);
             Contract.Requires<ArgumentNullException>(type != null);
 
 
-            var i = type.GetTypeInfo();
-
             while (type != null)
             while (type != null)
             {
             {
                 // Ensure the type's static ctor has been run.
                 // Ensure the type's static ctor has been run.
@@ -265,7 +263,8 @@ namespace Avalonia
         /// <param name="property">The property.</param>
         /// <param name="property">The property.</param>
         /// <remarks>
         /// <remarks>
         /// You won't usually want to call this method directly, instead use the
         /// You won't usually want to call this method directly, instead use the
-        /// <see cref="AvaloniaProperty.Register"/> method.
+        /// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TOwner, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
+        /// method.
         /// </remarks>
         /// </remarks>
         public void Register(Type type, AvaloniaProperty property)
         public void Register(Type type, AvaloniaProperty property)
         {
         {

+ 0 - 1
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@@ -207,7 +207,6 @@ namespace Avalonia.Collections
 
 
             if (CollectionChanged != null)
             if (CollectionChanged != null)
             {
             {
-                var val = new KeyValuePair<TKey, TValue>(key, value);
                 var e = new NotifyCollectionChangedEventArgs(
                 var e = new NotifyCollectionChangedEventArgs(
                     NotifyCollectionChangedAction.Add,
                     NotifyCollectionChangedAction.Add,
                     new[] { new KeyValuePair<TKey, TValue>(key, value) },
                     new[] { new KeyValuePair<TKey, TValue>(key, value) },

+ 0 - 2
src/Avalonia.Base/Collections/AvaloniaList.cs

@@ -397,8 +397,6 @@ namespace Avalonia.Collections
         {
         {
             Contract.Requires<ArgumentNullException>(items != null);
             Contract.Requires<ArgumentNullException>(items != null);
 
 
-            var list = (items as IList) ?? items.ToList();
-
             foreach (var i in items)
             foreach (var i in items)
             {
             {
                 // TODO: Optimize to only send as many notifications as necessary.
                 // TODO: Optimize to only send as many notifications as necessary.

+ 25 - 32
src/Avalonia.Base/Data/BindingChainNullException.cs → src/Avalonia.Base/Data/BindingChainException.cs

@@ -10,36 +10,39 @@ namespace Avalonia.Data
     /// requested binding expression could not be evaluated because of a null in one of the links
     /// requested binding expression could not be evaluated because of a null in one of the links
     /// of the binding chain.
     /// of the binding chain.
     /// </summary>
     /// </summary>
-    public class BindingChainNullException : Exception
+    public class BindingChainException : Exception
     {
     {
         private string _message;
         private string _message;
 
 
         /// <summary>
         /// <summary>
-        /// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
+        /// Initalizes a new instance of the <see cref="BindingChainException"/> class.
         /// </summary>
         /// </summary>
-        public BindingChainNullException()
+        public BindingChainException()
         {
         {
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
+        /// Initalizes a new instance of the <see cref="BindingChainException"/> class.
         /// </summary>
         /// </summary>
-        public BindingChainNullException(string message)
+        /// <param name="message">The error message.</param>
+        public BindingChainException(string message)
         {
         {
             _message = message;
             _message = message;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
+        /// Initalizes a new instance of the <see cref="BindingChainException"/> class.
         /// </summary>
         /// </summary>
+        /// <param name="message">The error message.</param>
         /// <param name="expression">The expression.</param>
         /// <param name="expression">The expression.</param>
-        /// <param name="expressionNullPoint">
-        /// The point in the expression at which the null was encountered.
+        /// <param name="errorPoint">
+        /// The point in the expression at which the error was encountered.
         /// </param>
         /// </param>
-        public BindingChainNullException(string expression, string expressionNullPoint)
+        public BindingChainException(string message, string expression, string errorPoint)
         {
         {
+            _message = message;
             Expression = expression;
             Expression = expression;
-            ExpressionNullPoint = expressionNullPoint;
+            ExpressionErrorPoint = errorPoint;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -48,37 +51,27 @@ namespace Avalonia.Data
         public string Expression { get; protected set; }
         public string Expression { get; protected set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the point in the expression at which the null was encountered.
+        /// Gets the point in the expression at which the error occured.
         /// </summary>
         /// </summary>
-        public string ExpressionNullPoint { get; protected set; }
+        public string ExpressionErrorPoint { get; protected set; }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
         public override string Message
         public override string Message
         {
         {
             get
             get
             {
             {
-                if (_message == null)
+                if (Expression != null && ExpressionErrorPoint != null)
                 {
                 {
-                    _message = BuildMessage();
+                    return $"{_message} in expression '{Expression}' at '{ExpressionErrorPoint}'.";
+                }
+                else if (ExpressionErrorPoint != null)
+                {
+                    return $"{_message} in expression '{ExpressionErrorPoint}'.";
+                }
+                else
+                {
+                    return $"{_message} in expression.";
                 }
                 }
-
-                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.";
             }
             }
         }
         }
     }
     }

+ 1 - 1
src/Avalonia.Base/Data/InstancedBinding.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Data
     /// <remarks>
     /// <remarks>
     /// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
     /// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
     /// property on a control's DataContext"; this class represents a binding that has been 
     /// property on a control's DataContext"; this class represents a binding that has been 
-    /// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object)"/>
+    /// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object, bool)"/>
     /// on a target object.
     /// on a target object.
     /// 
     /// 
     /// When a binding is initiated, it can return one of 3 possible sources for the binding:
     /// When a binding is initiated, it can return one of 3 possible sources for the binding:

+ 0 - 3
src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs

@@ -21,9 +21,6 @@ namespace Avalonia.Platform
         /// <returns>An <see cref="IDisposable"/> used to stop the timer.</returns>
         /// <returns>An <see cref="IDisposable"/> used to stop the timer.</returns>
         IDisposable StartTimer(TimeSpan interval, Action tick);
         IDisposable StartTimer(TimeSpan interval, Action tick);
 
 
-        /// <summary>
-        /// Sends a message that causes <see cref="ProcessMessage"/> to exit.
-        /// </summary>
         void Signal();
         void Signal();
 
 
         bool CurrentThreadIsLoopThread { get; }
         bool CurrentThreadIsLoopThread { get; }

+ 0 - 1
src/Avalonia.Base/PriorityBindingEntry.cs

@@ -21,7 +21,6 @@ namespace Avalonia
         /// <param name="index">
         /// <param name="index">
         /// The binding index. Later bindings should have higher indexes.
         /// The binding index. Later bindings should have higher indexes.
         /// </param>
         /// </param>
-        /// <param name="validation">The validation settings for the binding.</param>
         public PriorityBindingEntry(PriorityLevel owner, int index)
         public PriorityBindingEntry(PriorityLevel owner, int index)
         {
         {
             _owner = owner;
             _owner = owner;

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

@@ -9,9 +9,9 @@ using Avalonia.Platform;
 namespace Avalonia.Controls
 namespace Avalonia.Controls
 {
 {
     /// <summary>
     /// <summary>
-    /// Initializes up platform-specific services for an <see cref="Application"/>.
+    /// Base class for initializing platform-specific services for an <see cref="Application"/>.
     /// </summary>
     /// </summary>
-    /// <typeparam name="TAppBuilder"></typeparam>
+    /// <typeparam name="TAppBuilder">The type of the AppBuilder class itself.</typeparam>
     public abstract class AppBuilderBase<TAppBuilder> where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
     public abstract class AppBuilderBase<TAppBuilder> where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
     {
     {
         /// <summary>
         /// <summary>
@@ -140,6 +140,7 @@ namespace Avalonia.Controls
         /// Specifies a windowing subsystem to use.
         /// Specifies a windowing subsystem to use.
         /// </summary>
         /// </summary>
         /// <param name="initializer">The method to call to initialize the windowing subsystem.</param>
         /// <param name="initializer">The method to call to initialize the windowing subsystem.</param>
+        /// <param name="name">The name of the windowing subsystem.</param>
         /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
         /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
         public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "")
         public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "")
         {
         {
@@ -159,6 +160,7 @@ namespace Avalonia.Controls
         /// Specifies a rendering subsystem to use.
         /// Specifies a rendering subsystem to use.
         /// </summary>
         /// </summary>
         /// <param name="initializer">The method to call to initialize the rendering subsystem.</param>
         /// <param name="initializer">The method to call to initialize the rendering subsystem.</param>
+        /// <param name="name">The name of the rendering subsystem.</param>
         /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
         /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
         public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "")
         public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "")
         {
         {

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Controls.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Controls.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">

+ 4 - 4
src/Avalonia.Controls/Canvas.cs

@@ -22,25 +22,25 @@ namespace Avalonia.Controls
         /// Defines the Left attached property.
         /// Defines the Left attached property.
         /// </summary>
         /// </summary>
         public static readonly AttachedProperty<double> LeftProperty =
         public static readonly AttachedProperty<double> LeftProperty =
-            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Left");
+            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Left", double.NaN);
 
 
         /// <summary>
         /// <summary>
         /// Defines the Top attached property.
         /// Defines the Top attached property.
         /// </summary>
         /// </summary>
         public static readonly AttachedProperty<double> TopProperty =
         public static readonly AttachedProperty<double> TopProperty =
-            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Top");
+            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Top", double.NaN);
 
 
         /// <summary>
         /// <summary>
         /// Defines the Right attached property.
         /// Defines the Right attached property.
         /// </summary>
         /// </summary>
         public static readonly AttachedProperty<double> RightProperty =
         public static readonly AttachedProperty<double> RightProperty =
-            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Right");
+            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Right", double.NaN);
 
 
         /// <summary>
         /// <summary>
         /// Defines the Bottom attached property.
         /// Defines the Bottom attached property.
         /// </summary>
         /// </summary>
         public static readonly AttachedProperty<double> BottomProperty =
         public static readonly AttachedProperty<double> BottomProperty =
-            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Bottom");
+            AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Bottom", double.NaN);
 
 
         /// <summary>
         /// <summary>
         /// Initializes static members of the <see cref="Canvas"/> class.
         /// Initializes static members of the <see cref="Canvas"/> class.

+ 0 - 1
src/Avalonia.Controls/Classes.cs

@@ -197,7 +197,6 @@ namespace Avalonia.Controls
         /// <param name="count">The number of items to remove.</param>
         /// <param name="count">The number of items to remove.</param>
         public override void RemoveRange(int index, int count)
         public override void RemoveRange(int index, int count)
         {
         {
-            var names = GetRange(index, count);
             base.RemoveRange(index, count);
             base.RemoveRange(index, count);
         }
         }
 
 

+ 0 - 5
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@@ -124,11 +124,6 @@ namespace Avalonia.Controls.Generators
             return false;
             return false;
         }
         }
 
 
-        /// <summary>
-        /// Gets the data template for the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>The template.</returns>
         private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate primary)
         private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate primary)
         {
         {
             var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default;
             var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default;

+ 2 - 1
src/Avalonia.Controls/HotkeyManager.cs

@@ -30,8 +30,9 @@ namespace Avalonia.Controls
 
 
             public void Execute(object parameter) => GetCommand()?.Execute(parameter);
             public void Execute(object parameter) => GetCommand()?.Execute(parameter);
 
 
-            //Implementation isn't needed in this case
+#pragma warning disable 67 // Event not used
             public event EventHandler CanExecuteChanged;
             public event EventHandler CanExecuteChanged;
+#pragma warning restore 67
         }
         }
 
 
 
 

+ 0 - 2
src/Avalonia.Controls/Platform/PlatformManager.cs

@@ -11,7 +11,6 @@ namespace Avalonia.Controls.Platform
             => AvaloniaLocator.Current.GetService<IPlatformSettings>();
             => AvaloniaLocator.Current.GetService<IPlatformSettings>();
 
 
         static bool s_designerMode;
         static bool s_designerMode;
-        private static double _designerScalingFactor = 1;
 
 
         public static IDisposable DesignerMode()
         public static IDisposable DesignerMode()
         {
         {
@@ -21,7 +20,6 @@ namespace Avalonia.Controls.Platform
 
 
         public static void SetDesignerScalingFactor(double factor)
         public static void SetDesignerScalingFactor(double factor)
         {
         {
-            _designerScalingFactor = factor;
         }
         }
 
 
         public static IWindowImpl CreateWindow()
         public static IWindowImpl CreateWindow()

+ 3 - 2
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@@ -97,7 +97,9 @@ namespace Avalonia.Controls.Presenters
         /// <inheritdoc/>
         /// <inheritdoc/>
         protected override void PanelCreated(IPanel panel)
         protected override void PanelCreated(IPanel panel)
         {
         {
-            var task = MoveToPage(-1, SelectedIndex);
+#pragma warning disable 4014
+            MoveToPage(-1, SelectedIndex);
+#pragma warning restore 4014
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -139,7 +141,6 @@ namespace Avalonia.Controls.Presenters
 
 
                 if (toIndex != -1)
                 if (toIndex != -1)
                 {
                 {
-                    var item = Items.Cast<object>().ElementAt(toIndex);
                     to = GetOrCreateContainer(toIndex);
                     to = GetOrCreateContainer(toIndex);
                 }
                 }
 
 

+ 61 - 6
src/Avalonia.Controls/Presenters/ItemVirtualizer.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Controls.Presenters
     /// </summary>
     /// </summary>
     internal abstract class ItemVirtualizer : IVirtualizingController, IDisposable
     internal abstract class ItemVirtualizer : IVirtualizingController, IDisposable
     {
     {
-        private bool disposedValue;
+        private double _crossAxisOffset;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ItemVirtualizer"/> class.
         /// Initializes a new instance of the <see cref="ItemVirtualizer"/> class.
@@ -62,7 +62,7 @@ namespace Avalonia.Controls.Presenters
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether the items should be scroll horizontally or vertically.
         /// Gets a value indicating whether the items should be scroll horizontally or vertically.
         /// </summary>
         /// </summary>
-        public bool Vertical => VirtualizingPanel.ScrollDirection == Orientation.Vertical;
+        public bool Vertical => VirtualizingPanel?.ScrollDirection == Orientation.Vertical;
 
 
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether logical scrolling is enabled.
         /// Gets a value indicating whether logical scrolling is enabled.
@@ -87,12 +87,28 @@ namespace Avalonia.Controls.Presenters
         /// <summary>
         /// <summary>
         /// Gets the <see cref="ExtentValue"/> as a <see cref="Size"/>.
         /// Gets the <see cref="ExtentValue"/> as a <see cref="Size"/>.
         /// </summary>
         /// </summary>
-        public Size Extent => Vertical ? new Size(0, ExtentValue) : new Size(ExtentValue, 0);
+        public Size Extent
+        {
+            get
+            {
+                return Vertical ?
+                    new Size(Owner.Panel.DesiredSize.Width, ExtentValue) :
+                    new Size(ExtentValue, Owner.Panel.DesiredSize.Height);
+            }
+        }
 
 
         /// <summary>
         /// <summary>
         /// Gets the <see cref="ViewportValue"/> as a <see cref="Size"/>.
         /// Gets the <see cref="ViewportValue"/> as a <see cref="Size"/>.
         /// </summary>
         /// </summary>
-        public Size Viewport => Vertical ? new Size(0, ViewportValue) : new Size(ViewportValue, 0);
+        public Size Viewport
+        {
+            get
+            {
+                return Vertical ? 
+                    new Size(Owner.Panel.Bounds.Width, ViewportValue) :
+                    new Size(ViewportValue, Owner.Panel.Bounds.Height);
+            }
+        }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the <see cref="OffsetValue"/> as a <see cref="Vector"/>.
         /// Gets or sets the <see cref="OffsetValue"/> as a <see cref="Vector"/>.
@@ -101,12 +117,28 @@ namespace Avalonia.Controls.Presenters
         {
         {
             get
             get
             {
             {
-                return Vertical ? new Vector(0, OffsetValue) : new Vector(OffsetValue, 0);
+                return Vertical ? new Vector(_crossAxisOffset, OffsetValue) : new Vector(OffsetValue, _crossAxisOffset);
             }
             }
 
 
             set
             set
             {
             {
-                OffsetValue = Vertical ? value.Y : value.X;
+                var oldCrossAxisOffset = _crossAxisOffset;
+
+                if (Vertical)
+                {
+                    OffsetValue = value.Y;
+                    _crossAxisOffset = value.X;
+                }
+                else
+                {
+                    OffsetValue = value.X;
+                    _crossAxisOffset = value.Y;
+                }
+
+                if (_crossAxisOffset != oldCrossAxisOffset)
+                {
+                    Owner.InvalidateArrange();
+                }
             }
             }
         }
         }
         
         
@@ -145,6 +177,29 @@ namespace Avalonia.Controls.Presenters
             return result;
             return result;
         }
         }
 
 
+        /// <summary>
+        /// Carries out a measure for the related <see cref="ItemsPresenter"/>.
+        /// </summary>
+        /// <param name="availableSize">The size available to the control.</param>
+        /// <returns>The desired size for the control.</returns>
+        public virtual Size MeasureOverride(Size availableSize)
+        {
+            Owner.Panel.Measure(availableSize);
+            return Owner.Panel.DesiredSize;
+        }
+
+        /// <summary>
+        /// Carries out an arrange for the related <see cref="ItemsPresenter"/>.
+        /// </summary>
+        /// <param name="finalSize">The size available to the control.</param>
+        /// <returns>The actual size used.</returns>
+        public virtual Size ArrangeOverride(Size finalSize)
+        {
+            var origin = Vertical ? new Point(-_crossAxisOffset, 0) : new Point(0, _crossAxisOffset);
+            Owner.Panel.Arrange(new Rect(origin, finalSize));
+            return finalSize;
+        }
+
         /// <inheritdoc/>
         /// <inheritdoc/>
         public virtual void UpdateControls()
         public virtual void UpdateControls()
         {
         {

+ 50 - 3
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@@ -94,6 +94,44 @@ namespace Avalonia.Controls.Presenters
             }
             }
         }
         }
 
 
+        /// <inheritdoc/>
+        public override Size MeasureOverride(Size availableSize)
+        {
+            var window = Owner.GetVisualRoot() as TopLevel;
+
+            // If infinity is passed as the available size and we're virtualized then we need to
+            // fill the available space, but to do that we *don't* want to materialize all our
+            // items! Take a look at the root of the tree for a MaxClientSize and use that as
+            // the available size.
+            if (VirtualizingPanel.ScrollDirection == Orientation.Vertical)
+            {
+                if (availableSize.Height == double.PositiveInfinity)
+                {
+                    if (window != null)
+                    {
+                        availableSize = availableSize.WithHeight(window.PlatformImpl.MaxClientSize.Height);
+                    }
+                }
+
+                availableSize = availableSize.WithWidth(double.PositiveInfinity);
+            }
+            else
+            {
+                if (availableSize.Width == double.PositiveInfinity)
+                {
+                    if (window != null)
+                    {
+                        availableSize = availableSize.WithWidth(window.PlatformImpl.MaxClientSize.Width);
+                    }
+                }
+
+                availableSize = availableSize.WithHeight(double.PositiveInfinity);
+            }
+
+            Owner.Panel.Measure(availableSize);
+            return Owner.Panel.DesiredSize;
+        }
+
         /// <inheritdoc/>
         /// <inheritdoc/>
         public override void UpdateControls()
         public override void UpdateControls()
         {
         {
@@ -359,7 +397,6 @@ namespace Avalonia.Controls.Presenters
             var count = Math.Min(Math.Abs(delta), panel.Children.Count);
             var count = Math.Min(Math.Abs(delta), panel.Children.Count);
             var move = count < panel.Children.Count;
             var move = count < panel.Children.Count;
             var first = delta < 0 && move ? panel.Children.Count + delta : 0;
             var first = delta < 0 && move ? panel.Children.Count + delta : 0;
-            var containers = panel.Children.GetRange(first, count).ToList();
 
 
             for (var i = 0; i < count; ++i)
             for (var i = 0; i < count; ++i)
             {
             {
@@ -482,9 +519,19 @@ namespace Avalonia.Controls.Presenters
                 {
                 {
                     layoutManager.ExecuteLayoutPass();
                     layoutManager.ExecuteLayoutPass();
 
 
-                    if (!new Rect(panel.Bounds.Size).Contains(container.Bounds))
+                    if (panel.ScrollDirection == Orientation.Vertical)
                     {
                     {
-                        OffsetValue += 1;
+                        if (container.Bounds.Y < panel.Bounds.Y || container.Bounds.Bottom > panel.Bounds.Bottom)
+                        {
+                            OffsetValue += 1;
+                        }
+                    }
+                    else
+                    {
+                        if (container.Bounds.X < panel.Bounds.X || container.Bounds.Right > panel.Bounds.Right)
+                        {
+                            OffsetValue += 1;
+                        }
                     }
                     }
                 }
                 }
 
 

+ 6 - 15
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@@ -91,24 +91,15 @@ namespace Avalonia.Controls.Presenters
             _virtualizer?.ScrollIntoView(item);
             _virtualizer?.ScrollIntoView(item);
         }
         }
 
 
+        /// <inheritdoc/>
         protected override Size MeasureOverride(Size availableSize)
         protected override Size MeasureOverride(Size availableSize)
         {
         {
-            // If infinity is passed as the available size and we're virtualized then we need to
-            // fill the available space, but to do that we *don't* want to materialize all our
-            // items! Take a look at the root of the tree for a MaxClientSize and use that as
-            // the available size.
-            if (availableSize == Size.Infinity && VirtualizationMode != ItemVirtualizationMode.None)
-            {
-                var window = VisualRoot as TopLevel;
-
-                if (window != null)
-                {
-                    availableSize = window.PlatformImpl.MaxClientSize;
-                }
-            }
+            return _virtualizer?.MeasureOverride(availableSize) ?? Size.Empty;
+        }
 
 
-            Panel.Measure(availableSize);
-            return Panel.DesiredSize;
+        protected override Size ArrangeOverride(Size finalSize)
+        {
+            return _virtualizer?.ArrangeOverride(finalSize) ?? Size.Empty;
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>

+ 1 - 1
src/Avalonia.Controls/Presenters/TextPresenter.cs

@@ -219,7 +219,7 @@ namespace Avalonia.Controls.Presenters
         {
         {
             var text = Text;
             var text = Text;
 
 
-            if (!string.IsNullOrWhiteSpace(text))
+            if (!string.IsNullOrEmpty(text))
             {
             {
                 return base.MeasureOverride(availableSize);
                 return base.MeasureOverride(availableSize);
             }
             }

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

@@ -157,7 +157,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// </summary>
         public ScrollViewer()
         public ScrollViewer()
         {
         {
-            var extentAndViewport = Observable.CombineLatest(
+            Observable.CombineLatest(
                 this.GetObservable(ExtentProperty),
                 this.GetObservable(ExtentProperty),
                 this.GetObservable(ViewportProperty))
                 this.GetObservable(ViewportProperty))
                 .Select(x => new { Extent = x[0], Viewport = x[1] });
                 .Select(x => new { Extent = x[0], Viewport = x[1] });

+ 60 - 19
src/Avalonia.Controls/TextBox.cs

@@ -105,7 +105,7 @@ namespace Avalonia.Controls
 
 
         public TextBox()
         public TextBox()
         {
         {
-            var canScrollHorizontally = this.GetObservable(TextWrappingProperty)
+            this.GetObservable(TextWrappingProperty)
                 .Select(x => x == TextWrapping.NoWrap)
                 .Select(x => x == TextWrapping.NoWrap)
                 .Subscribe(x => CanScrollHorizontally = x);
                 .Subscribe(x => CanScrollHorizontally = x);
 
 
@@ -148,7 +148,8 @@ namespace Avalonia.Controls
             {
             {
                 value = CoerceCaretIndex(value);
                 value = CoerceCaretIndex(value);
                 SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
                 SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
-                if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState.Text == Text)
+                UndoRedoState state;
+                if (_undoRedoHelper.TryGetLastState(out state) && state.Text == Text)
                     _undoRedoHelper.UpdateLastState();
                     _undoRedoHelper.UpdateLastState();
             }
             }
         }
         }
@@ -239,7 +240,21 @@ namespace Avalonia.Controls
         protected override void OnGotFocus(GotFocusEventArgs e)
         protected override void OnGotFocus(GotFocusEventArgs e)
         {
         {
             base.OnGotFocus(e);
             base.OnGotFocus(e);
-            _presenter.ShowCaret();
+
+            // when navigating to a textbox via the tab key, select all text if
+            //   1) this textbox is *not* a multiline textbox
+            //   2) this textbox has any text to select
+            if (e.NavigationMethod == NavigationMethod.Tab &&
+                !AcceptsReturn &&
+                Text?.Length > 0)
+            {
+                SelectionStart = 0;
+                SelectionEnd = Text.Length;
+            }
+            else
+            {
+                _presenter.ShowCaret();
+            }
         }
         }
 
 
         protected override void OnLostFocus(RoutedEventArgs e)
         protected override void OnLostFocus(RoutedEventArgs e)
@@ -379,8 +394,20 @@ namespace Avalonia.Controls
 
 
                     if (!DeleteSelection() && CaretIndex > 0)
                     if (!DeleteSelection() && CaretIndex > 0)
                     {
                     {
-                        SetTextInternal(text.Substring(0, caretIndex - 1) + text.Substring(caretIndex));
-                        --CaretIndex;
+                        var removedCharacters = 1;
+                        // handle deleting /r/n
+                        // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if 
+                        // a /r should also be deleted.
+                        if (CaretIndex > 1 &&
+                            text[CaretIndex - 1] == '\n' &&
+                            text[CaretIndex - 2] == '\r')
+                        {
+                            removedCharacters = 2;
+                        }
+
+                        SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex));
+                        CaretIndex -= removedCharacters;
+                        SelectionStart = SelectionEnd = CaretIndex;
                     }
                     }
 
 
                     break;
                     break;
@@ -393,7 +420,18 @@ namespace Avalonia.Controls
 
 
                     if (!DeleteSelection() && caretIndex < text.Length)
                     if (!DeleteSelection() && caretIndex < text.Length)
                     {
                     {
-                        SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + 1));
+                        var removedCharacters = 1;
+                        // handle deleting /r/n
+                        // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if 
+                        // a /r should also be deleted.
+                        if (CaretIndex < text.Length - 1 &&
+                            text[caretIndex + 1] == '\n' &&
+                            text[caretIndex] == '\r')
+                        {
+                            removedCharacters = 2;
+                        }
+
+                        SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters));
                     }
                     }
 
 
                     break;
                     break;
@@ -457,10 +495,10 @@ namespace Avalonia.Controls
                         case 2:
                         case 2:
                             if (!StringUtils.IsStartOfWord(text, index))
                             if (!StringUtils.IsStartOfWord(text, index))
                             {
                             {
-                                SelectionStart = StringUtils.PreviousWord(text, index, false);
+                                SelectionStart = StringUtils.PreviousWord(text, index);
                             }
                             }
 
 
-                            SelectionEnd = StringUtils.NextWord(text, index, false);
+                            SelectionEnd = StringUtils.NextWord(text, index);
                             break;
                             break;
                         case 3:
                         case 3:
                             SelectionStart = 0;
                             SelectionStart = 0;
@@ -509,7 +547,7 @@ namespace Avalonia.Controls
                 var exceptions = aggregate == null ?
                 var exceptions = aggregate == null ?
                     (IEnumerable<Exception>)new[] { exception } :
                     (IEnumerable<Exception>)new[] { exception } :
                     aggregate.InnerExceptions;
                     aggregate.InnerExceptions;
-                var filtered = exceptions.Where(x => !(x is BindingChainNullException)).ToList();
+                var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList();
 
 
                 if (filtered.Count > 0)
                 if (filtered.Count > 0)
                 {
                 {
@@ -575,8 +613,13 @@ namespace Avalonia.Controls
             {
             {
                 var index = caretIndex + direction;
                 var index = caretIndex + direction;
 
 
-                if (index < 0 || index >= text.Length)
+                if (index < 0 || index > text.Length)
+                {
+                    return;
+                }
+                else if (index == text.Length)
                 {
                 {
+                    CaretIndex = index;
                     return;
                     return;
                 }
                 }
 
 
@@ -595,11 +638,11 @@ namespace Avalonia.Controls
             {
             {
                 if (direction > 0)
                 if (direction > 0)
                 {
                 {
-                    CaretIndex += StringUtils.NextWord(text, caretIndex, false) - caretIndex;
+                    CaretIndex += StringUtils.NextWord(text, caretIndex) - caretIndex;
                 }
                 }
                 else
                 else
                 {
                 {
-                    CaretIndex += StringUtils.PreviousWord(text, caretIndex, false) - caretIndex;
+                    CaretIndex += StringUtils.PreviousWord(text, caretIndex) - caretIndex;
                 }
                 }
             }
             }
         }
         }
@@ -675,6 +718,10 @@ namespace Avalonia.Controls
                         if (pos < text.Length)
                         if (pos < text.Length)
                         {
                         {
                             --pos;
                             --pos;
+                            if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n')
+                            {
+                                --pos;
+                            }
                         }
                         }
 
 
                         break;
                         break;
@@ -690,7 +737,7 @@ namespace Avalonia.Controls
         private void SelectAll()
         private void SelectAll()
         {
         {
             SelectionStart = 0;
             SelectionStart = 0;
-            SelectionEnd = Text.Length;
+            SelectionEnd = Text?.Length ?? 0;
         }
         }
 
 
         private bool DeleteSelection()
         private bool DeleteSelection()
@@ -777,12 +824,6 @@ namespace Avalonia.Controls
             SelectionStart = CaretIndex;
             SelectionStart = CaretIndex;
             MoveHorizontal(1, modifiers);
             MoveHorizontal(1, modifiers);
             SelectionEnd = CaretIndex;
             SelectionEnd = CaretIndex;
-
-            string selection = GetSelection();
-            if (selection != " " && selection.EndsWith(" "))
-            {
-                SelectionEnd = CaretIndex - 1;
-            }
         }
         }
 
 
         UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState
         UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState

+ 0 - 1
src/Avalonia.Controls/TopLevel.cs

@@ -87,7 +87,6 @@ namespace Avalonia.Controls
 
 
             PlatformImpl = impl;
             PlatformImpl = impl;
             dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
             dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
-
             var styler = TryGetService<IStyler>(dependencyResolver);
             var styler = TryGetService<IStyler>(dependencyResolver);
 
 
             _accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
             _accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);

+ 24 - 94
src/Avalonia.Controls/Utils/StringUtils.cs

@@ -57,7 +57,7 @@ namespace Avalonia.Controls.Utils
             }
             }
         }
         }
 
 
-        public static int PreviousWord(string text, int cursor, bool gtkMode)
+        public static int PreviousWord(string text, int cursor)
         {
         {
             int begin;
             int begin;
             int i;
             int i;
@@ -81,60 +81,21 @@ namespace Avalonia.Controls.Utils
                 return (cr > 0) ? cr : 0;
                 return (cr > 0) ? cr : 0;
             }
             }
 
 
-            if (gtkMode)
-            {
-                CharClass cc = GetCharClass(text[cursor - 1]);
-                begin = lf + 1;
-                i = cursor;
-
-                // skip over the word, punctuation, or run of whitespace
-                while (i > begin && GetCharClass(text[i - 1]) == cc)
-                {
-                    i--;
-                }
+            CharClass cc = GetCharClass(text[cursor - 1]);
+            begin = lf + 1;
+            i = cursor;
 
 
-                // if the cursor was at whitespace, skip back a word too
-                if (cc == CharClass.CharClassWhitespace && i > begin)
-                {
-                    cc = GetCharClass(text[i - 1]);
-                    while (i > begin && GetCharClass(text[i - 1]) == cc)
-                    {
-                        i--;
-                    }
-                }
-            }
-            else
+            // skip over the word, punctuation, or run of whitespace
+            while (i > begin && GetCharClass(text[i - 1]) == cc)
             {
             {
-                begin = lf + 1;
-                i = cursor;
-
-                if (cursor < text.Length)
-                {
-                    // skip to the beginning of this word
-                    while (i > begin && !char.IsWhiteSpace(text[i - 1]))
-                    {
-                        i--;
-                    }
-
-                    if (i < cursor && IsStartOfWord(text, i))
-                    {
-                        return i;
-                    }
-                }
-
-                // skip to the start of the lwsp
-                while (i > begin && char.IsWhiteSpace(text[i - 1]))
-                {
-                    i--;
-                }
-
-                if (i > begin)
-                {
-                    i--;
-                }
+                i--;
+            }
 
 
-                // skip to the beginning of the word
-                while (i > begin && !IsStartOfWord(text, i))
+            // if the cursor was at whitespace, skip back a word too
+            if (cc == CharClass.CharClassWhitespace && i > begin)
+            {
+                cc = GetCharClass(text[i - 1]);
+                while (i > begin && GetCharClass(text[i - 1]) == cc)
                 {
                 {
                     i--;
                     i--;
                 }
                 }
@@ -143,7 +104,7 @@ namespace Avalonia.Controls.Utils
             return i;
             return i;
         }
         }
 
 
-        public static int NextWord(string text, int cursor, bool gtkMode)
+        public static int NextWord(string text, int cursor)
         {
         {
             int i, lf, cr;
             int i, lf, cr;
 
 
@@ -169,50 +130,19 @@ namespace Avalonia.Controls.Utils
                 return cursor;
                 return cursor;
             }
             }
 
 
-            if (gtkMode)
-            {
-                CharClass cc = GetCharClass(text[cursor]);
-                i = cursor;
-
-                // skip over the word, punctuation, or run of whitespace
-                while (i < cr && GetCharClass(text[i]) == cc)
-                {
-                    i++;
-                }
+            CharClass cc = GetCharClass(text[cursor]);
+            i = cursor;
 
 
-                // skip any whitespace after the word/punct
-                while (i < cr && char.IsWhiteSpace(text[i]))
-                {
-                    i++;
-                }
-            }
-            else
+            // skip over the word, punctuation, or run of whitespace
+            while (i < cr && GetCharClass(text[i]) == cc)
             {
             {
-                i = cursor;
-
-                // skip any whitespace before the word
-                while (i < cr && char.IsWhiteSpace(text[i]))
-                {
-                    i++;
-                }
-
-                // skip to the end of the current word
-                while (i < cr && !char.IsWhiteSpace(text[i]))
-                {
-                    i++;
-                }
-
-                // skip any whitespace after the word
-                while (i < cr && char.IsWhiteSpace(text[i]))
-                {
-                    i++;
-                }
+                i++;
+            }
 
 
-                // find the start of the next word
-                while (i < cr && !IsStartOfWord(text, i))
-                {
-                    i++;
-                }
+            // skip any whitespace after the word/punct
+            while (i < cr && char.IsWhiteSpace(text[i]))
+            {
+                i++;
             }
             }
 
 
             return i;
             return i;

+ 31 - 28
src/Avalonia.Controls/Utils/UndoRedoHelper.cs

@@ -8,20 +8,19 @@ using Avalonia.Utilities;
 
 
 namespace Avalonia.Controls.Utils
 namespace Avalonia.Controls.Utils
 {
 {
-    class UndoRedoHelper<TState> : WeakTimer.IWeakTimerSubscriber where TState : IEquatable<TState>
+    class UndoRedoHelper<TState> : WeakTimer.IWeakTimerSubscriber where TState : struct, IEquatable<TState>
     {
     {
         private readonly IUndoRedoHost _host;
         private readonly IUndoRedoHost _host;
 
 
         public interface IUndoRedoHost
         public interface IUndoRedoHost
         {
         {
-             TState UndoRedoState { get; set; }
+            TState UndoRedoState { get; set; }
         }
         }
-        
+
 
 
 
 
         private readonly LinkedList<TState> _states = new LinkedList<TState>();
         private readonly LinkedList<TState> _states = new LinkedList<TState>();
 
 
-        [NotNull]
         private LinkedListNode<TState> _currentNode;
         private LinkedListNode<TState> _currentNode;
 
 
         public int Limit { get; set; } = 10;
         public int Limit { get; set; } = 10;
@@ -29,24 +28,31 @@ namespace Avalonia.Controls.Utils
         public UndoRedoHelper(IUndoRedoHost host)
         public UndoRedoHelper(IUndoRedoHost host)
         {
         {
             _host = host;
             _host = host;
-            _states.AddFirst(_host.UndoRedoState);
-            _currentNode = _states.First;
-            WeakTimer.StartWeakTimer(this, new TimeSpan(0, 0, 1));
-
+            WeakTimer.StartWeakTimer(this, TimeSpan.FromSeconds(1));
         }
         }
 
 
         public void Undo()
         public void Undo()
         {
         {
-			if (_currentNode?.Previous != null)  
-			{
-				_currentNode = _currentNode.Previous;
-			}
-
-			_host.UndoRedoState = _currentNode.Value;            
+            if (_currentNode?.Previous != null)
+            {
+                _currentNode = _currentNode.Previous;
+                _host.UndoRedoState = _currentNode.Value;
+            }
         }
         }
 
 
-        public bool IsLastState => _currentNode.Next == null;
+        public bool IsLastState => _currentNode != null && _currentNode.Next == null;
+
+        public bool TryGetLastState(out TState _state)
+        {
+            _state = default(TState);
+            if (!IsLastState)
+                return false;
 
 
+            _state = _currentNode.Value;
+            return true;
+        }
+
+        public bool HasState => _currentNode != null;
         public void UpdateLastState(TState state)
         public void UpdateLastState(TState state)
         {
         {
             _states.Last.Value = state;
             _states.Last.Value = state;
@@ -57,34 +63,31 @@ namespace Avalonia.Controls.Utils
             _states.Last.Value = _host.UndoRedoState;
             _states.Last.Value = _host.UndoRedoState;
         }
         }
 
 
-        public TState LastState => _currentNode.Value;
-
         public void DiscardRedo()
         public void DiscardRedo()
         {
         {
-            //Linked list sucks, so we are doing this
-            while (_currentNode.Next != null)
+            while (_currentNode?.Next != null)
                 _states.Remove(_currentNode.Next);
                 _states.Remove(_currentNode.Next);
         }
         }
 
 
         public void Redo()
         public void Redo()
-        {            
-			if (_currentNode?.Next != null) {
-				_currentNode = _currentNode.Next;
-			}
-
-			_host.UndoRedoState = _currentNode.Value;
+        {
+            if (_currentNode?.Next != null)
+            {
+                _currentNode = _currentNode.Next;
+                _host.UndoRedoState = _currentNode.Value;
+            }
         }
         }
 
 
         public void Snapshot()
         public void Snapshot()
         {
         {
             var current = _host.UndoRedoState;
             var current = _host.UndoRedoState;
-            if (!_currentNode.Value.Equals(current))
+            if (_currentNode == null || !_currentNode.Value.Equals(current))
             {
             {
-                if(_currentNode.Next != null)
+                if (_currentNode?.Next != null)
                     DiscardRedo();
                     DiscardRedo();
                 _states.AddLast(current);
                 _states.AddLast(current);
                 _currentNode = _states.Last;
                 _currentNode = _states.Last;
-                if(_states.Count > Limit)
+                if (_states.Count > Limit)
                     _states.RemoveFirst();
                     _states.RemoveFirst();
             }
             }
         }
         }

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Diagnostics.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Diagnostics.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

+ 0 - 3
src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs

@@ -11,8 +11,6 @@ namespace Avalonia.Diagnostics.ViewModels
 {
 {
     internal class DevToolsViewModel : ReactiveObject
     internal class DevToolsViewModel : ReactiveObject
     {
     {
-        private IControl _root;
-
         private ReactiveObject _content;
         private ReactiveObject _content;
 
 
         private int _selectedTab;
         private int _selectedTab;
@@ -27,7 +25,6 @@ namespace Avalonia.Diagnostics.ViewModels
 
 
         public DevToolsViewModel(IControl root)
         public DevToolsViewModel(IControl root)
         {
         {
-            _root = root;
             _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
             _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
             _visualTree = new TreePageViewModel(VisualTreeNode.Create(root));
             _visualTree = new TreePageViewModel(VisualTreeNode.Create(root));
 
 

+ 4 - 4
src/Avalonia.Diagnostics/Views/ControlDetailsView.cs

@@ -49,7 +49,7 @@ namespace Avalonia.Diagnostics.Views
                         },
                         },
                     },
                     },
                     [GridRepeater.TemplateProperty] = pt,
                     [GridRepeater.TemplateProperty] = pt,
-                    [!GridRepeater.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties).AsBinding(),
+                    [!GridRepeater.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties).ToBinding(),
                 }
                 }
             };
             };
         }
         }
@@ -64,7 +64,7 @@ namespace Avalonia.Diagnostics.Views
                 TextWrapping = TextWrapping.NoWrap,
                 TextWrapping = TextWrapping.NoWrap,
                 [!ToolTip.TipProperty] = property
                 [!ToolTip.TipProperty] = property
                     .WhenAnyValue(x => x.Diagnostic)
                     .WhenAnyValue(x => x.Diagnostic)
-                    .AsBinding(),
+                    .ToBinding(),
             };
             };
 
 
             yield return new TextBlock
             yield return new TextBlock
@@ -73,13 +73,13 @@ namespace Avalonia.Diagnostics.Views
                 [!TextBlock.TextProperty] = property
                 [!TextBlock.TextProperty] = property
                     .WhenAnyValue(v => v.Value)
                     .WhenAnyValue(v => v.Value)
                     .Select(v => v?.ToString())
                     .Select(v => v?.ToString())
-                    .AsBinding(),
+                    .ToBinding(),
             };
             };
 
 
             yield return new TextBlock
             yield return new TextBlock
             {
             {
                 TextWrapping = TextWrapping.NoWrap,
                 TextWrapping = TextWrapping.NoWrap,
-                [!TextBlock.TextProperty] = property.WhenAnyValue(x => x.Priority).AsBinding(),
+                [!TextBlock.TextProperty] = property.WhenAnyValue(x => x.Priority).ToBinding(),
             };
             };
         }
         }
     }
     }

+ 17 - 5
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@@ -1,27 +1,39 @@
 using System;
 using System;
-using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Platform;
 using Avalonia.Platform;
 using Avalonia.Shared.PlatformSupport;
 using Avalonia.Shared.PlatformSupport;
-using System.IO;
 
 
 namespace Avalonia
 namespace Avalonia
 {
 {
+    /// <summary>
+    /// Initializes platform-specific services for an <see cref="Application"/>.
+    /// </summary>
     public sealed class AppBuilder : AppBuilderBase<AppBuilder>
     public sealed class AppBuilder : AppBuilderBase<AppBuilder>
     {
     {
-        public AppBuilder() : base(new StandardRuntimePlatform(), () => StandardRuntimePlatformServices.Register())
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppBuilder"/> class.
+        /// </summary>
+        public AppBuilder()
+            : base(new StandardRuntimePlatform(), () => StandardRuntimePlatformServices.Register())
         {
         {
         }
         }
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppBuilder"/> class.
+        /// </summary>
+        /// <param name="app">The <see cref="Application"/> instance.</param>
         public AppBuilder(Application app) : this()
         public AppBuilder(Application app) : this()
         {
         {
             Instance = app;
             Instance = app;
         }
         }
 
 
+        /// <summary>
+        /// Instructs the <see cref="AppBuilder"/> to use the best settings for the platform.
+        /// </summary>
+        /// <returns>An <see cref="AppBuilder"/> instance.</returns>
         public AppBuilder UsePlatformDetect()
         public AppBuilder UsePlatformDetect()
         {
         {
             var os = RuntimePlatform.GetRuntimeInfo().OperatingSystem;
             var os = RuntimePlatform.GetRuntimeInfo().OperatingSystem;

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

@@ -31,6 +31,7 @@
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System" />

+ 25 - 0
src/Avalonia.Input/AccessKeyHandler.cs

@@ -43,6 +43,16 @@ namespace Avalonia.Input
         /// </summary>
         /// </summary>
         private bool _ignoreAltUp;
         private bool _ignoreAltUp;
 
 
+        /// <summary>
+        /// Whether the AltKey is down.
+        /// </summary>
+        private bool _altIsDown;
+
+        /// <summary>
+        /// Element to restore folowing AltKey taking focus.
+        /// </summary>
+        private IInputElement _restoreFocusElement;
+
         /// <summary>
         /// <summary>
         /// Gets or sets the window's main menu.
         /// Gets or sets the window's main menu.
         /// </summary>
         /// </summary>
@@ -110,8 +120,14 @@ namespace Avalonia.Input
         {
         {
             if (e.Key == Key.LeftAlt)
             if (e.Key == Key.LeftAlt)
             {
             {
+                _altIsDown = true;
+
                 if (MainMenu == null || !MainMenu.IsOpen)
                 if (MainMenu == null || !MainMenu.IsOpen)
                 {
                 {
+                    // TODO: Use FocusScopes to store the current element and restore it when context menu is closed.
+                    // Save currently focused input element.
+                    _restoreFocusElement = FocusManager.Instance.Current;                    
+
                     // When Alt is pressed without a main menu, or with a closed main menu, show
                     // When Alt is pressed without a main menu, or with a closed main menu, show
                     // access key markers in the window (i.e. "_File").
                     // access key markers in the window (i.e. "_File").
                     _owner.ShowAccessKeys = _showingAccessKeys = true;
                     _owner.ShowAccessKeys = _showingAccessKeys = true;
@@ -121,11 +137,18 @@ namespace Avalonia.Input
                     // If the Alt key is pressed and the main menu is open, close the main menu.
                     // If the Alt key is pressed and the main menu is open, close the main menu.
                     CloseMenu();
                     CloseMenu();
                     _ignoreAltUp = true;
                     _ignoreAltUp = true;
+
+                    _restoreFocusElement?.Focus();
+                    _restoreFocusElement = null;
                 }
                 }
 
 
                 // We always handle the Alt key.
                 // We always handle the Alt key.
                 e.Handled = true;
                 e.Handled = true;
             }
             }
+            else if (_altIsDown)
+            {
+                _ignoreAltUp = true;
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -179,6 +202,8 @@ namespace Avalonia.Input
             switch (e.Key)
             switch (e.Key)
             {
             {
                 case Key.LeftAlt:
                 case Key.LeftAlt:
+                    _altIsDown = false;
+
                     if (_ignoreAltUp)
                     if (_ignoreAltUp)
                     {
                     {
                         _ignoreAltUp = false;
                         _ignoreAltUp = false;

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Input.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Input.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

+ 0 - 1
src/Avalonia.Input/Navigation/DirectionalNavigation.cs

@@ -90,7 +90,6 @@ namespace Avalonia.Input.Navigation
         /// <returns>The element's focusable descendents.</returns>
         /// <returns>The element's focusable descendents.</returns>
         private static IEnumerable<IInputElement> GetFocusableDescendents(IInputElement element)
         private static IEnumerable<IInputElement> GetFocusableDescendents(IInputElement element)
         {
         {
-            var mode = KeyboardNavigation.GetDirectionalNavigation((InputElement)element);
             var children = element.GetVisualChildren().OfType<IInputElement>();
             var children = element.GetVisualChildren().OfType<IInputElement>();
 
 
             foreach (var child in children)
             foreach (var child in children)

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Interactivity.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Interactivity.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Layout.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Layout.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

+ 1 - 1
src/Avalonia.Layout/LayoutManager.cs

@@ -168,7 +168,7 @@ namespace Avalonia.Layout
 
 
         private void QueueLayoutPass()
         private void QueueLayoutPass()
         {
         {
-            if (!_queued)
+            if (!_queued && !_running)
             {
             {
                 Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Render);
                 Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Render);
                 _queued = true;
                 _queued = true;

+ 2 - 1
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <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')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
   <PropertyGroup>
@@ -34,6 +34,7 @@
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Logging.Serilog.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Logging.Serilog.XML</DocumentationFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="SerilogLogger.cs" />
     <Compile Include="SerilogLogger.cs" />

+ 12 - 0
src/Avalonia.Logging.Serilog/SerilogLogger.cs

@@ -8,21 +8,33 @@ using SerilogLogEventLevel = Serilog.Events.LogEventLevel;
 
 
 namespace Avalonia.Logging.Serilog
 namespace Avalonia.Logging.Serilog
 {
 {
+    /// <summary>
+    /// Sends log output to serilog.
+    /// </summary>
     public class SerilogLogger : ILogSink
     public class SerilogLogger : ILogSink
     {
     {
         private readonly ILogger _output;
         private readonly ILogger _output;
         private readonly Dictionary<string, ILogger> _areas = new Dictionary<string, ILogger>();
         private readonly Dictionary<string, ILogger> _areas = new Dictionary<string, ILogger>();
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SerilogLogger"/> class.
+        /// </summary>
+        /// <param name="output">The serilog logger to use.</param>
         public SerilogLogger(ILogger output)
         public SerilogLogger(ILogger output)
         {
         {
             _output = output;
             _output = output;
         }
         }
 
 
+        /// <summary>
+        /// Initializes the Avalonia logging with a new instance of a <see cref="SerilogLogger"/>.
+        /// </summary>
+        /// <param name="output">The serilog logger to use.</param>
         public static void Initialize(ILogger output)
         public static void Initialize(ILogger output)
         {
         {
             Logger.Sink = new SerilogLogger(output);
             Logger.Sink = new SerilogLogger(output);
         }
         }
 
 
+        /// <inheritdoc/>
         public void Log(
         public void Log(
             AvaloniaLogEventLevel level, 
             AvaloniaLogEventLevel level, 
             string area, 
             string area, 

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.SceneGraph.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.SceneGraph.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

+ 21 - 3
src/Avalonia.SceneGraph/Media/DrawingContext.cs

@@ -208,6 +208,9 @@ namespace Avalonia.Media
         /// Pushes an opacity mask.
         /// Pushes an opacity mask.
         /// </summary>
         /// </summary>
         /// <param name="mask">The opacity mask.</param>
         /// <param name="mask">The opacity mask.</param>
+        /// <param name="bounds">
+        /// The size of the brush's target area. TODO: Are we sure this is needed?
+        /// </param>
         /// <returns>A disposable to undo the opacity mask.</returns>
         /// <returns>A disposable to undo the opacity mask.</returns>
         public PushedState PushOpacityMask(IBrush mask, Rect bounds)
         public PushedState PushOpacityMask(IBrush mask, Rect bounds)
         {
         {
@@ -216,15 +219,24 @@ namespace Avalonia.Media
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Pushes a matrix transformation.
+        /// Pushes a matrix post-transformation.
         /// </summary>
         /// </summary>
         /// <param name="matrix">The matrix</param>
         /// <param name="matrix">The matrix</param>
         /// <returns>A disposable used to undo the transformation.</returns>
         /// <returns>A disposable used to undo the transformation.</returns>
         public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform*matrix);
         public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform*matrix);
 
 
+        /// <summary>
+        /// Pushes a matrix pre-transformation.
+        /// </summary>
+        /// <param name="matrix">The matrix</param>
+        /// <returns>A disposable used to undo the transformation.</returns>
         public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix*CurrentTransform);
         public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix*CurrentTransform);
-        
 
 
+        /// <summary>
+        /// Sets the current matrix transformation.
+        /// </summary>
+        /// <param name="matrix">The matrix</param>
+        /// <returns>A disposable used to undo the transformation.</returns>
         PushedState PushSetTransform(Matrix matrix)
         PushedState PushSetTransform(Matrix matrix)
         {
         {
             var oldMatrix = CurrentTransform;
             var oldMatrix = CurrentTransform;
@@ -233,7 +245,10 @@ namespace Avalonia.Media
             return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
             return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
         }
         }
 
 
-
+        /// <summary>
+        /// Pushes a new transform context.
+        /// </summary>
+        /// <returns>A disposable used to undo the transformation.</returns>
         public PushedState PushTransformContainer()
         public PushedState PushTransformContainer()
         {
         {
             _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
             _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
@@ -242,6 +257,9 @@ namespace Avalonia.Media
             return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
             return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
         }
         }
 
 
+        /// <summary>
+        /// Disposes of any resources held by the <see cref="DrawingContext"/>.
+        /// </summary>
         public void Dispose()
         public void Dispose()
         {
         {
             while (_states.Count != 0)
             while (_states.Count != 0)

+ 10 - 1
src/Avalonia.SceneGraph/Media/FormattedText.cs

@@ -33,7 +33,16 @@ namespace Avalonia.Media
         {
         {
             Contract.Requires<ArgumentNullException>(text != null);
             Contract.Requires<ArgumentNullException>(text != null);
             Contract.Requires<ArgumentNullException>(fontFamilyName != null);
             Contract.Requires<ArgumentNullException>(fontFamilyName != null);
-            Contract.Requires<ArgumentException>(fontSize > 0);
+
+            if (fontSize <= 0)
+            {
+                throw new ArgumentException("FontSize must be greater than 0");
+            }
+
+            if (fontWeight <= 0)
+            {
+                throw new ArgumentException("FontWeight must be greater than 0");
+            }
 
 
             Text = text;
             Text = text;
             FontFamilyName = fontFamilyName;
             FontFamilyName = fontFamilyName;

+ 1 - 0
src/Avalonia.SceneGraph/Media/Imaging/RenderTargetBitmap.cs

@@ -4,6 +4,7 @@
 using System;
 using System;
 using Avalonia.Platform;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Rendering;
+using Avalonia.VisualTree;
 
 
 namespace Avalonia.Media.Imaging
 namespace Avalonia.Media.Imaging
 {
 {

+ 1 - 0
src/Avalonia.SceneGraph/Media/MatrixTransform.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
+using Avalonia.VisualTree;
 
 
 namespace Avalonia.Media
 namespace Avalonia.Media
 {
 {

+ 2 - 6
src/Avalonia.SceneGraph/Media/PathMarkupParser.cs

@@ -32,18 +32,14 @@ namespace Avalonia.Media
             {'1', FillRule.NonZero }
             {'1', FillRule.NonZero }
         };
         };
 
 
-        private StreamGeometry _geometry;
-
         private readonly StreamGeometryContext _context;
         private readonly StreamGeometryContext _context;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="PathMarkupParser"/> class.
         /// Initializes a new instance of the <see cref="PathMarkupParser"/> class.
         /// </summary>
         /// </summary>
-        /// <param name="geometry">The geometry in which the path should be stored.</param>
-        /// <param name="context">The context for <paramref name="geometry"/>.</param>
-        public PathMarkupParser(StreamGeometry geometry, StreamGeometryContext context)
+        /// <param name="context">The context for the geometry.</param>
+        public PathMarkupParser(StreamGeometryContext context)
         {
         {
-            _geometry = geometry;
             _context = context;
             _context = context;
         }
         }
 
 

+ 1 - 0
src/Avalonia.SceneGraph/Media/RotateTransform.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
+using Avalonia.VisualTree;
 
 
 namespace Avalonia.Media
 namespace Avalonia.Media
 {
 {

+ 1 - 0
src/Avalonia.SceneGraph/Media/ScaleTransform.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
+using Avalonia.VisualTree;
 
 
 namespace Avalonia.Media
 namespace Avalonia.Media
 {
 {

+ 1 - 1
src/Avalonia.SceneGraph/Media/StreamGeometry.cs

@@ -39,7 +39,7 @@ namespace Avalonia.Media
 
 
             using (StreamGeometryContext ctx = result.Open())
             using (StreamGeometryContext ctx = result.Open())
             {
             {
-                PathMarkupParser parser = new PathMarkupParser(result, ctx);
+                PathMarkupParser parser = new PathMarkupParser(ctx);
                 parser.Parse(s);
                 parser.Parse(s);
                 return result;
                 return result;
             }
             }

+ 1 - 0
src/Avalonia.SceneGraph/Media/Transform.cs

@@ -3,6 +3,7 @@
 
 
 using System;
 using System;
 using Avalonia.Animation;
 using Avalonia.Animation;
+using Avalonia.VisualTree;
 
 
 namespace Avalonia.Media
 namespace Avalonia.Media
 {
 {

+ 1 - 0
src/Avalonia.SceneGraph/Media/TranslateTransform.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
+using Avalonia.VisualTree;
 
 
 namespace Avalonia.Media
 namespace Avalonia.Media
 {
 {

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

@@ -38,6 +38,7 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Styling.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Styling.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">

+ 3 - 3
src/Avalonia.Styling/Styling/SelectorMatch.cs

@@ -6,12 +6,12 @@ using System;
 namespace Avalonia.Styling
 namespace Avalonia.Styling
 {
 {
     /// <summary>
     /// <summary>
-    /// Holds the result of a <see cref="FuncSelector"/> match.
+    /// Holds the result of a <see cref="Selector"/> match.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     /// There are two types of selectors - ones whose match can never change for a particular
     /// There are two types of selectors - ones whose match can never change for a particular
-    /// control (such as <see cref="Selectors.OfType{T}(FuncSelector)"/>) and ones whose result can
-    /// change over time (such as <see cref="Selectors.Class(FuncSelector, string)"/>. For the first
+    /// control (such as <see cref="Selectors.OfType(Selector, Type)"/>) and ones whose result can
+    /// change over time (such as <see cref="Selectors.Class(Selector, string)"/>. For the first
     /// category of selectors, the value of <see cref="ImmediateResult"/> will be set but for the
     /// category of selectors, the value of <see cref="ImmediateResult"/> will be set but for the
     /// second, <see cref="ImmediateResult"/> will be null and <see cref="ObservableResult"/> will
     /// second, <see cref="ImmediateResult"/> will be null and <see cref="ObservableResult"/> will
     /// hold an observable which tracks the match.
     /// hold an observable which tracks the match.

+ 0 - 1
src/Avalonia.Styling/Styling/Style.cs

@@ -82,7 +82,6 @@ namespace Avalonia.Styling
         {
         {
             if (Selector != null)
             if (Selector != null)
             {
             {
-                var description = "Style " + Selector.ToString();
                 var match = Selector.Match(control);
                 var match = Selector.Match(control);
 
 
                 if (match.ImmediateResult != false)
                 if (match.ImmediateResult != false)

+ 1 - 0
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@@ -36,6 +36,7 @@
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Themes.Default.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Themes.Default.XML</DocumentationFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <!-- A reference to the entire .NET Framework is automatically included -->
     <!-- A reference to the entire .NET Framework is automatically included -->

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

@@ -372,7 +372,10 @@ namespace Avalonia.Cairo.Media
         public void PopOpacityMask()
         public void PopOpacityMask()
         {
         {
             _context.PopGroupToSource();
             _context.PopGroupToSource();
-            _context.Mask(_maskStack.Pop().PlatformBrush);
+			var brushImpl = _maskStack.Pop ();
+
+            _context.Mask(brushImpl.PlatformBrush);
+			brushImpl.Dispose ();
         }
         }
     }
     }
 }
 }

+ 2 - 0
src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs

@@ -14,7 +14,9 @@ namespace Avalonia.Gtk
 {
 {
     class EmbeddableImpl : WindowImplBase, IEmbeddableWindowImpl
     class EmbeddableImpl : WindowImplBase, IEmbeddableWindowImpl
     {
     {
+#pragma warning disable CS0067 // Method not used
         public event Action LostFocus;
         public event Action LostFocus;
+#pragma warning restore CS0067
 
 
         public EmbeddableImpl(DrawingArea area) : base(area)
         public EmbeddableImpl(DrawingArea area) : base(area)
         {
         {

+ 3 - 1
src/Gtk/Avalonia.Gtk/WindowImplBase.cs

@@ -134,7 +134,7 @@ namespace Avalonia.Gtk
 
 
         public void Invalidate(Rect rect)
         public void Invalidate(Rect rect)
         {
         {
-            if (_window.GdkWindow != null)
+            if (_window?.GdkWindow != null)
                 _window.GdkWindow.InvalidateRect(
                 _window.GdkWindow.InvalidateRect(
                     new Rectangle((int) rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height), true);
                     new Rectangle((int) rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height), true);
         }
         }
@@ -306,7 +306,9 @@ namespace Avalonia.Gtk
 
 
         public void Dispose()
         public void Dispose()
         {
         {
+            _window.Hide();
             _window.Dispose();
             _window.Dispose();
+            _window = null;
         }
         }
     }
     }
 }
 }

+ 6 - 6
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@@ -8,14 +8,14 @@ using System.Reflection;
 using System.Text;
 using System.Text;
 using OmniXaml;
 using OmniXaml;
 using Avalonia.Platform;
 using Avalonia.Platform;
+using Avalonia.Markup.Xaml.Context;
+using Avalonia.Markup.Xaml.Styling;
+using OmniXaml.ObjectAssembler;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml.Data;
 
 
 namespace Avalonia.Markup.Xaml
 namespace Avalonia.Markup.Xaml
 {
 {
-    using Context;
-    using Controls;
-    using Data;
-    using OmniXaml.ObjectAssembler;
-    using System.Linq;
     /// <summary>
     /// <summary>
     /// Loads XAML for a avalonia application.
     /// Loads XAML for a avalonia application.
     /// </summary>
     /// </summary>
@@ -47,7 +47,7 @@ namespace Avalonia.Markup.Xaml
         /// </summary>
         /// </summary>
         /// <remarks>
         /// <remarks>
         /// TODO: Making this internal for now as I'm not sure that this is the correct
         /// TODO: Making this internal for now as I'm not sure that this is the correct
-        /// thing to do, but its needd by <see cref="StyleInclude"/> to get the URL of
+        /// thing to do, but its needed by <see cref="StyleInclude"/> to get the URL of
         /// the currently loading XAML file, as we can't use the OmniXAML parsing context
         /// the currently loading XAML file, as we can't use the OmniXAML parsing context
         /// there. Maybe we need a way to inject OmniXAML context into the objects its
         /// there. Maybe we need a way to inject OmniXAML context into the objects its
         /// constructing?
         /// constructing?

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/OmniXAML

@@ -1 +1 @@
-Subproject commit b122549406107170bbe6e67c0d6a1a4252beef77
+Subproject commit 544af79d218127b4174da4be19896c5ca78eaa5d

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

@@ -36,12 +36,14 @@
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Avalonia.Markup.XML</DocumentationFile>
     <DocumentationFile>bin\Release\Avalonia.Markup.XML</DocumentationFile>
     <NoWarn>CS1591</NoWarn>
     <NoWarn>CS1591</NoWarn>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <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\MarkupBindingChainNullException.cs" />
+    <Compile Include="Data\StreamNode.cs" />
+    <Compile Include="Data\MarkupBindingChainException.cs" />
     <Compile Include="Data\CommonPropertyNames.cs" />
     <Compile Include="Data\CommonPropertyNames.cs" />
     <Compile Include="Data\EmptyExpressionNode.cs" />
     <Compile Include="Data\EmptyExpressionNode.cs" />
     <Compile Include="Data\ExpressionNodeBuilder.cs" />
     <Compile Include="Data\ExpressionNodeBuilder.cs" />
@@ -62,9 +64,9 @@
     <Compile Include="Data\Parsers\IdentifierParser.cs" />
     <Compile Include="Data\Parsers\IdentifierParser.cs" />
     <Compile Include="Data\Parsers\ExpressionParser.cs" />
     <Compile Include="Data\Parsers\ExpressionParser.cs" />
     <Compile Include="Data\Parsers\Reader.cs" />
     <Compile Include="Data\Parsers\Reader.cs" />
-    <Compile Include="Data\Plugins\ObservableValuePlugin.cs" />
-    <Compile Include="Data\Plugins\TaskValuePlugin.cs" />
-    <Compile Include="Data\Plugins\IValuePlugin.cs" />
+    <Compile Include="Data\Plugins\ObservableStreamPlugin.cs" />
+    <Compile Include="Data\Plugins\TaskStreamPlugin.cs" />
+    <Compile Include="Data\Plugins\IStreamPlugin.cs" />
     <Compile Include="Data\Plugins\PropertyAccessorBase.cs" />
     <Compile Include="Data\Plugins\PropertyAccessorBase.cs" />
     <Compile Include="Data\Plugins\PropertyError.cs" />
     <Compile Include="Data\Plugins\PropertyError.cs" />
     <Compile Include="Data\Plugins\DataValidatiorBase.cs" />
     <Compile Include="Data\Plugins\DataValidatiorBase.cs" />

+ 14 - 43
src/Markup/Avalonia.Markup/Data/ExpressionNode.cs

@@ -17,7 +17,6 @@ namespace Avalonia.Markup.Data
         private WeakReference _target = UnsetReference;
         private WeakReference _target = UnsetReference;
         private IDisposable _valueSubscription;
         private IDisposable _valueSubscription;
         private IObserver<object> _observer;
         private IObserver<object> _observer;
-        private IDisposable _valuePluginSubscription;
 
 
         public abstract string Description { get; }
         public abstract string Description { get; }
         public ExpressionNode Next { get; set; }
         public ExpressionNode Next { get; set; }
@@ -37,7 +36,6 @@ namespace Avalonia.Markup.Data
                 {
                 {
                     _valueSubscription?.Dispose();
                     _valueSubscription?.Dispose();
                     _valueSubscription = null;
                     _valueSubscription = null;
-                    _valuePluginSubscription?.Dispose();
                     _target = value;
                     _target = value;
 
 
                     if (running)
                     if (running)
@@ -63,8 +61,6 @@ namespace Avalonia.Markup.Data
             {
             {
                 _valueSubscription?.Dispose();
                 _valueSubscription?.Dispose();
                 _valueSubscription = null;
                 _valueSubscription = null;
-                _valuePluginSubscription?.Dispose();
-                _valuePluginSubscription = null;
                 nextSubscription?.Dispose();
                 nextSubscription?.Dispose();
                 _observer = null;
                 _observer = null;
             });
             });
@@ -92,7 +88,7 @@ namespace Avalonia.Markup.Data
 
 
         protected virtual void NextValueChanged(object value)
         protected virtual void NextValueChanged(object value)
         {
         {
-            var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainNullException;
+            var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
             bindingBroken?.AddNode(Description);
             bindingBroken?.AddNode(Description);
             _observer.OnNext(value);
             _observer.OnNext(value);
         }
         }
@@ -115,25 +111,22 @@ namespace Avalonia.Markup.Data
                 source = StartListeningCore(_target);
                 source = StartListeningCore(_target);
             }
             }
 
 
-            return source.Subscribe(TargetValueChanged);
+            return source.Subscribe(ValueChanged);
         }
         }
 
 
-        private void TargetValueChanged(object value)
+        private void ValueChanged(object value)
         {
         {
             var notification = value as BindingNotification;
             var notification = value as BindingNotification;
 
 
             if (notification == null)
             if (notification == null)
             {
             {
-                if (!HandleSpecialValue(value))
+                if (Next != null)
                 {
                 {
-                    if (Next != null)
-                    {
-                        Next.Target = new WeakReference(value);
-                    }
-                    else
-                    {
-                        _observer.OnNext(value);
-                    }
+                    Next.Target = new WeakReference(value);
+                }
+                else
+                {
+                    _observer.OnNext(value);
                 }
                 }
             }
             }
             else
             else
@@ -144,44 +137,22 @@ namespace Avalonia.Markup.Data
                 }
                 }
                 else if (notification.HasValue)
                 else if (notification.HasValue)
                 {
                 {
-                    if (!HandleSpecialValue(notification.Value))
+                    if (Next != null)
                     {
                     {
-                        if (Next != null)
-                        {
-                            Next.Target = new WeakReference(notification.Value);
-                        }
-                        else
-                        {
-                            _observer.OnNext(value);
-                        }
+                        Next.Target = new WeakReference(notification.Value);
                     }
                     }
-                }
-            }
-        }
-
-        private bool HandleSpecialValue(object value)
-        {
-            if (_valuePluginSubscription == null)
-            {
-                var reference = new WeakReference(value);
-
-                foreach (var plugin in ExpressionObserver.ValueHandlers)
-                {
-                    if (plugin.Match(reference))
+                    else
                     {
                     {
-                        _valuePluginSubscription = plugin.Start(reference)?.Subscribe(TargetValueChanged);
-                        return true;
+                        _observer.OnNext(value);
                     }
                     }
                 }
                 }
             }
             }
-
-            return false;
         }
         }
 
 
         private BindingNotification TargetNullNotification()
         private BindingNotification TargetNullNotification()
         {
         {
             return new BindingNotification(
             return new BindingNotification(
-                new MarkupBindingChainNullException(),
+                new MarkupBindingChainException("Null value"),
                 BindingErrorType.Error,
                 BindingErrorType.Error,
                 AvaloniaProperty.UnsetValue);
                 AvaloniaProperty.UnsetValue);
         }
         }

+ 7 - 7
src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs

@@ -41,14 +41,14 @@ namespace Avalonia.Markup.Data
             };
             };
 
 
         /// <summary>
         /// <summary>
-        /// An ordered collection of value handlers that can be used to customize the handling
-        /// of certain values.
+        /// An ordered collection of stream plugins that can be used to customize the behavior
+        /// of the '^' stream binding operator.
         /// </summary>
         /// </summary>
-        public static readonly IList<IValuePlugin> ValueHandlers =
-            new List<IValuePlugin>
+        public static readonly IList<IStreamPlugin> StreamHandlers =
+            new List<IStreamPlugin>
             {
             {
-                new TaskValuePlugin(),
-                new ObservableValuePlugin(),
+                new TaskStreamPlugin(),
+                new ObservableStreamPlugin(),
             };
             };
 
 
         private static readonly object UninitializedValue = new object();
         private static readonly object UninitializedValue = new object();
@@ -235,7 +235,7 @@ namespace Avalonia.Markup.Data
             }
             }
             else
             else
             {
             {
-                var broken = BindingNotification.ExtractError(o) as MarkupBindingChainNullException;
+                var broken = BindingNotification.ExtractError(o) as MarkupBindingChainException;
 
 
                 if (broken != null)
                 if (broken != null)
                 {
                 {

+ 42 - 0
src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Data;
+
+namespace Avalonia.Markup.Data
+{
+    internal class MarkupBindingChainException : BindingChainException
+    {
+        private IList<string> _nodes = new List<string>();
+
+        public MarkupBindingChainException(string message)
+            : base(message)
+        {
+        }
+
+        public MarkupBindingChainException(string message, string node)
+            : base(message)
+        {
+            AddNode(node);
+        }
+
+        public MarkupBindingChainException(string message, string expression, string expressionNullPoint)
+            : base(message, expression, expressionNullPoint)
+        {
+            _nodes = null;
+        }
+
+        public bool HasNodes => _nodes.Count > 0;
+        public void AddNode(string node) => _nodes.Add(node);
+
+        public void Commit(string expression)
+        {
+            Expression = expression;
+            ExpressionErrorPoint = string.Join(".", _nodes.Reverse())
+                .Replace(".!", "!")
+                .Replace(".[", "[")
+                .Replace(".^", "^");
+            _nodes = null;
+        }
+    }
+}

+ 0 - 33
src/Markup/Avalonia.Markup/Data/MarkupBindingChainNullException.cs

@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Data;
-
-namespace Avalonia.Markup.Data
-{
-    internal class MarkupBindingChainNullException : BindingChainNullException
-    {
-        private IList<string> _nodes = new List<string>();
-
-        public MarkupBindingChainNullException()
-        {
-        }
-
-        public MarkupBindingChainNullException(string expression, string expressionNullPoint)
-            : base(expression, expressionNullPoint)
-        {
-            _nodes = null;
-        }
-
-        public bool HasNodes => _nodes.Count > 0;
-        public void AddNode(string node) => _nodes.Add(node);
-
-        public void Commit(string expression)
-        {
-            Expression = expression;
-            ExpressionNullPoint = string.Join(".", _nodes.Reverse())
-                .Replace(".!", "!")
-                .Replace(".[", "[");
-            _nodes = null;
-        }
-    }
-}

+ 10 - 0
src/Markup/Avalonia.Markup/Data/Parsers/ExpressionParser.cs

@@ -87,6 +87,11 @@ namespace Avalonia.Markup.Data.Parsers
             {
             {
                 return State.BeforeMember;
                 return State.BeforeMember;
             }
             }
+            else if (ParseStreamOperator(r))
+            {
+                nodes.Add(new StreamNode());
+                return State.AfterMember;
+            }
             else
             else
             {
             {
                 var args = ArgumentListParser.Parse(r, '[', ']');
                 var args = ArgumentListParser.Parse(r, '[', ']');
@@ -161,6 +166,11 @@ namespace Avalonia.Markup.Data.Parsers
             return !r.End && r.TakeIf('(');
             return !r.End && r.TakeIf('(');
         }
         }
 
 
+        private static bool ParseStreamOperator(Reader r)
+        {
+            return !r.End && r.TakeIf('^');
+        }
+
         private enum State
         private enum State
         {
         {
             Start,
             Start,

+ 2 - 2
src/Markup/Avalonia.Markup/Data/Plugins/IValuePlugin.cs → src/Markup/Avalonia.Markup/Data/Plugins/IStreamPlugin.cs

@@ -6,9 +6,9 @@ using System;
 namespace Avalonia.Markup.Data.Plugins
 namespace Avalonia.Markup.Data.Plugins
 {
 {
     /// <summary>
     /// <summary>
-    /// Defines how values are observed by an <see cref="ExpressionObserver"/>.
+    /// Defines a plugin that handles the '^' stream binding operator.
     /// </summary>
     /// </summary>
-    public interface IValuePlugin
+    public interface IStreamPlugin
     {
     {
         /// <summary>
         /// <summary>
         /// Checks whether this plugin handles the specified value.
         /// Checks whether this plugin handles the specified value.

+ 3 - 15
src/Markup/Avalonia.Markup/Data/Plugins/ObservableValuePlugin.cs → src/Markup/Avalonia.Markup/Data/Plugins/ObservableStreamPlugin.cs

@@ -2,32 +2,20 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
-using System.Reactive.Linq;
-using System.Reactive.Subjects;
-using System.Reflection;
-using System.Threading.Tasks;
-using System.Windows.Input;
-using Avalonia.Data;
 
 
 namespace Avalonia.Markup.Data.Plugins
 namespace Avalonia.Markup.Data.Plugins
 {
 {
     /// <summary>
     /// <summary>
-    /// Handles binding to <see cref="IObservable{T}"/>s in an <see cref="ExpressionObserver"/>.
+    /// Handles binding to <see cref="IObservable{T}"/>s for the '^' stream binding operator.
     /// </summary>
     /// </summary>
-    public class ObservableValuePlugin : IValuePlugin
+    public class ObservableStreamPlugin : IStreamPlugin
     {
     {
         /// <summary>
         /// <summary>
         /// Checks whether this plugin handles the specified value.
         /// Checks whether this plugin handles the specified value.
         /// </summary>
         /// </summary>
         /// <param name="reference">A weak reference to the value.</param>
         /// <param name="reference">A weak reference to the value.</param>
         /// <returns>True if the plugin can handle the value; otherwise false.</returns>
         /// <returns>True if the plugin can handle the value; otherwise false.</returns>
-        public virtual bool Match(WeakReference reference)
-        {
-            var target = reference.Target;
-
-            // ReactiveCommand is an IObservable but we want to bind to it, not its value.
-            return target is IObservable<object> && !(target is ICommand);
-        }
+        public virtual bool Match(WeakReference reference) => reference.Target is IObservable<object>;
 
 
         /// <summary>
         /// <summary>
         /// Starts producing output based on the specified value.
         /// Starts producing output based on the specified value.

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

@@ -12,9 +12,9 @@ using Avalonia.Data;
 namespace Avalonia.Markup.Data.Plugins
 namespace Avalonia.Markup.Data.Plugins
 {
 {
     /// <summary>
     /// <summary>
-    /// Handles binding to <see cref="Task"/>s in an <see cref="ExpressionObserver"/>.
+    /// Handles binding to <see cref="Task"/>s for the '^' stream binding operator.
     /// </summary>
     /// </summary>
-    public class TaskValuePlugin : IValuePlugin
+    public class TaskStreamPlugin : IStreamPlugin
     {
     {
         /// <summary>
         /// <summary>
         /// Checks whether this plugin handles the specified value.
         /// Checks whether this plugin handles the specified value.

+ 31 - 0
src/Markup/Avalonia.Markup/Data/StreamNode.cs

@@ -0,0 +1,31 @@
+// 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.Globalization;
+using Avalonia.Data;
+using System.Reactive.Linq;
+
+namespace Avalonia.Markup.Data
+{
+    internal class StreamNode : ExpressionNode
+    {
+        public override string Description => "^";
+
+        protected override IObservable<object> StartListeningCore(WeakReference reference)
+        {
+            foreach (var plugin in ExpressionObserver.StreamHandlers)
+            {
+                if (plugin.Match(reference))
+                {
+                    return plugin.Start(reference);
+                }
+            }
+
+            // TODO: Improve error.
+            return Observable.Return(new BindingNotification(
+                new MarkupBindingChainException("Stream operator applied to unsupported type", Description),
+                BindingErrorType.Error));
+        }
+    }
+}

+ 10 - 2
src/Shared/PlatformSupport/AssetLoader.cs

@@ -20,6 +20,12 @@ namespace Avalonia.Shared.PlatformSupport
 
 
         private AssemblyDescriptor _defaultAssembly;
         private AssemblyDescriptor _defaultAssembly;
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AssetLoader"/> class.
+        /// </summary>
+        /// <param name="assembly">
+        /// The default assembly from which to load assets for which no assembly is specified.
+        /// </param>
         public AssetLoader(Assembly assembly = null)
         public AssetLoader(Assembly assembly = null)
         {
         {
             if (assembly == null)
             if (assembly == null)
@@ -28,6 +34,10 @@ namespace Avalonia.Shared.PlatformSupport
                 _defaultAssembly = new AssemblyDescriptor(assembly);
                 _defaultAssembly = new AssemblyDescriptor(assembly);
         }
         }
 
 
+        /// <summary>
+        /// Sets the default assembly from which to load assets for which no assembly is specified.
+        /// </summary>
+        /// <param name="assembly">The default assembly.</param>
         public void SetDefaultAssembly(Assembly assembly)
         public void SetDefaultAssembly(Assembly assembly)
         {
         {
             _defaultAssembly = new AssemblyDescriptor(assembly);
             _defaultAssembly = new AssemblyDescriptor(assembly);
@@ -73,8 +83,6 @@ namespace Avalonia.Shared.PlatformSupport
         {
         {
             if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
             if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
             {
             {
-                var uriQueryParams = ParseQueryString(uri);
-                var baseUriQueryParams = uri != null ? ParseQueryString(uri) : null;
                 var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
                 var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
 
 
                 if (asm == null && _defaultAssembly == null)
                 if (asm == null && _defaultAssembly == null)

+ 7 - 0
src/Skia/Avalonia.Skia.Android.TestApp/App.cs

@@ -0,0 +1,7 @@
+
+namespace Avalonia.Skia.Android.TestApp
+{
+    public class App : Application
+    {
+    }
+}

+ 24 - 8
src/Skia/Avalonia.Skia.Android.TestApp/Avalonia.Skia.Android.TestApp.csproj

@@ -20,22 +20,19 @@
     <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
     <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
+    <DebugSymbols>True</DebugSymbols>
     <DebugType>full</DebugType>
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
     <OutputPath>bin\Debug\</OutputPath>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
-    <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
+    <AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
     <AndroidLinkMode>None</AndroidLinkMode>
     <AndroidLinkMode>None</AndroidLinkMode>
-    <AndroidLinkSkip />
-    <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
+    <EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
     <BundleAssemblies>False</BundleAssemblies>
     <BundleAssemblies>False</BundleAssemblies>
     <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
     <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
-    <AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis>
-    <AndroidStoreUncompressedFileExtensions />
-    <MandroidI18n />
+    <AndroidSupportedAbis>armeabi;armeabi-v7a;x86</AndroidSupportedAbis>
     <Debugger>Xamarin</Debugger>
     <Debugger>Xamarin</Debugger>
     <AndroidEnableMultiDex>False</AndroidEnableMultiDex>
     <AndroidEnableMultiDex>False</AndroidEnableMultiDex>
     <DevInstrumentationEnabled>True</DevInstrumentationEnabled>
     <DevInstrumentationEnabled>True</DevInstrumentationEnabled>
@@ -48,7 +45,17 @@
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <WarningLevel>4</WarningLevel>
     <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
     <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
-    <AndroidLinkMode>SdkOnly</AndroidLinkMode>
+    <AndroidLinkMode>Full</AndroidLinkMode>
+    <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
+    <BundleAssemblies>False</BundleAssemblies>
+    <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
+    <Debugger>Xamarin</Debugger>
+    <AotAssemblies>False</AotAssemblies>
+    <EnableLLVM>False</EnableLLVM>
+    <AndroidEnableMultiDex>False</AndroidEnableMultiDex>
+    <EnableProguard>False</EnableProguard>
+    <DebugSymbols>False</DebugSymbols>
+    <AndroidSupportedAbis>armeabi;armeabi-v7a;x86</AndroidSupportedAbis>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <Reference Include="Mono.Android" />
     <Reference Include="Mono.Android" />
@@ -61,6 +68,7 @@
     <Reference Include="System.Xml" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <Compile Include="App.cs" />
     <Compile Include="MainActivity.cs" />
     <Compile Include="MainActivity.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Resources\Resource.Designer.cs" />
     <Compile Include="Resources\Resource.Designer.cs" />
@@ -70,6 +78,10 @@
     <AndroidResource Include="Resources\layout\Main.axml" />
     <AndroidResource Include="Resources\layout\Main.axml" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\Android\Avalonia.Android\Avalonia.Android.csproj">
+      <Project>{7b92af71-6287-4693-9dcb-bd5b6e927e23}</Project>
+      <Name>Avalonia.Android</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj">
     <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj">
       <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
       <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
       <Name>Avalonia.Animation</Name>
       <Name>Avalonia.Animation</Name>
@@ -82,6 +94,10 @@
       <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
       <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
       <Name>Avalonia.Controls</Name>
       <Name>Avalonia.Controls</Name>
     </ProjectReference>
     </ProjectReference>
+    <ProjectReference Include="..\..\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
+      <Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
+      <Name>Avalonia.DotNetFrameworkRuntime</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj">
     <ProjectReference Include="..\..\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>

+ 16 - 9
src/Skia/Avalonia.Skia.Android.TestApp/MainActivity.cs

@@ -1,15 +1,9 @@
-using System;
 using Android.App;
 using Android.App;
-using Android.Content;
-using Android.Graphics;
-using Android.Runtime;
-using Android.Views;
-using Android.Widget;
 using Android.OS;
 using Android.OS;
-using Android.Util;
-using Avalonia.Media;
-using Avalonia.Platform;
+using Android.Views;
 using Avalonia;
 using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media;
 
 
 namespace Avalonia.Skia.Android.TestApp
 namespace Avalonia.Skia.Android.TestApp
 {
 {
@@ -20,6 +14,19 @@ namespace Avalonia.Skia.Android.TestApp
         protected override void OnCreate(Bundle bundle)
         protected override void OnCreate(Bundle bundle)
         {
         {
             base.OnCreate(bundle);
             base.OnCreate(bundle);
+
+            App app;
+            if (Avalonia.Application.Current != null)
+                app = (App)Avalonia.Application.Current;
+            else
+            {
+                app = new App();
+                AppBuilder.Configure(app)
+                    .UseAndroid()
+                    .UseSkia()
+                    .SetupWithoutStarting();
+            }
+
             SetContentView(new MainView(this));
             SetContentView(new MainView(this));
         }
         }
 
 

+ 2 - 0
src/Skia/Avalonia.Skia.Android.TestApp/Resources/Resource.Designer.cs

@@ -26,6 +26,8 @@ namespace Avalonia.Skia.Android.TestApp
 		
 		
 		public static void UpdateIdValues()
 		public static void UpdateIdValues()
 		{
 		{
+			global::Avalonia.Android.Resource.String.ApplicationName = global::Avalonia.Skia.Android.TestApp.Resource.String.ApplicationName;
+			global::Avalonia.Android.Resource.String.Hello = global::Avalonia.Skia.Android.TestApp.Resource.String.Hello;
 		}
 		}
 		
 		
 		public partial class Attribute
 		public partial class Attribute

+ 3 - 1
src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj

@@ -16,6 +16,7 @@
     <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
     <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
     <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
     <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
     <TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
     <TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
+    <ShouldIncludeNativeSkiaSharp>True</ShouldIncludeNativeSkiaSharp>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
     <DebugSymbols>true</DebugSymbols>
@@ -40,7 +41,7 @@
     <Reference Include="Mono.Android" />
     <Reference Include="Mono.Android" />
     <Reference Include="mscorlib" />
     <Reference Include="mscorlib" />
     <Reference Include="SkiaSharp, Version=1.54.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
     <Reference Include="SkiaSharp, Version=1.54.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
-      <HintPath>..\..\..\packages\SkiaSharp.1.54.0\lib\MonoAndroid\SkiaSharp.dll</HintPath>
+      <HintPath>..\..\..\packages\SkiaSharp.1.54.1\lib\MonoAndroid\SkiaSharp.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System" />
@@ -87,6 +88,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="AndroidRenderTarget.cs" />
     <Compile Include="AndroidRenderTarget.cs" />
+    <Compile Include="RenderTarget.cs" />
     <Compile Include="SkiaRenderView.cs" />
     <Compile Include="SkiaRenderView.cs" />
     <Compile Include="SkiaView.cs" />
     <Compile Include="SkiaView.cs" />
   </ItemGroup>
   </ItemGroup>

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff