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}"
EndProject
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
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@ -196,7 +197,6 @@ Global
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\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{ff69b927-c545-49ae-8e16-3d14d621aa12}*SharedItemsImports = 4
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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|x86.ActiveCfg = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.AppStore|x86.Build.0 = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.Build.0 = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.ActiveCfg = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Any CPU.Build.0 = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.ActiveCfg = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhone.Build.0 = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|Mono.ActiveCfg = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.ActiveCfg = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.Build.0 = Debug|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.Build.0 = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.ActiveCfg = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Debug|x86.Build.0 = Debug|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.ActiveCfg = Release|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|Any CPU.Build.0 = Release|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhone.ActiveCfg = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|iPhone.Build.0 = 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|Mono.ActiveCfg = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.ActiveCfg = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.Build.0 = Release|Any CPU
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.ActiveCfg = Release|x86
{D35A9F3D-8BB0-496E-BF72-444038A7DEBB}.Release|x86.Build.0 = Release|x86
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = 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|x86.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2460,5 +2516,6 @@ Global
{C7A69145-60B6-4882-97D6-A3921DD43978} = {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}
{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
EndGlobal

25
build.cake

@ -635,21 +635,19 @@ Task("Run-Unit-Tests")
.WithCriteria(() => !skipTests)
.Does(() =>
{
var pattern = "./tests/Avalonia.*.UnitTests/bin/" + dirSuffix + "/Avalonia.*.UnitTests.dll";
Func<IFileSystemInfo, bool> ExcludeWindowsTests = i => {
return !(i.Path.FullPath.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0);
};
var unitTests = isRunningOnWindows ? GetFiles(pattern) : GetFiles(pattern, ExcludeWindowsTests);
var unitTests = GetDirectories("./tests/Avalonia.*.UnitTests")
.Select(dir => System.IO.Path.GetFileName(dir.FullPath))
.Where(name => isRunningOnWindows ? true : !(name.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0))
.Select(name => MakeAbsolute(File("./tests/" + name + "/bin/" + dirSuffix + "/" + name + ".dll")))
.ToList();
if (isRunningOnWindows)
{
var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*Tests.dll") +
GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*Tests.dll") +
GetFiles("./tests/Avalonia.RenderTests/bin/" + dirSuffix + "/*Tests.dll");
var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*.Tests.dll") +
GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*.LeakTests.dll") +
GetFiles("./tests/Avalonia.RenderTests/bin/" + dirSuffix + "/*.RenderTests.dll");
unitTests += windowsTests;
unitTests.AddRange(windowsTests);
}
var toolPath = (isPlatformAnyCPU || isPlatformX86) ?
@ -662,10 +660,7 @@ Task("Run-Unit-Tests")
Parallelism = ParallelismOption.None
};
if (isRunningOnWindows)
{
settings.NoAppDomain = false;
}
settings.NoAppDomain = !isRunningOnWindows;
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
- name: Logging
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);
```
To read more about creating bindings from code, see [Binding from Code](binding-from-code.md).
# Subscribing to a Property on Any Object
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}"/>
<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
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
@ -92,13 +81,8 @@ referred to using the `{StyleResource}` markup extension both inside and outside
styles.
For non-style-related resources, we suggest defining them in code and referring
to them in markup using the `{Static}` markup extension. There are [various
reasons](http://www.codemag.com/article/1501091) for this, but briefly:
- Resources have to be parsed
- The tree has to be traversed to find them
- XAML doesn't handle immutable objects
- XAML syntax can be long-winded compared to C#
to them in markup using the `{Static}` markup extension. To read more about the reasoning for this,
see [this issue comment](https://github.com/AvaloniaUI/Avalonia/issues/462#issuecomment-191849723).
## Grid

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

13
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

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

82
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

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

2
src/Avalonia.Animation/Avalonia.Animation.csproj

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

3
src/Avalonia.Base/Avalonia.Base.csproj

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

2
src/Avalonia.Base/AvaloniaObject.cs

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

8
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -16,7 +16,13 @@ namespace Avalonia
/// </summary>
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));
}

5
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -64,8 +64,6 @@ namespace Avalonia
{
Contract.Requires<ArgumentNullException>(type != null);
var i = type.GetTypeInfo();
while (type != null)
{
// Ensure the type's static ctor has been run.
@ -265,7 +263,8 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// 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>
public void Register(Type type, AvaloniaProperty property)
{

1
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -207,7 +207,6 @@ namespace Avalonia.Collections
if (CollectionChanged != null)
{
var val = new KeyValuePair<TKey, TValue>(key, value);
var e = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
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);
var list = (items as IList) ?? items.ToList();
foreach (var i in items)
{
// 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
/// of the binding chain.
/// </summary>
public class BindingChainNullException : Exception
public class BindingChainException : Exception
{
private string _message;
/// <summary>
/// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
/// Initalizes a new instance of the <see cref="BindingChainException"/> class.
/// </summary>
public BindingChainNullException()
public BindingChainException()
{
}
/// <summary>
/// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
/// Initalizes a new instance of the <see cref="BindingChainException"/> class.
/// </summary>
public BindingChainNullException(string message)
/// <param name="message">The error message.</param>
public BindingChainException(string message)
{
_message = message;
}
/// <summary>
/// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
/// Initalizes a new instance of the <see cref="BindingChainException"/> class.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="expression">The expression.</param>
/// <param name="expressionNullPoint">
/// The point in the expression at which the null was encountered.
/// <param name="errorPoint">
/// The point in the expression at which the error was encountered.
/// </param>
public BindingChainNullException(string expression, string expressionNullPoint)
public BindingChainException(string message, string expression, string errorPoint)
{
_message = message;
Expression = expression;
ExpressionNullPoint = expressionNullPoint;
ExpressionErrorPoint = errorPoint;
}
/// <summary>
@ -48,37 +51,27 @@ namespace Avalonia.Data
public string Expression { get; protected set; }
/// <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>
public string ExpressionNullPoint { get; protected set; }
public string ExpressionErrorPoint { get; protected set; }
/// <inheritdoc/>
public override string Message
{
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>
/// 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
/// *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.
///
/// 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>
IDisposable StartTimer(TimeSpan interval, Action tick);
/// <summary>
/// Sends a message that causes <see cref="ProcessMessage"/> to exit.
/// </summary>
void Signal();
bool CurrentThreadIsLoopThread { get; }

1
src/Avalonia.Base/PriorityBindingEntry.cs

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

6
src/Avalonia.Controls/AppBuilderBase.cs

@ -9,9 +9,9 @@ using Avalonia.Platform;
namespace Avalonia.Controls
{
/// <summary>
/// Initializes up platform-specific services for an <see cref="Application"/>.
/// Base class for initializing platform-specific services for an <see cref="Application"/>.
/// </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()
{
/// <summary>
@ -140,6 +140,7 @@ namespace Avalonia.Controls
/// Specifies a windowing subsystem to use.
/// </summary>
/// <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>
public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "")
{
@ -159,6 +160,7 @@ namespace Avalonia.Controls
/// Specifies a rendering subsystem to use.
/// </summary>
/// <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>
public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "")
{

1
src/Avalonia.Controls/Avalonia.Controls.csproj

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

8
src/Avalonia.Controls/Canvas.cs

@ -22,25 +22,25 @@ namespace Avalonia.Controls
/// Defines the Left attached property.
/// </summary>
public static readonly AttachedProperty<double> LeftProperty =
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Left");
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Left", double.NaN);
/// <summary>
/// Defines the Top attached property.
/// </summary>
public static readonly AttachedProperty<double> TopProperty =
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Top");
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Top", double.NaN);
/// <summary>
/// Defines the Right attached property.
/// </summary>
public static readonly AttachedProperty<double> RightProperty =
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Right");
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Right", double.NaN);
/// <summary>
/// Defines the Bottom attached property.
/// </summary>
public static readonly AttachedProperty<double> BottomProperty =
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Bottom");
AvaloniaProperty.RegisterAttached<Canvas, Control, double>("Bottom", double.NaN);
/// <summary>
/// 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>
public override void RemoveRange(int index, int count)
{
var names = GetRange(index, count);
base.RemoveRange(index, count);
}

5
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@ -124,11 +124,6 @@ namespace Avalonia.Controls.Generators
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)
{
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);
//Implementation isn't needed in this case
#pragma warning disable 67 // Event not used
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>();
static bool s_designerMode;
private static double _designerScalingFactor = 1;
public static IDisposable DesignerMode()
{
@ -21,7 +20,6 @@ namespace Avalonia.Controls.Platform
public static void SetDesignerScalingFactor(double factor)
{
_designerScalingFactor = factor;
}
public static IWindowImpl CreateWindow()

5
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

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

67
src/Avalonia.Controls/Presenters/ItemVirtualizer.cs

@ -16,7 +16,7 @@ namespace Avalonia.Controls.Presenters
/// </summary>
internal abstract class ItemVirtualizer : IVirtualizingController, IDisposable
{
private bool disposedValue;
private double _crossAxisOffset;
/// <summary>
/// Initializes a new instance of the <see cref="ItemVirtualizer"/> class.
@ -62,7 +62,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Gets a value indicating whether the items should be scroll horizontally or vertically.
/// </summary>
public bool Vertical => VirtualizingPanel.ScrollDirection == Orientation.Vertical;
public bool Vertical => VirtualizingPanel?.ScrollDirection == Orientation.Vertical;
/// <summary>
/// Gets a value indicating whether logical scrolling is enabled.
@ -87,12 +87,28 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Gets the <see cref="ExtentValue"/> as a <see cref="Size"/>.
/// </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>
/// Gets the <see cref="ViewportValue"/> as a <see cref="Size"/>.
/// </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>
/// Gets or sets the <see cref="OffsetValue"/> as a <see cref="Vector"/>.
@ -101,12 +117,28 @@ namespace Avalonia.Controls.Presenters
{
get
{
return Vertical ? new Vector(0, OffsetValue) : new Vector(OffsetValue, 0);
return Vertical ? new Vector(_crossAxisOffset, OffsetValue) : new Vector(OffsetValue, _crossAxisOffset);
}
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;
}
/// <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/>
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/>
public override void UpdateControls()
{
@ -359,7 +397,6 @@ namespace Avalonia.Controls.Presenters
var count = Math.Min(Math.Abs(delta), panel.Children.Count);
var move = count < panel.Children.Count;
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)
{
@ -482,9 +519,19 @@ namespace Avalonia.Controls.Presenters
{
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);
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
// If infinity is passed as the available size and we're virtualized then we need to
// fill the available space, but to do that we *don't* want to materialize all our
// items! Take a look at the root of the tree for a MaxClientSize and use that as
// the available size.
if (availableSize == Size.Infinity && VirtualizationMode != ItemVirtualizationMode.None)
{
var window = VisualRoot as TopLevel;
if (window != null)
{
availableSize = window.PlatformImpl.MaxClientSize;
}
}
return _virtualizer?.MeasureOverride(availableSize) ?? Size.Empty;
}
Panel.Measure(availableSize);
return Panel.DesiredSize;
protected override Size ArrangeOverride(Size finalSize)
{
return _virtualizer?.ArrangeOverride(finalSize) ?? Size.Empty;
}
/// <inheritdoc/>

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

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

2
src/Avalonia.Controls/ScrollViewer.cs

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

79
src/Avalonia.Controls/TextBox.cs

@ -105,7 +105,7 @@ namespace Avalonia.Controls
public TextBox()
{
var canScrollHorizontally = this.GetObservable(TextWrappingProperty)
this.GetObservable(TextWrappingProperty)
.Select(x => x == TextWrapping.NoWrap)
.Subscribe(x => CanScrollHorizontally = x);
@ -148,7 +148,8 @@ namespace Avalonia.Controls
{
value = CoerceCaretIndex(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();
}
}
@ -239,7 +240,21 @@ namespace Avalonia.Controls
protected override void OnGotFocus(GotFocusEventArgs 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)
@ -379,8 +394,20 @@ namespace Avalonia.Controls
if (!DeleteSelection() && CaretIndex > 0)
{
SetTextInternal(text.Substring(0, caretIndex - 1) + text.Substring(caretIndex));
--CaretIndex;
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
{
removedCharacters = 2;
}
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex));
CaretIndex -= removedCharacters;
SelectionStart = SelectionEnd = CaretIndex;
}
break;
@ -393,7 +420,18 @@ namespace Avalonia.Controls
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;
@ -457,10 +495,10 @@ namespace Avalonia.Controls
case 2:
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;
case 3:
SelectionStart = 0;
@ -509,7 +547,7 @@ namespace Avalonia.Controls
var exceptions = aggregate == null ?
(IEnumerable<Exception>)new[] { exception } :
aggregate.InnerExceptions;
var filtered = exceptions.Where(x => !(x is BindingChainNullException)).ToList();
var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList();
if (filtered.Count > 0)
{
@ -575,8 +613,13 @@ namespace Avalonia.Controls
{
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;
}
@ -595,11 +638,11 @@ namespace Avalonia.Controls
{
if (direction > 0)
{
CaretIndex += StringUtils.NextWord(text, caretIndex, false) - caretIndex;
CaretIndex += StringUtils.NextWord(text, caretIndex) - caretIndex;
}
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)
{
--pos;
if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n')
{
--pos;
}
}
break;
@ -690,7 +737,7 @@ namespace Avalonia.Controls
private void SelectAll()
{
SelectionStart = 0;
SelectionEnd = Text.Length;
SelectionEnd = Text?.Length ?? 0;
}
private bool DeleteSelection()
@ -777,12 +824,6 @@ namespace Avalonia.Controls
SelectionStart = CaretIndex;
MoveHorizontal(1, modifiers);
SelectionEnd = CaretIndex;
string selection = GetSelection();
if (selection != " " && selection.EndsWith(" "))
{
SelectionEnd = CaretIndex - 1;
}
}
UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState

1
src/Avalonia.Controls/TopLevel.cs

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

59
src/Avalonia.Controls/Utils/UndoRedoHelper.cs

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

1
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -38,6 +38,7 @@
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Diagnostics.XML</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<!-- 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
{
private IControl _root;
private ReactiveObject _content;
private int _selectedTab;
@ -27,7 +25,6 @@ namespace Avalonia.Diagnostics.ViewModels
public DevToolsViewModel(IControl root)
{
_root = root;
_logicalTree = new TreePageViewModel(LogicalTreeNode.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.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,
[!ToolTip.TipProperty] = property
.WhenAnyValue(x => x.Diagnostic)
.AsBinding(),
.ToBinding(),
};
yield return new TextBlock
@ -73,13 +73,13 @@ namespace Avalonia.Diagnostics.Views
[!TextBlock.TextProperty] = property
.WhenAnyValue(v => v.Value)
.Select(v => v?.ToString())
.AsBinding(),
.ToBinding(),
};
yield return new TextBlock
{
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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using System.IO;
namespace Avalonia
{
/// <summary>
/// Initializes platform-specific services for an <see cref="Application"/>.
/// </summary>
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()
{
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()
{
var os = RuntimePlatform.GetRuntimeInfo().OperatingSystem;

1
src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj

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

25
src/Avalonia.Input/AccessKeyHandler.cs

@ -43,6 +43,16 @@ namespace Avalonia.Input
/// </summary>
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>
/// Gets or sets the window's main menu.
/// </summary>
@ -110,8 +120,14 @@ namespace Avalonia.Input
{
if (e.Key == Key.LeftAlt)
{
_altIsDown = true;
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
// access key markers in the window (i.e. "_File").
_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.
CloseMenu();
_ignoreAltUp = true;
_restoreFocusElement?.Focus();
_restoreFocusElement = null;
}
// We always handle the Alt key.
e.Handled = true;
}
else if (_altIsDown)
{
_ignoreAltUp = true;
}
}
/// <summary>
@ -179,6 +202,8 @@ namespace Avalonia.Input
switch (e.Key)
{
case Key.LeftAlt:
_altIsDown = false;
if (_ignoreAltUp)
{
_ignoreAltUp = false;

1
src/Avalonia.Input/Avalonia.Input.csproj

@ -38,6 +38,7 @@
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Input.XML</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<!-- 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>
private static IEnumerable<IInputElement> GetFocusableDescendents(IInputElement element)
{
var mode = KeyboardNavigation.GetDirectionalNavigation((InputElement)element);
var children = element.GetVisualChildren().OfType<IInputElement>();
foreach (var child in children)

1
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

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

1
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -38,6 +38,7 @@
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Layout.XML</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<!-- 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()
{
if (!_queued)
if (!_queued && !_running)
{
Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Render);
_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">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@ -34,6 +34,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Logging.Serilog.XML</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="SerilogLogger.cs" />

12
src/Avalonia.Logging.Serilog/SerilogLogger.cs

@ -8,21 +8,33 @@ using SerilogLogEventLevel = Serilog.Events.LogEventLevel;
namespace Avalonia.Logging.Serilog
{
/// <summary>
/// Sends log output to serilog.
/// </summary>
public class SerilogLogger : ILogSink
{
private readonly ILogger _output;
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)
{
_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)
{
Logger.Sink = new SerilogLogger(output);
}
/// <inheritdoc/>
public void Log(
AvaloniaLogEventLevel level,
string area,

1
src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj

@ -38,6 +38,7 @@
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.SceneGraph.XML</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<!-- 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.
/// </summary>
/// <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>
public PushedState PushOpacityMask(IBrush mask, Rect bounds)
{
@ -216,15 +219,24 @@ namespace Avalonia.Media
}
/// <summary>
/// Pushes a matrix transformation.
/// Pushes a matrix post-transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
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);
/// <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)
{
var oldMatrix = CurrentTransform;
@ -233,7 +245,10 @@ namespace Avalonia.Media
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()
{
_transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
@ -242,6 +257,9 @@ namespace Avalonia.Media
return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
}
/// <summary>
/// Disposes of any resources held by the <see cref="DrawingContext"/>.
/// </summary>
public void Dispose()
{
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>(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;
FontFamilyName = fontFamilyName;

1
src/Avalonia.SceneGraph/Media/Imaging/RenderTargetBitmap.cs

@ -4,6 +4,7 @@
using System;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.VisualTree;
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.
using System;
using Avalonia.VisualTree;
namespace Avalonia.Media
{

8
src/Avalonia.SceneGraph/Media/PathMarkupParser.cs

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

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.
using System;
using Avalonia.VisualTree;
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.
using System;
using Avalonia.VisualTree;
namespace Avalonia.Media
{

2
src/Avalonia.SceneGraph/Media/StreamGeometry.cs

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

1
src/Avalonia.SceneGraph/Media/Transform.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Animation;
using Avalonia.VisualTree;
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.
using System;
using Avalonia.VisualTree;
namespace Avalonia.Media
{

1
src/Avalonia.Styling/Avalonia.Styling.csproj

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

6
src/Avalonia.Styling/Styling/SelectorMatch.cs

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

1
src/Avalonia.Styling/Styling/Style.cs

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

1
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -36,6 +36,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Themes.Default.XML</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<!-- 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()
{
_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
{
#pragma warning disable CS0067 // Method not used
public event Action LostFocus;
#pragma warning restore CS0067
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)
{
if (_window.GdkWindow != null)
if (_window?.GdkWindow != null)
_window.GdkWindow.InvalidateRect(
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()
{
_window.Hide();
_window.Dispose();
_window = null;
}
}
}

12
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -8,14 +8,14 @@ using System.Reflection;
using System.Text;
using OmniXaml;
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
{
using Context;
using Controls;
using Data;
using OmniXaml.ObjectAssembler;
using System.Linq;
/// <summary>
/// Loads XAML for a avalonia application.
/// </summary>
@ -47,7 +47,7 @@ namespace Avalonia.Markup.Xaml
/// </summary>
/// <remarks>
/// 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
/// there. Maybe we need a way to inject OmniXAML context into the objects its
/// 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>
<DocumentationFile>bin\Release\Avalonia.Markup.XML</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Data\MarkupBindingChainNullException.cs" />
<Compile Include="Data\StreamNode.cs" />
<Compile Include="Data\MarkupBindingChainException.cs" />
<Compile Include="Data\CommonPropertyNames.cs" />
<Compile Include="Data\EmptyExpressionNode.cs" />
<Compile Include="Data\ExpressionNodeBuilder.cs" />
@ -62,9 +64,9 @@
<Compile Include="Data\Parsers\IdentifierParser.cs" />
<Compile Include="Data\Parsers\ExpressionParser.cs" />
<Compile Include="Data\Parsers\Reader.cs" />
<Compile Include="Data\Plugins\ObservableValuePlugin.cs" />
<Compile Include="Data\Plugins\TaskValuePlugin.cs" />
<Compile Include="Data\Plugins\IValuePlugin.cs" />
<Compile Include="Data\Plugins\ObservableStreamPlugin.cs" />
<Compile Include="Data\Plugins\TaskStreamPlugin.cs" />
<Compile Include="Data\Plugins\IStreamPlugin.cs" />
<Compile Include="Data\Plugins\PropertyAccessorBase.cs" />
<Compile Include="Data\Plugins\PropertyError.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 IDisposable _valueSubscription;
private IObserver<object> _observer;
private IDisposable _valuePluginSubscription;
public abstract string Description { get; }
public ExpressionNode Next { get; set; }
@ -37,7 +36,6 @@ namespace Avalonia.Markup.Data
{
_valueSubscription?.Dispose();
_valueSubscription = null;
_valuePluginSubscription?.Dispose();
_target = value;
if (running)
@ -63,8 +61,6 @@ namespace Avalonia.Markup.Data
{
_valueSubscription?.Dispose();
_valueSubscription = null;
_valuePluginSubscription?.Dispose();
_valuePluginSubscription = null;
nextSubscription?.Dispose();
_observer = null;
});
@ -92,7 +88,7 @@ namespace Avalonia.Markup.Data
protected virtual void NextValueChanged(object value)
{
var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainNullException;
var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
bindingBroken?.AddNode(Description);
_observer.OnNext(value);
}
@ -115,25 +111,22 @@ namespace Avalonia.Markup.Data
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;
if (notification == null)
{
if (!HandleSpecialValue(value))
if (Next != null)
{
if (Next != null)
{
Next.Target = new WeakReference(value);
}
else
{
_observer.OnNext(value);
}
Next.Target = new WeakReference(value);
}
else
{
_observer.OnNext(value);
}
}
else
@ -144,44 +137,22 @@ namespace Avalonia.Markup.Data
}
else if (notification.HasValue)
{
if (!HandleSpecialValue(notification.Value))
if (Next != null)
{
if (Next != null)
{
Next.Target = new WeakReference(notification.Value);
}
else
{
_observer.OnNext(value);
}
Next.Target = new WeakReference(notification.Value);
}
}
}
}
private bool HandleSpecialValue(object value)
{
if (_valuePluginSubscription == null)
{
var reference = new WeakReference(value);
foreach (var plugin in ExpressionObserver.ValueHandlers)
{
if (plugin.Match(reference))
else
{
_valuePluginSubscription = plugin.Start(reference)?.Subscribe(TargetValueChanged);
return true;
_observer.OnNext(value);
}
}
}
return false;
}
private BindingNotification TargetNullNotification()
{
return new BindingNotification(
new MarkupBindingChainNullException(),
new MarkupBindingChainException("Null value"),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue);
}

14
src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs

@ -41,14 +41,14 @@ namespace Avalonia.Markup.Data
};
/// <summary>
/// An ordered collection of value handlers that can be used to customize the handling
/// of certain values.
/// An ordered collection of stream plugins that can be used to customize the behavior
/// of the '^' stream binding operator.
/// </summary>
public static readonly IList<IValuePlugin> ValueHandlers =
new List<IValuePlugin>
public static readonly IList<IStreamPlugin> StreamHandlers =
new List<IStreamPlugin>
{
new TaskValuePlugin(),
new ObservableValuePlugin(),
new TaskStreamPlugin(),
new ObservableStreamPlugin(),
};
private static readonly object UninitializedValue = new object();
@ -235,7 +235,7 @@ namespace Avalonia.Markup.Data
}
else
{
var broken = BindingNotification.ExtractError(o) as MarkupBindingChainNullException;
var broken = BindingNotification.ExtractError(o) as MarkupBindingChainException;
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;
}
else if (ParseStreamOperator(r))
{
nodes.Add(new StreamNode());
return State.AfterMember;
}
else
{
var args = ArgumentListParser.Parse(r, '[', ']');
@ -161,6 +166,11 @@ namespace Avalonia.Markup.Data.Parsers
return !r.End && r.TakeIf('(');
}
private static bool ParseStreamOperator(Reader r)
{
return !r.End && r.TakeIf('^');
}
private enum State
{
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
{
/// <summary>
/// Defines how values are observed by an <see cref="ExpressionObserver"/>.
/// Defines a plugin that handles the '^' stream binding operator.
/// </summary>
public interface IValuePlugin
public interface IStreamPlugin
{
/// <summary>
/// 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.
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
{
/// <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>
public class ObservableValuePlugin : IValuePlugin
public class ObservableStreamPlugin : IStreamPlugin
{
/// <summary>
/// Checks whether this plugin handles the specified value.
/// </summary>
/// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns>
public virtual bool Match(WeakReference reference)
{
var target = reference.Target;
// ReactiveCommand is an IObservable but we want to bind to it, not its value.
return target is IObservable<object> && !(target is ICommand);
}
public virtual bool Match(WeakReference reference) => reference.Target is IObservable<object>;
/// <summary>
/// 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
{
/// <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>
public class TaskValuePlugin : IValuePlugin
public class TaskStreamPlugin : IStreamPlugin
{
/// <summary>
/// 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;
/// <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)
{
if (assembly == null)
@ -28,6 +34,10 @@ namespace Avalonia.Shared.PlatformSupport
_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)
{
_defaultAssembly = new AssemblyDescriptor(assembly);
@ -73,8 +83,6 @@ namespace Avalonia.Shared.PlatformSupport
{
if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
{
var uriQueryParams = ParseQueryString(uri);
var baseUriQueryParams = uri != null ? ParseQueryString(uri) : null;
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
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>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<AndroidLinkSkip />
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis>
<AndroidStoreUncompressedFileExtensions />
<MandroidI18n />
<AndroidSupportedAbis>armeabi;armeabi-v7a;x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
@ -48,7 +45,17 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<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>
<ItemGroup>
<Reference Include="Mono.Android" />
@ -61,6 +68,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="App.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
@ -70,6 +78,10 @@
<AndroidResource Include="Resources\layout\Main.axml" />
</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">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
@ -82,6 +94,10 @@
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</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">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>

25
src/Skia/Avalonia.Skia.Android.TestApp/MainActivity.cs

@ -1,15 +1,9 @@
using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Util;
using Avalonia.Media;
using Avalonia.Platform;
using Android.Views;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace Avalonia.Skia.Android.TestApp
{
@ -20,6 +14,19 @@ namespace Avalonia.Skia.Android.TestApp
protected override void OnCreate(Bundle 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));
}

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

@ -26,6 +26,8 @@ namespace Avalonia.Skia.Android.TestApp
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

4
src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj

@ -16,6 +16,7 @@
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
<ShouldIncludeNativeSkiaSharp>True</ShouldIncludeNativeSkiaSharp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -40,7 +41,7 @@
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<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>
</Reference>
<Reference Include="System" />
@ -87,6 +88,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AndroidRenderTarget.cs" />
<Compile Include="RenderTarget.cs" />
<Compile Include="SkiaRenderView.cs" />
<Compile Include="SkiaView.cs" />
</ItemGroup>

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

Loading…
Cancel
Save