diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 70a7583daf..7888249bdd 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -155,6 +155,11 @@ namespace Avalonia.Controls.Presenters } } + protected override void PanelCreated(IPanel panel) + { + ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + /// /// Moves to the selected page, animating if a is set. /// diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index 23846bcd2e..52f173fc71 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -229,8 +229,6 @@ namespace Avalonia.Controls.Presenters } PanelCreated(Panel); - - ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// diff --git a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs index f93de5ca15..f64c26682b 100644 --- a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs +++ b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs @@ -33,7 +33,7 @@ namespace Avalonia.Controls AlignVerticalCenterWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); BelowProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); LeftOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - LeftOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); + RightOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); } /// diff --git a/src/Avalonia.Controls/RelativePanel.cs b/src/Avalonia.Controls/RelativePanel.cs index 033a5559f5..a3ad30db76 100644 --- a/src/Avalonia.Controls/RelativePanel.cs +++ b/src/Avalonia.Controls/RelativePanel.cs @@ -1,4 +1,4 @@ -/// Ported from https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs +// Ported from https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs using System; using System.Collections.Generic; using System.Linq; @@ -14,25 +14,28 @@ namespace Avalonia.Controls public RelativePanel() => _childGraph = new Graph(); - protected override Size MeasureOverride(Size availableSize) + private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child) { - foreach (var child in Children) + var dependency = child.GetValue(property); + + if (dependency is Layoutable layoutable) { - child?.Measure(availableSize); + if (Children.Contains((ILayoutable)layoutable)) + return layoutable; + + throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}"); } - return availableSize; + return null; } - protected override Size ArrangeOverride(Size arrangeSize) + protected override Size MeasureOverride(Size availableSize) { - _childGraph.Reset(arrangeSize); - - foreach (var child in Children.OfType()) + _childGraph.Clear(); + foreach (Layoutable child in Children) { if (child == null) continue; - var node = _childGraph.AddNode(child); node.AlignLeftWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignLeftWithProperty, child)); @@ -47,63 +50,44 @@ namespace Avalonia.Controls node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignHorizontalCenterWithProperty, child)); node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignVerticalCenterWithProperty, child)); - } - if (_childGraph.CheckCyclic()) - { - throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete."); } + _childGraph.Measure(availableSize); - var size = new Size(); - - foreach (var child in Children) - { - if (child.Bounds.Bottom > size.Height) - { - size = size.WithHeight(child.Bounds.Bottom); - } + _childGraph.Reset(); + var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN()); + _childGraph.Reset(); + _childGraph.Measure(boundingSize); + return boundingSize; + } - if (child.Bounds.Right > size.Width) - { - size = size.WithWidth(child.Bounds.Right); - } - } + protected override Size ArrangeOverride(Size arrangeSize) + { + _childGraph.GetNodes().Do(node => node.Arrange(arrangeSize)); + return arrangeSize; + } - if (VerticalAlignment == VerticalAlignment.Stretch) - { - size = size.WithHeight(arrangeSize.Height); - } + private class GraphNode + { + public bool Measured { get; set; } - if (HorizontalAlignment == HorizontalAlignment.Stretch) - { - size = size.WithWidth(arrangeSize.Width); - } + public Layoutable Element { get; } - return size; - } + private bool HorizontalOffsetFlag { get; set; } - private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child) - { - var dependency = child.GetValue(property); + private bool VerticalOffsetFlag { get; set; } - if (dependency is Layoutable layoutable) - { - if (Children.Contains((ILayoutable)layoutable)) - return layoutable; + private Size BoundingSize { get; set; } - throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}"); - } + public Size OriginDesiredSize { get; set; } - return null; - } + public double Left { get; set; } = double.NaN; - private class GraphNode - { - public Point Position { get; set; } + public double Top { get; set; } = double.NaN; - public bool Arranged { get; set; } + public double Right { get; set; } = double.NaN; - public Layoutable Element { get; } + public double Bottom { get; set; } = double.NaN; public HashSet OutgoingNodes { get; } @@ -132,19 +116,101 @@ namespace Avalonia.Controls OutgoingNodes = new HashSet(); Element = element; } + + public void Arrange(Size arrangeSize) => Element.Arrange(new Rect(Left, Top, Math.Max(arrangeSize.Width - Left - Right, 0), Math.Max(arrangeSize.Height - Top - Bottom, 0))); + + public void Reset() + { + Left = double.NaN; + Top = double.NaN; + Right = double.NaN; + Bottom = double.NaN; + Measured = false; + } + + public Size GetBoundingSize() + { + if (Measured) + return BoundingSize; + + if (!OutgoingNodes.Any()) + { + BoundingSize = Element.DesiredSize; + Measured = true; + } + else + { + BoundingSize = GetBoundingSize(this, Element.DesiredSize, OutgoingNodes); + Measured = true; + } + + return BoundingSize; + } + + private static Size GetBoundingSize(GraphNode prevNode, Size prevSize, IEnumerable nodes) + { + foreach (var node in nodes) + { + if (node.Measured || !node.OutgoingNodes.Any()) + { + if (prevNode.LeftOfNode != null && prevNode.LeftOfNode == node || + prevNode.RightOfNode != null && prevNode.RightOfNode == node) + { + prevSize = prevSize.WithWidth(prevSize.Width + node.BoundingSize.Width); + if (GetAlignHorizontalCenterWithPanel(node.Element) || node.HorizontalOffsetFlag) + { + prevSize = prevSize.WithWidth(prevSize.Width + prevNode.OriginDesiredSize.Width); + prevNode.HorizontalOffsetFlag = true; + } + if (node.VerticalOffsetFlag) + { + prevNode.VerticalOffsetFlag = true; + } + } + + if (prevNode.AboveNode != null && prevNode.AboveNode == node || + prevNode.BelowNode != null && prevNode.BelowNode == node) + { + prevSize = prevSize.WithHeight(prevSize.Height + node.BoundingSize.Height); + if (GetAlignVerticalCenterWithPanel(node.Element) || node.VerticalOffsetFlag) + { + prevSize = prevSize.WithHeight(prevSize.Height + node.OriginDesiredSize.Height); + prevNode.VerticalOffsetFlag = true; + } + if (node.HorizontalOffsetFlag) + { + prevNode.HorizontalOffsetFlag = true; + } + } + } + else + { + return GetBoundingSize(node, prevSize, node.OutgoingNodes); + } + } + + return prevSize; + } } private class Graph { private readonly Dictionary _nodeDic; - private Size _arrangeSize; + private Size AvailableSize { get; set; } - public Graph() + public Graph() => _nodeDic = new Dictionary(); + + public IEnumerable GetNodes() => _nodeDic.Values; + + public void Clear() { - _nodeDic = new Dictionary(); + AvailableSize = new Size(); + _nodeDic.Clear(); } + public void Reset() => _nodeDic.Values.Do(node => node.Reset()); + public GraphNode? AddLink(GraphNode from, Layoutable? to) { if (to == null) @@ -177,177 +243,296 @@ namespace Avalonia.Controls return _nodeDic[value]; } - public void Reset(Size arrangeSize) + public void Measure(Size availableSize) { - _arrangeSize = arrangeSize; - _nodeDic.Clear(); + AvailableSize = availableSize; + Measure(_nodeDic.Values, null); } - public bool CheckCyclic() => CheckCyclic(_nodeDic.Values, null); - - private bool CheckCyclic(IEnumerable nodes, HashSet? set) + private void Measure(IEnumerable nodes, HashSet? set) { - set ??= new HashSet(); + set ??= new HashSet(); foreach (var node in nodes) { - if (!node.Arranged && node.OutgoingNodes.Count == 0) + /* + * 该节点无任何依赖,所以从这里开始计算元素位置。 + * 因为无任何依赖,所以忽略同级元素 + */ + if (!node.Measured && !node.OutgoingNodes.Any()) { - ArrangeChild(node, true); + MeasureChild(node); continue; } - if (node.OutgoingNodes.All(item => item.Arranged)) + // 判断依赖元素是否全部排列完毕 + if (node.OutgoingNodes.All(item => item.Measured)) { - ArrangeChild(node); + MeasureChild(node); continue; } + // 判断是否有循环 if (!set.Add(node.Element)) - return true; + throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete."); - return CheckCyclic(node.OutgoingNodes, set); - } + // 没有循环,且有依赖,则继续往下 + Measure(node.OutgoingNodes, set); - return false; + if (!node.Measured) + { + MeasureChild(node); + } + } } - private void ArrangeChild(GraphNode node, bool ignoneSibling = false) + private void MeasureChild(GraphNode node) { var child = node.Element; - var childSize = child.DesiredSize; - var childPos = new Point(); + child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + node.OriginDesiredSize = child.DesiredSize; - if (GetAlignHorizontalCenterWithPanel(child)) + var alignLeftWithPanel = GetAlignLeftWithPanel(child); + var alignTopWithPanel = GetAlignTopWithPanel(child); + var alignRightWithPanel = GetAlignRightWithPanel(child); + var alignBottomWithPanel = GetAlignBottomWithPanel(child); + + if (alignLeftWithPanel) + node.Left = 0; + if (alignTopWithPanel) + node.Top = 0; + if (alignRightWithPanel) + node.Right = 0; + if (alignBottomWithPanel) + node.Bottom = 0; + + if (node.AlignLeftWithNode != null) { - childPos = childPos.WithX((_arrangeSize.Width - childSize.Width) / 2); + node.Left = node.Left.IsNaN() ? node.AlignLeftWithNode.Left : node.AlignLeftWithNode.Left * 0.5; } - if (GetAlignVerticalCenterWithPanel(child)) + if (node.AlignTopWithNode != null) { - childPos = childPos.WithY((_arrangeSize.Height - childSize.Height) / 2); + node.Top = node.Top.IsNaN() ? node.AlignTopWithNode.Top : node.AlignTopWithNode.Top * 0.5; } - var alignLeftWithPanel = GetAlignLeftWithPanel(child); - var alignTopWithPanel = GetAlignTopWithPanel(child); - var alignRightWithPanel = GetAlignRightWithPanel(child); - var alignBottomWithPanel = GetAlignBottomWithPanel(child); + if (node.AlignRightWithNode != null) + { + node.Right = node.Right.IsNaN() + ? node.AlignRightWithNode.Right + : node.AlignRightWithNode.Right * 0.5; + } - if (!ignoneSibling) + if (node.AlignBottomWithNode != null) { - if (node.LeftOfNode != null) - { - childPos = childPos.WithX(node.LeftOfNode.Position.X - childSize.Width); - } + node.Bottom = node.Bottom.IsNaN() + ? node.AlignBottomWithNode.Bottom + : node.AlignBottomWithNode.Bottom * 0.5; + } - if (node.AboveNode != null) - { - childPos = childPos.WithY(node.AboveNode.Position.Y - childSize.Height); - } + var availableHeight = AvailableSize.Height - node.Top - node.Bottom; + if (availableHeight.IsNaN()) + { + availableHeight = AvailableSize.Height; - if (node.RightOfNode != null) + if (!node.Top.IsNaN() && node.Bottom.IsNaN()) { - childPos = childPos.WithX(node.RightOfNode.Position.X + node.RightOfNode.Element.DesiredSize.Width); + availableHeight -= node.Top; } - - if (node.BelowNode != null) + else if (node.Top.IsNaN() && !node.Bottom.IsNaN()) { - childPos = childPos.WithY(node.BelowNode.Position.Y + node.BelowNode.Element.DesiredSize.Height); + availableHeight -= node.Bottom; } + } + + var availableWidth = AvailableSize.Width - node.Left - node.Right; + if (availableWidth.IsNaN()) + { + availableWidth = AvailableSize.Width; - if (node.AlignHorizontalCenterWith != null) + if (!node.Left.IsNaN() && node.Right.IsNaN()) { - childPos = childPos.WithX(node.AlignHorizontalCenterWith.Position.X + - (node.AlignHorizontalCenterWith.Element.DesiredSize.Width - childSize.Width) / 2); + availableWidth -= node.Left; } - - if (node.AlignVerticalCenterWith != null) + else if (node.Left.IsNaN() && !node.Right.IsNaN()) { - childPos = childPos.WithY(node.AlignVerticalCenterWith.Position.Y + - (node.AlignVerticalCenterWith.Element.DesiredSize.Height - childSize.Height) / 2); + availableWidth -= node.Right; } + } + + child.Measure(new Size(Math.Max(availableWidth, 0), Math.Max(availableHeight, 0))); + var childSize = child.DesiredSize; + + if (node.LeftOfNode != null && node.Left.IsNaN()) + { + node.Left = node.LeftOfNode.Left - childSize.Width; + } + + if (node.AboveNode != null && node.Top.IsNaN()) + { + node.Top = node.AboveNode.Top - childSize.Height; + } - if (node.AlignLeftWithNode != null) + if (node.RightOfNode != null) + { + if (node.Right.IsNaN()) { - childPos = childPos.WithX(node.AlignLeftWithNode.Position.X); + node.Right = node.RightOfNode.Right - childSize.Width; } - if (node.AlignTopWithNode != null) + if (node.Left.IsNaN()) { - childPos = childPos.WithY(node.AlignTopWithNode.Position.Y); + node.Left = AvailableSize.Width - node.RightOfNode.Right; } + } - if (node.AlignRightWithNode != null) + if (node.BelowNode != null) + { + if (node.Bottom.IsNaN()) { - childPos = childPos.WithX(node.AlignRightWithNode.Element.DesiredSize.Width + node.AlignRightWithNode.Position.X - childSize.Width); + node.Bottom = node.BelowNode.Bottom - childSize.Height; } - if (node.AlignBottomWithNode != null) + if (node.Top.IsNaN()) { - childPos = childPos.WithY(node.AlignBottomWithNode.Element.DesiredSize.Height + node.AlignBottomWithNode.Position.Y - childSize.Height); + node.Top = AvailableSize.Height - node.BelowNode.Bottom; } } - if (alignLeftWithPanel) + if (node.AlignHorizontalCenterWith != null) { - if (node.AlignRightWithNode != null) - { - childPos = childPos.WithX((node.AlignRightWithNode.Element.DesiredSize.Width + node.AlignRightWithNode.Position.X - childSize.Width) / 2); - } + var halfWidthLeft = (AvailableSize.Width + node.AlignHorizontalCenterWith.Left - node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5; + var halfWidthRight = (AvailableSize.Width - node.AlignHorizontalCenterWith.Left + node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5; + + if (node.Left.IsNaN()) + node.Left = halfWidthLeft; else - { - childPos = childPos.WithX(0); - } + node.Left = (node.Left + halfWidthLeft) * 0.5; + + if (node.Right.IsNaN()) + node.Right = halfWidthRight; + else + node.Right = (node.Right + halfWidthRight) * 0.5; } - if (alignTopWithPanel) + if (node.AlignVerticalCenterWith != null) { - if (node.AlignBottomWithNode != null) - { - childPos = childPos.WithY((node.AlignBottomWithNode.Element.DesiredSize.Height + node.AlignBottomWithNode.Position.Y - childSize.Height) / 2); - } + var halfHeightTop = (AvailableSize.Height + node.AlignVerticalCenterWith.Top - node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5; + var halfHeightBottom = (AvailableSize.Height - node.AlignVerticalCenterWith.Top + node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5; + + if (node.Top.IsNaN()) + node.Top = halfHeightTop; else - { - childPos = childPos.WithY(0); - } + node.Top = (node.Top + halfHeightTop) * 0.5; + + if (node.Bottom.IsNaN()) + node.Bottom = halfHeightBottom; + else + node.Bottom = (node.Bottom + halfHeightBottom) * 0.5; } - if (alignRightWithPanel) + if (GetAlignHorizontalCenterWithPanel(child)) { - if (alignLeftWithPanel) - { - childPos = childPos.WithX((_arrangeSize.Width - childSize.Width) / 2); - } - else if (node.AlignLeftWithNode == null) - { - childPos = childPos.WithX(_arrangeSize.Width - childSize.Width); - } + var halfSubWidth = (AvailableSize.Width - childSize.Width) * 0.5; + + if (node.Left.IsNaN()) + node.Left = halfSubWidth; else - { - childPos = childPos.WithX((_arrangeSize.Width + node.AlignLeftWithNode.Position.X - childSize.Width) / 2); - } + node.Left = (node.Left + halfSubWidth) * 0.5; + + if (node.Right.IsNaN()) + node.Right = halfSubWidth; + else + node.Right = (node.Right + halfSubWidth) * 0.5; } - if (alignBottomWithPanel) + if (GetAlignVerticalCenterWithPanel(child)) { - if (alignTopWithPanel) - { - childPos = childPos.WithY((_arrangeSize.Height - childSize.Height) / 2); - } - else if (node.AlignTopWithNode == null) + var halfSubHeight = (AvailableSize.Height - childSize.Height) * 0.5; + + if (node.Top.IsNaN()) + node.Top = halfSubHeight; + else + node.Top = (node.Top + halfSubHeight) * 0.5; + + if (node.Bottom.IsNaN()) + node.Bottom = halfSubHeight; + else + node.Bottom = (node.Bottom + halfSubHeight) * 0.5; + } + + if (node.Left.IsNaN()) + { + if (!node.Right.IsNaN()) + node.Left = AvailableSize.Width - node.Right - childSize.Width; + else { - childPos = childPos.WithY(_arrangeSize.Height - childSize.Height); + node.Left = 0; + node.Right = AvailableSize.Width - childSize.Width; } + } + else if (!node.Left.IsNaN() && node.Right.IsNaN()) + { + node.Right = AvailableSize.Width - node.Left - childSize.Width; + } + + if (node.Top.IsNaN()) + { + if (!node.Bottom.IsNaN()) + node.Top = AvailableSize.Height - node.Bottom - childSize.Height; else { - childPos = childPos.WithY((_arrangeSize.Height + node.AlignTopWithNode.Position.Y - childSize.Height) / 2); + node.Top = 0; + node.Bottom = AvailableSize.Height - childSize.Height; } } + else if (!node.Top.IsNaN() && node.Bottom.IsNaN()) + { + node.Bottom = AvailableSize.Height - node.Top - childSize.Height; + } + + node.Measured = true; + } + + public Size GetBoundingSize(bool calcWidth, bool calcHeight) + { + var boundingSize = new Size(); + + foreach (var node in _nodeDic.Values) + { + var size = node.GetBoundingSize(); + boundingSize = boundingSize.WithWidth(Math.Max(boundingSize.Width, size.Width)); + boundingSize = boundingSize.WithHeight(Math.Max(boundingSize.Height, size.Height)); + } - child.Arrange(new Rect(childPos.X, childPos.Y, childSize.Width, childSize.Height)); - node.Position = childPos; - node.Arranged = true; + boundingSize = boundingSize.WithWidth(calcWidth ? boundingSize.Width : AvailableSize.Width); + boundingSize = boundingSize.WithHeight(calcHeight ? boundingSize.Height : AvailableSize.Height); + return boundingSize; } } } + + internal static partial class Extensions + { + /// + /// Returns a value that indicates whether the specified value is not a number (). + /// + /// A double-precision floating-point number. + /// true if evaluates to ; otherwise, false. + public static bool IsNaN(this double d) + { + return double.IsNaN(d); + } + + public static IEnumerable Do(this IEnumerable source, Action predicate) + { + var enumerable = source as IList ?? source.ToList(); + foreach (var item in enumerable) + { + predicate.Invoke(item); + } + + return enumerable; + } + } } diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index f5c673d5f9..931c27c575 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -192,7 +192,7 @@ namespace Avalonia.Controls.Remote.Server GetAvaloniaInputModifiers(pressed.Modifiers))); }, DispatcherPriority.Input); } - if (obj is PointerPressedEventMessage released) + if (obj is PointerReleasedEventMessage released) { Dispatcher.UIThread.Post(() => { diff --git a/src/Avalonia.Themes.Default/ToggleSwitch.xaml b/src/Avalonia.Themes.Default/ToggleSwitch.xaml index 893d64f505..ded121f5f6 100644 --- a/src/Avalonia.Themes.Default/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Default/ToggleSwitch.xaml @@ -43,7 +43,8 @@ - + + diff --git a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml index e7f8fb1641..4309edefe3 100644 --- a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml @@ -43,7 +43,8 @@ - + + diff --git a/src/Markup/Avalonia.Markup/Data/BindingBase.cs b/src/Markup/Avalonia.Markup/Data/BindingBase.cs index 7c4e7b5efe..3dbc83a7df 100644 --- a/src/Markup/Avalonia.Markup/Data/BindingBase.cs +++ b/src/Markup/Avalonia.Markup/Data/BindingBase.cs @@ -137,9 +137,9 @@ namespace Avalonia.Data { Contract.Requires(target != null); - if (!(target is IStyledElement)) + if (!(target is IDataContextProvider)) { - target = anchor as IStyledElement; + target = anchor as IDataContextProvider; if (target == null) { diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index e533001242..58ddc8ca60 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -1,6 +1,5 @@ using System; -using System.Collections.Generic; -using Avalonia.Threading; +using Avalonia.Data; using Avalonia.UnitTests; using Xunit; @@ -32,5 +31,20 @@ namespace Avalonia.Controls.UnitTests Assert.True(raised); } } + + [Fact] + public void Can_Bind_To_DataContext() + { + using (UnitTestApplication.Start()) + { + var application = Application.Current; + + application.DataContext = "Test"; + + application.Bind(Application.NameProperty, new Binding(".")); + + Assert.Equal("Test", Application.Current.Name); + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs index fddc02f19c..fab57cec49 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs @@ -60,6 +60,25 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.IsType(target.Panel.Children[1]); } + [Fact] + public void Should_Create_Containers_Only_Once() + { + var parent = new TestItemsControl(); + var target = new ItemsPresenter + { + Items = new[] { "foo", "bar" }, + [StyledElement.TemplatedParentProperty] = parent, + }; + var raised = 0; + + parent.ItemContainerGenerator.Materialized += (s, e) => ++raised; + + target.ApplyTemplate(); + + Assert.Equal(2, target.Panel.Children.Count); + Assert.Equal(2, raised); + } + [Fact] public void ItemContainerGenerator_Should_Be_Picked_Up_From_TemplatedControl() { diff --git a/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs b/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs index 4248e643eb..7b1f6d07b7 100644 --- a/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs @@ -31,6 +31,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Rect(20, 0, 20, 20), target.Children[1].Bounds); } + [Fact] public void Lays_Out_1_Child_Below_the_other() { var rect1 = new Rectangle { Height = 20, Width = 20 }; @@ -55,5 +56,31 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds); Assert.Equal(new Rect(0, 20, 20, 20), target.Children[1].Bounds); } + + [Fact] + public void RelativePanel_Can_Center() + { + var rect1 = new Rectangle { Height = 20, Width = 20 }; + var rect2 = new Rectangle { Height = 20, Width = 20 }; + + var target = new RelativePanel + { + VerticalAlignment = Layout.VerticalAlignment.Center, + HorizontalAlignment = Layout.HorizontalAlignment.Center, + Children = + { + rect1, rect2 + } + }; + + RelativePanel.SetAlignLeftWithPanel(rect1, true); + RelativePanel.SetBelow(rect2, rect1); + target.Measure(new Size(400, 400)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.Equal(new Size(20, 40), target.Bounds.Size); + Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds); + Assert.Equal(new Rect(0, 20, 20, 20), target.Children[1].Bounds); + } } }