diff --git a/samples/Previewer/MainWindow.xaml.cs b/samples/Previewer/MainWindow.xaml.cs
index c72b1f7e55..8eabf44bc3 100644
--- a/samples/Previewer/MainWindow.xaml.cs
+++ b/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;
diff --git a/samples/RemoteTest/Program.cs b/samples/RemoteTest/Program.cs
index dce168c7ea..f518e77143 100644
--- a/samples/RemoteTest/Program.cs
+++ b/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()
{
diff --git a/samples/VirtualizationTest/MainWindow.xaml b/samples/VirtualizationTest/MainWindow.xaml
index 55bd729fec..52c2b33680 100644
--- a/samples/VirtualizationTest/MainWindow.xaml
+++ b/samples/VirtualizationTest/MainWindow.xaml
@@ -21,6 +21,12 @@
+ Horiz. ScrollBar
+
+ Vert. ScrollBar
+
@@ -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}">
@@ -43,7 +51,7 @@
-
+
diff --git a/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
index f47627acb4..8eab91e06d 100644
--- a/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
+++ b/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 _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 Orientations =>
Enum.GetValues(typeof(Orientation)).Cast();
+ 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 ScrollBarVisibilities =>
+ Enum.GetValues(typeof(ScrollBarVisibility)).Cast();
+
public ItemVirtualizationMode VirtualizationMode
{
get { return _virtualizationMode; }
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 17e6ea8f0f..a46d567d28 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -774,7 +774,7 @@ namespace Avalonia
}
else
{
- Dispatcher.UIThread.InvokeAsync(Set);
+ Dispatcher.UIThread.Post(Set);
}
}
diff --git a/src/Avalonia.Base/PriorityBindingEntry.cs b/src/Avalonia.Base/PriorityBindingEntry.cs
index b44b845f25..570bfe03dc 100644
--- a/src/Avalonia.Base/PriorityBindingEntry.cs
+++ b/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));
}
}
}
diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs
index f9d67470c1..46529f0a5a 100644
--- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs
+++ b/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)
{
diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
index 7a0249f876..6af5ab63cf 100644
--- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
+++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
@@ -36,7 +36,7 @@ namespace Avalonia.Threading
///
public override void Post(SendOrPostCallback d, object state)
{
- Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send);
+ Dispatcher.UIThread.Post(() => d(state), DispatcherPriority.Send);
}
///
@@ -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();
}
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index 4a096fc326..7d29a4f969 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -79,13 +79,13 @@ namespace Avalonia.Threading
public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
///
- public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+ public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
return _jobRunner?.InvokeAsync(action, priority);
}
///
- public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+ public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
_jobRunner?.Post(action, priority);
}
diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs
index 6301015a9a..4009dcdeab 100644
--- a/src/Avalonia.Base/Threading/IDispatcher.cs
+++ b/src/Avalonia.Base/Threading/IDispatcher.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Threading
/// The method.
/// The priority with which to invoke the method.
/// A task that can be used to track the method's execution.
- void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
+ void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
///
/// Post action that will be invoked on main thread
@@ -34,6 +34,6 @@ namespace Avalonia.Threading
/// The priority with which to invoke the method.
// 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);
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
index 20602d5475..c1489e7138 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
+++ b/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
///
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);
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
index 185193f889..590bfa25ac 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
+++ b/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;
///
/// Initializes static members of the class.
@@ -46,6 +48,31 @@ namespace Avalonia.Controls.Presenters
set { SetValue(VirtualizationModeProperty, value); }
}
+ ///
+ /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+ ///
+ bool ILogicalScrollable.CanHorizontallyScroll
+ {
+ get { return _canHorizontallyScroll; }
+ set
+ {
+ _canHorizontallyScroll = value;
+ InvalidateMeasure();
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+ ///
+ bool ILogicalScrollable.CanVerticallyScroll
+ {
+ get { return _canVerticallyScroll; }
+ set
+ {
+ _canVerticallyScroll = value;
+ InvalidateMeasure();
+ }
+ }
///
bool ILogicalScrollable.IsLogicalScrollEnabled
{
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index e41c4e1e28..6c61375054 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -17,6 +17,24 @@ namespace Avalonia.Controls.Presenters
///
public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable
{
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty CanHorizontallyScrollProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanHorizontallyScroll),
+ o => o.CanHorizontallyScroll,
+ (o, v) => o.CanHorizontallyScroll = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty CanVerticallyScrollProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanVerticallyScroll),
+ o => o.CanVerticallyScroll,
+ (o, v) => o.CanVerticallyScroll = v);
+
///
/// Defines the property.
///
@@ -41,12 +59,8 @@ namespace Avalonia.Controls.Presenters
o => o.Viewport,
(o, v) => o.Viewport = v);
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty CanScrollHorizontallyProperty =
- ScrollViewer.CanScrollHorizontallyProperty.AddOwner();
-
+ 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);
}
+ ///
+ /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+ ///
+ public bool CanHorizontallyScroll
+ {
+ get { return _canHorizontallyScroll; }
+ set { SetAndRaise(CanHorizontallyScrollProperty, ref _canHorizontallyScroll, value); }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+ ///
+ public bool CanVerticallyScroll
+ {
+ get { return _canVerticallyScroll; }
+ set { SetAndRaise(CanVerticallyScrollProperty, ref _canVerticallyScroll, value); }
+ }
+
///
/// Gets the extent of the scrollable content.
///
@@ -100,11 +132,6 @@ namespace Avalonia.Controls.Presenters
private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
}
- ///
- /// Gets a value indicating whether the content can be scrolled horizontally.
- ///
- public bool CanScrollHorizontally => GetValue(CanScrollHorizontallyProperty);
-
///
/// Attempts to bring a portion of the target visual into view by scrolling the content.
///
@@ -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);
}
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 5f6b3ad4c8..d2d4151e3d 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
index d7862881fb..a469f09867 100644
--- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs
+++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
@@ -18,8 +18,6 @@ namespace Avalonia.Controls.Primitives
private static readonly AttachedProperty s_adornedElementInfoProperty =
AvaloniaProperty.RegisterAttached("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();
diff --git a/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs b/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
index 6c8f463a96..490beb12b3 100644
--- a/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
+++ b/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
@@ -19,6 +19,16 @@ namespace Avalonia.Controls.Primitives
///
public interface ILogicalScrollable : IScrollable
{
+ ///
+ /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+ ///
+ bool CanHorizontallyScroll { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the content can be scrolled horizontally.
+ ///
+ bool CanVerticallyScroll { get; set; }
+
///
/// Gets a value indicating whether logical scrolling is enabled on the control.
///
diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs
index 009e1d0ab8..0057b15150 100644
--- a/src/Avalonia.Controls/Primitives/ScrollBar.cs
+++ b/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;
diff --git a/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs b/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs
index 17413d2233..f1cca8f909 100644
--- a/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs
+++ b/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs
@@ -5,8 +5,9 @@ namespace Avalonia.Controls.Primitives
{
public enum ScrollBarVisibility
{
+ Disabled,
Auto,
- Visible,
Hidden,
+ Visible,
}
}
diff --git a/src/Avalonia.Controls/Remote/RemoteWidget.cs b/src/Avalonia.Controls/Remote/RemoteWidget.cs
index c05aeaf970..83360a0010 100644
--- a/src/Avalonia.Controls/Remote/RemoteWidget.cs
+++ b/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[]
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
index c2e6a200f9..cf4cec9268 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
+++ b/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();
diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs
index 0442652540..39854e0071 100644
--- a/src/Avalonia.Controls/ScrollViewer.cs
+++ b/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
{
///
- /// Defines the property.
+ /// Defines the property.
///
- public static readonly StyledProperty CanScrollHorizontallyProperty =
- AvaloniaProperty.Register(nameof(CanScrollHorizontally), true);
+ ///
+ /// There is no public C# accessor for this property as it is intended to be bound to by a
+ /// in the control's template.
+ ///
+ public static readonly DirectProperty CanHorizontallyScrollProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanHorizontallyScroll),
+ o => o.CanHorizontallyScroll);
+
+ ///
+ /// Defines the property.
+ ///
+ ///
+ /// There is no public C# accessor for this property as it is intended to be bound to by a
+ /// in the control's template.
+ ///
+ public static readonly DirectProperty CanVerticallyScrollProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(CanVerticallyScroll),
+ o => o.CanVerticallyScroll);
///
/// Defines the property.
///
public static readonly DirectProperty ExtentProperty =
- AvaloniaProperty.RegisterDirect(nameof(Extent),
+ AvaloniaProperty.RegisterDirect(nameof(Extent),
o => o.Extent,
(o, v) => o.Extent = v);
@@ -41,7 +57,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly DirectProperty ViewportProperty =
- AvaloniaProperty.RegisterDirect(nameof(Viewport),
+ AvaloniaProperty.RegisterDirect(nameof(Viewport),
o => o.Viewport,
(o, v) => o.Viewport = v);
@@ -85,14 +101,10 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- ///
- /// There is no public C# accessor for this property as it is intended to be bound to by a
- /// in the control's template.
- ///
public static readonly AttachedProperty HorizontalScrollBarVisibilityProperty =
AvaloniaProperty.RegisterAttached(
nameof(HorizontalScrollBarVisibility),
- ScrollBarVisibility.Auto);
+ ScrollBarVisibility.Hidden);
///
/// Defines the VerticalScrollBarMaximum property.
@@ -136,7 +148,7 @@ namespace Avalonia.Controls
///
public static readonly AttachedProperty VerticalScrollBarVisibilityProperty =
AvaloniaProperty.RegisterAttached(
- 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(x => x.ScrollBarVisibilityChanged);
+ VerticalScrollBarVisibilityProperty.Changed.AddClassHandler(x => x.ScrollBarVisibilityChanged);
}
///
@@ -218,15 +232,6 @@ namespace Avalonia.Controls
}
}
- ///
- /// Gets a value indicating whether the content can be scrolled horizontally.
- ///
- public bool CanScrollHorizontally
- {
- get { return GetValue(CanScrollHorizontallyProperty); }
- set { SetValue(CanScrollHorizontallyProperty, value); }
- }
-
///
/// Gets or sets the horizontal scrollbar visibility.
///
@@ -245,6 +250,22 @@ namespace Avalonia.Controls
set { SetValue(VerticalScrollBarVisibilityProperty, value); }
}
+ ///
+ /// Gets a value indicating whether the viewer can scroll horizontally.
+ ///
+ protected bool CanHorizontallyScroll
+ {
+ get { return HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled; }
+ }
+
+ ///
+ /// Gets a value indicating whether the viewer can scroll vertically.
+ ///
+ protected bool CanVerticallyScroll
+ {
+ get { return VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; }
+ }
+
///
/// Gets the maximum horizontal scrollbar value.
///
@@ -316,7 +337,7 @@ namespace Avalonia.Controls
///
/// The control to read the value from.
/// The value of the property.
- public ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
+ public static ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
{
return control.GetValue(HorizontalScrollBarVisibilityProperty);
}
@@ -326,7 +347,7 @@ namespace Avalonia.Controls
///
/// The control to set the value on.
/// The value of the property.
- 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
///
/// The control to read the value from.
/// The value of the property.
- public ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
+ public static ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
{
return control.GetValue(VerticalScrollBarVisibilityProperty);
}
@@ -346,7 +367,7 @@ namespace Avalonia.Controls
///
/// The control to set the value on.
/// The value of the property.
- 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,
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 1a663ed3b6..8f4606884e 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/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 AcceptsTabProperty =
AvaloniaProperty.Register(nameof(AcceptsTab));
- public static readonly DirectProperty CanScrollHorizontallyProperty =
- AvaloniaProperty.RegisterDirect(nameof(CanScrollHorizontally), o => o.CanScrollHorizontally);
-
public static readonly DirectProperty CaretIndexProperty =
AvaloniaProperty.RegisterDirect(
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 _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
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index fa3ecdedef..2e1c011685 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/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;
diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs
index 409dd231ad..dee537029c 100644
--- a/src/Avalonia.Controls/VirtualizingStackPanel.cs
+++ b/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
{
diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
index 535dfd700b..3c7ef86d5d 100644
--- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
+++ b/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);
diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
index ac3438d71c..51cf1d4dde 100644
--- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
+++ b/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)
{
diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs
index f8911dc036..b6b786a077 100644
--- a/src/Avalonia.Layout/LayoutManager.cs
+++ b/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;
}
}
diff --git a/src/Avalonia.Themes.Default/ListBox.xaml b/src/Avalonia.Themes.Default/ListBox.xaml
index aa63a1b6c3..57b0c541b8 100644
--- a/src/Avalonia.Themes.Default/ListBox.xaml
+++ b/src/Avalonia.Themes.Default/ListBox.xaml
@@ -3,11 +3,16 @@
+
+
-
+
+ Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"/>
-
diff --git a/src/Avalonia.Themes.Default/TreeView.xaml b/src/Avalonia.Themes.Default/TreeView.xaml
index 4ce677667b..42c0b2cdd9 100644
--- a/src/Avalonia.Themes.Default/TreeView.xaml
+++ b/src/Avalonia.Themes.Default/TreeView.xaml
@@ -7,7 +7,7 @@
-
+
0))
{
_updateQueued = true;
- _dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
+ _dispatcher.Post(UpdateScene, DispatcherPriority.Render);
}
Scene scene = null;
diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
index 84313f0906..e830d5c313 100644
--- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
+++ b/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))
{
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
index 8f4f487e08..41ff802164 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
+++ b/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)
{
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index 3662fe50be..5f3861a51a 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -32,6 +32,11 @@ namespace Avalonia
public static readonly DirectProperty BoundsProperty =
AvaloniaProperty.RegisterDirect(nameof(Bounds), o => o.Bounds);
+ public static readonly DirectProperty TransformedBoundsProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(TransformedBounds),
+ o => o.TransformedBounds);
+
///
/// Defines the property.
///
@@ -87,6 +92,7 @@ namespace Avalonia
AvaloniaProperty.Register(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); }
}
+ ///
+ /// Gets the bounds of the control relative to the window, accounting for rendering transforms.
+ ///
+ public TransformedBounds? TransformedBounds => _transformedBounds;
+
///
/// Gets a value indicating whether the control should be clipped to its bounds.
///
@@ -253,6 +264,12 @@ namespace Avalonia
/// Gets the root of the visual tree, if the control is attached to a visual tree.
///
IRenderRoot IVisual.VisualRoot => VisualRoot;
+
+ TransformedBounds? IVisual.TransformedBounds
+ {
+ get { return _transformedBounds; }
+ set { SetAndRaise(TransformedBoundsProperty, ref _transformedBounds, value); }
+ }
///
/// Invalidates the visual and queues a repaint.
diff --git a/src/Avalonia.Visuals/VisualTree/BoundsTracker.cs b/src/Avalonia.Visuals/VisualTree/BoundsTracker.cs
deleted file mode 100644
index 42c4e3c98e..0000000000
--- a/src/Avalonia.Visuals/VisualTree/BoundsTracker.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Tracks the bounds of a control.
- ///
- ///
- /// 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.
- ///
- public class BoundsTracker
- {
- ///
- /// Defines the TransformedBounds attached property.
- ///
- private static AttachedProperty TransformedBoundsProperty =
- AvaloniaProperty.RegisterAttached("TransformedBounds");
-
- ///
- /// Starts tracking the specified visual.
- ///
- /// The visual.
- /// An observable that returns the tracked bounds.
- public IObservable Track(Visual visual)
- {
- return visual.GetObservable(TransformedBoundsProperty);
- }
-
- ///
- /// Sets the transformed bounds of the visual.
- ///
- /// The visual.
- /// The transformed bounds.
- internal static void SetTransformedBounds(Visual visual, TransformedBounds? value)
- {
- visual.SetValue(TransformedBoundsProperty, value);
- }
-
- ///
- /// Gets the transformed bounds of the visual.
- ///
- /// The visual.
- /// The transformed bounds or null if the visual is not visible.
- public static TransformedBounds? GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
- }
-}
diff --git a/src/Avalonia.Visuals/VisualTree/IVisual.cs b/src/Avalonia.Visuals/VisualTree/IVisual.cs
index 2047996c3e..278a802597 100644
--- a/src/Avalonia.Visuals/VisualTree/IVisual.cs
+++ b/src/Avalonia.Visuals/VisualTree/IVisual.cs
@@ -36,6 +36,11 @@ namespace Avalonia.VisualTree
///
Rect Bounds { get; }
+ ///
+ /// Gets the bounds of the control relative to the window, accounting for rendering transforms.
+ ///
+ TransformedBounds? TransformedBounds { get; set; }
+
///
/// Gets a value indicating whether the control should be clipped to its bounds.
///
diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
index 136023c31d..c41a136bce 100644
--- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
+++ b/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)
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
index daff4dd751..0db622ba13 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
+++ b/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;
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
index e733beae27..896c91d087 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
+++ b/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())));
}
}
diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
index 3f6f15ed5b..90eabc69fb 100644
--- a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
+++ b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
@@ -104,7 +104,7 @@ namespace Avalonia.Markup.Data.Plugins
protected override void SubscribeCore(IObserver