Browse Source

Merge branch 'master' into render-loop

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

79
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.ActiveCfg = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.Build.0 = Debug|Any CPU {D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.Build.0 = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.ActiveCfg = Debug|Any CPU {D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.ActiveCfg = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.Build.0 = Debug|Any CPU {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.ActiveCfg = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.Build.0 = Debug|Any CPU {D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.Build.0 = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.ActiveCfg = Release|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.Build.0 = Release|Any CPU {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.ActiveCfg = Release|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.Build.0 = Release|Any CPU {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

25
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"; var unitTests = GetDirectories("./tests/Avalonia.*.UnitTests")
.Select(dir => System.IO.Path.GetFileName(dir.FullPath))
Func<IFileSystemInfo, bool> ExcludeWindowsTests = i => { .Where(name => isRunningOnWindows ? true : !(name.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0))
return !(i.Path.FullPath.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0); .Select(name => MakeAbsolute(File("./tests/" + name + "/bin/" + dirSuffix + "/" + name + ".dll")))
}; .ToList();
var unitTests = isRunningOnWindows ? GetFiles(pattern) : GetFiles(pattern, ExcludeWindowsTests);
if (isRunningOnWindows) if (isRunningOnWindows)
{ {
var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*Tests.dll") + var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*.Tests.dll") +
GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*Tests.dll") + GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*.LeakTests.dll") +
GetFiles("./tests/Avalonia.RenderTests/bin/" + dirSuffix + "/*Tests.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 = !isRunningOnWindows;
{
settings.NoAppDomain = false;
}
foreach (var file in unitTests) foreach (var file in unitTests)
{ {

156
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
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
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
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

20
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 to them in markup using the `{Static}` markup extension. To read more about the reasoning for this,
reasons](http://www.codemag.com/article/1501091) for this, but briefly: see [this issue comment](https://github.com/AvaloniaUI/Avalonia/issues/462#issuecomment-191849723).
- 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#
## Grid ## Grid

19
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
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
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
samples/ControlCatalog.Android/GettingStarted.Xamarin

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

46
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
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
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
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
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

13
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
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>

29
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; namespace Avalonia
using System.Collections.Generic; {
using Avalonia.Android.Platform.SkiaPlatform; public static class AndroidApplicationExtensions
using System.IO; {
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);

2
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 { }

13
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.

82
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;
using Avalonia.Controls.Platform; using Avalonia.Controls.Templates;
using Avalonia.Platform; using Avalonia.Markup.Xaml;
using TestApplication; 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"; app.Run();
var window = MainWindow.Create(); }
}
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(); return window;
app.Run(window);
} }
} }
} }

2
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 -->

3
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" />

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);
} }
} }

8
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));
} }

5
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)
{ {

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) },

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.

57
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"> /// <param name="errorPoint">
/// The point in the expression at which the null was encountered. /// 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.";
} }
} }
} }

2
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:

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; }

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;

6
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
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">

8
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.

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);
} }

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;

3
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
} }

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()

5
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);
} }

67
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()
{ {

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

21
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 return _virtualizer?.MeasureOverride(availableSize) ?? Size.Empty;
// 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;
}
}
Panel.Measure(availableSize); protected override Size ArrangeOverride(Size finalSize)
return Panel.DesiredSize; {
return _virtualizer?.ArrangeOverride(finalSize) ?? Size.Empty;
} }
/// <inheritdoc/> /// <inheritdoc/>

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

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

79
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)); var removedCharacters = 1;
--CaretIndex; // 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

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

118
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;
CharClass cc = GetCharClass(text[cursor - 1]); i = cursor;
begin = lf + 1;
i = cursor;
// skip over the word, punctuation, or run of whitespace
while (i > begin && GetCharClass(text[i - 1]) == cc)
{
i--;
}
// if the cursor was at whitespace, skip back a word too // skip over the word, punctuation, or run of whitespace
if (cc == CharClass.CharClassWhitespace && i > begin) while (i > begin && GetCharClass(text[i - 1]) == cc)
{
cc = GetCharClass(text[i - 1]);
while (i > begin && GetCharClass(text[i - 1]) == cc)
{
i--;
}
}
}
else
{ {
begin = lf + 1; i--;
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--;
}
// skip to the beginning of the word // if the cursor was at whitespace, skip back a word too
while (i > begin && !IsStartOfWord(text, i)) 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;
CharClass cc = GetCharClass(text[cursor]);
i = cursor;
// skip over the word, punctuation, or run of whitespace
while (i < cr && GetCharClass(text[i]) == cc)
{
i++;
}
// skip any whitespace after the word/punct // skip over the word, punctuation, or run of whitespace
while (i < cr && char.IsWhiteSpace(text[i])) while (i < cr && GetCharClass(text[i]) == cc)
{
i++;
}
}
else
{ {
i = cursor; i++;
}
// 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++;
}
// find the start of the next word // skip any whitespace after the word/punct
while (i < cr && !IsStartOfWord(text, i)) while (i < cr && char.IsWhiteSpace(text[i]))
{ {
i++; i++;
}
} }
return i; return i;

59
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); WeakTimer.StartWeakTimer(this, TimeSpan.FromSeconds(1));
_currentNode = _states.First;
WeakTimer.StartWeakTimer(this, new TimeSpan(0, 0, 1));
} }
public void Undo() public void Undo()
{ {
if (_currentNode?.Previous != null) if (_currentNode?.Previous != null)
{ {
_currentNode = _currentNode.Previous; _currentNode = _currentNode.Previous;
} _host.UndoRedoState = _currentNode.Value;
}
_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) { if (_currentNode?.Next != null)
_currentNode = _currentNode.Next; {
} _currentNode = _currentNode.Next;
_host.UndoRedoState = _currentNode.Value;
_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
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 -->

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

8
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(),
}; };
} }
} }

22
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
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
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
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 -->

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

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

3
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
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
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 -->

24
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)

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

8
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 the geometry.</param>
/// <param name="context">The context for <paramref name="geometry"/>.</param> public PathMarkupParser(StreamGeometryContext context)
public PathMarkupParser(StreamGeometry geometry, StreamGeometryContext context)
{ {
_geometry = geometry;
_context = context; _context = context;
} }

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

2
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
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
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
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">

6
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 /// control (such as <see cref="Selectors.OfType(Selector, Type)"/>) and ones whose result can
/// change over time (such as <see cref="Selectors.Class(FuncSelector, string)"/>. For the first /// 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.

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

5
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
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)
{ {

4
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;
} }
} }
} }

12
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?

2
src/Markup/Avalonia.Markup.Xaml/OmniXAML

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

10
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\ObservableStreamPlugin.cs" />
<Compile Include="Data\Plugins\TaskValuePlugin.cs" /> <Compile Include="Data\Plugins\TaskStreamPlugin.cs" />
<Compile Include="Data\Plugins\IValuePlugin.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" />

57
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);
{ }
Next.Target = new WeakReference(value); else
} {
else _observer.OnNext(value);
{
_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);
{
Next.Target = new WeakReference(notification.Value);
}
else
{
_observer.OnNext(value);
}
} }
} else
}
}
private bool HandleSpecialValue(object value)
{
if (_valuePluginSubscription == null)
{
var reference = new WeakReference(value);
foreach (var plugin in ExpressionObserver.ValueHandlers)
{
if (plugin.Match(reference))
{ {
_valuePluginSubscription = plugin.Start(reference)?.Subscribe(TargetValueChanged); _observer.OnNext(value);
return true;
} }
} }
} }
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);
} }

14
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 /// An ordered collection of stream plugins that can be used to customize the behavior
/// of certain values. /// of the '^' stream binding operator.
/// </summary> /// </summary>
public static readonly IList<IValuePlugin> ValueHandlers = public static readonly IList<IStreamPlugin> StreamHandlers =
new List<IValuePlugin> new List<IStreamPlugin>
{ {
new TaskValuePlugin(), new TaskStreamPlugin(),
new ObservableValuePlugin(), 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
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;
}
}
}

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
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,

4
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.

18
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) public virtual bool Match(WeakReference reference) => reference.Target is IObservable<object>;
{
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);
}
/// <summary> /// <summary>
/// Starts producing output based on the specified value. /// Starts producing output based on the specified value.

4
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
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));
}
}
}

12
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
src/Skia/Avalonia.Skia.Android.TestApp/App.cs

@ -0,0 +1,7 @@

namespace Avalonia.Skia.Android.TestApp
{
public class App : Application
{
}
}

32
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>False</EmbedAssembliesIntoApk>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies> <BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi> <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis> <AndroidSupportedAbis>armeabi;armeabi-v7a;x86</AndroidSupportedAbis>
<AndroidStoreUncompressedFileExtensions />
<MandroidI18n />
<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>

25
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 Android.Views;
using Avalonia.Media;
using Avalonia.Platform;
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
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

4
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>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save