Browse Source

Merge branch 'master' into fixes/1065-canscrollhorizontally

 Conflicts:
	src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
Steven Kirk 7 years ago
parent
commit
a718e23b9d
100 changed files with 516 additions and 371 deletions
  1. 3 0
      .ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
  2. 4 2
      .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject
  3. 4 8
      Avalonia.sln
  4. 2 2
      build.cake
  5. 2 2
      samples/Previewer/MainWindow.xaml.cs
  6. 2 2
      samples/RemoteTest/Program.cs
  7. 10 2
      samples/VirtualizationTest/MainWindow.xaml
  8. 18 0
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  9. 1 1
      src/Avalonia.Base/AvaloniaObject.cs
  10. 2 2
      src/Avalonia.Base/PriorityBindingEntry.cs
  11. 1 1
      src/Avalonia.Base/Threading/AvaloniaScheduler.cs
  12. 2 2
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  13. 2 2
      src/Avalonia.Base/Threading/Dispatcher.cs
  14. 2 2
      src/Avalonia.Base/Threading/IDispatcher.cs
  15. 1 1
      src/Avalonia.Controls/Carousel.cs
  16. 3 3
      src/Avalonia.Controls/ColumnDefinition.cs
  17. 1 1
      src/Avalonia.Controls/DropDown.cs
  18. 10 2
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  19. 27 0
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  20. 48 23
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  21. 1 1
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  22. 1 3
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  23. 1 1
      src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
  24. 10 0
      src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
  25. 1 0
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  26. 2 1
      src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs
  27. 1 1
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  28. 3 3
      src/Avalonia.Controls/Primitives/Thumb.cs
  29. 1 0
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  30. 1 1
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  31. 5 5
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  32. 3 3
      src/Avalonia.Controls/RowDefinition.cs
  33. 71 26
      src/Avalonia.Controls/ScrollViewer.cs
  34. 2 2
      src/Avalonia.Controls/Shapes/Line.cs
  35. 1 1
      src/Avalonia.Controls/Shapes/Path.cs
  36. 22 7
      src/Avalonia.Controls/Shapes/Shape.cs
  37. 9 21
      src/Avalonia.Controls/TextBox.cs
  38. 1 1
      src/Avalonia.Controls/TreeView.cs
  39. 0 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  40. 1 1
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  41. 1 1
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  42. 1 1
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  43. 8 8
      src/Avalonia.HtmlRenderer/HtmlControl.cs
  44. 9 9
      src/Avalonia.Input/InputElement.cs
  45. 3 3
      src/Avalonia.Input/KeyBinding.cs
  46. 1 1
      src/Avalonia.Layout/LayoutManager.cs
  47. 6 1
      src/Avalonia.Themes.Default/ListBox.xaml
  48. 3 2
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  49. 1 2
      src/Avalonia.Themes.Default/TextBox.xaml
  50. 1 1
      src/Avalonia.Themes.Default/TreeView.xaml
  51. 1 1
      src/Avalonia.Visuals/Media/Geometry.cs
  52. 1 1
      src/Avalonia.Visuals/Media/ImageBrush.cs
  53. 1 1
      src/Avalonia.Visuals/Media/MatrixTransform.cs
  54. 1 1
      src/Avalonia.Visuals/Media/RotateTransform.cs
  55. 2 2
      src/Avalonia.Visuals/Media/TranslateTransform.cs
  56. 1 1
      src/Avalonia.Visuals/Media/VisualBrush.cs
  57. 1 1
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  58. 3 6
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  59. 3 9
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  60. 17 0
      src/Avalonia.Visuals/Visual.cs
  61. 0 51
      src/Avalonia.Visuals/VisualTree/BoundsTracker.cs
  62. 5 0
      src/Avalonia.Visuals/VisualTree/IVisual.cs
  63. 1 1
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  64. 1 1
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  65. 1 1
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  66. 1 1
      src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
  67. 1 1
      src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
  68. 3 3
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  69. 1 1
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetSubject.cs
  70. 1 1
      tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs
  71. 2 0
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  72. 2 0
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  73. 13 4
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
  74. 39 7
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs
  75. 29 1
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  76. 2 14
      tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
  77. 0 2
      tests/Avalonia.Direct2D1.UnitTests/Properties/AssemblyInfo.cs
  78. 1 1
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  79. 1 1
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AttachedProperty.cs
  80. 7 5
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs
  81. 1 1
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
  82. 3 3
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/NonControl.cs
  83. 0 2
      tests/Avalonia.RenderTests/.gitignore
  84. 0 33
      tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems
  85. 0 16
      tests/Avalonia.RenderTests/Avalonia.RenderTests.shproj
  86. 3 2
      tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
  87. 0 2
      tests/Avalonia.RenderTests/Properties/AssemblyInfo.cs
  88. 23 0
      tests/Avalonia.RenderTests/Shapes/PathTests.cs
  89. 14 1
      tests/Avalonia.RenderTests/TestBase.cs
  90. 3 17
      tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
  91. 1 1
      tests/Avalonia.Styling.UnitTests/StyleTests.cs
  92. 2 2
      tests/Avalonia.UnitTests/ImmediateDispatcher.cs
  93. 2 2
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  94. 2 0
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs
  95. 2 0
      tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs
  96. 2 3
      tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs
  97. BIN
      tests/TestFiles/Cairo/SVGPath/SVGPath.expected.png
  98. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/Path_With_Rotated_Geometry.expected.png
  99. BIN
      tests/TestFiles/Skia/Controls/TextBlock/Wrapping_NoWrap.expected.png
  100. BIN
      tests/TestFiles/Skia/Shapes/Path/Path_With_Rotated_Geometry.expected.png

+ 3 - 0
.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject

@@ -1,5 +1,8 @@
 <ProjectConfiguration>
   <Settings>
+    <AdditionalFilesToIncludeForProject>
+      <Value>..\TestFiles\Direct2D1\**.*</Value>
+    </AdditionalFilesToIncludeForProject>
     <DefaultTestTimeout>3000</DefaultTestTimeout>
     <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
   </Settings>

+ 4 - 2
.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject

@@ -1,7 +1,9 @@
 <ProjectConfiguration>
   <Settings>
-    <DefaultTestTimeout>1000</DefaultTestTimeout>
-    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+    <AdditionalFilesToIncludeForProject>
+      <Value>..\TestFiles\Skia\**.*</Value>
+    </AdditionalFilesToIncludeForProject>
+    <DefaultTestTimeout>3000</DefaultTestTimeout>
     <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
   </Settings>
 </ProjectConfiguration>

+ 4 - 8
Avalonia.sln

@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.27004.2008
+VisualStudioVersion = 15.0.27130.2024
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
 EndProject
@@ -45,11 +45,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout.UnitTests",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity.UnitTests", "tests\Avalonia.Interactivity.UnitTests\Avalonia.Interactivity.UnitTests.csproj", "{08478EF5-44E8-42E9-92D6-15E00EC038D8}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input.UnitTests", "tests\Avalonia.Input.UnitTests\Avalonia.Input.UnitTests.csproj", "{AC18926A-E784-40FE-B09D-BB0FE2B599F0}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}"
 EndProject
@@ -114,8 +114,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.Te
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}"
 EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Avalonia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.RenderTests.shproj", "{48840EDD-24BF-495D-911E-2EB12AE75D3B}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualizationTest", "samples\VirtualizationTest\VirtualizationTest.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC0258-D18C-4AB3-854F-7101680FC3F9}"
@@ -176,7 +174,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DInteropSample", "sa
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Remote.Protocol", "src\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj", "{D78A720C-C0C6-478B-8564-F167F9BDD01B}"
 EndProject
@@ -200,7 +198,6 @@ Global
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
 		src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{40759a76-d0f2-464e-8000-6ff0f5c4bd7c}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
-		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{7863ea94-f0fb-4386-bf8c-e5bfa761560a}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
@@ -2643,7 +2640,6 @@ Global
 		{57E0455D-D565-44BB-B069-EE1AA20F8337} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{52F55355-D120-42AC-8116-8410A7D602FA} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
-		{48840EDD-24BF-495D-911E-2EB12AE75D3B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{A0CC0258-D18C-4AB3-854F-7101680FC3F9} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{C7A69145-60B6-4882-97D6-A3921DD43978} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}

+ 2 - 2
build.cake

@@ -197,8 +197,8 @@ Task("Run-Render-Tests")
     .IsDependentOn("Build")
     .WithCriteria(() => !parameters.SkipTests && parameters.IsRunningOnWindows)
     .Does(() => {
-        RunCoreTest("./tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj", parameters, true);
-        RunCoreTest("./tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", parameters, true);
+        RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", parameters, true);
+        RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", parameters, true);
     });
 
 Task("Run-Designer-Unit-Tests")

+ 2 - 2
samples/Previewer/MainWindow.xaml.cs

@@ -39,7 +39,7 @@ namespace Previewer
             }));
             new BsonTcpTransport().Listen(IPAddress.Loopback, 25000, t =>
             {
-                Dispatcher.UIThread.InvokeAsync(() =>
+                Dispatcher.UIThread.Post(() =>
                 {
                     if (_connection != null)
                     {
@@ -61,7 +61,7 @@ namespace Previewer
 
         private void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
         {
-            Dispatcher.UIThread.InvokeAsync(() =>
+            Dispatcher.UIThread.Post(() =>
             {
                 if (transport != _connection)
                     return;

+ 2 - 2
samples/RemoteTest/Program.cs

@@ -25,7 +25,7 @@ namespace RemoteTest
             var transport = new BsonTcpTransport();
             transport.Listen(IPAddress.Loopback, port, sc =>
             {
-                Dispatcher.UIThread.InvokeAsync(() =>
+                Dispatcher.UIThread.Post(() =>
                 {
                     new RemoteServer(sc).Content = new MainView();
                 });
@@ -34,7 +34,7 @@ namespace RemoteTest
             var cts = new CancellationTokenSource();
             transport.Connect(IPAddress.Loopback, port).ContinueWith(t =>
             {
-                Dispatcher.UIThread.InvokeAsync(() =>
+                Dispatcher.UIThread.Post(() =>
                 {
                     var window = new Window()
                     {

+ 10 - 2
samples/VirtualizationTest/MainWindow.xaml

@@ -21,6 +21,12 @@
             <TextBox Watermark="Viewport"
                      UseFloatingWatermark="True"
                      Text="{Binding #listBox.Scroll.Viewport, Mode=OneWay}"/>
+            <TextBlock>Horiz. ScrollBar</TextBlock>
+            <DropDown Items="{Binding ScrollBarVisibilities}"
+                      SelectedItem="{Binding HorizontalScrollBarVisibility}"/>
+            <TextBlock>Vert. ScrollBar</TextBlock>
+            <DropDown Items="{Binding ScrollBarVisibilities}"
+                      SelectedItem="{Binding VerticalScrollBarVisibility}"/>
             <TextBox Watermark="Item to Create"
                      UseFloatingWatermark="True"
                      Text="{Binding NewItemString}"/>
@@ -35,7 +41,9 @@
                  Items="{Binding Items}" 
                  SelectedItems="{Binding SelectedItems}"
                  SelectionMode="Multiple"
-                 VirtualizationMode="{Binding VirtualizationMode}">
+                 VirtualizationMode="{Binding VirtualizationMode}"
+                 ScrollViewer.HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility, Mode=TwoWay}"
+                 ScrollViewer.VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility, Mode=TwoWay}">
             <ListBox.ItemsPanel>
                 <ItemsPanelTemplate>
                     <VirtualizingStackPanel Orientation="{Binding Orientation}"/>
@@ -43,7 +51,7 @@
             </ListBox.ItemsPanel>
             <ListBox.ItemTemplate>
                 <DataTemplate>
-                    <TextBlock Text="{Binding Header}"/>
+                    <TextBlock Text="{Binding Header}" TextWrapping="Wrap"/>
                 </DataTemplate>
             </ListBox.ItemTemplate>
         </ListBox>

+ 18 - 0
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using System.Linq;
 using Avalonia.Collections;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
 using ReactiveUI;
 
 namespace VirtualizationTest.ViewModels
@@ -17,6 +18,8 @@ namespace VirtualizationTest.ViewModels
         private int _newItemIndex;
         private IReactiveList<ItemViewModel> _items;
         private string _prefix = "Item";
+        private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+        private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
         private Orientation _orientation = Orientation.Vertical;
         private ItemVirtualizationMode _virtualizationMode = ItemVirtualizationMode.Simple;
 
@@ -64,6 +67,21 @@ namespace VirtualizationTest.ViewModels
         public IEnumerable<Orientation> Orientations =>
             Enum.GetValues(typeof(Orientation)).Cast<Orientation>();
 
+        public ScrollBarVisibility HorizontalScrollBarVisibility
+        {
+            get { return _horizontalScrollBarVisibility; }
+            set { this.RaiseAndSetIfChanged(ref _horizontalScrollBarVisibility, value); }
+        }
+
+        public ScrollBarVisibility VerticalScrollBarVisibility
+        {
+            get { return _verticalScrollBarVisibility; }
+            set { this.RaiseAndSetIfChanged(ref _verticalScrollBarVisibility, value); }
+        }
+
+        public IEnumerable<ScrollBarVisibility> ScrollBarVisibilities =>
+            Enum.GetValues(typeof(ScrollBarVisibility)).Cast<ScrollBarVisibility>();
+
         public ItemVirtualizationMode VirtualizationMode
         {
             get { return _virtualizationMode; }

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

@@ -774,7 +774,7 @@ namespace Avalonia
             }
             else
             {
-                Dispatcher.UIThread.InvokeAsync(Set);
+                Dispatcher.UIThread.Post(Set);
             }
         }
 

+ 2 - 2
src/Avalonia.Base/PriorityBindingEntry.cs

@@ -123,7 +123,7 @@ namespace Avalonia
             }
             else
             {
-                Dispatcher.UIThread.InvokeAsync(Signal);
+                Dispatcher.UIThread.Post(Signal);
             }
         }
 
@@ -135,7 +135,7 @@ namespace Avalonia
             }
             else
             {
-                Dispatcher.UIThread.InvokeAsync(() => _owner.Completed(this));
+                Dispatcher.UIThread.Post(() => _owner.Completed(this));
             }
         }
     }

+ 1 - 1
src/Avalonia.Base/Threading/AvaloniaScheduler.cs

@@ -33,7 +33,7 @@ namespace Avalonia.Threading
                 if (!Dispatcher.UIThread.CheckAccess())
                 {
                     var cancellation = new CancellationDisposable();
-                    Dispatcher.UIThread.InvokeAsync(() =>
+                    Dispatcher.UIThread.Post(() =>
                     {
                         if (!cancellation.Token.IsCancellationRequested)
                         {

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

@@ -36,7 +36,7 @@ namespace Avalonia.Threading
         /// <inheritdoc/>
         public override void Post(SendOrPostCallback d, object state)
         {
-           Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send);
+           Dispatcher.UIThread.Post(() => d(state), DispatcherPriority.Send);
         }
 
         /// <inheritdoc/>
@@ -45,7 +45,7 @@ namespace Avalonia.Threading
             if (Dispatcher.UIThread.CheckAccess())
                 d(state);
             else
-                Dispatcher.UIThread.InvokeTaskAsync(() => d(state), DispatcherPriority.Send).Wait();
+                Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
         }
     }
 }

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

@@ -79,13 +79,13 @@ namespace Avalonia.Threading
         public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
 
         /// <inheritdoc/>
-        public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+        public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
         {
             return _jobRunner?.InvokeAsync(action, priority);
         }
 
         /// <inheritdoc/>
-        public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+        public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
         {
             _jobRunner?.Post(action, priority);
         }

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

@@ -25,7 +25,7 @@ namespace Avalonia.Threading
         /// <param name="action">The method.</param>
         /// <param name="priority">The priority with which to invoke the method.</param>
         /// <returns>A task that can be used to track the method's execution.</returns>
-        void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
+        void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
 
         /// <summary>
         /// Post action that will be invoked on main thread
@@ -34,6 +34,6 @@ namespace Avalonia.Threading
         /// <param name="priority">The priority with which to invoke the method.</param>
         // TODO: The naming of this method is confusing: the Async suffix usually means return a task.
         // Remove this and rename InvokeTaskAsync as InvokeAsync. See #816.
-        Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
+        Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
     }
 }

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

@@ -24,7 +24,7 @@ namespace Avalonia.Controls
         /// Defines the <see cref="Transition"/> property.
         /// </summary>
         public static readonly StyledProperty<IPageTransition> TransitionProperty =
-            AvaloniaProperty.Register<Carousel, IPageTransition>("Transition");
+            AvaloniaProperty.Register<Carousel, IPageTransition>(nameof(Transition));
 
         /// <summary>
         /// The default value of <see cref="ItemsControl.ItemsPanelProperty"/> for 

+ 3 - 3
src/Avalonia.Controls/ColumnDefinition.cs

@@ -12,19 +12,19 @@ namespace Avalonia.Controls
         /// Defines the <see cref="MaxWidth"/> property.
         /// </summary>
         public static readonly StyledProperty<double> MaxWidthProperty =
-            AvaloniaProperty.Register<ColumnDefinition, double>("MaxWidth", double.PositiveInfinity);
+            AvaloniaProperty.Register<ColumnDefinition, double>(nameof(MaxWidth), double.PositiveInfinity);
 
         /// <summary>
         /// Defines the <see cref="MinWidth"/> property.
         /// </summary>
         public static readonly StyledProperty<double> MinWidthProperty =
-            AvaloniaProperty.Register<ColumnDefinition, double>("MinWidth");
+            AvaloniaProperty.Register<ColumnDefinition, double>(nameof(MinWidth));
 
         /// <summary>
         /// Defines the <see cref="Width"/> property.
         /// </summary>
         public static readonly StyledProperty<GridLength> WidthProperty =
-            AvaloniaProperty.Register<ColumnDefinition, GridLength>("Width", new GridLength(1, GridUnitType.Star));
+            AvaloniaProperty.Register<ColumnDefinition, GridLength>(nameof(Width), new GridLength(1, GridUnitType.Star));
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ColumnDefinition"/> class.

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

@@ -38,7 +38,7 @@ namespace Avalonia.Controls
         /// Defines the <see cref="SelectionBoxItem"/> property.
         /// </summary>
         public static readonly DirectProperty<DropDown, object> SelectionBoxItemProperty =
-            AvaloniaProperty.RegisterDirect<DropDown, object>("SelectionBoxItem", o => o.SelectionBoxItem);
+            AvaloniaProperty.RegisterDirect<DropDown, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
 
         private bool _isDropDownOpen;
         private Popup _popup;

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

@@ -5,6 +5,7 @@ using System;
 using System.Collections;
 using System.Collections.Specialized;
 using System.Linq;
+using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Utils;
 using Avalonia.Input;
 using Avalonia.Layout;
@@ -97,6 +98,7 @@ namespace Avalonia.Controls.Presenters
         /// <inheritdoc/>
         public override Size MeasureOverride(Size availableSize)
         {
+            var scrollable = (ILogicalScrollable)Owner;
             var visualRoot = Owner.GetVisualRoot();
             var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxClientSize
                  ?? (visualRoot as TopLevel)?.ClientSize;
@@ -115,7 +117,10 @@ namespace Avalonia.Controls.Presenters
                     }
                 }
 
-                availableSize = availableSize.WithWidth(double.PositiveInfinity);
+                if (scrollable.CanHorizontallyScroll)
+                {
+                    availableSize = availableSize.WithWidth(double.PositiveInfinity);
+                }
             }
             else
             {
@@ -127,7 +132,10 @@ namespace Avalonia.Controls.Presenters
                     }
                 }
 
-                availableSize = availableSize.WithHeight(double.PositiveInfinity);
+                if (scrollable.CanVerticallyScroll)
+                {
+                    availableSize = availableSize.WithHeight(double.PositiveInfinity);
+                }
             }
 
             Owner.Panel.Measure(availableSize);

+ 27 - 0
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@@ -23,6 +23,8 @@ namespace Avalonia.Controls.Presenters
                 defaultValue: ItemVirtualizationMode.None);
 
         private ItemVirtualizer _virtualizer;
+        private bool _canHorizontallyScroll;
+        private bool _canVerticallyScroll;
 
         /// <summary>
         /// Initializes static members of the <see cref="ItemsPresenter"/> class.
@@ -46,6 +48,31 @@ namespace Avalonia.Controls.Presenters
             set { SetValue(VirtualizationModeProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+        /// </summary>
+        bool ILogicalScrollable.CanHorizontallyScroll
+        {
+            get { return _canHorizontallyScroll; }
+            set
+            {
+                _canHorizontallyScroll = value;
+                InvalidateMeasure();
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+        /// </summary>
+        bool ILogicalScrollable.CanVerticallyScroll
+        {
+            get { return _canVerticallyScroll; }
+            set
+            {
+                _canVerticallyScroll = value;
+                InvalidateMeasure();
+            }
+        }
         /// <inheritdoc/>
         bool ILogicalScrollable.IsLogicalScrollEnabled
         {

+ 48 - 23
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@@ -17,6 +17,24 @@ namespace Avalonia.Controls.Presenters
     /// </summary>
     public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable
     {
+        /// <summary>
+        /// Defines the <see cref="CanHorizontallyScroll"/> property.
+        /// </summary>
+        public static readonly DirectProperty<ScrollContentPresenter, bool> CanHorizontallyScrollProperty =
+            AvaloniaProperty.RegisterDirect<ScrollContentPresenter, bool>(
+                nameof(CanHorizontallyScroll),
+                o => o.CanHorizontallyScroll,
+                (o, v) => o.CanHorizontallyScroll = v);
+
+        /// <summary>
+        /// Defines the <see cref="CanVerticallyScroll"/> property.
+        /// </summary>
+        public static readonly DirectProperty<ScrollContentPresenter, bool> CanVerticallyScrollProperty =
+            AvaloniaProperty.RegisterDirect<ScrollContentPresenter, bool>(
+                nameof(CanVerticallyScroll),
+                o => o.CanVerticallyScroll,
+                (o, v) => o.CanVerticallyScroll = v);
+
         /// <summary>
         /// Defines the <see cref="Extent"/> property.
         /// </summary>
@@ -41,12 +59,8 @@ namespace Avalonia.Controls.Presenters
                 o => o.Viewport,
                 (o, v) => o.Viewport = v);
 
-        /// <summary>
-        /// Defines the <see cref="CanScrollHorizontally"/> property.
-        /// </summary>
-        public static readonly StyledProperty<bool> CanScrollHorizontallyProperty =
-            ScrollViewer.CanScrollHorizontallyProperty.AddOwner<ScrollContentPresenter>();
-
+        private bool _canHorizontallyScroll;
+        private bool _canVerticallyScroll;
         private Size _extent;
         private Vector _offset;
         private IDisposable _logicalScrollSubscription;
@@ -72,6 +86,24 @@ namespace Avalonia.Controls.Presenters
             this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription);
         }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+        /// </summary>
+        public bool CanHorizontallyScroll
+        {
+            get { return _canHorizontallyScroll; }
+            set { SetAndRaise(CanHorizontallyScrollProperty, ref _canHorizontallyScroll, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+        /// </summary>
+        public bool CanVerticallyScroll
+        {
+            get { return _canVerticallyScroll; }
+            set { SetAndRaise(CanVerticallyScrollProperty, ref _canVerticallyScroll, value); }
+        }
+
         /// <summary>
         /// Gets the extent of the scrollable content.
         /// </summary>
@@ -99,16 +131,6 @@ namespace Avalonia.Controls.Presenters
             private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
         }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
-        /// </summary>
-        public bool CanScrollHorizontally
-        {
-            get => GetValue(CanScrollHorizontallyProperty);
-            set => SetValue(CanScrollHorizontallyProperty, value);
-        }
-
-        /// <summary>
         /// Attempts to bring a portion of the target visual into view by scrolling the content.
         /// </summary>
         /// <param name="target">The target visual.</param>
@@ -181,8 +203,8 @@ namespace Avalonia.Controls.Presenters
             }
 
             var constraint = new Size(
-                CanScrollHorizontally ? double.PositiveInfinity : availableSize.Width,
-                double.PositiveInfinity);
+                CanHorizontallyScroll ? double.PositiveInfinity : availableSize.Width,
+                CanVerticallyScroll ? double.PositiveInfinity : availableSize.Height);
 
             Child.Measure(constraint);
             return Child.DesiredSize.Constrain(availableSize);
@@ -197,10 +219,8 @@ namespace Avalonia.Controls.Presenters
             }
 
             var size = new Size(
-                CanScrollHorizontally ?
-                    Math.Max(Child.DesiredSize.Width, finalSize.Width) :
-                    finalSize.Width,
-                Math.Max(Child.DesiredSize.Height, finalSize.Height));
+                CanHorizontallyScroll ? Math.Max(Child.DesiredSize.Width, finalSize.Width) : finalSize.Width,
+                CanVerticallyScroll ? Math.Max(Child.DesiredSize.Height, finalSize.Height) : finalSize.Height);
             ArrangeOverrideImpl(size, -Offset);
             Viewport = finalSize;
             Extent = Child.Bounds.Size;
@@ -268,7 +288,12 @@ namespace Avalonia.Controls.Presenters
                 if (scrollable.IsLogicalScrollEnabled == true)
                 {
                     _logicalScrollSubscription = new CompositeDisposable(
-                        this.GetObservable(OffsetProperty).Skip(1).Subscribe(x => scrollable.Offset = x),
+                        this.GetObservable(CanHorizontallyScrollProperty)
+                            .Subscribe(x => scrollable.CanHorizontallyScroll = x),
+                        this.GetObservable(CanVerticallyScrollProperty)
+                            .Subscribe(x => scrollable.CanVerticallyScroll = x),
+                        this.GetObservable(OffsetProperty)
+                            .Skip(1).Subscribe(x => scrollable.Offset = x),
                         Disposable.Create(() => scrollable.InvalidateScroll = null));
                     UpdateFromScrollable(scrollable);
                 }

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

@@ -191,7 +191,7 @@ namespace Avalonia.Controls.Presenters
                     // The measure is currently invalid so there's no point trying to bring the 
                     // current char into view until a measure has been carried out as the scroll
                     // viewer extents may not be up-to-date.
-                    Dispatcher.UIThread.InvokeAsync(
+                    Dispatcher.UIThread.Post(
                         () =>
                         {
                             var rect = FormattedText.HitTestTextPosition(caretIndex);

+ 1 - 3
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@@ -18,8 +18,6 @@ namespace Avalonia.Controls.Primitives
         private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =
             AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo");
 
-        private readonly BoundsTracker _tracker = new BoundsTracker();
-
         static AdornerLayer()
         {
             AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
@@ -118,7 +116,7 @@ namespace Avalonia.Controls.Primitives
                     adorner.SetValue(s_adornedElementInfoProperty, info);
                 }
 
-                info.Subscription = _tracker.Track(adorned).Subscribe(x =>
+                info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x =>
                 {
                     info.Bounds = x;
                     InvalidateArrange();

+ 1 - 1
src/Avalonia.Controls/Primitives/HeaderedContentControl.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives
         /// Defines the <see cref="Header"/> property.
         /// </summary>
         public static readonly StyledProperty<object> HeaderProperty =
-            AvaloniaProperty.Register<ContentControl, object>("Header");
+            AvaloniaProperty.Register<ContentControl, object>(nameof(Header));
 
         /// <summary>
         /// Gets or sets the header content.

+ 10 - 0
src/Avalonia.Controls/Primitives/ILogicalScrollable.cs

@@ -19,6 +19,16 @@ namespace Avalonia.Controls.Primitives
     /// </remarks>
     public interface ILogicalScrollable : IScrollable
     {
+        /// <summary>
+        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+        /// </summary>
+        bool CanHorizontallyScroll { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+        /// </summary>
+        bool CanVerticallyScroll { get; set; }
+
         /// <summary>
         /// Gets a value indicating whether logical scrolling is enabled on the control.
         /// </summary>

+ 1 - 0
src/Avalonia.Controls/Primitives/ScrollBar.cs

@@ -99,6 +99,7 @@ namespace Avalonia.Controls.Primitives
                 case ScrollBarVisibility.Visible:
                     return true;
 
+                case ScrollBarVisibility.Disabled:
                 case ScrollBarVisibility.Hidden:
                     return false;
 

+ 2 - 1
src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs

@@ -5,8 +5,9 @@ namespace Avalonia.Controls.Primitives
 {
     public enum ScrollBarVisibility
     {
+        Disabled,
         Auto,
-        Visible,
         Hidden,
+        Visible,
     }
 }

+ 1 - 1
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@@ -75,7 +75,7 @@ namespace Avalonia.Controls.Primitives
         /// Defines the <see cref="Template"/> property.
         /// </summary>
         public static readonly StyledProperty<IControlTemplate> TemplateProperty =
-            AvaloniaProperty.Register<TemplatedControl, IControlTemplate>("Template");
+            AvaloniaProperty.Register<TemplatedControl, IControlTemplate>(nameof(Template));
 
         /// <summary>
         /// Defines the IsTemplateFocusTarget attached property.

+ 3 - 3
src/Avalonia.Controls/Primitives/Thumb.cs

@@ -11,13 +11,13 @@ namespace Avalonia.Controls.Primitives
     public class Thumb : TemplatedControl
     {
         public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent =
-            RoutedEvent.Register<Thumb, VectorEventArgs>("DragStarted", RoutingStrategies.Bubble);
+            RoutedEvent.Register<Thumb, VectorEventArgs>(nameof(DragStarted), RoutingStrategies.Bubble);
 
         public static readonly RoutedEvent<VectorEventArgs> DragDeltaEvent =
-            RoutedEvent.Register<Thumb, VectorEventArgs>("DragDelta", RoutingStrategies.Bubble);
+            RoutedEvent.Register<Thumb, VectorEventArgs>(nameof(DragDelta), RoutingStrategies.Bubble);
 
         public static readonly RoutedEvent<VectorEventArgs> DragCompletedEvent =
-            RoutedEvent.Register<Thumb, VectorEventArgs>("DragCompleted", RoutingStrategies.Bubble);
+            RoutedEvent.Register<Thumb, VectorEventArgs>(nameof(DragCompleted), RoutingStrategies.Bubble);
 
         private Point? _lastPoint;
 

+ 1 - 0
src/Avalonia.Controls/Primitives/ToggleButton.cs

@@ -14,6 +14,7 @@ namespace Avalonia.Controls.Primitives
                 nameof(IsChecked),
                 o => o.IsChecked,
                 (o, v) => o.IsChecked = v,
+                unsetValue: false,
                 defaultBindingMode: BindingMode.TwoWay);
 
         public static readonly StyledProperty<bool> IsThreeStateProperty =

+ 1 - 1
src/Avalonia.Controls/Remote/RemoteWidget.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Controls.Remote
         public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
         {
             _connection = connection;
-            _connection.OnMessage += (t, msg) => Dispatcher.UIThread.InvokeAsync(() => OnMessage(msg));
+            _connection.OnMessage += (t, msg) => Dispatcher.UIThread.Post(() => OnMessage(msg));
             _connection.Send(new ClientSupportedPixelFormatsMessage
             {
                 Formats = new[]

+ 5 - 5
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@@ -46,16 +46,16 @@ namespace Avalonia.Controls.Remote.Server
                     {
                         _lastReceivedFrame = lastFrame.SequenceId;
                     }
-                    Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
+                    Dispatcher.UIThread.Post(RenderIfNeeded);
                 }
                 if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
                 {
                     lock (_lock)
                         _supportedFormats = supportedFormats.Formats;
-                    Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
+                    Dispatcher.UIThread.Post(RenderIfNeeded);
                 }
                 if (obj is MeasureViewportMessage measure)
-                    Dispatcher.UIThread.InvokeAsync(() =>
+                    Dispatcher.UIThread.Post(() =>
                     {
                         var m = Measure(new Size(measure.Width, measure.Height));
                         _transport.Send(new MeasureViewportMessage
@@ -69,7 +69,7 @@ namespace Avalonia.Controls.Remote.Server
                     lock (_lock)
                     {
                         if (_pendingAllocation == null)
-                            Dispatcher.UIThread.InvokeAsync(() =>
+                            Dispatcher.UIThread.Post(() =>
                             {
                                 ClientViewportAllocatedMessage allocation;
                                 lock (_lock)
@@ -168,7 +168,7 @@ namespace Avalonia.Controls.Remote.Server
         public override void Invalidate(Rect rect)
         {
             _invalidated = true;
-            Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
+            Dispatcher.UIThread.Post(RenderIfNeeded);
         }
 
         public override IMouseDevice MouseDevice { get; } = new MouseDevice();

+ 3 - 3
src/Avalonia.Controls/RowDefinition.cs

@@ -12,19 +12,19 @@ namespace Avalonia.Controls
         /// Defines the <see cref="MaxHeight"/> property.
         /// </summary>
         public static readonly StyledProperty<double> MaxHeightProperty =
-            AvaloniaProperty.Register<RowDefinition, double>("MaxHeight", double.PositiveInfinity);
+            AvaloniaProperty.Register<RowDefinition, double>(nameof(MaxHeight), double.PositiveInfinity);
 
         /// <summary>
         /// Defines the <see cref="MinHeight"/> property.
         /// </summary>
         public static readonly StyledProperty<double> MinHeightProperty =
-            AvaloniaProperty.Register<RowDefinition, double>("MinHeight");
+            AvaloniaProperty.Register<RowDefinition, double>(nameof(MinHeight));
 
         /// <summary>
         /// Defines the <see cref="Height"/> property.
         /// </summary>
         public static readonly StyledProperty<GridLength> HeightProperty =
-            AvaloniaProperty.Register<RowDefinition, GridLength>("Height", new GridLength(1, GridUnitType.Star));
+            AvaloniaProperty.Register<RowDefinition, GridLength>(nameof(Height), new GridLength(1, GridUnitType.Star));
 
         /// <summary>
         /// Initializes a new instance of the <see cref="RowDefinition"/> class.

+ 71 - 26
src/Avalonia.Controls/ScrollViewer.cs

@@ -2,8 +2,6 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Linq;
-using System.Reactive.Linq;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 
@@ -15,16 +13,34 @@ namespace Avalonia.Controls
     public class ScrollViewer : ContentControl, IScrollable
     {
         /// <summary>
-        /// Defines the <see cref="CanScrollHorizontally"/> property.
+        /// Defines the <see cref="CanHorizontallyScroll"/> property.
         /// </summary>
-        public static readonly StyledProperty<bool> CanScrollHorizontallyProperty =
-            AvaloniaProperty.Register<ScrollViewer, bool>(nameof(CanScrollHorizontally), true);
+        /// <remarks>
+        /// There is no public C# accessor for this property as it is intended to be bound to by a 
+        /// <see cref="ScrollContentPresenter"/> in the control's template.
+        /// </remarks>
+        public static readonly DirectProperty<ScrollViewer, bool> CanHorizontallyScrollProperty =
+            AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
+                nameof(CanHorizontallyScroll),
+                o => o.CanHorizontallyScroll);
+
+        /// <summary>
+        /// Defines the <see cref="CanVerticallyScroll"/> property.
+        /// </summary>
+        /// <remarks>
+        /// There is no public C# accessor for this property as it is intended to be bound to by a 
+        /// <see cref="ScrollContentPresenter"/> in the control's template.
+        /// </remarks>
+        public static readonly DirectProperty<ScrollViewer, bool> CanVerticallyScrollProperty =
+            AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
+                nameof(CanVerticallyScroll),
+                o => o.CanVerticallyScroll);
 
         /// <summary>
         /// Defines the <see cref="Extent"/> property.
         /// </summary>
         public static readonly DirectProperty<ScrollViewer, Size> ExtentProperty =
-            AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent), 
+            AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent),
                 o => o.Extent,
                 (o, v) => o.Extent = v);
 
@@ -41,7 +57,7 @@ namespace Avalonia.Controls
         /// Defines the <see cref="Viewport"/> property.
         /// </summary>
         public static readonly DirectProperty<ScrollViewer, Size> ViewportProperty =
-            AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport), 
+            AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport),
                 o => o.Viewport,
                 (o, v) => o.Viewport = v);
 
@@ -85,14 +101,10 @@ namespace Avalonia.Controls
         /// <summary>
         /// Defines the <see cref="HorizontalScrollBarVisibility"/> property.
         /// </summary>
-        /// <remarks>
-        /// There is no public C# accessor for this property as it is intended to be bound to by a 
-        /// <see cref="ScrollContentPresenter"/> in the control's template.
-        /// </remarks>
         public static readonly AttachedProperty<ScrollBarVisibility> HorizontalScrollBarVisibilityProperty =
             AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
                 nameof(HorizontalScrollBarVisibility),
-                ScrollBarVisibility.Auto);
+                ScrollBarVisibility.Hidden);
 
         /// <summary>
         /// Defines the VerticalScrollBarMaximum property.
@@ -136,7 +148,7 @@ namespace Avalonia.Controls
         /// </summary>
         public static readonly AttachedProperty<ScrollBarVisibility> VerticalScrollBarVisibilityProperty =
             AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
-                nameof(VerticalScrollBarVisibility), 
+                nameof(VerticalScrollBarVisibility),
                 ScrollBarVisibility.Auto);
 
         private Size _extent;
@@ -150,6 +162,8 @@ namespace Avalonia.Controls
         {
             AffectsValidation(ExtentProperty, OffsetProperty);
             AffectsValidation(ViewportProperty, OffsetProperty);
+            HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged);
+            VerticalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged);
         }
 
         /// <summary>
@@ -218,15 +232,6 @@ namespace Avalonia.Controls
             }
         }
 
-        /// <summary>
-        /// Gets a value indicating whether the content can be scrolled horizontally.
-        /// </summary>
-        public bool CanScrollHorizontally
-        {
-            get { return GetValue(CanScrollHorizontallyProperty); }
-            set { SetValue(CanScrollHorizontallyProperty, value); }
-        }
-
         /// <summary>
         /// Gets or sets the horizontal scrollbar visibility.
         /// </summary>
@@ -245,6 +250,22 @@ namespace Avalonia.Controls
             set { SetValue(VerticalScrollBarVisibilityProperty, value); }
         }
 
+        /// <summary>
+        /// Gets a value indicating whether the viewer can scroll horizontally.
+        /// </summary>
+        protected bool CanHorizontallyScroll
+        {
+            get { return HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the viewer can scroll vertically.
+        /// </summary>
+        protected bool CanVerticallyScroll
+        {
+            get { return VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; }
+        }
+
         /// <summary>
         /// Gets the maximum horizontal scrollbar value.
         /// </summary>
@@ -316,7 +337,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// <param name="control">The control to read the value from.</param>
         /// <returns>The value of the property.</returns>
-        public ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
+        public static ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
         {
             return control.GetValue(HorizontalScrollBarVisibilityProperty);
         }
@@ -326,7 +347,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// <param name="control">The control to set the value on.</param>
         /// <param name="value">The value of the property.</param>
-        public void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
+        public static void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
         {
             control.SetValue(HorizontalScrollBarVisibilityProperty, value);
         }
@@ -336,7 +357,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// <param name="control">The control to read the value from.</param>
         /// <returns>The value of the property.</returns>
-        public ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
+        public static ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
         {
             return control.GetValue(VerticalScrollBarVisibilityProperty);
         }
@@ -346,7 +367,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// <param name="control">The control to set the value on.</param>
         /// <param name="value">The value of the property.</param>
-        public void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
+        public static void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
         {
             control.SetValue(VerticalScrollBarVisibilityProperty, value);
         }
@@ -385,6 +406,30 @@ namespace Avalonia.Controls
             }
         }
 
+        private void ScrollBarVisibilityChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            var wasEnabled = !ScrollBarVisibility.Disabled.Equals(e.OldValue);
+            var isEnabled = !ScrollBarVisibility.Disabled.Equals(e.NewValue);
+
+            if (wasEnabled != isEnabled)
+            {
+                if (e.Property == HorizontalScrollBarVisibilityProperty)
+                {
+                    RaisePropertyChanged(
+                        CanHorizontallyScrollProperty,
+                        wasEnabled,
+                        isEnabled);
+                }
+                else if (e.Property == VerticalScrollBarVisibilityProperty)
+                {
+                    RaisePropertyChanged(
+                        CanVerticallyScrollProperty,
+                        wasEnabled,
+                        isEnabled);
+                }
+            }
+        }
+
         private void CalculatedPropertiesChanged()
         {
             // Pass old values of 0 here because we don't have the old values at this point,

+ 2 - 2
src/Avalonia.Controls/Shapes/Line.cs

@@ -8,10 +8,10 @@ namespace Avalonia.Controls.Shapes
     public class Line : Shape
     {
         public static readonly StyledProperty<Point> StartPointProperty =
-            AvaloniaProperty.Register<Line, Point>("StartPoint");
+            AvaloniaProperty.Register<Line, Point>(nameof(StartPoint));
 
         public static readonly StyledProperty<Point> EndPointProperty =
-            AvaloniaProperty.Register<Line, Point>("EndPoint");
+            AvaloniaProperty.Register<Line, Point>(nameof(EndPoint));
 
         static Line()
         {

+ 1 - 1
src/Avalonia.Controls/Shapes/Path.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Controls.Shapes
     public class Path : Shape
     {
         public static readonly StyledProperty<Geometry> DataProperty =
-            AvaloniaProperty.Register<Path, Geometry>("Data");
+            AvaloniaProperty.Register<Path, Geometry>(nameof(Data));
 
         static Path()
         {

+ 22 - 7
src/Avalonia.Controls/Shapes/Shape.cs

@@ -12,19 +12,19 @@ namespace Avalonia.Controls.Shapes
     public abstract class Shape : Control
     {
         public static readonly StyledProperty<IBrush> FillProperty =
-            AvaloniaProperty.Register<Shape, IBrush>("Fill");
+            AvaloniaProperty.Register<Shape, IBrush>(nameof(Fill));
 
         public static readonly StyledProperty<Stretch> StretchProperty =
-            AvaloniaProperty.Register<Shape, Stretch>("Stretch");
+            AvaloniaProperty.Register<Shape, Stretch>(nameof(Stretch));
 
         public static readonly StyledProperty<IBrush> StrokeProperty =
-            AvaloniaProperty.Register<Shape, IBrush>("Stroke");
+            AvaloniaProperty.Register<Shape, IBrush>(nameof(Stroke));
 
         public static readonly StyledProperty<AvaloniaList<double>> StrokeDashArrayProperty =
             AvaloniaProperty.Register<Shape, AvaloniaList<double>>("StrokeDashArray");
 
         public static readonly StyledProperty<double> StrokeThicknessProperty =
-            AvaloniaProperty.Register<Shape, double>("StrokeThickness");
+            AvaloniaProperty.Register<Shape, double>(nameof(StrokeThickness));
 
         private Matrix _transform = Matrix.Identity;
         private Geometry _definingGeometry;
@@ -61,12 +61,26 @@ namespace Avalonia.Controls.Shapes
         {
             get
             {
-                if (_renderedGeometry == null)
+                if (_renderedGeometry == null && DefiningGeometry != null)
                 {
-                    if (DefiningGeometry != null)
+                    if (_transform == Matrix.Identity)
+                    {
+                        _renderedGeometry = DefiningGeometry;
+                    }
+                    else
                     {
                         _renderedGeometry = DefiningGeometry.Clone();
-                        _renderedGeometry.Transform = new MatrixTransform(_transform);
+
+                        if (_renderedGeometry.Transform == null ||
+                            _renderedGeometry.Transform.Value == Matrix.Identity)
+                        {
+                            _renderedGeometry.Transform = new MatrixTransform(_transform);
+                        }
+                        else
+                        {
+                            _renderedGeometry.Transform = new MatrixTransform(
+                                _renderedGeometry.Transform.Value * _transform);
+                        }
                     }
                 }
 
@@ -193,6 +207,7 @@ namespace Avalonia.Controls.Shapes
 
             return finalSize;
         }
+
         private Size CalculateShapeSizeAndSetTransform(Size availableSize)
         {
             // This should probably use GetRenderBounds(strokeThickness) but then the calculations

+ 9 - 21
src/Avalonia.Controls/TextBox.cs

@@ -8,7 +8,6 @@ using System.Linq;
 using System.Reactive.Linq;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Templates;
 using Avalonia.Controls.Utils;
 using Avalonia.Input;
 using Avalonia.Interactivity;
@@ -21,13 +20,10 @@ namespace Avalonia.Controls
     public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
     {
         public static readonly StyledProperty<bool> AcceptsReturnProperty =
-            AvaloniaProperty.Register<TextBox, bool>("AcceptsReturn");
+            AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsReturn));
 
         public static readonly StyledProperty<bool> AcceptsTabProperty =
-            AvaloniaProperty.Register<TextBox, bool>("AcceptsTab");
-
-        public static readonly DirectProperty<TextBox, bool> CanScrollHorizontallyProperty =
-            AvaloniaProperty.RegisterDirect<TextBox, bool>("CanScrollHorizontally", o => o.CanScrollHorizontally);
+            AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsTab));
 
         public static readonly DirectProperty<TextBox, int> CaretIndexProperty =
             AvaloniaProperty.RegisterDirect<TextBox, int>(
@@ -69,10 +65,10 @@ namespace Avalonia.Controls
             TextBlock.TextWrappingProperty.AddOwner<TextBox>();
 
         public static readonly StyledProperty<string> WatermarkProperty =
-            AvaloniaProperty.Register<TextBox, string>("Watermark");
+            AvaloniaProperty.Register<TextBox, string>(nameof(Watermark));
 
         public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
-            AvaloniaProperty.Register<TextBox, bool>("UseFloatingWatermark");
+            AvaloniaProperty.Register<TextBox, bool>(nameof(UseFloatingWatermark));
 
         struct UndoRedoState : IEquatable<UndoRedoState>
         {
@@ -92,7 +88,6 @@ namespace Avalonia.Controls
         private int _caretIndex;
         private int _selectionStart;
         private int _selectionEnd;
-        private bool _canScrollHorizontally;
         private TextPresenter _presenter;
         private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
         private bool _ignoreTextChanges;
@@ -106,12 +101,11 @@ namespace Avalonia.Controls
 
         public TextBox()
         {
-            this.GetObservable(TextWrappingProperty)
-                .Select(x => x == TextWrapping.NoWrap)
-                .Subscribe(x => CanScrollHorizontally = x);
-
-            var horizontalScrollBarVisibility = this.GetObservable(AcceptsReturnProperty)
-                .Select(x => x ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden);
+            var horizontalScrollBarVisibility = Observable.CombineLatest(
+                this.GetObservable(AcceptsReturnProperty),
+                this.GetObservable(TextWrappingProperty),
+                (acceptsReturn, wrapping) => acceptsReturn && wrapping == TextWrapping.NoWrap ?
+                    ScrollBarVisibility.Auto : ScrollBarVisibility.Disabled);
 
             Bind(
                 ScrollViewer.HorizontalScrollBarVisibilityProperty,
@@ -132,12 +126,6 @@ namespace Avalonia.Controls
             set { SetValue(AcceptsTabProperty, value); }
         }
 
-        public bool CanScrollHorizontally
-        {
-            get { return _canScrollHorizontally; }
-            private set { SetAndRaise(CanScrollHorizontallyProperty, ref _canScrollHorizontally, value); }
-        }
-
         public int CaretIndex
         {
             get

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

@@ -250,7 +250,7 @@ namespace Avalonia.Controls
 
                         if (AutoScrollToSelectedItem)
                         {
-                            Dispatcher.UIThread.InvokeAsync(container.ContainerControl.BringIntoView);
+                            Dispatcher.UIThread.Post(container.ContainerControl.BringIntoView);
                         }
 
                         break;

+ 0 - 2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@@ -3,11 +3,9 @@
 
 using System;
 using System.Collections.Specialized;
-using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Layout;
-using Avalonia.VisualTree;
 
 namespace Avalonia.Controls
 {

+ 1 - 1
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@@ -49,7 +49,7 @@ namespace Avalonia.DesignerSupport.Remote
             // In previewer mode we completely ignore client-side viewport size
             if (obj is ClientViewportAllocatedMessage alloc)
             {
-                Dispatcher.UIThread.InvokeAsync(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
+                Dispatcher.UIThread.Post(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
                 return;
             }
             base.OnMessage(transport, obj);

+ 1 - 1
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@@ -140,7 +140,7 @@ namespace Avalonia.DesignerSupport.Remote
             };
         }
         
-        private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.InvokeAsync(() =>
+        private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.Post(() =>
         {
             if (obj is ClientSupportedPixelFormatsMessage formats)
             {

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

@@ -14,7 +14,7 @@ namespace Avalonia.Diagnostics.Views
     internal class ControlDetailsView : UserControl
     {
         private static readonly StyledProperty<ControlDetailsViewModel> ViewModelProperty =
-            AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>("ViewModel");
+            AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>(nameof(ViewModel));
         private SimpleGrid _grid;
 
         public ControlDetailsView()

+ 8 - 8
src/Avalonia.HtmlRenderer/HtmlControl.cs

@@ -74,29 +74,29 @@ namespace Avalonia.Controls.Html
         protected Point _lastScrollOffset;
 
         public static readonly AvaloniaProperty AvoidImagesLateLoadingProperty = 
-            PropertyHelper.Register<HtmlControl, bool>("AvoidImagesLateLoading", false, OnAvaloniaProperty_valueChanged);
+            PropertyHelper.Register<HtmlControl, bool>(nameof(AvoidImagesLateLoading), false, OnAvaloniaProperty_valueChanged);
         public static readonly AvaloniaProperty IsSelectionEnabledProperty =
-            PropertyHelper.Register<HtmlControl, bool>("IsSelectionEnabled", true, OnAvaloniaProperty_valueChanged);
+            PropertyHelper.Register<HtmlControl, bool>(nameof(IsSelectionEnabled), true, OnAvaloniaProperty_valueChanged);
         public static readonly AvaloniaProperty IsContextMenuEnabledProperty =
-            PropertyHelper.Register<HtmlControl, bool>("IsContextMenuEnabled", true, OnAvaloniaProperty_valueChanged);
+            PropertyHelper.Register<HtmlControl, bool>(nameof(IsContextMenuEnabled), true, OnAvaloniaProperty_valueChanged);
 
         public static readonly AvaloniaProperty BaseStylesheetProperty =
-            PropertyHelper.Register<HtmlControl, string>("BaseStylesheet", null, OnAvaloniaProperty_valueChanged);
+            PropertyHelper.Register<HtmlControl, string>(nameof(BaseStylesheet), null, OnAvaloniaProperty_valueChanged);
 
         public static readonly AvaloniaProperty TextProperty =
-            PropertyHelper.Register<HtmlControl, string>("Text", null, OnAvaloniaProperty_valueChanged);
+            PropertyHelper.Register<HtmlControl, string>(nameof(Text), null, OnAvaloniaProperty_valueChanged);
 
         public static readonly StyledProperty<IBrush> BackgroundProperty =
             Border.BackgroundProperty.AddOwner<HtmlControl>();
 
         public static readonly AvaloniaProperty BorderThicknessProperty =
-            AvaloniaProperty.Register<HtmlControl, Thickness>("BorderThickness", new Thickness(0));
+            AvaloniaProperty.Register<HtmlControl, Thickness>(nameof(BorderThickness), new Thickness(0));
 
         public static readonly AvaloniaProperty BorderBrushProperty =
-    AvaloniaProperty.Register<HtmlControl, IBrush>("BorderBrush");
+    AvaloniaProperty.Register<HtmlControl, IBrush>(nameof(BorderBrush));
 
         public static readonly AvaloniaProperty PaddingProperty =
-            AvaloniaProperty.Register<HtmlControl, Thickness>("Padding", new Thickness(0));
+            AvaloniaProperty.Register<HtmlControl, Thickness>(nameof(Padding), new Thickness(0));
 
         public static readonly RoutedEvent LoadCompleteEvent =
             RoutedEvent.Register<RoutedEventArgs>("LoadComplete",  RoutingStrategies.Bubble, typeof(HtmlControl));

+ 9 - 9
src/Avalonia.Input/InputElement.cs

@@ -31,43 +31,43 @@ namespace Avalonia.Input
         /// Defines the <see cref="IsEnabledCore"/> property.
         /// </summary>
         public static readonly StyledProperty<bool> IsEnabledCoreProperty =
-            AvaloniaProperty.Register<InputElement, bool>("IsEnabledCore", true);
+            AvaloniaProperty.Register<InputElement, bool>(nameof(IsEnabledCore), true);
 
         /// <summary>
         /// Gets or sets associated mouse cursor.
         /// </summary>
         public static readonly StyledProperty<Cursor> CursorProperty =
-            AvaloniaProperty.Register<InputElement, Cursor>("Cursor", null, true);
+            AvaloniaProperty.Register<InputElement, Cursor>(nameof(Cursor), null, true);
 
         /// <summary>
         /// Defines the <see cref="IsFocused"/> property.
         /// </summary>
         public static readonly DirectProperty<InputElement, bool> IsFocusedProperty =
-            AvaloniaProperty.RegisterDirect<InputElement, bool>("IsFocused", o => o.IsFocused);
+            AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsFocused), o => o.IsFocused);
 
         /// <summary>
         /// Defines the <see cref="IsHitTestVisible"/> property.
         /// </summary>
         public static readonly StyledProperty<bool> IsHitTestVisibleProperty =
-            AvaloniaProperty.Register<InputElement, bool>("IsHitTestVisible", true);
+            AvaloniaProperty.Register<InputElement, bool>(nameof(IsHitTestVisible), true);
 
         /// <summary>
         /// Defines the <see cref="IsPointerOver"/> property.
         /// </summary>
         public static readonly DirectProperty<InputElement, bool> IsPointerOverProperty =
-            AvaloniaProperty.RegisterDirect<InputElement, bool>("IsPointerOver", o => o.IsPointerOver);
+            AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsPointerOver), o => o.IsPointerOver);
 
         /// <summary>
         /// Defines the <see cref="GotFocus"/> event.
         /// </summary>
         public static readonly RoutedEvent<GotFocusEventArgs> GotFocusEvent =
-            RoutedEvent.Register<InputElement, GotFocusEventArgs>("GotFocus", RoutingStrategies.Bubble);
+            RoutedEvent.Register<InputElement, GotFocusEventArgs>(nameof(GotFocus), RoutingStrategies.Bubble);
 
         /// <summary>
         /// Defines the <see cref="LostFocus"/> event.
         /// </summary>
         public static readonly RoutedEvent<RoutedEventArgs> LostFocusEvent =
-            RoutedEvent.Register<InputElement, RoutedEventArgs>("LostFocus", RoutingStrategies.Bubble);
+            RoutedEvent.Register<InputElement, RoutedEventArgs>(nameof(LostFocus), RoutingStrategies.Bubble);
 
         /// <summary>
         /// Defines the <see cref="KeyDown"/> event.
@@ -97,13 +97,13 @@ namespace Avalonia.Input
         /// Defines the <see cref="PointerEnter"/> event.
         /// </summary>
         public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent =
-            RoutedEvent.Register<InputElement, PointerEventArgs>("PointerEnter", RoutingStrategies.Direct);
+            RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerEnter), RoutingStrategies.Direct);
 
         /// <summary>
         /// Defines the <see cref="PointerLeave"/> event.
         /// </summary>
         public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
-            RoutedEvent.Register<InputElement, PointerEventArgs>("PointerLeave", RoutingStrategies.Direct);
+            RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerLeave), RoutingStrategies.Direct);
 
         /// <summary>
         /// Defines the <see cref="PointerMoved"/> event.

+ 3 - 3
src/Avalonia.Input/KeyBinding.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Input
     public class KeyBinding : AvaloniaObject
     {
         public static readonly StyledProperty<ICommand> CommandProperty =
-            AvaloniaProperty.Register<KeyBinding, ICommand>("Command");
+            AvaloniaProperty.Register<KeyBinding, ICommand>(nameof(Command));
 
         public ICommand Command
         {
@@ -19,7 +19,7 @@ namespace Avalonia.Input
         }
 
         public static readonly StyledProperty<object> CommandParameterProperty =
-            AvaloniaProperty.Register<KeyBinding, object>("CommandParameter");
+            AvaloniaProperty.Register<KeyBinding, object>(nameof(CommandParameter));
 
         public object CommandParameter
         {
@@ -28,7 +28,7 @@ namespace Avalonia.Input
         }
 
         public static readonly StyledProperty<KeyGesture> GestureProperty =
-            AvaloniaProperty.Register<KeyBinding, KeyGesture>("Gesture");
+            AvaloniaProperty.Register<KeyBinding, KeyGesture>(nameof(Gesture));
 
         public KeyGesture Gesture
         {

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

@@ -203,7 +203,7 @@ namespace Avalonia.Layout
         {
             if (!_queued && !_running)
             {
-                Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Layout);
+                Dispatcher.UIThread.Post(ExecuteLayoutPass, DispatcherPriority.Layout);
                 _queued = true;
             }
         }

+ 6 - 1
src/Avalonia.Themes.Default/ListBox.xaml

@@ -3,11 +3,16 @@
   <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
   <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
   <Setter Property="Padding" Value="4"/>
+  <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
+  <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
   <Setter Property="Template">
     <ControlTemplate>
       <Border BorderBrush="{TemplateBinding BorderBrush}"
               BorderThickness="{TemplateBinding BorderThickness}">
-        <ScrollViewer Name="PART_ScrollViewer" Background="{TemplateBinding Background}">
+        <ScrollViewer Name="PART_ScrollViewer"
+                      Background="{TemplateBinding Background}"
+                      HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
+                      VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
           <ItemsPresenter Name="PART_ItemsPresenter"
                           Items="{TemplateBinding Items}"
                           ItemsPanel="{TemplateBinding ItemsPanel}"

+ 3 - 2
src/Avalonia.Themes.Default/ScrollViewer.xaml

@@ -6,12 +6,13 @@
       <Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
         <ScrollContentPresenter Name="PART_ContentPresenter"
                                 Background="{TemplateBinding Background}"
+                                CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
+                                CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
                                 Content="{TemplateBinding Content}"
                                 Extent="{TemplateBinding Path=Extent, Mode=TwoWay}"
                                 Margin="{TemplateBinding Padding}"
                                 Offset="{TemplateBinding Path=Offset, Mode=TwoWay}"
-                                Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"
-                                CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"/>
+                                Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"/>
         <ScrollBar Name="horizontalScrollBar"
                    Orientation="Horizontal"
                    Maximum="{TemplateBinding HorizontalScrollBarMaximum}"

+ 1 - 2
src/Avalonia.Themes.Default/TextBox.xaml

@@ -36,8 +36,7 @@
                 <Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource ErrorBrush}" StrokeThickness="2"/>
               </Canvas>
               
-              <ScrollViewer CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"
-                            HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
+              <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
                             VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
                 
               <Panel>

+ 1 - 1
src/Avalonia.Themes.Default/TreeView.xaml

@@ -7,7 +7,7 @@
     <ControlTemplate>
       <Border BorderBrush="{TemplateBinding BorderBrush}"
               BorderThickness="{TemplateBinding BorderThickness}">
-        <ScrollViewer CanScrollHorizontally="True" Background="{TemplateBinding Background}">
+        <ScrollViewer Background="{TemplateBinding Background}">
           <ItemsPresenter Name="PART_ItemsPresenter"
                           Items="{TemplateBinding Items}"
                           ItemsPanel="{TemplateBinding ItemsPanel}"

+ 1 - 1
src/Avalonia.Visuals/Media/Geometry.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Transform"/> property.
         /// </summary>
         public static readonly StyledProperty<Transform> TransformProperty =
-            AvaloniaProperty.Register<Geometry, Transform>("Transform");
+            AvaloniaProperty.Register<Geometry, Transform>(nameof(Transform));
 
         /// <summary>
         /// Initializes static members of the <see cref="Geometry"/> class.

+ 1 - 1
src/Avalonia.Visuals/Media/ImageBrush.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Visual"/> property.
         /// </summary>
         public static readonly StyledProperty<IBitmap> SourceProperty =
-            AvaloniaProperty.Register<ImageBrush, IBitmap>("Source");
+            AvaloniaProperty.Register<ImageBrush, IBitmap>(nameof(Source));
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageBrush"/> class.

+ 1 - 1
src/Avalonia.Visuals/Media/MatrixTransform.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Matrix"/> property.
         /// </summary>
         public static readonly StyledProperty<Matrix> MatrixProperty =
-            AvaloniaProperty.Register<MatrixTransform, Matrix>("Matrix", Matrix.Identity);
+            AvaloniaProperty.Register<MatrixTransform, Matrix>(nameof(Matrix), Matrix.Identity);
 
         /// <summary>
         /// Initializes a new instance of the <see cref="MatrixTransform"/> class.

+ 1 - 1
src/Avalonia.Visuals/Media/RotateTransform.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Angle"/> property.
         /// </summary>
         public static readonly StyledProperty<double> AngleProperty =
-            AvaloniaProperty.Register<RotateTransform, double>("Angle");
+            AvaloniaProperty.Register<RotateTransform, double>(nameof(Angle));
 
         /// <summary>
         /// Initializes a new instance of the <see cref="RotateTransform"/> class.

+ 2 - 2
src/Avalonia.Visuals/Media/TranslateTransform.cs

@@ -15,13 +15,13 @@ namespace Avalonia.Media
         /// Defines the <see cref="X"/> property.
         /// </summary>
         public static readonly StyledProperty<double> XProperty =
-            AvaloniaProperty.Register<TranslateTransform, double>("X");
+            AvaloniaProperty.Register<TranslateTransform, double>(nameof(X));
 
         /// <summary>
         /// Defines the <see cref="Y"/> property.
         /// </summary>
         public static readonly StyledProperty<double> YProperty =
-            AvaloniaProperty.Register<TranslateTransform, double>("Y");
+            AvaloniaProperty.Register<TranslateTransform, double>(nameof(Y));
 
         /// <summary>
         /// Initializes a new instance of the <see cref="TranslateTransform"/> class.

+ 1 - 1
src/Avalonia.Visuals/Media/VisualBrush.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Visual"/> property.
         /// </summary>
         public static readonly StyledProperty<IVisual> VisualProperty =
-            AvaloniaProperty.Register<VisualBrush, IVisual>("Visual");
+            AvaloniaProperty.Register<VisualBrush, IVisual>(nameof(Visual));
 
         /// <summary>
         /// Initializes a new instance of the <see cref="VisualBrush"/> class.

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

@@ -415,7 +415,7 @@ namespace Avalonia.Rendering
                     if (!_updateQueued && (_dirty == null || _dirty.Count > 0))
                     {
                         _updateQueued = true;
-                        _dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
+                        _dispatcher.Post(UpdateScene, DispatcherPriority.Render);
                     }
 
                     Scene scene = null;

+ 3 - 6
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@@ -169,7 +169,7 @@ namespace Avalonia.Rendering
         {
             foreach (var e in visual.GetSelfAndVisualDescendants())
             {
-                BoundsTracker.SetTransformedBounds((Visual)visual, null);
+                visual.TransformedBounds = null;
             }
         }
 
@@ -197,7 +197,7 @@ namespace Avalonia.Rendering
 
             if (filter?.Invoke(visual) != false)
             {
-                bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true;
+                bool containsPoint = visual.TransformedBounds?.Contains(p) == true;
 
                 if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0)
                 {
@@ -257,10 +257,7 @@ namespace Avalonia.Rendering
                         new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform);
 #pragma warning restore 0618
 
-                    if (visual is Visual)
-                    {
-                        BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
-                    }
+                    visual.TransformedBounds = transformed;
 
                     foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance))
                     {

+ 3 - 9
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@@ -209,11 +209,8 @@ namespace Avalonia.Rendering.SceneGraph
                     }
                     catch { }
 
-                    if (visual is Visual)
-                    {
-                        var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
-                        BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
-                    }
+                    var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
+                    visual.TransformedBounds = transformed;
 
                     if (forceRecurse)
                     {
@@ -279,10 +276,7 @@ namespace Avalonia.Rendering.SceneGraph
 
             scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds);
 
-            if (node.Visual is Visual v)
-            {
-                BoundsTracker.SetTransformedBounds(v, null);
-            }
+            node.Visual.TransformedBounds = null;
 
             foreach (VisualNode child in node.Children)
             {

+ 17 - 0
src/Avalonia.Visuals/Visual.cs

@@ -32,6 +32,11 @@ namespace Avalonia
         public static readonly DirectProperty<Visual, Rect> BoundsProperty =
             AvaloniaProperty.RegisterDirect<Visual, Rect>(nameof(Bounds), o => o.Bounds);
 
+        public static readonly DirectProperty<Visual, TransformedBounds?> TransformedBoundsProperty =
+            AvaloniaProperty.RegisterDirect<Visual, TransformedBounds?>(
+                nameof(TransformedBounds),
+                o => o.TransformedBounds);
+
         /// <summary>
         /// Defines the <see cref="ClipToBounds"/> property.
         /// </summary>
@@ -87,6 +92,7 @@ namespace Avalonia
             AvaloniaProperty.Register<Visual, int>(nameof(ZIndex));
 
         private Rect _bounds;
+        private TransformedBounds? _transformedBounds;
         private IRenderRoot _visualRoot;
         private IVisual _visualParent;
 
@@ -135,6 +141,11 @@ namespace Avalonia
             protected set { SetAndRaise(BoundsProperty, ref _bounds, value); }
         }
 
+        /// <summary>
+        /// Gets the bounds of the control relative to the window, accounting for rendering transforms.
+        /// </summary>
+        public TransformedBounds? TransformedBounds => _transformedBounds;
+
         /// <summary>
         /// Gets a value indicating whether the control should be clipped to its bounds.
         /// </summary>
@@ -253,6 +264,12 @@ namespace Avalonia
         /// Gets the root of the visual tree, if the control is attached to a visual tree.
         /// </summary>
         IRenderRoot IVisual.VisualRoot => VisualRoot;
+        
+        TransformedBounds? IVisual.TransformedBounds
+        {
+            get { return _transformedBounds; }
+            set { SetAndRaise(TransformedBoundsProperty, ref _transformedBounds, value); }
+        }
 
         /// <summary>
         /// Invalidates the visual and queues a repaint.

+ 0 - 51
src/Avalonia.Visuals/VisualTree/BoundsTracker.cs

@@ -1,51 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-
-namespace Avalonia.VisualTree
-{
-    /// <summary>
-    /// Tracks the bounds of a control.
-    /// </summary>
-    /// <remarks>
-    /// This class is used to track a controls's bounds for hit testing.
-    /// TODO: This shouldn't be implemented as an attached property: it would be more performant
-    /// to just store bounds in some sort of central repository.
-    /// </remarks>
-    public class BoundsTracker
-    {
-        /// <summary>
-        /// Defines the TransformedBounds attached property.
-        /// </summary>
-        private static AttachedProperty<TransformedBounds?> TransformedBoundsProperty =
-            AvaloniaProperty.RegisterAttached<BoundsTracker, Visual, TransformedBounds?>("TransformedBounds");
-
-        /// <summary>
-        /// Starts tracking the specified visual.
-        /// </summary>
-        /// <param name="visual">The visual.</param>
-        /// <returns>An observable that returns the tracked bounds.</returns>
-        public IObservable<TransformedBounds?> Track(Visual visual)
-        {
-            return visual.GetObservable(TransformedBoundsProperty);
-        }
-
-        /// <summary>
-        /// Sets the transformed bounds of the visual.
-        /// </summary>
-        /// <param name="visual">The visual.</param>
-        /// <param name="value">The transformed bounds.</param>
-        internal static void SetTransformedBounds(Visual visual, TransformedBounds? value)
-        {
-            visual.SetValue(TransformedBoundsProperty, value);
-        }
-
-        /// <summary>
-        /// Gets the transformed bounds of the visual.
-        /// </summary>
-        /// <param name="visual">The visual.</param>
-        /// <returns>The transformed bounds or null if the visual is not visible.</returns>
-        public static TransformedBounds? GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
-    }
-}

+ 5 - 0
src/Avalonia.Visuals/VisualTree/IVisual.cs

@@ -36,6 +36,11 @@ namespace Avalonia.VisualTree
         /// </summary>
         Rect Bounds { get; }
 
+        /// <summary>
+        /// Gets the bounds of the control relative to the window, accounting for rendering transforms.
+        /// </summary>
+        TransformedBounds? TransformedBounds { get; set; }
+
         /// <summary>
         /// Gets a value indicating whether the control should be clipped to its bounds.
         /// </summary>

+ 1 - 1
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -351,7 +351,7 @@ namespace Avalonia.Gtk3
 
         void OnInput(RawInputEventArgs args)
         {
-            Dispatcher.UIThread.InvokeAsync(() => Input?.Invoke(args), DispatcherPriority.Input);
+            Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input);
         }
 
         public Point PointToClient(Point point)

+ 1 - 1
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -41,7 +41,7 @@ namespace Avalonia.LinuxFramebuffer
             if(_renderQueued)
                 return;
             _renderQueued = true;
-            Dispatcher.UIThread.InvokeAsync(() =>
+            Dispatcher.UIThread.Post(() =>
             {
                 Paint?.Invoke(new Rect(default(Point), ClientSize));
                 _renderQueued = false;

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

@@ -62,7 +62,7 @@ public static class LinuxFramebufferPlatformExtensions
 
         public TokenClosable(CancellationToken token)
         {
-            token.Register(() => Dispatcher.UIThread.InvokeAsync(() => Closed?.Invoke(this, new EventArgs())));
+            token.Register(() => Dispatcher.UIThread.Post(() => Closed?.Invoke(this, new EventArgs())));
         }
     }
 

+ 1 - 1
src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs

@@ -104,7 +104,7 @@ namespace Avalonia.Markup.Data.Plugins
 
             protected override void SubscribeCore(IObserver<object> observer)
             {
-                _subscription = Instance.GetWeakObservable(_property).Subscribe(observer);
+                _subscription = Instance?.GetWeakObservable(_property).Subscribe(observer);
             }
         }
     }

+ 1 - 1
src/OSX/Avalonia.MonoMac/TopLevelImpl.cs

@@ -107,7 +107,7 @@ namespace Avalonia.MonoMac
                     if (_nonUiRedrawQueued)
                         return;
                     _nonUiRedrawQueued = true;
-                    Dispatcher.UIThread.InvokeAsync(
+                    Dispatcher.UIThread.Post(
                         () =>
                         {
                             lock (SyncRoot)

+ 3 - 3
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@@ -506,17 +506,17 @@ namespace Avalonia.Base.UnitTests
         {
             public static readonly DirectProperty<Class1, string> FooProperty =
                 AvaloniaProperty.RegisterDirect<Class1, string>(
-                    "Foo",
+                    nameof(Foo),
                     o => o.Foo,
                     (o, v) => o.Foo = v,
                     unsetValue: "unset");
 
             public static readonly DirectProperty<Class1, string> BarProperty =
-                AvaloniaProperty.RegisterDirect<Class1, string>("Bar", o => o.Bar);
+                AvaloniaProperty.RegisterDirect<Class1, string>(nameof(Bar), o => o.Bar);
 
             public static readonly DirectProperty<Class1, int> BazProperty =
                 AvaloniaProperty.RegisterDirect<Class1, int>(
-                    "Bar",
+                    nameof(Baz),
                     o => o.Baz,
                     (o, v) => o.Baz = v,
                     unsetValue: -1);

+ 1 - 1
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetSubject.cs

@@ -37,7 +37,7 @@ namespace Avalonia.Base.UnitTests
         private class Class1 : AvaloniaObject
         {
             public static readonly StyledProperty<string> FooProperty =
-                AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
+                AvaloniaProperty.Register<Class1, string>(nameof(Foo), "foodefault");
 
             public string Foo
             {

+ 1 - 1
tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs

@@ -85,7 +85,7 @@ namespace Avalonia.Base.UnitTests
         private class Class1 : AvaloniaObject
         {
             public static readonly DirectProperty<Class1, string> FooProperty =
-                AvaloniaProperty.RegisterDirect<Class1, string>("Foo", o => o.Foo, (o, v) => o.Foo = v);
+                AvaloniaProperty.RegisterDirect<Class1, string>(nameof(Foo), o => o.Foo, (o, v) => o.Foo = v);
 
             private string _foo = "foo";
 

+ 2 - 0
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@@ -281,6 +281,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
             var scroller = new TestScroller
             {
+                CanHorizontallyScroll = false,
+                CanVerticallyScroll = true,
                 Content = result = new TestItemsPresenter(useContainers)
                 {
                     Items = items,

+ 2 - 0
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@@ -998,6 +998,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
             var scroller = new TestScroller
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = result = new TestItemsPresenter(useContainers)
                 {
                     Items = items,

+ 13 - 4
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Reactive.Linq;
 using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Primitives;
 using Avalonia.Layout;
 using Xunit;
 
@@ -94,6 +95,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             TestControl content;
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = content = new TestControl(),
             };
 
@@ -110,6 +113,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             Border content;
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = content = new Border
                 {
                     Width = 150,
@@ -131,8 +136,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var child = new TestControl();
             var target = new ScrollContentPresenter
             {
+                CanVerticallyScroll = true,
                 Content = child,
-                [ScrollContentPresenter.CanScrollHorizontallyProperty] = false,
             };
 
             target.UpdateChild();
@@ -147,6 +152,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var child = new TestControl();
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = child,
             };
 
@@ -191,7 +198,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
         }
 
         [Fact]
-        public void Arrange_Should_Constrain_Child_Width_When_CanScrollHorizontally_False()
+        public void Arrange_Should_Constrain_Child_Width_When_CanHorizontallyScroll_False()
         {
             var child = new WrapPanel
             {
@@ -206,7 +213,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var target = new ScrollContentPresenter
             {
                 Content = child,
-                CanScrollHorizontally = false,
+                CanHorizontallyScroll = false,
             };
 
             target.UpdateChild();
@@ -232,7 +239,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var target = new ScrollContentPresenter
             {
                 Content = child,
-                CanScrollHorizontally = false,
+                CanHorizontallyScroll = false,
             };
 
             target.UpdateChild();
@@ -287,6 +294,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             Border border;
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Width = 100,
                 Height = 100,
                 Content = new Decorator

+ 39 - 7
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs

@@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests
     public class ScrollContentPresenterTests_ILogicalScrollable
     {
         [Fact]
-        public void Measure_Should_Pass_Unchanged_Bounds_To_IScrollable()
+        public void Measure_Should_Pass_Unchanged_Bounds_To_ILogicalScrollable()
         {
             var scrollable = new TestScrollable();
             var target = new ScrollContentPresenter
@@ -28,7 +28,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Arrange_Should_Not_Offset_IScrollable_Bounds()
+        public void Arrange_Should_Not_Offset_ILogicalScrollable_Bounds()
         {
             var scrollable = new TestScrollable
             {
@@ -50,7 +50,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Arrange_Should_Offset_IScrollable_Bounds_When_Logical_Scroll_Disabled()
+        public void Arrange_Should_Offset_ILogicalScrollable_Bounds_When_Logical_Scroll_Disabled()
         {
             var scrollable = new TestScrollable
             {
@@ -59,6 +59,8 @@ namespace Avalonia.Controls.UnitTests
 
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = scrollable,
                 Offset = new Vector(25, 25),
             };
@@ -71,7 +73,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Arrange_Should_Not_Set_Viewport_And_Extent_With_IScrollable()
+        public void Arrange_Should_Not_Set_Viewport_And_Extent_With_ILogicalScrollable()
         {
             var target = new ScrollContentPresenter
             {
@@ -122,7 +124,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Extent_Offset_And_Viewport_Should_Be_Read_From_IScrollable()
+        public void Extent_Offset_And_Viewport_Should_Be_Read_From_ILogicalScrollable()
         {
             var scrollable = new TestScrollable
             {
@@ -152,7 +154,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Offset_Should_Be_Written_To_IScrollable()
+        public void Offset_Should_Be_Written_To_ILogicalScrollable()
         {
             var scrollable = new TestScrollable
             {
@@ -172,7 +174,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Offset_Should_Not_Be_Written_To_IScrollable_After_Removal()
+        public void Offset_Should_Not_Be_Written_To_ILogicalScrollable_After_Removal()
         {
             var scrollable = new TestScrollable
             {
@@ -203,6 +205,8 @@ namespace Avalonia.Controls.UnitTests
 
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = scrollable,
             };
 
@@ -253,6 +257,8 @@ namespace Avalonia.Controls.UnitTests
 
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = logicalScrollable,
             };
 
@@ -286,12 +292,38 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(new Rect(0, 0, 100, 100), logicalScrollable.Bounds);
         }
 
+        [Fact]
+        public void Should_Set_ILogicalScrolable_CanHorizontallyScroll()
+        {
+            var logicalScrollable = new TestScrollable();
+            var target = new ScrollContentPresenter { Content = logicalScrollable };
+
+            target.UpdateChild();
+            Assert.False(logicalScrollable.CanHorizontallyScroll);
+            target.CanHorizontallyScroll = true;
+            Assert.True(logicalScrollable.CanHorizontallyScroll);
+        }
+
+        [Fact]
+        public void Should_Set_ILogicalScrolable_CanVerticallyScroll()
+        {
+            var logicalScrollable = new TestScrollable();
+            var target = new ScrollContentPresenter { Content = logicalScrollable };
+
+            target.UpdateChild();
+            Assert.False(logicalScrollable.CanVerticallyScroll);
+            target.CanVerticallyScroll = true;
+            Assert.True(logicalScrollable.CanVerticallyScroll);
+        }
+
         private class TestScrollable : Control, ILogicalScrollable
         {
             private Size _extent;
             private Vector _offset;
             private Size _viewport;
 
+            public bool CanHorizontallyScroll { get; set; }
+            public bool CanVerticallyScroll { get; set; }
             public bool IsLogicalScrollEnabled { get; set; } = true;
             public Size AvailableSize { get; private set; }
             public Action InvalidateScroll { get; set; }

+ 29 - 1
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@@ -1,6 +1,8 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
+using System.Collections.Generic;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
@@ -25,6 +27,32 @@ namespace Avalonia.Controls.UnitTests
             Assert.IsType<TextBlock>(target.Presenter.Child);
         }
 
+        [Fact]
+        public void CanHorizontallyScroll_Should_Track_HorizontalScrollBarVisibility()
+        {
+            var target = new ScrollViewer();
+            var values = new List<bool>();
+
+            target.GetObservable(ScrollViewer.CanHorizontallyScrollProperty).Subscribe(x => values.Add(x));
+            target.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
+            target.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+
+            Assert.Equal(new[] { true, false, true }, values);
+        }
+
+        [Fact]
+        public void CanVerticallyScroll_Should_Track_VerticalScrollBarVisibility()
+        {
+            var target = new ScrollViewer();
+            var values = new List<bool>();
+
+            target.GetObservable(ScrollViewer.CanVerticallyScrollProperty).Subscribe(x => values.Add(x));
+            target.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
+            target.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
+
+            Assert.Equal(new[] { true, false, true }, values);
+        }
+
         [Fact]
         public void Offset_Should_Be_Coerced_To_Viewport()
         {
@@ -59,7 +87,7 @@ namespace Avalonia.Controls.UnitTests
                         [~~ScrollContentPresenter.ExtentProperty] = control[~~ScrollViewer.ExtentProperty],
                         [~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty],
                         [~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty],
-                        [~ScrollContentPresenter.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty],
+                        [~ScrollContentPresenter.CanHorizontallyScrollProperty] = control[~ScrollViewer.CanHorizontallyScrollProperty],
                     },
                     new ScrollBar
                     {

+ 2 - 14
tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.csproj → tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj

@@ -1,19 +1,9 @@
-<Project>
-  <PropertyGroup>
-    <BaseIntermediateOutputPath>obj-Direct2D1</BaseIntermediateOutputPath>
-  </PropertyGroup>
-  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFrameworks>netcoreapp2.0</TargetFrameworks>
-    <OutputPath>bin\Direct2D\$(Configuration)</OutputPath>
-    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-    <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
-    <DefineConstants>$(DefineConstants);AVALONIA_DIRECT2D</DefineConstants>
-    <OutputType>Library</OutputType>
   </PropertyGroup>
   <ItemGroup>
-    <EmbeddedResource Remove="obj\**" />
-    <None Remove="obj\**" />
+    <Compile Include="..\Avalonia.RenderTests\**\*.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
@@ -33,7 +23,5 @@
   <Import Project="..\..\build\Moq.props" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\XUnit.props" />
-  <Import Project="Avalonia.RenderTests.projitems" Label="Shared" />
   <Import Project="..\..\build\Magick.NET-Q16-AnyCPU.props" />
-  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
 </Project>

+ 0 - 2
tests/Avalonia.Direct2D1.UnitTests/Properties/AssemblyInfo.cs

@@ -4,7 +4,5 @@
 using System.Reflection;
 using Xunit;
 
-[assembly: AssemblyTitle("Avalonia.Direct2D1.UnitTests")]
-
 // Don't run tests in parallel.
 [assembly: CollectionBehavior(DisableTestParallelization = true)]

+ 1 - 1
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@@ -85,7 +85,7 @@ namespace Avalonia.Layout.UnitTests
                     {
                         Width = 200,
                         Height = 200,
-                        CanScrollHorizontally = true,
+                        HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
                         HorizontalAlignment = HorizontalAlignment.Center,
                         VerticalAlignment = VerticalAlignment.Center,
                         Content = textBlock = new TextBlock

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AttachedProperty.cs

@@ -136,7 +136,7 @@ namespace Avalonia.Markup.UnitTests.Data
         private class Class1 : AvaloniaObject
         {
             public static readonly StyledProperty<Class1> NextProperty =
-                AvaloniaProperty.Register<Class1, Class1>("Next");
+                AvaloniaProperty.Register<Class1, Class1>(nameof(Next));
 
             public Class1 Next
             {

+ 7 - 5
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs

@@ -103,20 +103,22 @@ namespace Avalonia.Markup.UnitTests.Data
         {
             using (var sync = UnitTestSynchronizationContext.Begin())
             {
-                var data = new Class1();
-                var target = new ExpressionObserver(data, "Next^.Foo", true);
+                var data1 = new Class1();
+                var data2 = new Class2("foo");
+                var target = new ExpressionObserver(data1, "Next^.Foo", true);
                 var result = new List<object>();
 
                 var sub = target.Subscribe(x => result.Add(x));
-                data.Next.OnNext(new Class2("foo"));
+                data1.Next.OnNext(data2);
                 sync.ExecutePostedCallbacks();
 
                 Assert.Equal(new[] { new BindingNotification("foo") }, result);
 
                 sub.Dispose();
-                Assert.Equal(0, data.PropertyChangedSubscriptionCount);
+                Assert.Equal(0, data1.PropertyChangedSubscriptionCount);
 
-                GC.KeepAlive(data);
+                GC.KeepAlive(data1);
+                GC.KeepAlive(data2);
             }
         }
 

+ 1 - 1
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

@@ -558,7 +558,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
         private class InheritanceTest : Decorator
         {
             public static readonly StyledProperty<int> BazProperty =
-                AvaloniaProperty.Register<InheritanceTest, int>("Baz", defaultValue: 6, inherits: true);
+                AvaloniaProperty.Register<InheritanceTest, int>(nameof(Baz), defaultValue: 6, inherits: true);
 
             public int Baz
             {

+ 3 - 3
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/NonControl.cs

@@ -8,10 +8,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
     public class NonControl : AvaloniaObject
     {
         public static readonly StyledProperty<Control> ControlProperty =
-            AvaloniaProperty.Register<NonControl, Control>("Control");
+            AvaloniaProperty.Register<NonControl, Control>(nameof(Control));
 
         public static readonly StyledProperty<string> StringProperty =
-            AvaloniaProperty.Register<NonControl, string>("String");
+            AvaloniaProperty.Register<NonControl, string>(nameof(String));
 
         //No getter or setter Avalonia property
         public static readonly StyledProperty<int> FooProperty =
@@ -19,7 +19,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
         //getter only Avalonia property
         public static readonly StyledProperty<string> BarProperty =
-                AvaloniaProperty.Register<NonControl, string>("Bar");
+                AvaloniaProperty.Register<NonControl, string>(nameof(Bar));
 
         public Control Control
         {

+ 0 - 2
tests/Avalonia.RenderTests/.gitignore

@@ -1,2 +0,0 @@
-obj-Skia/
-obj-Skia/*

+ 0 - 33
tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems

@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
-    <HasSharedItems>true</HasSharedItems>
-    <SharedGUID>48840edd-24bf-495d-911e-2eb12ae75d3b</SharedGUID>
-  </PropertyGroup>
-  <PropertyGroup Label="Configuration">
-    <Import_RootNamespace>Avalonia.RenderTests</Import_RootNamespace>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="$(MSBuildThisFileDirectory)Controls\CustomRenderTests.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)GeometryClippingTests.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Media\RadialGradientBrushTests.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Media\BitmapTests.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)OpacityMaskTests.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)SVGPathTests.cs" />
-    <Compile Include="Media\FormattedTextImplTests.cs" />
-    <Compile Include="Controls\ImageTests.cs" />
-    <Compile Include="Controls\BorderTests.cs" />
-    <Compile Include="Media\ImageBrushTests.cs" />
-    <Compile Include="Media\LinearGradientBrushTests.cs" />
-    <Compile Include="Media\VisualBrushTests.cs" />
-    <Compile Include="Shapes\LineTests.cs" />
-    <Compile Include="Shapes\PathTests.cs" />
-    <Compile Include="Shapes\EllipseTests.cs" />
-    <Compile Include="Shapes\PolygonTests.cs" />
-    <Compile Include="Shapes\PolylineTests.cs" />
-    <Compile Include="Shapes\RectangleTests.cs" />
-    <Compile Include="TestBase.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-  </ItemGroup>
-</Project>

+ 0 - 16
tests/Avalonia.RenderTests/Avalonia.RenderTests.shproj

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>48840edd-24bf-495d-911e-2eb12ae75d3b</ProjectGuid>
-    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
-  </PropertyGroup>
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
-  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
-  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
-  <PropertyGroup />
-  <ItemGroup>
-    <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
-  </ItemGroup>
-  <Import Project="Avalonia.RenderTests.projitems" Label="Shared" />
-  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
-</Project>

+ 3 - 2
tests/Avalonia.RenderTests/Controls/TextBlockTests.cs

@@ -1,6 +1,7 @@
 // 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.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.Layout;
 using Avalonia.Media;
@@ -20,7 +21,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
         }
 
         [Fact]
-        public void Wrapping_NoWrap()
+        public async Task Wrapping_NoWrap()
         {
             Decorator target = new Decorator
             {
@@ -38,7 +39,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
                 }
             };
 
-            RenderToFile(target);
+            await RenderToFile(target);
             CompareImages();
         }
     }

+ 0 - 2
tests/Avalonia.RenderTests/Properties/AssemblyInfo.cs

@@ -4,7 +4,5 @@
 using System.Reflection;
 using Xunit;
 
-[assembly: AssemblyTitle("Avalonia.Direct2D1.RenderTests")]
-
 // Don't run tests in parallel.
 [assembly: CollectionBehavior(DisableTestParallelization = true)]

+ 23 - 0
tests/Avalonia.RenderTests/Shapes/PathTests.cs

@@ -362,5 +362,28 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
             await RenderToFile(target);
             CompareImages();
         }
+
+        [Fact]
+        public async Task Path_With_Rotated_Geometry()
+        {
+            var target = new Border
+            {
+                Width = 200,
+                Height = 200,
+                Background = Brushes.White,
+                Child = new Path
+                {
+                    Fill = Brushes.Red,
+                    Data = new RectangleGeometry
+                    {
+                        Rect = new Rect(50, 50, 100, 100),
+                        Transform = new RotateTransform(45),
+                    }
+                }
+            };
+
+            await RenderToFile(target);
+            CompareImages();
+        }
     }
 }

+ 14 - 1
tests/Avalonia.RenderTests/TestBase.cs

@@ -46,7 +46,8 @@ namespace Avalonia.Direct2D1.RenderTests
 
         public TestBase(string outputPath)
         {
-            var testFiles = Path.GetFullPath(@"..\..\..\..\..\TestFiles\");
+            var testPath = GetTestsDirectory();
+            var testFiles = Path.Combine(testPath, "TestFiles");
 #if AVALONIA_SKIA
             var platform = "Skia";
 #else
@@ -142,6 +143,18 @@ namespace Avalonia.Direct2D1.RenderTests
             }
         }
 
+        private string GetTestsDirectory()
+        {
+            var path = Directory.GetCurrentDirectory();
+
+            while (path.Length > 0 && Path.GetFileName(path) != "tests")
+            {
+                path = Path.GetDirectoryName(path);
+            }
+
+            return path;
+        }
+
         private class TestThreadingInterface : IPlatformThreadingInterface
         {
             public bool CurrentThreadIsLoopThread => MainThread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;

+ 3 - 17
tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj → tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj

@@ -1,21 +1,10 @@
-<Project>
-  <PropertyGroup>
-    <BaseIntermediateOutputPath>obj-Skia</BaseIntermediateOutputPath>
-  </PropertyGroup>
-  
-  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
-  
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFrameworks>netcoreapp2.0</TargetFrameworks>
-    <OutputPath>bin\Skia\$(Configuration)</OutputPath>
-    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-    <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
-    <DefineConstants>$(DefineConstants);AVALONIA_SKIA;AVALONIA_SKIA_SKIP_FAIL</DefineConstants>
-    <OutputType>Library</OutputType>
+    <DefineConstants>AVALONIA_SKIA;AVALONIA_SKIA_SKIP_FAIL</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <EmbeddedResource Remove="obj\**" />
-    <None Remove="obj\**" />
+    <Compile Include="..\Avalonia.RenderTests\**\*.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
@@ -35,9 +24,6 @@
   <Import Project="..\..\build\Moq.props" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\XUnit.props" />
-  <Import Project="Avalonia.RenderTests.projitems" Label="Shared" />
   <Import Project="..\..\build\Magick.NET-Q16-AnyCPU.props" />
   <Import Project="..\..\build\SkiaSharp.props" />
-  
-  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
 </Project>

+ 1 - 1
tests/Avalonia.Styling.UnitTests/StyleTests.cs

@@ -170,7 +170,7 @@ namespace Avalonia.Styling.UnitTests
         private class Class1 : Control
         {
             public static readonly StyledProperty<string> FooProperty =
-                AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
+                AvaloniaProperty.Register<Class1, string>(nameof(Foo), "foodefault");
 
             public string Foo
             {

+ 2 - 2
tests/Avalonia.UnitTests/ImmediateDispatcher.cs

@@ -14,12 +14,12 @@ namespace Avalonia.UnitTests
             return true;
         }
 
-        public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+        public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
         {
             action();
         }
 
-        public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+        public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
         {
             action();
             return Task.FromResult<object>(null);

+ 2 - 2
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@@ -24,13 +24,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering
             var root = new TestRoot();
 
             var dispatcher = new Mock<IDispatcher>();
-            dispatcher.Setup(x => x.InvokeAsync(It.IsAny<Action>(), DispatcherPriority.Render))
+            dispatcher.Setup(x => x.Post(It.IsAny<Action>(), DispatcherPriority.Render))
                 .Callback<Action, DispatcherPriority>((a, p) => a());
 
             CreateTargetAndRunFrame(root, dispatcher: dispatcher.Object);
 
             dispatcher.Verify(x => 
-                x.InvokeAsync(
+                x.Post(
                     It.Is<Action>(a => a.Method.Name == "UpdateScene"),
                     DispatcherPriority.Render));
         }

+ 2 - 0
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs

@@ -372,6 +372,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                                 Margin = new Thickness(0, 100, 0, 0),
                                 Child = scroll = new ScrollContentPresenter()
                                 {
+                                    CanHorizontallyScroll = true,
+                                    CanVerticallyScroll = true,
                                     Content = new StackPanel()
                                     {
                                         Children =

+ 2 - 0
tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs

@@ -357,6 +357,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                                 Margin = new Thickness(0, 100, 0, 0),
                                 Child = scroll = new ScrollContentPresenter()
                                 {
+                                    CanHorizontallyScroll = true,
+                                    CanVerticallyScroll = true,
                                     Content = new StackPanel()
                                     {
                                         Children =

+ 2 - 3
tests/Avalonia.Visuals.UnitTests/VisualTree/BoundsTrackerTests.cs → tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs

@@ -17,14 +17,13 @@ using Avalonia.Platform;
 
 namespace Avalonia.Visuals.UnitTests.VisualTree
 {
-    public class BoundsTrackerTests
+    public class TransformedBoundsTests
     {
         [Fact]
         public void Should_Track_Bounds()
         {
             using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var target = new BoundsTracker();
                 var control = default(Rectangle);
                 var tree = new Decorator
                 {
@@ -46,7 +45,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                 tree.Arrange(new Rect(0, 0, 100, 100));
                 ImmediateRenderer.Render(tree, context);
 
-                var track = target.Track(control);
+                var track = control.GetObservable(Visual.TransformedBoundsProperty);
                 var results = new List<TransformedBounds?>();
                 track.Subscribe(results.Add);
 

BIN
tests/TestFiles/Cairo/SVGPath/SVGPath.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Path/Path_With_Rotated_Geometry.expected.png


BIN
tests/TestFiles/Skia/Controls/TextBlock/Wrapping_NoWrap.expected.png


BIN
tests/TestFiles/Skia/Shapes/Path/Path_With_Rotated_Geometry.expected.png


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