A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

152 lines
5.2 KiB

// 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.Specialized;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using static Avalonia.Utilities.MathUtilities;
namespace Avalonia.Controls.Presenters
{
/// <summary>
/// Displays items inside an <see cref="ItemsControl"/>.
/// </summary>
public class ItemsPresenter : ItemsPresenterBase, ILogicalScrollable
{
/// <summary>
/// Defines the <see cref="VirtualizationMode"/> property.
/// </summary>
public static readonly StyledProperty<ItemVirtualizationMode> VirtualizationModeProperty =
AvaloniaProperty.Register<ItemsPresenter, ItemVirtualizationMode>(
nameof(VirtualizationMode),
defaultValue: ItemVirtualizationMode.None);
private ItemVirtualizer _virtualizer;
/// <summary>
/// Initializes static members of the <see cref="ItemsPresenter"/> class.
/// </summary>
static ItemsPresenter()
{
KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue(
typeof(ItemsPresenter),
KeyboardNavigationMode.Once);
VirtualizationModeProperty.Changed
.AddClassHandler<ItemsPresenter>(x => x.VirtualizationModeChanged);
}
/// <summary>
/// Gets or sets the virtualization mode for the items.
/// </summary>
public ItemVirtualizationMode VirtualizationMode
{
get { return GetValue(VirtualizationModeProperty); }
set { SetValue(VirtualizationModeProperty, value); }
}
/// <inheritdoc/>
bool ILogicalScrollable.IsLogicalScrollEnabled
{
get { return _virtualizer?.IsLogicalScrollEnabled ?? false; }
}
/// <inheritdoc/>
Size IScrollable.Extent => _virtualizer.Extent;
/// <inheritdoc/>
Vector IScrollable.Offset
{
get { return _virtualizer.Offset; }
set { _virtualizer.Offset = CoerceOffset(value); }
}
/// <inheritdoc/>
Size IScrollable.Viewport => _virtualizer.Viewport;
/// <inheritdoc/>
Action ILogicalScrollable.InvalidateScroll { get; set; }
/// <inheritdoc/>
Size ILogicalScrollable.ScrollSize => new Size(1, 1);
/// <inheritdoc/>
Size ILogicalScrollable.PageScrollSize => new Size(0, 1);
/// <inheritdoc/>
bool ILogicalScrollable.BringIntoView(IControl target, Rect targetRect)
{
return false;
}
/// <inheritdoc/>
IControl ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from)
{
return _virtualizer?.GetControlInDirection(direction, from);
}
public override void ScrollIntoView(object item)
{
_virtualizer?.ScrollIntoView(item);
}
protected override Size MeasureOverride(Size availableSize)
{
// If infinity is passed as the available size and we're virtualized then we need to
// fill the available space, but to do that we *don't* want to materialize all our
// items! Take a look at the root of the tree for a MaxClientSize and use that as
// the available size.
if (availableSize == Size.Infinity && VirtualizationMode != ItemVirtualizationMode.None)
{
var window = VisualRoot as TopLevel;
if (window != null)
{
availableSize = window.PlatformImpl.MaxClientSize;
}
}
Panel.Measure(availableSize);
return Panel.DesiredSize;
}
/// <inheritdoc/>
protected override void PanelCreated(IPanel panel)
{
_virtualizer = ItemVirtualizer.Create(this);
((ILogicalScrollable)this).InvalidateScroll?.Invoke();
if (!Panel.IsSet(KeyboardNavigation.DirectionalNavigationProperty))
{
KeyboardNavigation.SetDirectionalNavigation(
(InputElement)Panel,
KeyboardNavigationMode.Contained);
}
KeyboardNavigation.SetTabNavigation(
(InputElement)Panel,
KeyboardNavigation.GetTabNavigation(this));
}
protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
{
_virtualizer?.ItemsChanged(Items, e);
}
private Vector CoerceOffset(Vector value)
{
var scrollable = (ILogicalScrollable)this;
var maxX = Math.Max(scrollable.Extent.Width - scrollable.Viewport.Width, 0);
var maxY = Math.Max(scrollable.Extent.Height - scrollable.Viewport.Height, 0);
return new Vector(Clamp(value.X, 0, maxX), Clamp(value.Y, 0, maxY));
}
private void VirtualizationModeChanged(AvaloniaPropertyChangedEventArgs e)
{
_virtualizer?.Dispose();
_virtualizer = ItemVirtualizer.Create(this);
((ILogicalScrollable)this).InvalidateScroll?.Invoke();
}
}
}