Просмотр исходного кода

Implement disabling of ListBox scrollbars.

Fixes #1344.
Steven Kirk 7 лет назад
Родитель
Сommit
8a80a724d7

+ 2 - 2
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@@ -18,8 +18,8 @@ namespace VirtualizationTest.ViewModels
         private int _newItemIndex;
         private IReactiveList<ItemViewModel> _items;
         private string _prefix = "Item";
-        private ScrollBarVisibility _horizontalScrollBarVisibility;
-        private ScrollBarVisibility _verticalScrollBarVisibility;
+        private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+        private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
         private Orientation _orientation = Orientation.Vertical;
         private ItemVirtualizationMode _virtualizationMode = ItemVirtualizationMode.Simple;
 

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

+ 50 - 13
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 Size _measuredExtent;
         private Vector _offset;
@@ -73,6 +87,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>
@@ -100,11 +132,6 @@ namespace Avalonia.Controls.Presenters
             private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
         }
 
-        /// <summary>
-        /// Gets a value indicating whether the content can be scrolled horizontally.
-        /// </summary>
-        public bool CanScrollHorizontally => GetValue(CanScrollHorizontallyProperty);
-
         /// <summary>
         /// Attempts to bring a portion of the target visual into view by scrolling the content.
         /// </summary>
@@ -182,10 +209,15 @@ namespace Avalonia.Controls.Presenters
                 {
                     measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
 
-                    if (!CanScrollHorizontally)
+                    if (!CanHorizontallyScroll)
                     {
                         measureSize = measureSize.WithWidth(availableSize.Width);
                     }
+
+                    if (!CanVerticallyScroll)
+                    {
+                        measureSize = measureSize.WithHeight(availableSize.Height);
+                    }
                 }
 
                 child.Measure(measureSize);
@@ -289,7 +321,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);
                 }

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

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

+ 5 - 17
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;
@@ -26,9 +25,6 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<bool> AcceptsTabProperty =
             AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsTab));
 
-        public static readonly DirectProperty<TextBox, bool> CanScrollHorizontallyProperty =
-            AvaloniaProperty.RegisterDirect<TextBox, bool>(nameof(CanScrollHorizontally), o => o.CanScrollHorizontally);
-
         public static readonly DirectProperty<TextBox, int> CaretIndexProperty =
             AvaloniaProperty.RegisterDirect<TextBox, int>(
                 nameof(CaretIndex),
@@ -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

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

+ 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}"

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

+ 10 - 1
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;
 
@@ -118,6 +119,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             TestControl content;
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = content = new TestControl(),
             };
 
@@ -134,6 +137,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             Border content;
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = content = new Border
                 {
                     Width = 150,
@@ -155,8 +160,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var child = new TestControl();
             var target = new ScrollContentPresenter
             {
+                CanVerticallyScroll = true,
                 Content = child,
-                [ScrollContentPresenter.CanScrollHorizontallyProperty] = false,
             };
 
             target.UpdateChild();
@@ -171,6 +176,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
             var child = new TestControl();
             var target = new ScrollContentPresenter
             {
+                CanHorizontallyScroll = true,
+                CanVerticallyScroll = true,
                 Content = child,
             };
 
@@ -246,6 +253,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
                     {

+ 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

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