csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
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.
178 lines
5.5 KiB
178 lines
5.5 KiB
using Avalonia.Media;
|
|
using Avalonia.Media.Immutable;
|
|
using Avalonia.Metadata;
|
|
|
|
namespace Avalonia.Controls
|
|
{
|
|
/// <summary>
|
|
/// Viewbox is used to scale single child to fit in the available space.
|
|
/// </summary>
|
|
public class Viewbox : Control
|
|
{
|
|
private readonly ViewboxContainer _containerVisual;
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="Stretch"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<Stretch> StretchProperty =
|
|
AvaloniaProperty.Register<Viewbox, Stretch>(nameof(Stretch), Stretch.Uniform);
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="StretchDirection"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<StretchDirection> StretchDirectionProperty =
|
|
AvaloniaProperty.Register<Viewbox, StretchDirection>(nameof(StretchDirection), StretchDirection.Both);
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="Child"/> property
|
|
/// </summary>
|
|
public static readonly StyledProperty<IControl?> ChildProperty =
|
|
Decorator.ChildProperty.AddOwner<Viewbox>();
|
|
|
|
static Viewbox()
|
|
{
|
|
ClipToBoundsProperty.OverrideDefaultValue<Viewbox>(true);
|
|
UseLayoutRoundingProperty.OverrideDefaultValue<Viewbox>(true);
|
|
AffectsMeasure<Viewbox>(StretchProperty, StretchDirectionProperty);
|
|
}
|
|
|
|
public Viewbox()
|
|
{
|
|
// The Child control is hosted inside a ViewboxContainer control so that the transform
|
|
// can be applied independently of the Viewbox and Child transforms.
|
|
_containerVisual = new ViewboxContainer();
|
|
_containerVisual.RenderTransformOrigin = RelativePoint.TopLeft;
|
|
VisualChildren.Add(_containerVisual);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the stretch mode,
|
|
/// which determines how child fits into the available space.
|
|
/// </summary>
|
|
public Stretch Stretch
|
|
{
|
|
get => GetValue(StretchProperty);
|
|
set => SetValue(StretchProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value controlling in what direction contents will be stretched.
|
|
/// </summary>
|
|
public StretchDirection StretchDirection
|
|
{
|
|
get => GetValue(StretchDirectionProperty);
|
|
set => SetValue(StretchDirectionProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the child of the Viewbox
|
|
/// </summary>
|
|
[Content]
|
|
public IControl? Child
|
|
{
|
|
get => GetValue(ChildProperty);
|
|
set => SetValue(ChildProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the transform applied to the container visual that
|
|
/// hosts the child of the Viewbox
|
|
/// </summary>
|
|
protected internal ITransform? InternalTransform
|
|
{
|
|
get => _containerVisual.RenderTransform;
|
|
set => _containerVisual.RenderTransform = value;
|
|
}
|
|
|
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
{
|
|
base.OnPropertyChanged(change);
|
|
|
|
if (change.Property == ChildProperty)
|
|
{
|
|
var (oldChild, newChild) = change.GetOldAndNewValue<IControl>();
|
|
|
|
if (oldChild is not null)
|
|
{
|
|
((ISetLogicalParent)oldChild).SetParent(null);
|
|
LogicalChildren.Remove(oldChild);
|
|
}
|
|
|
|
_containerVisual.Child = newChild;
|
|
|
|
if (newChild is not null)
|
|
{
|
|
((ISetLogicalParent)newChild).SetParent(this);
|
|
LogicalChildren.Add(newChild);
|
|
}
|
|
|
|
InvalidateMeasure();
|
|
}
|
|
}
|
|
|
|
protected override Size MeasureOverride(Size availableSize)
|
|
{
|
|
var child = _containerVisual;
|
|
|
|
if (child != null)
|
|
{
|
|
child.Measure(Size.Infinity);
|
|
|
|
var childSize = child.DesiredSize;
|
|
|
|
var size = Stretch.CalculateSize(availableSize, childSize, StretchDirection);
|
|
|
|
return size;
|
|
}
|
|
|
|
return new Size();
|
|
}
|
|
|
|
protected override Size ArrangeOverride(Size finalSize)
|
|
{
|
|
var child = _containerVisual;
|
|
|
|
if (child != null)
|
|
{
|
|
var childSize = child.DesiredSize;
|
|
var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection);
|
|
|
|
InternalTransform = new ImmutableTransform(Matrix.CreateScale(scale.X, scale.Y));
|
|
|
|
child.Arrange(new Rect(childSize));
|
|
|
|
return childSize * scale;
|
|
}
|
|
|
|
return finalSize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A simple container control which hosts its child as a visual but not logical child.
|
|
/// </summary>
|
|
private class ViewboxContainer : Control
|
|
{
|
|
private IControl? _child;
|
|
|
|
public IControl? Child
|
|
{
|
|
get => _child;
|
|
set
|
|
{
|
|
if (_child != value)
|
|
{
|
|
if (_child is not null)
|
|
VisualChildren.Remove(_child);
|
|
|
|
_child = value;
|
|
|
|
if (_child is not null)
|
|
VisualChildren.Add(_child);
|
|
|
|
InvalidateMeasure();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|