diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 2450f1a3a1..0499907ab8 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -210,7 +210,11 @@ namespace Avalonia
/// The value.
public object GetValue(AvaloniaProperty property)
{
- Contract.Requires(property != null);
+ if (property is null)
+ {
+ throw new ArgumentNullException(nameof(property));
+ }
+
VerifyAccess();
if (property.IsDirect)
@@ -231,7 +235,10 @@ namespace Avalonia
/// The value.
public T GetValue(AvaloniaProperty property)
{
- Contract.Requires(property != null);
+ if (property is null)
+ {
+ throw new ArgumentNullException(nameof(property));
+ }
return (T)GetValue((AvaloniaProperty)property);
}
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index 56ad241187..ac7d2c60af 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -28,6 +28,8 @@ namespace Avalonia
private readonly Dictionary _metadata;
private readonly Dictionary _metadataCache = new Dictionary();
+ private bool _hasMetadataOverrides;
+
///
/// Initializes a new instance of the class.
///
@@ -92,6 +94,9 @@ namespace Avalonia
Id = source.Id;
_defaultMetadata = source._defaultMetadata;
+ // Properties that have different owner can't use fast path for metadata.
+ _hasMetadataOverrides = true;
+
if (metadata != null)
{
_metadata.Add(ownerType, metadata);
@@ -446,31 +451,12 @@ namespace Avalonia
///
public PropertyMetadata GetMetadata(Type type)
{
- Contract.Requires(type != null);
-
- PropertyMetadata result;
- Type currentType = type;
-
- if (_metadataCache.TryGetValue(type, out result))
+ if (!_hasMetadataOverrides)
{
- return result;
+ return _defaultMetadata;
}
- while (currentType != null)
- {
- if (_metadata.TryGetValue(currentType, out result))
- {
- _metadataCache[type] = result;
-
- return result;
- }
-
- currentType = currentType.GetTypeInfo().BaseType;
- }
-
- _metadataCache[type] = _defaultMetadata;
-
- return _defaultMetadata;
+ return GetMetadataWithOverrides(type);
}
///
@@ -535,6 +521,39 @@ namespace Avalonia
metadata.Merge(baseMetadata, this);
_metadata.Add(type, metadata);
_metadataCache.Clear();
+
+ _hasMetadataOverrides = true;
+ }
+
+ private PropertyMetadata GetMetadataWithOverrides(Type type)
+ {
+ if (type is null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ if (_metadataCache.TryGetValue(type, out PropertyMetadata result))
+ {
+ return result;
+ }
+
+ Type currentType = type;
+
+ while (currentType != null)
+ {
+ if (_metadata.TryGetValue(currentType, out result))
+ {
+ _metadataCache[type] = result;
+
+ return result;
+ }
+
+ currentType = currentType.GetTypeInfo().BaseType;
+ }
+
+ _metadataCache[type] = _defaultMetadata;
+
+ return _defaultMetadata;
}
[DebuggerHidden]
diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs
index d77cc269f9..cfb4b14b1c 100644
--- a/src/Avalonia.Layout/LayoutHelper.cs
+++ b/src/Avalonia.Layout/LayoutHelper.cs
@@ -21,8 +21,11 @@ namespace Avalonia.Layout
/// The control's size.
public static Size ApplyLayoutConstraints(ILayoutable control, Size constraints)
{
- double width = (control.Width > 0) ? control.Width : constraints.Width;
- double height = (control.Height > 0) ? control.Height : constraints.Height;
+ var controlWidth = control.Width;
+ var controlHeight = control.Height;
+
+ double width = (controlWidth > 0) ? controlWidth : constraints.Width;
+ double height = (controlHeight > 0) ? controlHeight : constraints.Height;
width = Math.Min(width, control.MaxWidth);
width = Math.Max(width, control.MinWidth);
height = Math.Min(height, control.MaxHeight);
diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs
index 855f123748..1792655a13 100644
--- a/src/Avalonia.Layout/LayoutManager.cs
+++ b/src/Avalonia.Layout/LayoutManager.cs
@@ -15,9 +15,15 @@ namespace Avalonia.Layout
{
private readonly LayoutQueue _toMeasure = new LayoutQueue(v => !v.IsMeasureValid);
private readonly LayoutQueue _toArrange = new LayoutQueue(v => !v.IsArrangeValid);
+ private readonly Action _executeLayoutPass;
private bool _queued;
private bool _running;
+ public LayoutManager()
+ {
+ _executeLayoutPass = ExecuteLayoutPass;
+ }
+
///
public void InvalidateMeasure(ILayoutable control)
{
@@ -215,7 +221,7 @@ namespace Avalonia.Layout
{
if (!_queued && !_running)
{
- Dispatcher.UIThread.Post(ExecuteLayoutPass, DispatcherPriority.Layout);
+ Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout);
_queued = true;
}
}
diff --git a/src/Avalonia.Layout/LayoutQueue.cs b/src/Avalonia.Layout/LayoutQueue.cs
index eb0e4bd9f3..96f893e7b0 100644
--- a/src/Avalonia.Layout/LayoutQueue.cs
+++ b/src/Avalonia.Layout/LayoutQueue.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Linq;
namespace Avalonia.Layout
{
@@ -18,9 +17,11 @@ namespace Avalonia.Layout
_shouldEnqueue = shouldEnqueue;
}
- private Func _shouldEnqueue;
- private Queue _inner = new Queue();
- private Dictionary _loopQueueInfo = new Dictionary();
+ private readonly Func _shouldEnqueue;
+ private readonly Queue _inner = new Queue();
+ private readonly Dictionary _loopQueueInfo = new Dictionary();
+ private readonly List> _notFinalizedBuffer = new List>();
+
private int _maxEnqueueCountPerLoop = 1;
public int Count => _inner.Count;
@@ -60,13 +61,19 @@ namespace Avalonia.Layout
public void EndLoop()
{
- var notfinalized = _loopQueueInfo.Where(v => v.Value.Count >= _maxEnqueueCountPerLoop).ToArray();
+ foreach (KeyValuePair info in _loopQueueInfo)
+ {
+ if (info.Value.Count >= _maxEnqueueCountPerLoop)
+ {
+ _notFinalizedBuffer.Add(info);
+ }
+ }
_loopQueueInfo.Clear();
- //prevent layout cycle but add to next layout the non arranged/measured items that might have caused cycle
- //one more time as a final attempt
- foreach (var item in notfinalized)
+ // Prevent layout cycle but add to next layout the non arranged/measured items that might have caused cycle
+ // one more time as a final attempt.
+ foreach (var item in _notFinalizedBuffer)
{
if (_shouldEnqueue(item.Key))
{
@@ -74,6 +81,8 @@ namespace Avalonia.Layout
_inner.Enqueue(item.Key);
}
}
+
+ _notFinalizedBuffer.Clear();
}
}
}
diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs
index b0757a823d..4732808b91 100644
--- a/src/Avalonia.Layout/Layoutable.cs
+++ b/src/Avalonia.Layout/Layoutable.cs
@@ -518,17 +518,25 @@ namespace Avalonia.Layout
var width = measured.Width;
var height = measured.Height;
- if (!double.IsNaN(Width))
{
- width = Width;
+ double widthCache = Width;
+
+ if (!double.IsNaN(widthCache))
+ {
+ width = widthCache;
+ }
}
width = Math.Min(width, MaxWidth);
width = Math.Max(width, MinWidth);
- if (!double.IsNaN(Height))
{
- height = Height;
+ double heightCache = Height;
+
+ if (!double.IsNaN(heightCache))
+ {
+ height = heightCache;
+ }
}
height = Math.Min(height, MaxHeight);
@@ -562,11 +570,19 @@ namespace Avalonia.Layout
double width = 0;
double height = 0;
- foreach (ILayoutable child in this.GetVisualChildren())
+ var visualChildren = VisualChildren;
+ var visualCount = visualChildren.Count;
+
+ for (var i = 0; i < visualCount; i++)
{
- child.Measure(availableSize);
- width = Math.Max(width, child.DesiredSize.Width);
- height = Math.Max(height, child.DesiredSize.Height);
+ IVisual visual = visualChildren[i];
+
+ if (visual is ILayoutable layoutable)
+ {
+ layoutable.Measure(availableSize);
+ width = Math.Max(width, layoutable.DesiredSize.Width);
+ height = Math.Max(height, layoutable.DesiredSize.Height);
+ }
}
return new Size(width, height);
@@ -594,6 +610,7 @@ namespace Avalonia.Layout
var verticalAlignment = VerticalAlignment;
var size = availableSizeMinusMargins;
var scale = GetLayoutScale();
+ var useLayoutRounding = UseLayoutRounding;
if (horizontalAlignment != HorizontalAlignment.Stretch)
{
@@ -607,7 +624,7 @@ namespace Avalonia.Layout
size = LayoutHelper.ApplyLayoutConstraints(this, size);
- if (UseLayoutRounding)
+ if (useLayoutRounding)
{
size = new Size(
Math.Ceiling(size.Width * scale) / scale,
@@ -641,7 +658,7 @@ namespace Avalonia.Layout
break;
}
- if (UseLayoutRounding)
+ if (useLayoutRounding)
{
originX = Math.Floor(originX * scale) / scale;
originY = Math.Floor(originY * scale) / scale;
@@ -658,9 +675,19 @@ namespace Avalonia.Layout
/// The actual size used.
protected virtual Size ArrangeOverride(Size finalSize)
{
- foreach (ILayoutable child in this.GetVisualChildren().OfType())
+ var arrangeRect = new Rect(finalSize);
+
+ var visualChildren = VisualChildren;
+ var visualCount = visualChildren.Count;
+
+ for (var i = 0; i < visualCount; i++)
{
- child.Arrange(new Rect(finalSize));
+ IVisual visual = visualChildren[i];
+
+ if (visual is ILayoutable layoutable)
+ {
+ layoutable.Arrange(arrangeRect);
+ }
}
return finalSize;