diff --git a/readme.md b/readme.md
index 33da0e82b6..42b1e52205 100644
--- a/readme.md
+++ b/readme.md
@@ -24,6 +24,8 @@ Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?it
For those without Visual Studio, a starter guide for .NET Core CLI can be found [here](http://avaloniaui.net/docs/quickstart/create-new-project#net-core).
+If you need to develop Avalonia app with JetBrains Rider, go and *vote* on [this issue](https://youtrack.jetbrains.com/issue/RIDER-39247) in their tracker. JetBrains won't do things without their users telling them that they want the feature, so only **YOU** can make it happen.
+
Avalonia is delivered via NuGet package manager. You can find the packages here: [stable(ish)](https://www.nuget.org/packages/Avalonia/)
Use these commands in the Package Manager console to install Avalonia manually:
diff --git a/samples/ControlCatalog/Pages/ProgressBarPage.xaml b/samples/ControlCatalog/Pages/ProgressBarPage.xaml
index 39bbf391bb..13bae59805 100644
--- a/samples/ControlCatalog/Pages/ProgressBarPage.xaml
+++ b/samples/ControlCatalog/Pages/ProgressBarPage.xaml
@@ -6,15 +6,19 @@
A progress bar control
+
-
+
-
+
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 6a00feaf79..b0ff591682 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -34,7 +34,6 @@ namespace Avalonia
public AvaloniaObject()
{
VerifyAccess();
- AvaloniaPropertyRegistry.Instance.NotifyInitialized(this);
}
///
@@ -479,7 +478,13 @@ namespace Avalonia
}
}
- void IValueSink.Completed(AvaloniaProperty property, IPriorityValueEntry entry) { }
+ void IValueSink.Completed(
+ StyledPropertyBase property,
+ IPriorityValueEntry entry,
+ Optional oldValue)
+ {
+ ((IValueSink)this).ValueChanged(property, BindingPriority.Unset, oldValue, default);
+ }
///
/// Called for each inherited property when the changes.
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index b305a9aaa2..aa7a675764 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -20,7 +20,6 @@ namespace Avalonia
public static readonly object UnsetValue = new UnsetValueType();
private static int s_nextId;
- private readonly Subject _initialized;
private readonly Subject _changed;
private readonly PropertyMetadata _defaultMetadata;
private readonly Dictionary _metadata;
@@ -53,7 +52,6 @@ namespace Avalonia
throw new ArgumentException("'name' may not contain periods.");
}
- _initialized = new Subject();
_changed = new Subject();
_metadata = new Dictionary();
@@ -81,7 +79,6 @@ namespace Avalonia
Contract.Requires(source != null);
Contract.Requires(ownerType != null);
- _initialized = source._initialized;
_changed = source._changed;
_metadata = new Dictionary();
@@ -136,22 +133,6 @@ namespace Avalonia
///
public virtual bool IsReadOnly => false;
- ///
- /// Gets an observable that is fired when this property is initialized on a
- /// new instance.
- ///
- ///
- /// This observable is fired each time a new is constructed
- /// for all properties registered on the object's type. The default value of the property
- /// for the object is passed in the args' NewValue (OldValue will always be
- /// .
- ///
- ///
- /// An observable that is fired when this property is initialized on a new
- /// instance.
- ///
- public IObservable Initialized => _initialized;
-
///
/// Gets an observable that is fired when this property changes on any
/// instance.
@@ -488,26 +469,6 @@ namespace Avalonia
return Name;
}
- ///
- /// True if has any observers.
- ///
- internal bool HasNotifyInitializedObservers => _initialized.HasObservers;
-
- ///
- /// Notifies the observable.
- ///
- /// The object being initialized.
- internal abstract void NotifyInitialized(IAvaloniaObject o);
-
- ///
- /// Notifies the observable.
- ///
- /// The observable arguments.
- internal void NotifyInitialized(AvaloniaPropertyChangedEventArgs e)
- {
- _initialized.OnNext(e);
- }
-
///
/// Notifies the observable.
///
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index 14c8630599..29ab10278b 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
@@ -415,51 +415,6 @@ namespace Avalonia
_inheritedCache.Clear();
}
- internal void NotifyInitialized(AvaloniaObject o)
- {
- Contract.Requires(o != null);
-
- var type = o.GetType();
-
- if (!_initializedCache.TryGetValue(type, out var initializationData))
- {
- var visited = new HashSet();
-
- initializationData = new List();
-
- foreach (AvaloniaProperty property in GetRegistered(type))
- {
- if (property.IsDirect)
- {
- initializationData.Add(new PropertyInitializationData(property, (IDirectPropertyAccessor)property));
- }
- else
- {
- initializationData.Add(new PropertyInitializationData(property, (IStyledPropertyAccessor)property, type));
- }
-
- visited.Add(property);
- }
-
- foreach (AvaloniaProperty property in GetRegisteredAttached(type))
- {
- if (!visited.Contains(property))
- {
- initializationData.Add(new PropertyInitializationData(property, (IStyledPropertyAccessor)property, type));
-
- visited.Add(property);
- }
- }
-
- _initializedCache.Add(type, initializationData);
- }
-
- foreach (PropertyInitializationData data in initializationData)
- {
- data.Property.NotifyInitialized(o);
- }
- }
-
private readonly struct PropertyInitializationData
{
public AvaloniaProperty Property { get; }
diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs
index 7a0be065eb..39ed3b084f 100644
--- a/src/Avalonia.Base/DirectPropertyBase.cs
+++ b/src/Avalonia.Base/DirectPropertyBase.cs
@@ -101,21 +101,6 @@ namespace Avalonia
return (DirectPropertyMetadata)base.GetMetadata(type);
}
- ///
- internal override void NotifyInitialized(IAvaloniaObject o)
- {
- if (HasNotifyInitializedObservers)
- {
- var e = new AvaloniaPropertyChangedEventArgs(
- o,
- this,
- default,
- InvokeGetter(o),
- BindingPriority.Unset);
- NotifyInitialized(e);
- }
- }
-
///
internal override void RouteClearValue(IAvaloniaObject o)
{
diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs
index 09a0f169df..3249b31d66 100644
--- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs
+++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs
@@ -48,10 +48,10 @@ namespace Avalonia.PropertyStore
{
_subscription?.Dispose();
_subscription = null;
- _sink.Completed(Property, this);
+ _sink.Completed(Property, this, Value);
}
- public void OnCompleted() => _sink.Completed(Property, this);
+ public void OnCompleted() => _sink.Completed(Property, this, Value);
public void OnError(Exception error)
{
diff --git a/src/Avalonia.Base/PropertyStore/IValueSink.cs b/src/Avalonia.Base/PropertyStore/IValueSink.cs
index 223b0058c1..9012a985ac 100644
--- a/src/Avalonia.Base/PropertyStore/IValueSink.cs
+++ b/src/Avalonia.Base/PropertyStore/IValueSink.cs
@@ -15,6 +15,9 @@ namespace Avalonia.PropertyStore
Optional oldValue,
BindingValue newValue);
- void Completed(AvaloniaProperty property, IPriorityValueEntry entry);
+ void Completed(
+ StyledPropertyBase property,
+ IPriorityValueEntry entry,
+ Optional oldValue);
}
}
diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs
index 2785dc6840..4ef8f650fa 100644
--- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs
+++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs
@@ -117,7 +117,10 @@ namespace Avalonia.PropertyStore
UpdateEffectiveValue();
}
- void IValueSink.Completed(AvaloniaProperty property, IPriorityValueEntry entry)
+ void IValueSink.Completed(
+ StyledPropertyBase property,
+ IPriorityValueEntry entry,
+ Optional oldValue)
{
_entries.Remove((IPriorityValueEntry)entry);
UpdateEffectiveValue();
diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs
index 8c4d683ae0..d1f961a567 100644
--- a/src/Avalonia.Base/StyledPropertyBase.cs
+++ b/src/Avalonia.Base/StyledPropertyBase.cs
@@ -181,21 +181,6 @@ namespace Avalonia
///
object IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultBoxedValue(type);
- ///
- internal override void NotifyInitialized(IAvaloniaObject o)
- {
- if (HasNotifyInitializedObservers)
- {
- var e = new AvaloniaPropertyChangedEventArgs(
- o,
- this,
- default,
- o.GetValue(this),
- BindingPriority.Unset);
- NotifyInitialized(e);
- }
- }
-
///
internal override void RouteClearValue(IAvaloniaObject o)
{
diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs
index d1393a9c0d..8a25d29c3f 100644
--- a/src/Avalonia.Base/Utilities/TypeUtilities.cs
+++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
@@ -185,6 +186,14 @@ namespace Avalonia.Utilities
}
}
+ var typeConverter = TypeDescriptor.GetConverter(to);
+
+ if (typeConverter.CanConvertFrom(from) == true)
+ {
+ result = typeConverter.ConvertFrom(null, culture, value);
+ return true;
+ }
+
var cast = FindTypeConversionOperatorMethod(from, to, OperatorType.Implicit | OperatorType.Explicit);
if (cast != null)
diff --git a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
index 37e25d0fac..aca08f5259 100644
--- a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
+++ b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
@@ -183,11 +183,16 @@ namespace Avalonia.Utilities
for (int c = 0; c < _count; c++)
{
var r = _data[c];
+
+ TSubscriber target = null;
+
+ r.Subscriber?.TryGetTarget(out target);
+
//Mark current index as first empty
- if (r.Subscriber == null && empty == -1)
+ if (target == null && empty == -1)
empty = c;
//If current element isn't null and we have an empty one
- if (r.Subscriber != null && empty != -1)
+ if (target != null && empty != -1)
{
_data[c] = default;
_data[empty] = r;
diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs
index 58ebc48652..e9118af9f1 100644
--- a/src/Avalonia.Base/ValueStore.cs
+++ b/src/Avalonia.Base/ValueStore.cs
@@ -148,7 +148,7 @@ namespace Avalonia
_values.Remove(property);
_sink.ValueChanged(
property,
- BindingPriority.LocalValue,
+ BindingPriority.Unset,
old,
BindingValue.Unset);
}
@@ -190,13 +190,17 @@ namespace Avalonia
_sink.ValueChanged(property, priority, oldValue, newValue);
}
- void IValueSink.Completed(AvaloniaProperty property, IPriorityValueEntry entry)
+ void IValueSink.Completed(
+ StyledPropertyBase property,
+ IPriorityValueEntry entry,
+ Optional oldValue)
{
if (_values.TryGetValue(property, out var slot))
{
if (slot == entry)
{
_values.Remove(property);
+ _sink.Completed(property, entry, oldValue);
}
}
}
@@ -228,6 +232,7 @@ namespace Avalonia
else
{
var priorityValue = new PriorityValue(_owner, property, this, l);
+ priorityValue.SetValue(value, priority);
_values.SetValue(property, priorityValue);
}
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index e16e11cdd6..214132f03c 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -149,6 +149,9 @@ namespace Avalonia.Controls
private IEnumerable _items;
+ public event EventHandler HorizontalScroll;
+ public event EventHandler VerticalScroll;
+
///
/// Identifies the CanUserReorderColumns dependency property.
///
@@ -373,7 +376,11 @@ namespace Avalonia.Controls
public bool IsValid
{
get { return _isValid; }
- internal set { SetAndRaise(IsValidProperty, ref _isValid, value); }
+ internal set
+ {
+ SetAndRaise(IsValidProperty, ref _isValid, value);
+ PseudoClasses.Set(":invalid", !value);
+ }
}
public static readonly StyledProperty MaxColumnWidthProperty =
@@ -656,8 +663,6 @@ namespace Avalonia.Controls
HorizontalScrollBarVisibilityProperty,
VerticalScrollBarVisibilityProperty);
- PseudoClass(IsValidProperty, x => !x, ":invalid");
-
ItemsProperty.Changed.AddClassHandler((x, e) => x.OnItemsPropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnColumnWidthChanged(e));
@@ -4223,6 +4228,7 @@ namespace Avalonia.Controls
private void HorizontalScrollBar_Scroll(object sender, ScrollEventArgs e)
{
ProcessHorizontalScroll(e.ScrollEventType);
+ HorizontalScroll?.Invoke(sender, e);
}
private bool IsColumnOutOfBounds(int columnIndex)
@@ -5555,6 +5561,7 @@ namespace Avalonia.Controls
private void VerticalScrollBar_Scroll(object sender, ScrollEventArgs e)
{
ProcessVerticalScroll(e.ScrollEventType);
+ VerticalScroll?.Invoke(sender, e);
}
//TODO: Ensure left button is checked for
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
index 14ad9c23e6..17e7ecba43 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
@@ -202,24 +202,24 @@
-
+
-
-
+
+
-
+
-
-
+
+
diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index 2e115463ac..0bc6394b19 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -91,7 +91,11 @@ namespace Avalonia.Controls
CommandProperty.Changed.Subscribe(CommandChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
IsCancelProperty.Changed.Subscribe(IsCancelChanged);
- PseudoClass
@@ -58,7 +59,7 @@
-
+
diff --git a/src/Avalonia.Themes.Default/ProgressBar.xaml b/src/Avalonia.Themes.Default/ProgressBar.xaml
index 72271e785a..d3c2f0c784 100644
--- a/src/Avalonia.Themes.Default/ProgressBar.xaml
+++ b/src/Avalonia.Themes.Default/ProgressBar.xaml
@@ -1,14 +1,25 @@
@@ -22,42 +33,49 @@
+
diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml
index 423d19da14..2c4cafde26 100644
--- a/src/Avalonia.Themes.Default/TextBox.xaml
+++ b/src/Avalonia.Themes.Default/TextBox.xaml
@@ -12,7 +12,9 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
-
+
$"A {Size} {RotationAngle.ToString(CultureInfo.InvariantCulture)} {(IsLargeArc ? 1 : 0)} {(int)SweepDirection} {Point}";
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/BezierSegment .cs b/src/Avalonia.Visuals/Media/BezierSegment .cs
index 8bea03bc23..ddb818c3ac 100644
--- a/src/Avalonia.Visuals/Media/BezierSegment .cs
+++ b/src/Avalonia.Visuals/Media/BezierSegment .cs
@@ -61,5 +61,8 @@ namespace Avalonia.Media
{
ctx.CubicBezierTo(Point1, Point2, Point3);
}
+
+ public override string ToString()
+ => $"C {Point1} {Point2} {Point3}";
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/LineSegment.cs b/src/Avalonia.Visuals/Media/LineSegment.cs
index d81f67d99f..81e53ff7c6 100644
--- a/src/Avalonia.Visuals/Media/LineSegment.cs
+++ b/src/Avalonia.Visuals/Media/LineSegment.cs
@@ -27,5 +27,8 @@ namespace Avalonia.Media
{
ctx.LineTo(Point);
}
+
+ public override string ToString()
+ => $"L {Point}";
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs
index 6a438a85ee..720fca8337 100644
--- a/src/Avalonia.Visuals/Media/PathFigure.cs
+++ b/src/Avalonia.Visuals/Media/PathFigure.cs
@@ -98,5 +98,8 @@ namespace Avalonia.Media
}
private PathSegments _segments;
+
+ public override string ToString()
+ => $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}";
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Visuals/Media/PathGeometry.cs
index 3a91c924d2..be7ffe57a7 100644
--- a/src/Avalonia.Visuals/Media/PathGeometry.cs
+++ b/src/Avalonia.Visuals/Media/PathGeometry.cs
@@ -112,5 +112,9 @@ namespace Avalonia.Media
() => InvalidateGeometry());
_figuresPropertiesObserver = figures?.TrackItemPropertyChanged(_ => InvalidateGeometry());
}
+
+
+ public override string ToString()
+ => $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}";
}
}
diff --git a/src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs b/src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs
index 31c364edf4..f1a0ccaaa0 100644
--- a/src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs
+++ b/src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs
@@ -42,5 +42,8 @@ namespace Avalonia.Media
{
ctx.QuadraticBezierTo(Point1, Point2);
}
+
+ public override string ToString()
+ => $"Q {Point1} {Point2}";
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index ef7a254f86..bd2ab6e614 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -386,11 +386,18 @@ namespace Avalonia
AttachedToVisualTree?.Invoke(this, e);
InvalidateVisual();
- if (VisualChildren != null)
+ var visualChildren = VisualChildren;
+
+ if (visualChildren != null)
{
- foreach (Visual child in VisualChildren.OfType())
+ var visualChildrenCount = visualChildren.Count;
+
+ for (var i = 0; i < visualChildrenCount; i++)
{
- child.OnAttachedToVisualTreeCore(e);
+ if (visualChildren[i] is Visual child)
+ {
+ child.OnAttachedToVisualTreeCore(e);
+ }
}
}
}
@@ -415,11 +422,18 @@ namespace Avalonia
DetachedFromVisualTree?.Invoke(this, e);
e.Root?.Renderer?.AddDirty(this);
- if (VisualChildren != null)
+ var visualChildren = VisualChildren;
+
+ if (visualChildren != null)
{
- foreach (Visual child in VisualChildren.OfType())
+ var visualChildrenCount = visualChildren.Count;
+
+ for (var i = 0; i < visualChildrenCount; i++)
{
- child.OnDetachedFromVisualTreeCore(e);
+ if (visualChildren[i] is Visual child)
+ {
+ child.OnDetachedFromVisualTreeCore(e);
+ }
}
}
}
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 919abae243..94f1dbc8d1 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -995,11 +995,18 @@ namespace Avalonia.X11
public void SetIcon(IWindowIconImpl icon)
{
- var data = ((X11IconData)icon).Data;
- fixed (void* pdata = data)
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON,
- new IntPtr((int)Atom.XA_CARDINAL), 32, PropertyMode.Replace,
- pdata, data.Length);
+ if (icon != null)
+ {
+ var data = ((X11IconData)icon).Data;
+ fixed (void* pdata = data)
+ XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON,
+ new IntPtr((int)Atom.XA_CARDINAL), 32, PropertyMode.Replace,
+ pdata, data.Length);
+ }
+ else
+ {
+ XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON);
+ }
}
public void ShowTaskbarIcon(bool value)
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index c16b76b539..cea9f88799 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -942,7 +942,7 @@ namespace Avalonia.Win32
public void SetIcon(IWindowIconImpl icon)
{
var impl = (IconImpl)icon;
- var hIcon = impl.HIcon;
+ var hIcon = impl?.HIcon ?? IntPtr.Zero;
UnmanagedMethods.PostMessage(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_SETICON,
new IntPtr((int)UnmanagedMethods.Icons.ICON_BIG), hIcon);
}
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs
index 44e2976e03..474ef9abec 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs
@@ -16,19 +16,6 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("foodefault", target.GetValue(Class2.FooProperty));
}
- [Fact]
- public void AvaloniaProperty_Initialized_Is_Called_For_Attached_Property()
- {
- bool raised = false;
-
- using (Class1.FooProperty.Initialized.Subscribe(x => raised = true))
- {
- new Class3();
- }
-
- Assert.True(raised);
- }
-
private class Base : AvaloniaObject
{
}
@@ -46,9 +33,5 @@ namespace Avalonia.Base.UnitTests
public static readonly AttachedProperty FooProperty =
Class1.FooProperty.AddOwner();
}
-
- private class Class3 : Base
- {
- }
}
}
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
index 4c00d2a1ea..2839fde320 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
@@ -132,6 +132,111 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("foo", target.GetValue(property));
}
+ [Fact]
+ public void Completing_LocalValue_Binding_Raises_PropertyChanged()
+ {
+ var target = new Class1();
+ var source = new BehaviorSubject>("foo");
+ var property = Class1.FooProperty;
+ var raised = 0;
+
+ target.Bind(property, source);
+ Assert.Equal("foo", target.GetValue(property));
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(BindingPriority.Unset, e.Priority);
+ Assert.Equal(property, e.Property);
+ Assert.Equal("foo", e.OldValue as string);
+ Assert.Equal("foodefault", e.NewValue as string);
+ ++raised;
+ };
+
+ source.OnCompleted();
+
+ Assert.Equal("foodefault", target.GetValue(property));
+ Assert.Equal(1, raised);
+ }
+
+ [Fact]
+ public void Completing_Style_Binding_Raises_PropertyChanged()
+ {
+ var target = new Class1();
+ var source = new BehaviorSubject>("foo");
+ var property = Class1.FooProperty;
+ var raised = 0;
+
+ target.Bind(property, source, BindingPriority.Style);
+ Assert.Equal("foo", target.GetValue(property));
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(BindingPriority.Unset, e.Priority);
+ Assert.Equal(property, e.Property);
+ Assert.Equal("foo", e.OldValue as string);
+ Assert.Equal("foodefault", e.NewValue as string);
+ ++raised;
+ };
+
+ source.OnCompleted();
+
+ Assert.Equal("foodefault", target.GetValue(property));
+ Assert.Equal(1, raised);
+ }
+
+ [Fact]
+ public void Completing_LocalValue_Binding_With_Style_Binding_Raises_PropertyChanged()
+ {
+ var target = new Class1();
+ var source = new BehaviorSubject>("foo");
+ var property = Class1.FooProperty;
+ var raised = 0;
+
+ target.Bind(property, new BehaviorSubject("bar"), BindingPriority.Style);
+ target.Bind(property, source);
+ Assert.Equal("foo", target.GetValue(property));
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(BindingPriority.Style, e.Priority);
+ Assert.Equal(property, e.Property);
+ Assert.Equal("foo", e.OldValue as string);
+ Assert.Equal("bar", e.NewValue as string);
+ ++raised;
+ };
+
+ source.OnCompleted();
+
+ Assert.Equal("bar", target.GetValue(property));
+ Assert.Equal(1, raised);
+ }
+
+ [Fact]
+ public void Disposing_LocalValue_Binding_Raises_PropertyChanged()
+ {
+ var target = new Class1();
+ var source = new BehaviorSubject>("foo");
+ var property = Class1.FooProperty;
+ var raised = 0;
+
+ var sub = target.Bind(property, source);
+ Assert.Equal("foo", target.GetValue(property));
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(BindingPriority.Unset, e.Priority);
+ Assert.Equal(property, e.Property);
+ Assert.Equal("foo", e.OldValue as string);
+ Assert.Equal("foodefault", e.NewValue as string);
+ ++raised;
+ };
+
+ sub.Dispose();
+
+ Assert.Equal("foodefault", target.GetValue(property));
+ Assert.Equal(1, raised);
+ }
+
[Fact]
public void Setting_Style_Value_Overrides_Binding_Permanently()
{
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
index 4110c3771f..ca17afb94f 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
@@ -188,6 +188,7 @@ namespace Avalonia.Base.UnitTests
target.PropertyChanged += (s, e) =>
{
Assert.Same(target, s);
+ Assert.Equal(BindingPriority.LocalValue, e.Priority);
Assert.Equal(Class1.FooProperty, e.Property);
Assert.Equal("newvalue", (string)e.OldValue);
Assert.Equal("unset", (string)e.NewValue);
@@ -424,22 +425,6 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("second", target.Foo);
}
- [Fact]
- public void Property_Notifies_Initialized()
- {
- bool raised = false;
-
- Class1.FooProperty.Initialized.Subscribe(e =>
- raised = e.Property == Class1.FooProperty &&
- e.OldValue == AvaloniaProperty.UnsetValue &&
- (string)e.NewValue == "initial" &&
- e.Priority == BindingPriority.Unset);
-
- var target = new Class1();
-
- Assert.True(raised);
- }
-
[Fact]
public void Binding_Error_Reverts_To_Default_Value()
{
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
index 40631d04cf..4b477287e8 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
@@ -30,6 +30,7 @@ namespace Avalonia.Base.UnitTests
target.PropertyChanged += (s, e) =>
{
Assert.Same(target, s);
+ Assert.Equal(BindingPriority.Unset, e.Priority);
Assert.Equal(Class1.FooProperty, e.Property);
Assert.Equal("newvalue", (string)e.OldValue);
Assert.Equal("foodefault", (string)e.NewValue);
@@ -239,6 +240,17 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("two", target.GetValue(Class1.FooProperty));
}
+ [Fact]
+ public void SetValue_Animation_Overrides_LocalValue()
+ {
+ Class1 target = new Class1();
+
+ target.SetValue(Class1.FooProperty, "one", BindingPriority.LocalValue);
+ Assert.Equal("one", target.GetValue(Class1.FooProperty));
+ target.SetValue(Class1.FooProperty, "two", BindingPriority.Animation);
+ Assert.Equal("two", target.GetValue(Class1.FooProperty));
+ }
+
[Fact]
public void Setting_UnsetValue_Reverts_To_Default_Value()
{
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
index 90b8bcff63..6bb8dfe1f5 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
@@ -78,24 +78,6 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(BindingMode.TwoWay, result.DefaultBindingMode);
}
- [Fact]
- public void Initialized_Observable_Fired()
- {
- bool invoked = false;
-
- Class1.FooProperty.Initialized.Subscribe(x =>
- {
- Assert.Equal(AvaloniaProperty.UnsetValue, x.OldValue);
- Assert.Equal("default", x.NewValue);
- Assert.Equal(BindingPriority.Unset, x.Priority);
- invoked = true;
- });
-
- var target = new Class1();
-
- Assert.True(invoked);
- }
-
[Fact]
public void Changed_Observable_Fired()
{
@@ -141,11 +123,6 @@ namespace Avalonia.Base.UnitTests
OverrideMetadata(typeof(T), metadata);
}
- internal override void NotifyInitialized(IAvaloniaObject o)
- {
- throw new NotImplementedException();
- }
-
internal override IDisposable RouteBind(
IAvaloniaObject o,
IObservable> source,
diff --git a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
index ecf559951a..f29dabb6bf 100644
--- a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
@@ -50,6 +50,18 @@ namespace Avalonia.Base.UnitTests.Data.Converters
Assert.Equal(TestEnum.Bar, result);
}
+ [Fact]
+ public void Can_Convert_String_To_TimeSpan()
+ {
+ var result = DefaultValueConverter.Instance.Convert(
+ "00:00:10",
+ typeof(TimeSpan),
+ null,
+ CultureInfo.InvariantCulture);
+
+ Assert.Equal(TimeSpan.FromSeconds(10), result);
+ }
+
[Fact]
public void Can_Convert_Int_To_Enum()
{
diff --git a/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs b/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs
index c2a8b03f15..8f62dc9136 100644
--- a/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs
+++ b/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs
@@ -1,33 +1,12 @@
// 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.Reactive.Subjects;
-using Avalonia.Data;
using Xunit;
namespace Avalonia.Base.UnitTests
{
public class DirectPropertyTests
{
- [Fact]
- public void Initialized_Observable_Fired()
- {
- bool invoked = false;
-
- Class1.FooProperty.Initialized.Subscribe(x =>
- {
- Assert.Equal(AvaloniaProperty.UnsetValue, x.OldValue);
- Assert.Equal("foo", x.NewValue);
- Assert.Equal(BindingPriority.Unset, x.Priority);
- invoked = true;
- });
-
- var target = new Class1();
-
- Assert.True(invoked);
- }
-
[Fact]
public void IsDirect_Property_Returns_True()
{
@@ -69,7 +48,6 @@ namespace Avalonia.Base.UnitTests
var p2 = p1.AddOwner(o => null, (o, v) => { });
Assert.Same(p1.Changed, p2.Changed);
- Assert.Same(p1.Initialized, p2.Initialized);
}
private class Class1 : AvaloniaObject
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs
index 06716f7102..d2e38850f2 100644
--- a/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Benchmarks.Base
[MemoryDiagnoser]
public class AvaloniaObjectInitializationBenchmark
{
- [Benchmark(OperationsPerInvoke = 1000)]
+ [Benchmark]
public Button InitializeButton()
{
return new Button();
diff --git a/tests/Avalonia.Benchmarks/Layout/CalendarBenchmark.cs b/tests/Avalonia.Benchmarks/Layout/CalendarBenchmark.cs
new file mode 100644
index 0000000000..c55912d94d
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Layout/CalendarBenchmark.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.UnitTests;
+using BenchmarkDotNet.Attributes;
+
+namespace Avalonia.Benchmarks.Layout
+{
+ [MemoryDiagnoser]
+ public class CalendarBenchmark : IDisposable
+ {
+ private readonly IDisposable _app;
+ private readonly TestRoot _root;
+
+ public CalendarBenchmark()
+ {
+ _app = UnitTestApplication.Start(
+ TestServices.StyledWindow.With(
+ renderInterface: new NullRenderingPlatform(),
+ threadingInterface: new NullThreadingPlatform()));
+
+ _root = new TestRoot(true, null)
+ {
+ Renderer = new NullRenderer()
+ };
+
+ _root.LayoutManager.ExecuteInitialLayoutPass(_root);
+ }
+
+ [Benchmark]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void CreateCalendar()
+ {
+ var calendar = new Calendar();
+
+ _root.Child = calendar;
+
+ _root.LayoutManager.ExecuteLayoutPass();
+ }
+
+ public void Dispose()
+ {
+ _app.Dispose();
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/NullFormattedTextImpl.cs b/tests/Avalonia.Benchmarks/NullFormattedTextImpl.cs
new file mode 100644
index 0000000000..f886d077cc
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/NullFormattedTextImpl.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Media;
+using Avalonia.Platform;
+
+namespace Avalonia.Benchmarks
+{
+ internal class NullFormattedTextImpl : IFormattedTextImpl
+ {
+ public Size Constraint { get; }
+
+ public Rect Bounds { get; }
+
+ public string Text { get; }
+
+ public IEnumerable GetLines()
+ {
+ throw new NotImplementedException();
+ }
+
+ public TextHitTestResult HitTestPoint(Point point)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Rect HitTestTextPosition(int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable HitTestTextRange(int index, int length)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
new file mode 100644
index 0000000000..101d40f00a
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.UnitTests;
+
+namespace Avalonia.Benchmarks
+{
+ internal class NullRenderingPlatform : IPlatformRenderInterface
+ {
+ public IFormattedTextImpl CreateFormattedText(string text, Typeface typeface, double fontSize, TextAlignment textAlignment,
+ TextWrapping wrapping, Size constraint, IReadOnlyList spans)
+ {
+ return new NullFormattedTextImpl();
+ }
+
+ public IGeometryImpl CreateEllipseGeometry(Rect rect)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IGeometryImpl CreateRectangleGeometry(Rect rect)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IStreamGeometryImpl CreateStreamGeometry()
+ {
+ return new MockStreamGeometryImpl();
+ }
+
+ public IRenderTarget CreateRenderTarget(IEnumerable surfaces)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IBitmapImpl LoadBitmap(string fileName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IBitmapImpl LoadBitmap(Stream stream)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IFontManagerImpl CreateFontManager()
+ {
+ return new MockFontManagerImpl();
+ }
+
+ public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs b/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs
new file mode 100644
index 0000000000..ba84b5afcc
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Reactive.Disposables;
+using System.Threading;
+using Avalonia.Platform;
+using Avalonia.Threading;
+
+namespace Avalonia.Benchmarks
+{
+ internal class NullThreadingPlatform : IPlatformThreadingInterface
+ {
+ public void RunLoop(CancellationToken cancellationToken)
+ {
+ }
+
+ public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
+ {
+ return Disposable.Empty;
+ }
+
+ public void Signal(DispatcherPriority priority)
+ {
+ }
+
+ public bool CurrentThreadIsLoopThread => true;
+
+ public event Action Signaled;
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs b/tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs
new file mode 100644
index 0000000000..7bccd65c81
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.UnitTests;
+using BenchmarkDotNet.Attributes;
+
+namespace Avalonia.Benchmarks.Styling
+{
+ [MemoryDiagnoser]
+ public class StyleAttachBenchmark : IDisposable
+ {
+ private readonly IDisposable _app;
+ private readonly TestRoot _root;
+ private readonly TextBox _control;
+
+ public StyleAttachBenchmark()
+ {
+ _app = UnitTestApplication.Start(
+ TestServices.StyledWindow.With(
+ renderInterface: new NullRenderingPlatform(),
+ threadingInterface: new NullThreadingPlatform()));
+
+ _root = new TestRoot(true, null)
+ {
+ Renderer = new NullRenderer(),
+ };
+
+ _control = new TextBox();
+ }
+
+ [Benchmark]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void AttachTextBoxStyles()
+ {
+ var styles = UnitTestApplication.Current.Styles;
+
+ styles.Attach(_control, UnitTestApplication.Current);
+
+ styles.Detach();
+ }
+
+ public void Dispose()
+ {
+ _app.Dispose();
+ }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs
index b16ac6bb8e..1bcae8e736 100644
--- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs
@@ -51,10 +51,9 @@ namespace Avalonia.Controls.UnitTests
Assert.Single(target.GetLogicalChildren());
- var child = target.GetLogicalChildren().Single();
+ var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
- Assert.IsType(child);
- Assert.Equal("Foo", ((TextBlock)child).Text);
+ Assert.Equal("Foo", child.Text);
}
[Fact]
@@ -98,20 +97,18 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(3, target.GetLogicalChildren().Count());
- var child = target.GetLogicalChildren().First();
+ var child = GetContainerTextBlock(target.GetLogicalChildren().First());
- Assert.IsType(child);
- Assert.Equal("Foo", ((TextBlock)child).Text);
+ Assert.Equal("Foo", child.Text);
var newItems = items.ToList();
newItems.RemoveAt(0);
target.Items = newItems;
- child = target.GetLogicalChildren().First();
+ child = GetContainerTextBlock(target.GetLogicalChildren().First());
- Assert.IsType(child);
- Assert.Equal("Bar", ((TextBlock)child).Text);
+ Assert.Equal("Bar", child.Text);
}
[Fact]
@@ -136,20 +133,18 @@ namespace Avalonia.Controls.UnitTests
Assert.Single(target.GetLogicalChildren());
- var child = target.GetLogicalChildren().Single();
+ var child = GetContainerTextBlock(target.GetLogicalChildren().Single());
- Assert.IsType(child);
- Assert.Equal("Foo", ((TextBlock)child).Text);
+ Assert.Equal("Foo", child.Text);
var newItems = items.ToList();
newItems.RemoveAt(0);
target.Items = newItems;
- child = target.GetLogicalChildren().Single();
+ child = GetContainerTextBlock(target.GetLogicalChildren().Single());
- Assert.IsType(child);
- Assert.Equal("Bar", ((TextBlock)child).Text);
+ Assert.Equal("Bar", child.Text);
}
[Fact]
@@ -197,10 +192,9 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(3, target.GetLogicalChildren().Count());
- var child = target.GetLogicalChildren().First();
+ var child = GetContainerTextBlock(target.GetLogicalChildren().First());
- Assert.IsType(child);
- Assert.Equal("Foo", ((TextBlock)child).Text);
+ Assert.Equal("Foo", child.Text);
target.Items = null;
@@ -233,7 +227,7 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal("FooBar", target.SelectedItem);
- var child = target.GetVisualDescendants().LastOrDefault();
+ var child = GetContainerTextBlock(target.GetVisualDescendants().LastOrDefault());
Assert.IsType(child);
Assert.Equal("FooBar", ((TextBlock)child).Text);
@@ -261,14 +255,13 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(3, target.GetLogicalChildren().Count());
- var child = target.GetLogicalChildren().First();
+ var child = GetContainerTextBlock(target.GetLogicalChildren().First());
- Assert.IsType(child);
- Assert.Equal("Foo", ((TextBlock)child).Text);
+ Assert.Equal("Foo", child.Text);
items.RemoveAt(0);
- child = target.GetLogicalChildren().First();
+ child = GetContainerTextBlock(target.GetLogicalChildren().First());
Assert.IsType(child);
Assert.Equal("Bar", ((TextBlock)child).Text);
@@ -314,5 +307,12 @@ namespace Avalonia.Controls.UnitTests
[~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty],
}.RegisterInNameScope(scope);
}
+
+ private static TextBlock GetContainerTextBlock(object control)
+ {
+ var contentPresenter = Assert.IsType(control);
+ contentPresenter.UpdateChild();
+ return Assert.IsType(contentPresenter.Child);
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
index 599e214b31..81908b5147 100644
--- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
@@ -108,5 +108,36 @@ namespace Avalonia.Controls.UnitTests
};
});
}
+
+ [Fact]
+ public void Detaching_Closed_ComboBox_Keeps_Current_Focus()
+ {
+ using (UnitTestApplication.Start(TestServices.RealFocus))
+ {
+ var target = new ComboBox
+ {
+ Items = new[] { new Canvas() },
+ SelectedIndex = 0,
+ Template = GetTemplate(),
+ };
+
+ var other = new Control { Focusable = true };
+
+ StackPanel panel;
+
+ var root = new TestRoot { Child = panel = new StackPanel { Children = { target, other } } };
+
+ target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
+
+ other.Focus();
+
+ Assert.True(other.IsFocused);
+
+ panel.Children.Remove(target);
+
+ Assert.True(other.IsFocused);
+ }
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
index 227d783874..a728500316 100644
--- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
@@ -66,7 +66,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void Container_Child_Should_Have_LogicalParent_Set_To_Container()
+ public void Container_Should_Have_LogicalParent_Set_To_ItemsControl()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@@ -87,7 +87,7 @@ namespace Avalonia.Controls.UnitTests
var container = (ContentPresenter)target.Presenter.Panel.Children[0];
- Assert.Equal(container, container.Child.Parent);
+ Assert.Equal(target, container.Parent);
}
}
@@ -190,7 +190,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void Adding_String_Item_Should_Make_TextBlock_Appear_In_LogicalChildren()
+ public void Adding_String_Item_Should_Make_ContentPresenter_Appear_In_LogicalChildren()
{
var target = new ItemsControl();
var child = new Control();
@@ -202,7 +202,7 @@ namespace Avalonia.Controls.UnitTests
var logical = (ILogical)target;
Assert.Equal(1, logical.LogicalChildren.Count);
- Assert.IsType(logical.LogicalChildren[0]);
+ Assert.IsType(logical.LogicalChildren[0]);
}
[Fact]
@@ -575,6 +575,30 @@ namespace Avalonia.Controls.UnitTests
};
}
+ [Fact]
+ public void Detaching_Then_Reattaching_To_Logical_Tree_Twice_Does_Not_Throw()
+ {
+ // # Issue 3487
+ var target = new ItemsControl
+ {
+ Template = GetTemplate(),
+ Items = new[] { "foo", "bar" },
+ ItemTemplate = new FuncDataTemplate((_, __) => new Canvas()),
+ };
+
+ var root = new TestRoot(target);
+ root.Measure(Size.Infinity);
+ root.Arrange(new Rect(root.DesiredSize));
+
+ root.Child = null;
+ root.Child = target;
+
+ target.Measure(Size.Infinity);
+
+ root.Child = null;
+ root.Child = target;
+ }
+
private class Item
{
public Item(string value)
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index 7cb9fccee8..1f07482617 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -223,7 +223,33 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
-
+ [Fact]
+ public void Popup_Close_On_Closed_Popup_Should_Not_Raise_Closed_Event()
+ {
+ using (CreateServices())
+ {
+ var window = PreparedWindow();
+ var target = new Popup() { PlacementMode = PlacementMode.Pointer };
+
+ window.Content = target;
+ window.ApplyTemplate();
+
+ int closedCount = 0;
+
+ target.Closed += (sender, args) =>
+ {
+ closedCount++;
+ };
+
+ target.Close();
+ target.Close();
+ target.Close();
+ target.Close();
+
+ Assert.Equal(0, closedCount);
+ }
+ }
+
[Fact]
public void Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent()
{
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index 696c0dbf46..004cf009fc 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
@@ -1202,6 +1202,28 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.MoveSelection(NavigationDirection.Next, true);
}
+ [Fact]
+ public void MoveSelection_Does_Select_Disabled_Controls()
+ {
+ // Issue #3426.
+ var target = new TestSelector
+ {
+ Template = Template(),
+ Items = new[]
+ {
+ new ListBoxItem(),
+ new ListBoxItem { IsEnabled = false },
+ },
+ SelectedIndex = 0,
+ };
+
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+ target.MoveSelection(NavigationDirection.Next, true);
+
+ Assert.Equal(0, target.SelectedIndex);
+ }
+
[Fact]
public void Pre_Selecting_Item_Should_Set_Selection_After_It_Was_Added_When_AlwaysSelected()
{
diff --git a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
index 2238175a4a..f281c83bb3 100644
--- a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
@@ -65,6 +65,30 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Vector(10, 10), target.Offset);
}
+ [Fact]
+ public void Test_ScrollToHome()
+ {
+ var target = new ScrollViewer();
+ target.SetValue(ScrollViewer.ExtentProperty, new Size(50, 50));
+ target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10));
+ target.Offset = new Vector(25, 25);
+ target.ScrollToHome();
+
+ Assert.Equal(new Vector(0, 0), target.Offset);
+ }
+
+ [Fact]
+ public void Test_ScrollToEnd()
+ {
+ var target = new ScrollViewer();
+ target.SetValue(ScrollViewer.ExtentProperty, new Size(50, 50));
+ target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10));
+ target.Offset = new Vector(25, 25);
+ target.ScrollToEnd();
+
+ Assert.Equal(new Vector(0, 40), target.Offset);
+ }
+
private Control CreateTemplate(ScrollViewer control, INameScope scope)
{
return new Grid
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index 0508edd92f..81699b075c 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -341,11 +341,62 @@ namespace Avalonia.Controls.UnitTests
}
}
+ [Fact]
+ public void Child_Should_Be_Measured_With_Width_And_Height_If_SizeToContent_Is_Manual()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var child = new ChildControl();
+ var target = new Window
+ {
+ Width = 100,
+ Height = 50,
+ SizeToContent = SizeToContent.Manual,
+ Content = child
+ };
+
+ target.Show();
+
+ Assert.Equal(new Size(100, 50), child.MeasureSize);
+ }
+ }
+
+ [Fact]
+ public void Child_Should_Be_Measured_With_Infinity_If_SizeToContent_Is_WidthAndHeight()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var child = new ChildControl();
+ var target = new Window
+ {
+ Width = 100,
+ Height = 50,
+ SizeToContent = SizeToContent.WidthAndHeight,
+ Content = child
+ };
+
+ target.Show();
+
+ Assert.Equal(Size.Infinity, child.MeasureSize);
+ }
+ }
+
private IWindowImpl CreateImpl(Mock renderer)
{
return Mock.Of(x =>
x.Scaling == 1 &&
x.CreateRenderer(It.IsAny()) == renderer.Object);
}
+
+ private class ChildControl : Control
+ {
+ public Size MeasureSize { get; private set; }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ MeasureSize = availableSize;
+ return base.MeasureOverride(availableSize);
+ }
+ }
}
}
diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs
index 3f20c9a76a..0165b91844 100644
--- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs
+++ b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs
@@ -374,5 +374,38 @@ namespace Avalonia.Layout.UnitTests
Assert.True(control.Measured);
Assert.True(control.IsMeasureValid);
}
+
+ [Fact]
+ public void Calling_ExecuteLayoutPass_From_ExecuteInitialLayoutPass_Does_Not_Break_Measure()
+ {
+ // Test for issue #3550.
+ var control = new LayoutTestControl();
+ var root = new LayoutTestRoot { Child = control };
+ var count = 0;
+
+ root.LayoutManager.ExecuteInitialLayoutPass(root);
+ control.Measured = false;
+
+ control.DoMeasureOverride = (l, s) =>
+ {
+ if (count++ == 0)
+ {
+ control.InvalidateMeasure();
+ root.LayoutManager.ExecuteLayoutPass();
+ return new Size(100, 100);
+ }
+ else
+ {
+ return new Size(200, 200);
+ }
+ };
+
+ root.InvalidateMeasure();
+ control.InvalidateMeasure();
+ root.LayoutManager.ExecuteInitialLayoutPass(root);
+
+ Assert.Equal(new Size(200, 200), control.Bounds.Size);
+ Assert.Equal(new Size(200, 200), control.DesiredSize);
+ }
}
}
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 389b3c8df8..3b288dbf66 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -8,8 +8,10 @@ using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Diagnostics;
using Avalonia.Layout;
+using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
+using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using JetBrains.dotMemoryUnit;
@@ -370,6 +372,56 @@ namespace Avalonia.LeakTests
}
}
+ [Fact]
+ public void Control_With_Style_RenderTransform_Is_Freed()
+ {
+ // # Issue #3545
+ using (Start())
+ {
+ Func run = () =>
+ {
+ var window = new Window
+ {
+ Styles =
+ {
+ new Style(x => x.OfType