712 lines
24 KiB
C#
712 lines
24 KiB
C#
|
|
|
|
|
|
/* Based on VirtualizingWrapPanel created by S. Bäumlisberger licensed under MIT license.
|
|
https://github.com/sbaeumlisberger/VirtualizingWrapPanel
|
|
Copyright (C) S. Bäumlisberger
|
|
All Rights Reserved. */
|
|
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.Reflection;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Controls.Primitives;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace WPFluent.Controls;
|
|
|
|
/// <summary>
|
|
/// Base abstract class for creating virtualized panels. <para>Based on <see
|
|
/// href="https://github.com/sbaeumlisberger/VirtualizingWrapPanel"/>.</para>
|
|
/// </summary>
|
|
public abstract class VirtualizingPanelBase : VirtualizingPanel, IScrollInfo
|
|
{
|
|
/// <summary>
|
|
/// Identifies the <see cref="MouseWheelDeltaItem"/> dependency property.
|
|
/// </summary>
|
|
public static readonly DependencyProperty MouseWheelDeltaItemProperty = DependencyProperty.Register(
|
|
nameof(MouseWheelDeltaItem),
|
|
typeof(int),
|
|
typeof(VirtualizingPanelBase),
|
|
new FrameworkPropertyMetadata(3));
|
|
|
|
/// <summary>
|
|
/// Identifies the <see cref="MouseWheelDelta"/> dependency property.
|
|
/// </summary>
|
|
public static readonly DependencyProperty MouseWheelDeltaProperty = DependencyProperty.Register(
|
|
nameof(MouseWheelDelta),
|
|
typeof(double),
|
|
typeof(VirtualizingPanelBase),
|
|
new FrameworkPropertyMetadata(48.0));
|
|
|
|
/// <summary>
|
|
/// Identifies the <see cref="ScrollLineDeltaItem"/> dependency property.
|
|
/// </summary>
|
|
public static readonly DependencyProperty ScrollLineDeltaItemProperty = DependencyProperty.Register(
|
|
nameof(ScrollLineDeltaItem),
|
|
typeof(int),
|
|
typeof(VirtualizingPanelBase),
|
|
new FrameworkPropertyMetadata(1));
|
|
|
|
/// <summary>
|
|
/// Identifies the <see cref="ScrollLineDelta"/> dependency property.
|
|
/// </summary>
|
|
public static readonly DependencyProperty ScrollLineDeltaProperty = DependencyProperty.Register(
|
|
nameof(ScrollLineDelta),
|
|
typeof(double),
|
|
typeof(VirtualizingPanelBase),
|
|
new FrameworkPropertyMetadata(16.0));
|
|
|
|
/// <summary>
|
|
/// Items generator.
|
|
/// </summary>
|
|
private IRecyclingItemContainerGenerator _itemContainerGenerator;
|
|
/// <summary>
|
|
/// Owner of the displayed items.
|
|
/// </summary>
|
|
private DependencyObject _itemsOwner;
|
|
|
|
/// <summary>
|
|
/// Previously set visibility of the horizontal scroll bar.
|
|
/// </summary>
|
|
private Visibility _previousHorizontalScrollBarVisibility = Visibility.Collapsed;
|
|
|
|
/// <summary>
|
|
/// Previously set visibility of the vertical scroll bar.
|
|
/// </summary>
|
|
private Visibility _previousVerticalScrollBarVisibility = Visibility.Collapsed;
|
|
|
|
/// <summary>
|
|
/// Calculates the extent that would be needed to show all items.
|
|
/// </summary>
|
|
protected abstract Size CalculateExtent(Size availableSize);
|
|
|
|
/// <summary>
|
|
/// Gets the position of children from the generator.
|
|
/// </summary>
|
|
protected virtual GeneratorPosition GetGeneratorPositionFromChildIndex(int childIndex)
|
|
{ return new GeneratorPosition(childIndex, 0); }
|
|
|
|
/// <summary>
|
|
/// Gets item index from the generator.
|
|
/// </summary>
|
|
protected int GetItemIndexFromChildIndex(int childIndex)
|
|
{
|
|
var generatorPosition = GetGeneratorPositionFromChildIndex(childIndex);
|
|
return ItemContainerGenerator.IndexFromGeneratorPosition(generatorPosition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets line down scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetLineDownScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets line left scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetLineLeftScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets line right scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetLineRightScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets line up scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetLineUpScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets mouse wheel down scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetMouseWheelDownScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets mouse wheel left scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetMouseWheelLeftScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets mouse wheel right scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetMouseWheelRightScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets mouse wheel up scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetMouseWheelUpScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets page down scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetPageDownScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets page left scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetPageLeftScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets page right scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetPageRightScrollAmount();
|
|
|
|
/// <summary>
|
|
/// Gets page up scroll amount.
|
|
/// </summary>
|
|
protected abstract double GetPageUpScrollAmount();
|
|
|
|
/// <inheritdoc/>
|
|
protected override Size MeasureOverride(Size availableSize)
|
|
{
|
|
/* Sometimes when scrolling the scrollbar gets hidden without any reason. In this case the "IsMeasureValid"
|
|
* property of the ScrollOwner is false. To prevent a infinite circle the mesasure call is ignored. */
|
|
if (ScrollOwner != null)
|
|
{
|
|
var verticalScrollBarGotHidden =
|
|
ScrollOwner.VerticalScrollBarVisibility == ScrollBarVisibility.Auto &&
|
|
ScrollOwner.ComputedVerticalScrollBarVisibility != Visibility.Visible &&
|
|
ScrollOwner.ComputedVerticalScrollBarVisibility != _previousVerticalScrollBarVisibility;
|
|
|
|
var horizontalScrollBarGotHidden =
|
|
ScrollOwner.HorizontalScrollBarVisibility == ScrollBarVisibility.Auto &&
|
|
ScrollOwner.ComputedHorizontalScrollBarVisibility != Visibility.Visible &&
|
|
ScrollOwner.ComputedHorizontalScrollBarVisibility != _previousHorizontalScrollBarVisibility;
|
|
|
|
_previousVerticalScrollBarVisibility = ScrollOwner.ComputedVerticalScrollBarVisibility;
|
|
_previousHorizontalScrollBarVisibility = ScrollOwner.ComputedHorizontalScrollBarVisibility;
|
|
|
|
if (!ScrollOwner.IsMeasureValid && verticalScrollBarGotHidden || horizontalScrollBarGotHidden)
|
|
{
|
|
return availableSize;
|
|
}
|
|
}
|
|
|
|
Size extent;
|
|
Size desiredSize;
|
|
|
|
if (ItemsOwner is IHierarchicalVirtualizationAndScrollInfo groupItem)
|
|
{
|
|
/* If the ItemsOwner is a group item the availableSize is ifinity.
|
|
* Therfore the vieport size provided by the group item is used. */
|
|
var viewportSize = groupItem.Constraints.Viewport.Size;
|
|
var headerSize = groupItem.HeaderDesiredSizes.PixelSize;
|
|
var availableWidth = Math.Max(viewportSize.Width - 5, 0); // left margin of 5 dp
|
|
var availableHeight = Math.Max(viewportSize.Height - headerSize.Height, 0);
|
|
availableSize = new Size(availableWidth, availableHeight);
|
|
|
|
extent = CalculateExtent(availableSize);
|
|
|
|
desiredSize = new Size(extent.Width, extent.Height);
|
|
|
|
Extent = extent;
|
|
Offset = groupItem.Constraints.Viewport.Location;
|
|
Viewport = groupItem.Constraints.Viewport.Size;
|
|
CacheLength = groupItem.Constraints.CacheLength;
|
|
CacheLengthUnit = groupItem.Constraints.CacheLengthUnit; // can be Item or Pixel
|
|
}
|
|
else
|
|
{
|
|
extent = CalculateExtent(availableSize);
|
|
var desiredWidth = Math.Min(availableSize.Width, extent.Width);
|
|
var desiredHeight = Math.Min(availableSize.Height, extent.Height);
|
|
desiredSize = new Size(desiredWidth, desiredHeight);
|
|
|
|
UpdateScrollInfo(desiredSize, extent);
|
|
CacheLength = GetCacheLength(ItemsOwner);
|
|
CacheLengthUnit = GetCacheLengthUnit(ItemsOwner); // can be Page, Item or Pixel
|
|
}
|
|
|
|
ItemRange = UpdateItemRange();
|
|
|
|
RealizeItems();
|
|
VirtualizeItems();
|
|
|
|
return desiredSize;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)
|
|
{
|
|
switch (args.Action)
|
|
{
|
|
case NotifyCollectionChangedAction.Remove:
|
|
case NotifyCollectionChangedAction.Replace:
|
|
RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
|
|
break;
|
|
case NotifyCollectionChangedAction.Move:
|
|
RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Realizes visible and cached items.
|
|
/// </summary>
|
|
protected virtual void RealizeItems()
|
|
{
|
|
var startPosition = ItemContainerGenerator.GeneratorPositionFromIndex(ItemRange.StartIndex);
|
|
var childIndex = startPosition.Offset == 0 ? startPosition.Index : startPosition.Index + 1;
|
|
|
|
using var at = ItemContainerGenerator.StartAt(startPosition, GeneratorDirection.Forward, true);
|
|
|
|
for (int i = ItemRange.StartIndex; i <= ItemRange.EndIndex; i++, childIndex++)
|
|
{
|
|
var child = (UIElement)ItemContainerGenerator.GenerateNext(out var isNewlyRealized);
|
|
|
|
if (isNewlyRealized || !InternalChildren.Contains(child)/*recycled*/
|
|
)
|
|
{
|
|
if (childIndex >= InternalChildren.Count)
|
|
{
|
|
AddInternalChild(child);
|
|
}
|
|
else
|
|
{
|
|
InsertInternalChild(childIndex, child);
|
|
}
|
|
|
|
ItemContainerGenerator.PrepareItemContainer(child);
|
|
|
|
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
|
}
|
|
|
|
if (child is not IHierarchicalVirtualizationAndScrollInfo groupItem)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
groupItem.Constraints = new HierarchicalVirtualizationConstraints(
|
|
new VirtualizationCacheLength(0),
|
|
VirtualizationCacheLengthUnit.Item,
|
|
new Rect(0, 0, ViewportWidth, ViewportHeight));
|
|
|
|
child.Measure(new Size(ViewportWidth, ViewportHeight));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets horizontal scroll offset by given amount.
|
|
/// </summary>
|
|
/// <param name="amount">The value by which the offset is to be increased.</param>
|
|
protected void ScrollHorizontal(double amount) { SetHorizontalOffset(HorizontalOffset + amount); }
|
|
|
|
/// <summary>
|
|
/// Sets vertical scroll offset by given amount.
|
|
/// </summary>
|
|
/// <param name="amount">The value by which the offset is to be increased.</param>
|
|
protected void ScrollVertical(double amount) { SetVerticalOffset(VerticalOffset + amount); }
|
|
|
|
/// <summary>
|
|
/// Calculates the item range that is visible in the viewport or cached.
|
|
/// </summary>
|
|
protected abstract ItemRange UpdateItemRange();
|
|
|
|
/// <summary>
|
|
/// Updates scroll offset, extent and viewport.
|
|
/// </summary>
|
|
protected virtual void UpdateScrollInfo(Size availableSize, Size extent)
|
|
{
|
|
var invalidateScrollInfo = false;
|
|
|
|
if (extent != Extent)
|
|
{
|
|
Extent = extent;
|
|
invalidateScrollInfo = true;
|
|
}
|
|
|
|
if (availableSize != Viewport)
|
|
{
|
|
Viewport = availableSize;
|
|
invalidateScrollInfo = true;
|
|
}
|
|
|
|
if (ViewportHeight != 0 && VerticalOffset != 0 && VerticalOffset + ViewportHeight + 1 >= ExtentHeight)
|
|
{
|
|
Offset = new Point(Offset.X, extent.Height - availableSize.Height);
|
|
invalidateScrollInfo = true;
|
|
}
|
|
|
|
if (ViewportWidth != 0 && HorizontalOffset != 0 && HorizontalOffset + ViewportWidth + 1 >= ExtentWidth)
|
|
{
|
|
Offset = new Point(extent.Width - availableSize.Width, Offset.Y);
|
|
invalidateScrollInfo = true;
|
|
}
|
|
|
|
if (invalidateScrollInfo)
|
|
{
|
|
ScrollOwner?.InvalidateScrollInfo();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Virtualizes (cleanups) no longer visible or cached items.
|
|
/// </summary>
|
|
protected virtual void VirtualizeItems()
|
|
{
|
|
for (var childIndex = InternalChildren.Count - 1; childIndex >= 0; childIndex--)
|
|
{
|
|
var generatorPosition = GetGeneratorPositionFromChildIndex(childIndex);
|
|
|
|
var itemIndex = ItemContainerGenerator.IndexFromGeneratorPosition(generatorPosition);
|
|
|
|
if (itemIndex == -1 || ItemRange.Contains(itemIndex))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (VirtualizationMode == VirtualizationMode.Recycling)
|
|
{
|
|
ItemContainerGenerator.Recycle(generatorPosition, 1);
|
|
}
|
|
else
|
|
{
|
|
ItemContainerGenerator.Remove(generatorPosition, 1);
|
|
}
|
|
|
|
RemoveInternalChildRange(childIndex, 1);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the cache length before and after the viewport.
|
|
/// </summary>
|
|
protected VirtualizationCacheLength CacheLength { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets the Unit of the cache length. Can be Pixel, Item or Page. When the ItemsOwner is a group item it can only
|
|
/// be pixel or item.
|
|
/// </summary>
|
|
protected VirtualizationCacheLengthUnit CacheLengthUnit { get; private set; }
|
|
|
|
/// <inheritdoc/>
|
|
protected override bool CanHierarchicallyScrollAndVirtualizeCore => true;
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="Extent"/>.
|
|
/// </summary>
|
|
protected Size Extent { get; private set; } = new Size(0, 0);
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the panel is in VirtualizationMode.Recycling.
|
|
/// </summary>
|
|
protected bool IsRecycling => VirtualizationMode == VirtualizationMode.Recycling;
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the virtualizing is enabled.
|
|
/// </summary>
|
|
protected bool IsVirtualizing => GetIsVirtualizing(ItemsControl);
|
|
|
|
/// <summary>
|
|
/// Gets items container.
|
|
/// </summary>
|
|
protected new IRecyclingItemContainerGenerator ItemContainerGenerator
|
|
{
|
|
get
|
|
{
|
|
if (_itemContainerGenerator is not null)
|
|
{
|
|
return _itemContainerGenerator;
|
|
}
|
|
|
|
/* Because of a bug in the framework the ItemContainerGenerator
|
|
* is null until InternalChildren accessed at least one time. */
|
|
_ = InternalChildren;
|
|
_itemContainerGenerator = (IRecyclingItemContainerGenerator)base.ItemContainerGenerator;
|
|
|
|
return _itemContainerGenerator;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the range of items that a realized in <see cref="Viewport"/> or cache.
|
|
/// </summary>
|
|
protected ItemRange ItemRange { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets items collection.
|
|
/// </summary>
|
|
protected ReadOnlyCollection<object> Items => ((ItemContainerGenerator)ItemContainerGenerator).Items;
|
|
|
|
/// <summary>
|
|
/// Gets the ItemsControl (e.g. ListView).
|
|
/// </summary>
|
|
protected ItemsControl ItemsControl => ItemsControl.GetItemsOwner(this);
|
|
|
|
/// <summary>
|
|
/// Gets the ItemsControl (e.g. ListView) or if the ItemsControl is grouping a GroupItem.
|
|
/// </summary>
|
|
protected DependencyObject ItemsOwner
|
|
{
|
|
get
|
|
{
|
|
if (_itemsOwner is not null)
|
|
{
|
|
return _itemsOwner;
|
|
}
|
|
|
|
/* Use reflection to access internal method because the public
|
|
* GetItemsOwner method does always return the itmes control instead
|
|
* of the real items owner for example the group item when grouping */
|
|
var getItemsOwnerInternalMethod = typeof(ItemsControl).GetMethod(
|
|
"GetItemsOwnerInternal",
|
|
BindingFlags.Static | BindingFlags.NonPublic,
|
|
null,
|
|
[typeof(DependencyObject)],
|
|
null)!;
|
|
|
|
_itemsOwner = (DependencyObject)getItemsOwnerInternalMethod.Invoke(null, [this])!;
|
|
|
|
return _itemsOwner;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the direction in which the panel scrolls when user turns the mouse wheel.
|
|
/// </summary>
|
|
protected ScrollDirection MouseWheelScrollDirection { get; set; } = ScrollDirection.Vertical;
|
|
|
|
/// <summary>
|
|
/// Gets the offset.
|
|
/// </summary>
|
|
protected Point Offset { get; private set; } = new(0, 0);
|
|
|
|
/// <summary>
|
|
/// Gets the scroll unit.
|
|
/// </summary>
|
|
protected ScrollUnit ScrollUnit => GetScrollUnit(ItemsControl);
|
|
|
|
/// <summary>
|
|
/// Gets the viewport.
|
|
/// </summary>
|
|
protected Size Viewport { get; private set; } = new Size(0, 0);
|
|
|
|
/// <summary>
|
|
/// Gets the virtualization mode.
|
|
/// </summary>
|
|
protected VirtualizationMode VirtualizationMode => GetVirtualizationMode(ItemsControl);
|
|
|
|
/// <inheritdoc/>
|
|
public void LineDown() => ScrollVertical(
|
|
ScrollUnit == ScrollUnit.Pixel ? ScrollLineDelta : GetLineDownScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void LineLeft() => ScrollHorizontal(
|
|
ScrollUnit == ScrollUnit.Pixel ? -ScrollLineDelta : GetLineLeftScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void LineRight() => ScrollHorizontal(
|
|
ScrollUnit == ScrollUnit.Pixel ? ScrollLineDelta : GetLineRightScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void LineUp() => ScrollVertical(ScrollUnit == ScrollUnit.Pixel ? -ScrollLineDelta : GetLineUpScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public virtual Rect MakeVisible(Visual visual, Rect rectangle)
|
|
{
|
|
var pos = visual.TransformToAncestor(this).Transform(Offset);
|
|
|
|
var scrollAmountX = 0d;
|
|
var scrollAmountY = 0d;
|
|
|
|
if (pos.X < Offset.X)
|
|
{
|
|
scrollAmountX = -(Offset.X - pos.X);
|
|
}
|
|
else if (pos.X + rectangle.Width > Offset.X + Viewport.Width)
|
|
{
|
|
var notVisibleX = pos.X + rectangle.Width - (Offset.X + Viewport.Width);
|
|
var maxScrollX = pos.X - Offset.X; // keep left of the visual visible
|
|
scrollAmountX = Math.Min(notVisibleX, maxScrollX);
|
|
}
|
|
|
|
if (pos.Y < Offset.Y)
|
|
{
|
|
scrollAmountY = -(Offset.Y - pos.Y);
|
|
}
|
|
else if (pos.Y + rectangle.Height > Offset.Y + Viewport.Height)
|
|
{
|
|
var notVisibleY = pos.Y + rectangle.Height - (Offset.Y + Viewport.Height);
|
|
var maxScrollY = pos.Y - Offset.Y; // keep top of the visual visible
|
|
scrollAmountY = Math.Min(notVisibleY, maxScrollY);
|
|
}
|
|
|
|
SetHorizontalOffset(Offset.X + scrollAmountX);
|
|
SetVerticalOffset(Offset.Y + scrollAmountY);
|
|
|
|
var visibleRectWidth = Math.Min(rectangle.Width, Viewport.Width);
|
|
var visibleRectHeight = Math.Min(rectangle.Height, Viewport.Height);
|
|
|
|
return new Rect(scrollAmountX, scrollAmountY, visibleRectWidth, visibleRectHeight);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void MouseWheelDown()
|
|
{
|
|
if (MouseWheelScrollDirection == ScrollDirection.Vertical)
|
|
{
|
|
ScrollVertical(ScrollUnit == ScrollUnit.Pixel ? MouseWheelDelta : GetMouseWheelDownScrollAmount());
|
|
}
|
|
else
|
|
{
|
|
MouseWheelRight();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void MouseWheelLeft() => ScrollHorizontal(
|
|
ScrollUnit == ScrollUnit.Pixel ? -MouseWheelDelta : GetMouseWheelLeftScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void MouseWheelRight() => ScrollHorizontal(
|
|
ScrollUnit == ScrollUnit.Pixel ? MouseWheelDelta : GetMouseWheelRightScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void MouseWheelUp()
|
|
{
|
|
if (MouseWheelScrollDirection == ScrollDirection.Vertical)
|
|
{
|
|
ScrollVertical(ScrollUnit == ScrollUnit.Pixel ? -MouseWheelDelta : GetMouseWheelUpScrollAmount());
|
|
}
|
|
else
|
|
{
|
|
MouseWheelLeft();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void PageDown() => ScrollVertical(
|
|
ScrollUnit == ScrollUnit.Pixel ? ViewportHeight : GetPageDownScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void PageLeft() => ScrollHorizontal(
|
|
ScrollUnit == ScrollUnit.Pixel ? -ViewportHeight : GetPageLeftScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void PageRight() => ScrollHorizontal(
|
|
ScrollUnit == ScrollUnit.Pixel ? ViewportHeight : GetPageRightScrollAmount());
|
|
|
|
/// <inheritdoc/>
|
|
public void PageUp() => ScrollVertical(ScrollUnit == ScrollUnit.Pixel ? -ViewportHeight : GetPageUpScrollAmount());
|
|
|
|
/// <summary>
|
|
/// Sets the horizontal offset.
|
|
/// </summary>
|
|
public void SetHorizontalOffset(double offset)
|
|
{
|
|
if (offset < 0 || Viewport.Width >= Extent.Width)
|
|
{
|
|
offset = 0;
|
|
}
|
|
else if (offset + Viewport.Width >= Extent.Width)
|
|
{
|
|
offset = Extent.Width - Viewport.Width;
|
|
}
|
|
|
|
Offset = new Point(offset, Offset.Y);
|
|
ScrollOwner?.InvalidateScrollInfo();
|
|
InvalidateMeasure();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the vertical offset.
|
|
/// </summary>
|
|
public void SetVerticalOffset(double offset)
|
|
{
|
|
if (offset < 0 || Viewport.Height >= Extent.Height)
|
|
{
|
|
offset = 0;
|
|
}
|
|
else if (offset + Viewport.Height >= Extent.Height)
|
|
{
|
|
offset = Extent.Height - Viewport.Height;
|
|
}
|
|
|
|
Offset = new Point(Offset.X, offset);
|
|
ScrollOwner?.InvalidateScrollInfo();
|
|
|
|
InvalidateMeasure();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the content can be horizontally scrolled.
|
|
/// </summary>
|
|
public bool CanHorizontallyScroll { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the content can be vertically scrolled.
|
|
/// </summary>
|
|
public bool CanVerticallyScroll { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets height of the <see cref="Extent"/>.
|
|
/// </summary>
|
|
public double ExtentHeight => Extent.Height;
|
|
|
|
/// <summary>
|
|
/// Gets width of the <see cref="Extent"/>.
|
|
/// </summary>
|
|
public double ExtentWidth => Extent.Width;
|
|
|
|
/// <summary>
|
|
/// Gets the horizontal offset.
|
|
/// </summary>
|
|
public double HorizontalOffset => Offset.X;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the mouse wheel delta for pixel based scrolling. The default value is 48 dp.
|
|
/// </summary>
|
|
public double MouseWheelDelta
|
|
{
|
|
get => (double)GetValue(MouseWheelDeltaProperty);
|
|
set => SetValue(MouseWheelDeltaProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the mouse wheel delta for item based scrolling. The default value is 3 items.
|
|
/// </summary>
|
|
public int MouseWheelDeltaItem
|
|
{
|
|
get => (int)GetValue(MouseWheelDeltaItemProperty);
|
|
set => SetValue(MouseWheelDeltaItemProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scroll line delta for pixel based scrolling. The default value is 16 dp.
|
|
/// </summary>
|
|
public double ScrollLineDelta
|
|
{
|
|
get => (double)GetValue(ScrollLineDeltaProperty);
|
|
set => SetValue(ScrollLineDeltaProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scroll line delta for item based scrolling. The default value is 1 item.
|
|
/// </summary>
|
|
public int ScrollLineDeltaItem
|
|
{
|
|
get => (int)GetValue(ScrollLineDeltaItemProperty);
|
|
set => SetValue(ScrollLineDeltaItemProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scroll owner.
|
|
/// </summary>
|
|
public ScrollViewer ScrollOwner { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets the vertical offset.
|
|
/// </summary>
|
|
public double VerticalOffset => Offset.Y;
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="Viewport"/> height.
|
|
/// </summary>
|
|
public double ViewportHeight => Viewport.Height;
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="Viewport"/> width.
|
|
/// </summary>
|
|
public double ViewportWidth => Viewport.Width;
|
|
}
|