Browse Source

import sources.

pull/3987/head
Dan Walmsley 6 years ago
parent
commit
1c0e981343
  1. 511
      src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
  2. 424
      src/Avalonia.Controls/RelativePanel.cs

511
src/Avalonia.Controls/RelativePanel.AttachedProperties.cs

@ -0,0 +1,511 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Controls
{
public partial class RelativePanel
{
private static void OnAlignPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var elm = d as FrameworkElement;
if (elm.Parent is FrameworkElement)
((FrameworkElement)elm.Parent).InvalidateArrange();
}
/// <summary>
/// Gets the value of the RelativePanel.Above XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.Above XAML attached property value of the specified object.
/// (The element to position this element above.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAbove(DependencyObject obj)
{
return (object)obj.GetValue(AboveProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element above.)</param>
public static void SetAbove(DependencyObject obj, object value)
{
obj.SetValue(AboveProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AboveProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AboveProperty =
DependencyProperty.RegisterAttached("Above", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignBottomWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignBottomWithPanel XAML attached property value of the specified
/// object. (true to align this element's bottom edge with the panel's bottom edge;
/// otherwise, false.)
/// </returns>
public static bool GetAlignBottomWithPanel(DependencyObject obj)
{
return (bool)obj.GetValue(AlignBottomWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's bottom edge with the panel's
/// bottom edge; otherwise, false.)
/// </param>
public static void SetAlignBottomWithPanel(DependencyObject obj, bool value)
{
obj.SetValue(AlignBottomWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignBottomWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignBottomWithPanelProperty =
DependencyProperty.RegisterAttached("AlignBottomWithPanel", typeof(bool), typeof(RelativePanel), new PropertyMetadata(false, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignBottomWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignBottomWith XAML attached property value of the specified object.
/// (The element to align this element's bottom edge with.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAlignBottomWith(DependencyObject obj)
{
return (object)obj.GetValue(AlignBottomWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's bottom edge with.)</param>
public static void SetAlignBottomWith(DependencyObject obj, object value)
{
obj.SetValue(AlignBottomWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignBottomWithProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignBottomWithProperty =
DependencyProperty.RegisterAttached("AlignBottomWith", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignHorizontalCenterWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignHorizontalCenterWithPanel XAML attached property value
/// of the specified object. (true to horizontally center this element in the panel;
/// otherwise, false.)
/// </returns>
public static bool GetAlignHorizontalCenterWithPanel(DependencyObject obj)
{
return (bool)obj.GetValue(AlignHorizontalCenterWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to horizontally center this element in the panel; otherwise,
/// false.)
/// </param>
public static void SetAlignHorizontalCenterWithPanel(DependencyObject obj, bool value)
{
obj.SetValue(AlignHorizontalCenterWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignHorizontalCenterWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignHorizontalCenterWithPanelProperty =
DependencyProperty.RegisterAttached("AlignHorizontalCenterWithPanel", typeof(bool), typeof(RelativePanel), new PropertyMetadata(false, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignHorizontalCenterWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignHorizontalCenterWith XAML attached property value of the
/// specified object. (The element to align this element's horizontal center with.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAlignHorizontalCenterWith(DependencyObject obj)
{
return (object)obj.GetValue(AlignHorizontalCenterWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's horizontal center with.)</param>
public static void SetAlignHorizontalCenterWith(DependencyObject obj, object value)
{
obj.SetValue(AlignHorizontalCenterWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignHorizontalCenterWithProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignHorizontalCenterWithProperty =
DependencyProperty.RegisterAttached("AlignHorizontalCenterWith", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignLeftWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignLeftWithPanel XAML attached property value of the specified
/// object. (true to align this element's left edge with the panel's left edge; otherwise,
/// false.)
/// </returns>
public static bool GetAlignLeftWithPanel(DependencyObject obj)
{
return (bool)obj.GetValue(AlignLeftWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's left edge with the panel's left
/// edge; otherwise, false.)
/// </param>
public static void SetAlignLeftWithPanel(DependencyObject obj, bool value)
{
obj.SetValue(AlignLeftWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignLeftWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignLeftWithPanelProperty =
DependencyProperty.RegisterAttached("AlignLeftWithPanel", typeof(bool), typeof(RelativePanel), new PropertyMetadata(false, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignLeftWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignLeftWith XAML attached property value of the specified
/// object. (The element to align this element's left edge with.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAlignLeftWith(DependencyObject obj)
{
return (object)obj.GetValue(AlignLeftWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's left edge with.)</param>
public static void SetAlignLeftWith(DependencyObject obj, object value)
{
obj.SetValue(AlignLeftWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignLeftWithProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignLeftWithProperty =
DependencyProperty.RegisterAttached("AlignLeftWith", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignRightWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignRightWithPanel XAML attached property value of the specified
/// object. (true to align this element's right edge with the panel's right edge;
/// otherwise, false.)
/// </returns>
public static bool GetAlignRightWithPanel(DependencyObject obj)
{
return (bool)obj.GetValue(AlignRightWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's right edge with the panel's right
/// edge; otherwise, false.)
/// </param>
public static void SetAlignRightWithPanel(DependencyObject obj, bool value)
{
obj.SetValue(AlignRightWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignRightWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignRightWithPanelProperty =
DependencyProperty.RegisterAttached("AlignRightWithPanel", typeof(bool), typeof(RelativePanel), new PropertyMetadata(false, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignRightWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignRightWith XAML attached property value of the specified
/// object. (The element to align this element's right edge with.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAlignRightWith(DependencyObject obj)
{
return (object)obj.GetValue(AlignRightWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignRightWith XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's right edge with.)</param>
public static void SetAlignRightWith(DependencyObject obj, object value)
{
obj.SetValue(AlignRightWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignRightWithProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignRightWithProperty =
DependencyProperty.RegisterAttached("AlignRightWith", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignTopWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignTopWithPanel XAML attached property value of the specified
/// object. (true to align this element's top edge with the panel's top edge; otherwise,
/// false.)
/// </returns>
public static bool GetAlignTopWithPanel(DependencyObject obj)
{
return (bool)obj.GetValue(AlignTopWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignTopWithPanel XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's top edge with the panel's top
/// edge; otherwise, false.)
/// </param>
public static void SetAlignTopWithPanel(DependencyObject obj, bool value)
{
obj.SetValue(AlignTopWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignTopWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignTopWithPanelProperty =
DependencyProperty.RegisterAttached("AlignTopWithPanel", typeof(bool), typeof(RelativePanel), new PropertyMetadata(false, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignTopWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>The value to set. (The element to align this element's top edge with.)</returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAlignTopWith(DependencyObject obj)
{
return (object)obj.GetValue(AlignTopWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignTopWith XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's top edge with.)</param>
public static void SetAlignTopWith(DependencyObject obj, object value)
{
obj.SetValue(AlignTopWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignTopWithProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignTopWithProperty =
DependencyProperty.RegisterAttached("AlignTopWith", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignVerticalCenterWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignVerticalCenterWithPanel XAML attached property value of
/// the specified object. (true to vertically center this element in the panel; otherwise,
/// false.)
/// </returns>
public static bool GetAlignVerticalCenterWithPanel(DependencyObject obj)
{
return (bool)obj.GetValue(AlignVerticalCenterWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignVerticalCenterWithPanel XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to vertically center this element in the panel; otherwise,
/// false.)
/// </param>
public static void SetAlignVerticalCenterWithPanel(DependencyObject obj, bool value)
{
obj.SetValue(AlignVerticalCenterWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignVerticalCenterWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignVerticalCenterWithPanelProperty =
DependencyProperty.RegisterAttached("AlignVerticalCenterWithPanel", typeof(bool), typeof(RelativePanel), new PropertyMetadata(false, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.AlignVerticalCenterWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>The value to set. (The element to align this element's vertical center with.)</returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetAlignVerticalCenterWith(DependencyObject obj)
{
return (object)obj.GetValue(AlignVerticalCenterWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignVerticalCenterWith XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's horizontal center with.)</param>
public static void SetAlignVerticalCenterWith(DependencyObject obj, object value)
{
obj.SetValue(AlignVerticalCenterWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignVerticalCenterWithProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty AlignVerticalCenterWithProperty =
DependencyProperty.RegisterAttached("AlignVerticalCenterWith", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.Below XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.Below XAML attached property value of the specified object.
/// (The element to position this element below.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetBelow(DependencyObject obj)
{
return (object)obj.GetValue(BelowProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element below.)</param>
public static void SetBelow(DependencyObject obj, object value)
{
obj.SetValue(BelowProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.BelowProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty BelowProperty =
DependencyProperty.RegisterAttached("Below", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.LeftOf XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.LeftOf XAML attached property value of the specified object.
/// (The element to position this element to the left of.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetLeftOf(DependencyObject obj)
{
return (object)obj.GetValue(LeftOfProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.LeftOf XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element to the left of.)</param>
public static void SetLeftOf(DependencyObject obj, object value)
{
obj.SetValue(LeftOfProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.LeftOfProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty LeftOfProperty =
DependencyProperty.RegisterAttached("LeftOf", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
/// <summary>
/// Gets the value of the RelativePanel.RightOf XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.RightOf XAML attached property value of the specified object.
/// (The element to position this element to the right of.)
/// </returns>
[TypeConverter(typeof(NameReferenceConverter))]
public static object GetRightOf(DependencyObject obj)
{
return (object)obj.GetValue(RightOfProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.RightOf XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element to the right of.)</param>
public static void SetRightOf(DependencyObject obj, object value)
{
obj.SetValue(RightOfProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.RightOfProperty"/> XAML attached property.
/// </summary>
public static readonly DependencyProperty RightOfProperty =
DependencyProperty.RegisterAttached("RightOf", typeof(object), typeof(RelativePanel), new PropertyMetadata(null, OnAlignPropertiesChanged));
}
}

424
src/Avalonia.Controls/RelativePanel.cs

@ -0,0 +1,424 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Controls
{
/// <summary>
/// Defines an area within which you can position and align child objects in relation
/// to each other or the parent panel.
/// </summary>
/// <remarks>
/// <para><b>Default position</b></para>
/// <para>By default, any unconstrained element declared as a child of the RelativePanel is given the entire
/// available space and positioned at the(0, 0) coordinates(upper left corner) of the panel.So, if you
/// are positioning a second element relative to an unconstrained element, keep in mind that the second
/// element might get pushed out of the panel.
/// </para>
///<para><b>Conflicting relationships</b></para>
/// <para>
/// If you set multiple relationships that target the same edge of an element, you might have conflicting
/// relationships in your layout as a result.When this happens, the relationships are applied in the
/// following order of priority:
/// • Panel alignment relationships (AlignTopWithPanel, AlignLeftWithPanel, …) are applied first.
/// • Sibling alignment relationships(AlignTopWith, AlignLeftWith, …) are applied second.
/// • Sibling positional relationships(Above, Below, RightOf, LeftOf) are applied last.
/// </para>
/// <para>
/// The panel-center alignment properties(AlignVerticalCenterWith, AlignHorizontalCenterWithPanel, ...) are
/// typically used independently of other constraints and are applied if there is no conflict.
///</para>
/// <para>
/// The HorizontalAlignment and VerticalAlignment properties on UI elements are applied after relationship
/// properties are evaluated and applied. These properties control the placement of the element within the
/// available size for the element, if the desired size is smaller than the available size.
/// </para>
/// </remarks>
public partial class RelativePanel : Panel
{
// Dependency property for storing intermediate arrange state on the children
private static readonly DependencyProperty ArrangeStateProperty =
DependencyProperty.Register("ArrangeState", typeof(double[]), typeof(RelativePanel), new PropertyMetadata(null));
/// <summary>
/// When overridden in a derived class, measures the size in layout required for
/// child elements and determines a size for the System.Windows.FrameworkElement-derived
/// class.</summary>
/// <param name="availableSize">
/// The available size that this element can give to child elements. Infinity can
/// be specified as a value to indicate that the element will size to whatever content
/// is available.
/// </param>
/// <returns>
/// The size that this element determines it needs during layout, based on its calculations
/// of child element sizes.
/// </returns>
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in Children.OfType<FrameworkElement>())
{
child.Measure(availableSize);
}
foreach (var item in CalculateLocations(availableSize))
{
if (item.Item2.Size.Width < item.Item1.DesiredSize.Width || item.Item2.Size.Height < item.Item1.DesiredSize.Height)
item.Item1.Measure(item.Item2.Size);
}
return base.MeasureOverride(availableSize);
}
/// <summary>
/// When overridden in a derived class, positions child elements and determines a
/// size for a System.Windows.FrameworkElement derived class.
/// </summary>
/// <param name="finalSize">
/// The final area within the parent that this element should use to arrange itself
/// and its children.
/// </param>
/// <returns>The actual size used.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
foreach (var item in CalculateLocations(finalSize))
item.Item1.Arrange(item.Item2);
return base.ArrangeOverride(finalSize);
}
private IEnumerable<Tuple<UIElement, Rect>> CalculateLocations(Size finalSize)
{
//List of margins for each element between the element and panel (left, top, right, bottom)
List<double[]> arranges = new List<double[]>(Children.Count);
//First pass aligns all sides that aren't constrained by other elements
int arrangedCount = 0;
foreach (var child in Children.OfType<UIElement>())
{
//NaN means the arrange value is not constrained yet for that side
double[] rect = new[] { double.NaN, double.NaN, double.NaN, double.NaN };
arranges.Add(rect);
child.SetValue(ArrangeStateProperty, rect);
//Align with panels always wins, so do these first, or if no constraints are set at all set margin to 0
//Left side
if (GetAlignLeftWithPanel(child))
{
rect[0] = 0;
}
else if (
child.GetValue(AlignLeftWithProperty) == null &&
child.GetValue(RightOfProperty) == null &&
child.GetValue(AlignHorizontalCenterWithProperty) == null &&
!GetAlignHorizontalCenterWithPanel(child))
{
if (GetAlignRightWithPanel(child))
rect[0] = finalSize.Width - child.DesiredSize.Width;
else if (child.GetValue(AlignRightWithProperty) == null && child.GetValue(AlignHorizontalCenterWithProperty) == null && child.GetValue(LeftOfProperty) == null)
rect[0] = 0; //default fallback to 0
}
//Top side
if (GetAlignTopWithPanel(child))
{
rect[1] = 0;
}
else if (
child.GetValue(AlignTopWithProperty) == null &&
child.GetValue(BelowProperty) == null &&
child.GetValue(AlignVerticalCenterWithProperty) == null &&
!GetAlignVerticalCenterWithPanel(child))
{
if (GetAlignBottomWithPanel(child))
rect[1] = finalSize.Height - child.DesiredSize.Height;
else if (child.GetValue(AlignBottomWithProperty) == null && child.GetValue(AlignVerticalCenterWithProperty) == null && child.GetValue(AboveProperty) == null)
rect[1] = 0; //default fallback to 0
}
//Right side
if (GetAlignRightWithPanel(child))
{
rect[2] = 0;
}
else if (!double.IsNaN(rect[0]) &&
child.GetValue(AlignRightWithProperty) == null &&
child.GetValue(LeftOfProperty) == null &&
child.GetValue(AlignHorizontalCenterWithProperty) == null &&
!GetAlignHorizontalCenterWithPanel(child))
{
rect[2] = finalSize.Width - rect[0] - child.DesiredSize.Width;
}
//Bottom side
if (GetAlignBottomWithPanel(child))
rect[3] = 0;
else if (!double.IsNaN(rect[1]) &&
(child.GetValue(AlignBottomWithProperty) == null &&
child.GetValue(AboveProperty) == null) &&
child.GetValue(AlignVerticalCenterWithProperty) == null &&
!GetAlignVerticalCenterWithPanel(child))
{
rect[3] = finalSize.Height - rect[1] - child.DesiredSize.Height;
}
if (!double.IsNaN(rect[0]) && !double.IsNaN(rect[1]) &&
!double.IsNaN(rect[2]) && !double.IsNaN(rect[3]))
arrangedCount++;
}
int i = 0;
//Run iterative layout passes
while (arrangedCount < Children.Count)
{
bool valueChanged = false;
i = 0;
foreach (var child in Children.OfType<UIElement>())
{
double[] rect = arranges[i++];
if (!double.IsNaN(rect[0]) && !double.IsNaN(rect[1]) &&
!double.IsNaN(rect[2]) && !double.IsNaN(rect[3]))
continue; //Control is fully arranged
//Calculate left side
if (double.IsNaN(rect[0]))
{
var alignLeftWith = GetDependencyElement(RelativePanel.AlignLeftWithProperty, child);
if (alignLeftWith != null)
{
double[] r = (double[])alignLeftWith.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[0]))
{
rect[0] = r[0];
valueChanged = true;
}
}
else
{
var rightOf = GetDependencyElement(RelativePanel.RightOfProperty, child);
if (rightOf != null)
{
double[] r = (double[])rightOf.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[2]))
{
rect[0] = finalSize.Width - r[2];
valueChanged = true;
}
}
else if (!double.IsNaN(rect[2]))
{
rect[0] = finalSize.Width - rect[2] - child.DesiredSize.Width;
valueChanged = true;
}
}
}
//Calculate top side
if (double.IsNaN(rect[1]))
{
var alignTopWith = GetDependencyElement(RelativePanel.AlignTopWithProperty, child);
if (alignTopWith != null)
{
double[] r = (double[])alignTopWith.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[1]))
{
rect[1] = r[1];
valueChanged = true;
}
}
else
{
var below = GetDependencyElement(RelativePanel.BelowProperty, child);
if (below != null)
{
double[] r = (double[])below.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[3]))
{
rect[1] = finalSize.Height - r[3];
valueChanged = true;
}
}
else if (!double.IsNaN(rect[3]))
{
rect[1] = finalSize.Height - rect[3] - child.DesiredSize.Height;
valueChanged = true;
}
}
}
//Calculate right side
if (double.IsNaN(rect[2]))
{
var alignRightWith = GetDependencyElement(RelativePanel.AlignRightWithProperty, child);
if (alignRightWith != null)
{
double[] r = (double[])alignRightWith.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[2]))
{
rect[2] = r[2];
if (double.IsNaN(rect[0]))
{
if (child.GetValue(RelativePanel.AlignLeftWithProperty) == null)
{
rect[0] = rect[2] + child.DesiredSize.Width;
valueChanged = true;
}
}
}
}
else
{
var leftOf = GetDependencyElement(RelativePanel.LeftOfProperty, child);
if (leftOf != null)
{
double[] r = (double[])leftOf.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[0]))
{
rect[2] = finalSize.Width - r[0];
valueChanged = true;
}
}
else if (!double.IsNaN(rect[0]))
{
rect[2] = finalSize.Width - rect[0] - child.DesiredSize.Width;
valueChanged = true;
}
}
}
//Calculate bottom side
if (double.IsNaN(rect[3]))
{
var alignBottomWith = GetDependencyElement(RelativePanel.AlignBottomWithProperty, child);
if (alignBottomWith != null)
{
double[] r = (double[])alignBottomWith.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[3]))
{
rect[3] = r[3];
valueChanged = true;
if (double.IsNaN(rect[1]))
{
if (child.GetValue(RelativePanel.AlignTopWithProperty) == null)
{
rect[1] = finalSize.Height - rect[3] - child.DesiredSize.Height;
}
}
}
}
else
{
var above = GetDependencyElement(RelativePanel.AboveProperty, child);
if (above != null)
{
double[] r = (double[])above.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[1]))
{
rect[3] = finalSize.Height - r[1];
valueChanged = true;
}
}
else if (!double.IsNaN(rect[1]))
{
rect[3] = finalSize.Height - rect[1] - child.DesiredSize.Height;
valueChanged = true;
}
}
}
//Calculate horizontal alignment
if (double.IsNaN(rect[0]) && double.IsNaN(rect[2]))
{
var alignHorizontalCenterWith = GetDependencyElement(RelativePanel.AlignHorizontalCenterWithProperty, child);
if (alignHorizontalCenterWith != null)
{
double[] r = (double[])alignHorizontalCenterWith.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[0]) && !double.IsNaN(r[2]))
{
rect[0] = r[0] + (finalSize.Width - r[2] - r[0]) * .5 - child.DesiredSize.Width * .5;
rect[2] = finalSize.Width - rect[0] - child.DesiredSize.Width;
valueChanged = true;
}
}
else
{
if (GetAlignHorizontalCenterWithPanel(child))
{
var roomToSpare = finalSize.Width - child.DesiredSize.Width;
rect[0] = roomToSpare * .5;
rect[2] = roomToSpare * .5;
valueChanged = true;
}
}
}
//Calculate vertical alignment
if (double.IsNaN(rect[1]) && double.IsNaN(rect[3]))
{
var alignVerticalCenterWith = GetDependencyElement(RelativePanel.AlignVerticalCenterWithProperty, child);
if (alignVerticalCenterWith != null)
{
double[] r = (double[])alignVerticalCenterWith.GetValue(ArrangeStateProperty);
if (!double.IsNaN(r[1]) && !double.IsNaN(r[3]))
{
rect[1] = r[1] + (finalSize.Height - r[3] - r[1]) * .5 - child.DesiredSize.Height * .5;
rect[3] = finalSize.Height - rect[1] - child.DesiredSize.Height;
valueChanged = true;
}
}
else
{
if (GetAlignVerticalCenterWithPanel(child))
{
var roomToSpare = finalSize.Height - child.DesiredSize.Height;
rect[1] = roomToSpare * .5;
rect[3] = roomToSpare * .5;
valueChanged = true;
}
}
}
//if panel is now fully arranged, increase the counter
if (!double.IsNaN(rect[0]) && !double.IsNaN(rect[1]) &&
!double.IsNaN(rect[2]) && !double.IsNaN(rect[3]))
arrangedCount++;
}
if (!valueChanged)
{
//If a layout pass didn't increase number of arranged elements,
//there must be a circular dependency
throw new ArgumentException("RelativePanel error: Circular dependency detected. Layout could not complete");
}
}
i = 0;
//Arrange iterations complete - Apply the results to the child elements
foreach (var child in Children.OfType<UIElement>())
{
double[] rect = arranges[i++];
//Measure child again with the new calculated available size
//this helps for instance textblocks to reflow the text wrapping
//We should probably have done this during the measure step but it would cause a more expensive
//measure+arrange layout cycle
//if(child is TextBlock)
// child.Measure(new Size(Math.Max(0, finalSize.Width - rect[2] - rect[0]), Math.Max(0, finalSize.Height - rect[3] - rect[1])));
//if(child is TextBlock tb)
//{
// tb.ArrangeOverride(new Rect(rect[0], rect[1], Math.Max(0, finalSize.Width - rect[2] - rect[0]), Math.Max(0, finalSize.Height - rect[3] - rect[1])));
//}
//else
yield return new Tuple<UIElement, Rect>(child, new Rect(rect[0], rect[1], Math.Max(0, finalSize.Width - rect[2] - rect[0]), Math.Max(0, finalSize.Height - rect[3] - rect[1])));
}
}
//Gets the element that's referred to in the alignment attached properties
private UIElement GetDependencyElement(DependencyProperty property, DependencyObject child)
{
var dependency = child.GetValue(property);
if (dependency == null)
return null;
if (dependency is UIElement)
{
if (Children.Contains((UIElement)dependency))
return (UIElement)dependency;
throw new ArgumentException(string.Format("RelativePanel error: Element does not exist in the current context", property.Name));
}
throw new ArgumentException("RelativePanel error: Value must be of type UIElement");
}
}
}
Loading…
Cancel
Save