Browse Source

Merge pull request #9411 from AvaloniaUI/refactor/styles

Refactor style apply
pull/9528/head
Max Katz 3 years ago
committed by GitHub
parent
commit
9f9e99d645
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  2. 129
      src/Avalonia.Base/StyledElement.cs
  3. 27
      src/Avalonia.Base/Styling/ControlTheme.cs
  4. 10
      src/Avalonia.Base/Styling/IStyle.cs
  5. 5
      src/Avalonia.Base/Styling/IStyleable.cs
  6. 8
      src/Avalonia.Base/Styling/IStyler.cs
  7. 54
      src/Avalonia.Base/Styling/Style.cs
  8. 30
      src/Avalonia.Base/Styling/StyleBase.cs
  9. 58
      src/Avalonia.Base/Styling/StyleCache.cs
  10. 35
      src/Avalonia.Base/Styling/Styler.cs
  11. 47
      src/Avalonia.Base/Styling/Styles.cs
  12. 2
      src/Avalonia.Controls/Application.cs
  13. 3
      src/Avalonia.Controls/TopLevel.cs
  14. 2
      src/Avalonia.Controls/Window.cs
  15. 1
      src/Avalonia.Controls/WindowBase.cs
  16. 2
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  17. 2
      tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs
  18. 194
      tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs
  19. 78
      tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs
  20. 21
      tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs
  21. 1
      tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs
  22. 47
      tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs
  23. 126
      tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs
  24. 1
      tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs
  25. 1
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
  26. 63
      tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
  27. 10
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  28. 4
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  29. 3
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
  30. 1
      tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs
  31. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
  32. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  33. 12
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  34. 249
      tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  35. 28
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  36. 8
      tests/Avalonia.Controls.UnitTests/ToolTipTests.cs
  37. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  38. 25
      tests/Avalonia.Controls.UnitTests/UserControlTests.cs
  39. 21
      tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
  40. 6
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  41. 7
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  42. 14
      tests/Avalonia.UnitTests/TestServices.cs
  43. 5
      tests/Avalonia.UnitTests/UnitTestApplication.cs

3
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -610,8 +610,7 @@ namespace Avalonia.PropertyStore
private int InsertFrame(ValueFrame frame)
{
// Uncomment this line when #8549 is fixed.
//Debug.Assert(!_frames.Contains(frame));
Debug.Assert(!_frames.Contains(frame));
var index = BinarySearchFrame(frame.Priority);
_frames.Insert(index, frame);

129
src/Avalonia.Base/StyledElement.cs

@ -344,29 +344,30 @@ namespace Avalonia
/// Applies styling to the control if the control is initialized and styling is not
/// already applied.
/// </summary>
/// <remarks>
/// The styling system will automatically apply styling when required, so it should not
/// usually be necessary to call this method manually.
/// </remarks>
/// <returns>
/// A value indicating whether styling is now applied to the control.
/// </returns>
protected bool ApplyStyling()
public bool ApplyStyling()
{
if (_initCount == 0 && !_styled)
{
var styler = AvaloniaLocator.Current.GetService<IStyler>();
var hasPromotedTheme = _hasPromotedTheme;
if (styler is object)
{
GetValueStore().BeginStyling();
GetValueStore().BeginStyling();
try
{
styler.ApplyStyles(this);
}
finally
{
_styled = true;
GetValueStore().EndStyling();
}
try
{
ApplyControlTheme();
ApplyStyles(this);
}
finally
{
_styled = true;
GetValueStore().EndStyling();
}
if (hasPromotedTheme)
@ -505,31 +506,6 @@ namespace Avalonia
};
}
ControlTheme? IStyleable.GetEffectiveTheme()
{
var theme = Theme;
// Explitly set Theme property takes precedence.
if (theme is not null)
return theme;
// If the Theme property is not set, try to find a ControlTheme resource with our StyleKey.
if (_implicitTheme is null)
{
var key = ((IStyleable)this).StyleKey;
if (this.TryFindResource(key, out var value) && value is ControlTheme t)
_implicitTheme = t;
else
_implicitTheme = s_invalidTheme;
}
if (_implicitTheme != s_invalidTheme)
return _implicitTheme;
return null;
}
void IStyleable.DetachStyles() => DetachStyles();
void IStyleHost.StylesAdded(IReadOnlyList<IStyle> styles)
@ -666,6 +642,31 @@ namespace Avalonia
{
}
internal ControlTheme? GetEffectiveTheme()
{
var theme = Theme;
// Explitly set Theme property takes precedence.
if (theme is not null)
return theme;
// If the Theme property is not set, try to find a ControlTheme resource with our StyleKey.
if (_implicitTheme is null)
{
var key = ((IStyleable)this).StyleKey;
if (this.TryFindResource(key, out var value) && value is ControlTheme t)
_implicitTheme = t;
else
_implicitTheme = s_invalidTheme;
}
if (_implicitTheme != s_invalidTheme)
return _implicitTheme;
return null;
}
private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted)
{
if (o is StyledElement element)
@ -730,6 +731,56 @@ namespace Avalonia
}
}
private void ApplyControlTheme()
{
var theme = GetEffectiveTheme();
if (theme is not null)
ApplyControlTheme(theme);
if (TemplatedParent is StyledElement styleableParent &&
styleableParent.GetEffectiveTheme() is { } parentTheme)
{
ApplyControlTheme(parentTheme);
}
}
private void ApplyControlTheme(ControlTheme theme)
{
if (theme.BasedOn is ControlTheme basedOn)
ApplyControlTheme(basedOn);
theme.TryAttach(this, null);
if (theme.HasChildren)
{
foreach (var child in theme.Children)
ApplyStyle(child, null);
}
}
private void ApplyStyles(IStyleHost host)
{
var parent = host.StylingParent;
if (parent != null)
ApplyStyles(parent);
if (host.IsStylesInitialized)
{
foreach (var style in host.Styles)
ApplyStyle(style, host);
}
}
private void ApplyStyle(IStyle style, IStyleHost? host)
{
if (style is Style s)
s.TryAttach(this, host);
foreach (var child in style.Children)
ApplyStyle(child, host);
}
private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
{
if (this.GetLogicalParent() == null && !(this is ILogicalRoot))

27
src/Avalonia.Base/Styling/ControlTheme.cs

@ -29,34 +29,27 @@ namespace Avalonia.Styling
/// </summary>
public ControlTheme? BasedOn { get; set; }
public override SelectorMatchResult TryAttach(IStyleable target, object? host)
public override string ToString() => TargetType?.Name ?? "ControlTheme";
internal override void SetParent(StyleBase? parent)
{
throw new InvalidOperationException("ControlThemes cannot be added as a nested style.");
}
internal override SelectorMatchResult TryAttach(IStyleable target, object? host)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
if (TargetType is null)
throw new InvalidOperationException("ControlTheme has no TargetType.");
var result = BasedOn?.TryAttach(target, host) ?? SelectorMatchResult.NeverThisType;
if (HasSettersOrAnimations && TargetType.IsAssignableFrom(target.StyleKey))
{
Attach(target, null);
result = SelectorMatchResult.AlwaysThisType;
return SelectorMatchResult.AlwaysThisType;
}
var childResult = TryAttachChildren(target, host);
if (childResult > result)
result = childResult;
return result;
}
public override string ToString() => TargetType?.Name ?? "ControlTheme";
internal override void SetParent(StyleBase? parent)
{
throw new InvalidOperationException("ControlThemes cannot be added as a nested style.");
return SelectorMatchResult.NeverThisType;
}
}
}

10
src/Avalonia.Base/Styling/IStyle.cs

@ -14,15 +14,5 @@ namespace Avalonia.Styling
/// Gets a collection of child styles.
/// </summary>
IReadOnlyList<IStyle> Children { get; }
/// <summary>
/// Attaches the style and any child styles to a control if the style's selector matches.
/// </summary>
/// <param name="target">The control to attach to.</param>
/// <param name="host">The element that hosts the style.</param>
/// <returns>
/// A <see cref="SelectorMatchResult"/> describing how the style matches the control.
/// </returns>
SelectorMatchResult TryAttach(IStyleable target, object? host);
}
}

5
src/Avalonia.Base/Styling/IStyleable.cs

@ -25,11 +25,6 @@ namespace Avalonia.Styling
/// </summary>
ITemplatedControl? TemplatedParent { get; }
/// <summary>
/// Gets the effective theme for the control as used by the syling system.
/// </summary>
ControlTheme? GetEffectiveTheme();
void DetachStyles();
}
}

8
src/Avalonia.Base/Styling/IStyler.cs

@ -1,8 +0,0 @@
namespace Avalonia.Styling
{
public interface IStyler
{
void ApplyStyles(IStyleable control);
}
}

54
src/Avalonia.Base/Styling/Style.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.PropertyStore;
namespace Avalonia.Styling
{
@ -35,35 +34,6 @@ namespace Avalonia.Styling
set => _selector = ValidateSelector(value);
}
public override SelectorMatchResult TryAttach(IStyleable target, object? host)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
var result = SelectorMatchResult.NeverThisType;
if (HasSettersOrAnimations)
{
var match = Selector?.Match(target, Parent, true) ??
(target == host ?
SelectorMatch.AlwaysThisInstance :
SelectorMatch.NeverThisInstance);
if (match.IsMatch)
{
Attach(target, match.Activator);
}
result = match.Result;
}
var childResult = TryAttachChildren(target, host);
if (childResult > result)
result = childResult;
return result;
}
/// <summary>
/// Returns a string representation of the style.
/// </summary>
@ -88,6 +58,30 @@ namespace Avalonia.Styling
base.SetParent(parent);
}
internal override SelectorMatchResult TryAttach(IStyleable target, object? host)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
var result = SelectorMatchResult.NeverThisType;
if (HasSettersOrAnimations)
{
var match = Selector?.Match(target, Parent, true) ??
(target == host ?
SelectorMatch.AlwaysThisInstance :
SelectorMatch.NeverThisInstance);
if (match.IsMatch)
{
Attach(target, match.Activator);
}
result = match.Result;
}
return result;
}
private static Selector? ValidateSelector(Selector? selector)
{
if (selector is TemplateSelector)

30
src/Avalonia.Base/Styling/StyleBase.cs

@ -18,7 +18,6 @@ namespace Avalonia.Styling
private IResourceDictionary? _resources;
private List<ISetter>? _setters;
private List<IAnimation>? _animations;
private StyleCache? _childCache;
private StyleInstance? _sharedInstance;
public IList<IStyle> Children => _children ??= new(this);
@ -67,6 +66,7 @@ namespace Avalonia.Styling
bool IResourceNode.HasResources => _resources?.Count > 0;
IReadOnlyList<IStyle> IStyle.Children => (IReadOnlyList<IStyle>?)_children ?? Array.Empty<IStyle>();
internal bool HasChildren => _children?.Count > 0;
internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0;
public void Add(ISetter setter) => Setters.Add(setter);
@ -74,14 +74,26 @@ namespace Avalonia.Styling
public event EventHandler? OwnerChanged;
public abstract SelectorMatchResult TryAttach(IStyleable target, object? host);
public bool TryGetResource(object key, out object? result)
{
result = null;
return _resources?.TryGetResource(key, out result) ?? false;
if (_resources is not null && _resources.TryGetResource(key, out result))
return true;
if (_children is not null)
{
for (var i = 0; i < _children.Count; ++i)
{
if (_children[i].TryGetResource(key, out result))
return true;
}
}
result= null;
return false;
}
internal abstract SelectorMatchResult TryAttach(IStyleable target, object? host);
internal ValueFrame Attach(IStyleable target, IStyleActivator? activator)
{
if (target is not AvaloniaObject ao)
@ -124,14 +136,6 @@ namespace Avalonia.Styling
return instance;
}
internal SelectorMatchResult TryAttachChildren(IStyleable target, object? host)
{
if (_children is null || _children.Count == 0)
return SelectorMatchResult.NeverThisType;
_childCache ??= new StyleCache();
return _childCache.TryAttach(_children, target, host);
}
internal virtual void SetParent(StyleBase? parent) => Parent = parent;
void IResourceProvider.AddOwner(IResourceHost owner)

58
src/Avalonia.Base/Styling/StyleCache.cs

@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
namespace Avalonia.Styling
{
/// <summary>
/// Simple cache for improving performance of applying styles.
/// </summary>
/// <remarks>
/// Maps <see cref="IStyleable.StyleKey"/> to a list of styles that are known be be possible
/// matches.
/// </remarks>
internal class StyleCache : Dictionary<Type, List<IStyle>?>
{
public SelectorMatchResult TryAttach(IList<IStyle> styles, IStyleable target, object? host)
{
if (TryGetValue(target.StyleKey, out var cached))
{
if (cached is object)
{
var result = SelectorMatchResult.NeverThisType;
foreach (var style in cached)
{
var childResult = style.TryAttach(target, host);
if (childResult > result)
result = childResult;
}
return result;
}
else
{
return SelectorMatchResult.NeverThisType;
}
}
else
{
List<IStyle>? matches = null;
foreach (var child in styles)
{
if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType)
{
matches ??= new List<IStyle>();
matches.Add(child);
}
}
Add(target.StyleKey, matches);
return matches is null ?
SelectorMatchResult.NeverThisType :
SelectorMatchResult.AlwaysThisType;
}
}
}
}

35
src/Avalonia.Base/Styling/Styler.cs

@ -1,35 +0,0 @@
using System;
namespace Avalonia.Styling
{
public class Styler : IStyler
{
public void ApplyStyles(IStyleable target)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
// Apply the control theme.
target.GetEffectiveTheme()?.TryAttach(target, target);
// If the control has a themed templated parent then apply the styles from the
// templated parent theme.
if (target.TemplatedParent is IStyleable styleableParent)
styleableParent.GetEffectiveTheme()?.TryAttach(target, styleableParent);
// Apply styles from the rest of the tree.
if (target is IStyleHost styleHost)
ApplyStyles(target, styleHost);
}
private void ApplyStyles(IStyleable target, IStyleHost host)
{
var parent = host.StylingParent;
if (parent != null)
ApplyStyles(target, parent);
if (host.IsStylesInitialized)
host.Styles.TryAttach(target, host);
}
}
}

47
src/Avalonia.Base/Styling/Styles.cs

@ -5,8 +5,6 @@ using System.Collections.Specialized;
using Avalonia.Collections;
using Avalonia.Controls;
#nullable enable
namespace Avalonia.Styling
{
/// <summary>
@ -20,7 +18,6 @@ namespace Avalonia.Styling
private readonly AvaloniaList<IStyle> _styles = new();
private IResourceHost? _owner;
private IResourceDictionary? _resources;
private StyleCache? _cache;
public Styles()
{
@ -116,12 +113,6 @@ namespace Avalonia.Styling
set => _styles[index] = value;
}
public SelectorMatchResult TryAttach(IStyleable target, object? host)
{
_cache ??= new StyleCache();
return _cache.TryAttach(this, target, host);
}
/// <inheritdoc/>
public bool TryGetResource(object key, out object? value)
{
@ -234,6 +225,22 @@ namespace Avalonia.Styling
}
}
internal SelectorMatchResult TryAttach(IStyleable target, object? host)
{
var result = SelectorMatchResult.NeverThisType;
foreach (var s in this)
{
if (s is not Style style)
continue;
var r = style.TryAttach(target, host);
if (r > result)
result = r;
}
return result;
}
private static IReadOnlyList<T> ToReadOnlyList<T>(ICollection list)
{
if (list is IReadOnlyList<T> readOnlyList)
@ -246,7 +253,7 @@ namespace Avalonia.Styling
return result;
}
private static void InternalAdd(IList items, IResourceHost? owner, ref StyleCache? cache)
private static void InternalAdd(IList items, IResourceHost? owner)
{
if (owner is not null)
{
@ -260,14 +267,9 @@ namespace Avalonia.Styling
(owner as IStyleHost)?.StylesAdded(ToReadOnlyList<IStyle>(items));
}
if (items.Count > 0)
{
cache = null;
}
}
private static void InternalRemove(IList items, IResourceHost? owner, ref StyleCache? cache)
private static void InternalRemove(IList items, IResourceHost? owner)
{
if (owner is not null)
{
@ -281,11 +283,6 @@ namespace Avalonia.Styling
(owner as IStyleHost)?.StylesRemoved(ToReadOnlyList<IStyle>(items));
}
if (items.Count > 0)
{
cache = null;
}
}
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
@ -300,14 +297,14 @@ namespace Avalonia.Styling
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
InternalAdd(e.NewItems!, currentOwner, ref _cache);
InternalAdd(e.NewItems!, currentOwner);
break;
case NotifyCollectionChangedAction.Remove:
InternalRemove(e.OldItems!, currentOwner, ref _cache);
InternalRemove(e.OldItems!, currentOwner);
break;
case NotifyCollectionChangedAction.Replace:
InternalRemove(e.OldItems!, currentOwner, ref _cache);
InternalAdd(e.NewItems!, currentOwner, ref _cache);
InternalRemove(e.OldItems!, currentOwner);
InternalAdd(e.NewItems!, currentOwner);
break;
}

2
src/Avalonia.Controls/Application.cs

@ -39,7 +39,6 @@ namespace Avalonia
private readonly Lazy<IClipboard?> _clipboard =
new Lazy<IClipboard?>(() => (IClipboard?)AvaloniaLocator.Current.GetService(typeof(IClipboard)));
private readonly Styler _styler = new Styler();
private Styles? _styles;
private IResourceDictionary? _resources;
private bool _notifyingResourcesChanged;
@ -232,7 +231,6 @@ namespace Avalonia
.Bind<IFocusManager>().ToConstant(FocusManager)
.Bind<IInputManager>().ToConstant(InputManager)
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IStyler>().ToConstant(_styler)
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);

3
src/Avalonia.Controls/TopLevel.cs

@ -145,7 +145,6 @@ namespace Avalonia.Controls
_actualTransparencyLevel = PlatformImpl.TransparencyLevel;
dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
var styler = TryGetService<IStyler>(dependencyResolver);
_accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
_inputManager = TryGetService<IInputManager>(dependencyResolver);
@ -183,8 +182,6 @@ namespace Avalonia.Controls
_globalStyles.GlobalStylesRemoved += ((IStyleHost)this).StylesRemoved;
}
styler?.ApplyStyles(this);
ClientSize = impl.ClientSize;
FrameSize = impl.FrameSize;

2
src/Avalonia.Controls/Window.cs

@ -647,6 +647,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs(WindowOpenedEvent));
EnsureInitialized();
ApplyStyling();
IsVisible = true;
var initialSize = new Size(
@ -726,6 +727,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs(WindowOpenedEvent));
EnsureInitialized();
ApplyStyling();
IsVisible = true;
var initialSize = new Size(

1
src/Avalonia.Controls/WindowBase.cs

@ -149,6 +149,7 @@ namespace Avalonia.Controls
try
{
EnsureInitialized();
ApplyStyling();
IsVisible = true;
if (!_hasExecutedInitialLayoutPass)

2
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -82,8 +82,6 @@ namespace Avalonia.Markup.Xaml.Styling
}
}
public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host);
public bool TryGetResource(object key, out object? value)
{
if (!_isLoading)

2
tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs

@ -498,7 +498,7 @@ namespace Avalonia.Base.UnitTests.Animation
private static IDisposable Start()
{
var clock = new MockGlobalClock();
var services = TestServices.RealStyler.With(globalClock: clock);
var services = new TestServices(globalClock: clock);
return UnitTestApplication.Start(services);
}

194
tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs

@ -5,7 +5,6 @@ using Avalonia.Base.UnitTests.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
@ -301,8 +300,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Attach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -337,8 +334,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Bindings_Should_Not_Be_Made_Active_During_Style_Attach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -380,8 +375,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Detach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -417,8 +410,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Detach_2()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -454,8 +445,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Bindings_Should_Not_Be_Made_Active_During_Style_Detach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -598,41 +587,36 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Removing_Style_Should_Detach_From_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
{
Styles =
{
new Style(x => x.OfType<Border>())
Styles =
{
new Style(x => x.OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
},
Child = border,
};
}
},
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
[Fact]
public void Adding_Style_Should_Attach_To_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
{
Styles =
Styles =
{
new Style(x => x.OfType<Border>())
{
@ -642,34 +626,31 @@ namespace Avalonia.Base.UnitTests.Styling
}
}
},
Child = border,
};
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Styles.Add(new Style(x => x.OfType<Border>())
{
Setters =
root.Styles.Add(new Style(x => x.OfType<Border>())
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
});
});
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
[Fact]
public void Removing_Style_With_Nested_Style_Should_Detach_From_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
{
Styles =
Styles =
{
new Styles
{
@ -682,96 +663,89 @@ namespace Avalonia.Base.UnitTests.Styling
}
}
},
Child = border,
};
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
[Fact]
public void Adding_Nested_Style_Should_Attach_To_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
Styles =
{
Styles =
new Styles
{
new Styles
new Style(x => x.OfType<Border>())
{
new Style(x => x.OfType<Border>())
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
}
},
Child = border,
};
}
},
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
((Styles)root.Styles[0]).Add(new Style(x => x.OfType<Border>())
((Styles)root.Styles[0]).Add(new Style(x => x.OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
});
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
});
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
[Fact]
public void Removing_Nested_Style_Should_Detach_From_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
Styles =
{
Styles =
new Styles
{
new Styles
new Style(x => x.OfType<Border>())
{
new Style(x => x.OfType<Border>())
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
},
new Style(x => x.OfType<Border>())
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
},
new Style(x => x.OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
},
}
},
Child = border,
};
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
},
}
},
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
((Styles)root.Styles[0]).RemoveAt(1);
((Styles)root.Styles[0]).RemoveAt(1);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
}
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
}
[Fact]

78
tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs

@ -249,65 +249,67 @@ namespace Avalonia.Base.UnitTests.Styling
}
[Fact]
public void Adding_Tree_To_IStyleRoot_Should_Style_Controls()
public void Adding_Tree_To_Root_Should_Style_Controls()
{
using (AvaloniaLocator.EnterScope())
var root = new TestRoot
{
var root = new TestRoot();
var parent = new Border();
var child = new Border();
var grandchild = new Control();
var styler = new Mock<IStyler>();
AvaloniaLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
Styles =
{
new Style(x => x.Is<Control>())
{
Setters = { new Setter(Control.TagProperty, "foo") }
}
}
};
parent.Child = child;
child.Child = grandchild;
var grandchild = new Control();
var child = new Border { Child = grandchild };
var parent = new Border { Child = child };
styler.Verify(x => x.ApplyStyles(It.IsAny<IStyleable>()), Times.Never());
Assert.Null(parent.Tag);
Assert.Null(child.Tag);
Assert.Null(grandchild.Tag);
root.Child = parent;
root.Child = parent;
styler.Verify(x => x.ApplyStyles(parent), Times.Once());
styler.Verify(x => x.ApplyStyles(child), Times.Once());
styler.Verify(x => x.ApplyStyles(grandchild), Times.Once());
}
Assert.Equal("foo", parent.Tag);
Assert.Equal("foo", child.Tag);
Assert.Equal("foo", grandchild.Tag);
}
[Fact]
public void Styles_Not_Applied_Until_Initialization_Finished()
{
using (AvaloniaLocator.EnterScope())
var root = new TestRoot
{
var root = new TestRoot();
var child = new Border();
var styler = new Mock<IStyler>();
Styles =
{
new Style(x => x.Is<Control>())
{
Setters = { new Setter(Control.TagProperty, "foo") }
}
}
};
AvaloniaLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
var child = new Border();
((ISupportInitialize)child).BeginInit();
root.Child = child;
styler.Verify(x => x.ApplyStyles(It.IsAny<IStyleable>()), Times.Never());
((ISupportInitialize)child).BeginInit();
root.Child = child;
Assert.Null(child.Tag);
((ISupportInitialize)child).EndInit();
styler.Verify(x => x.ApplyStyles(child), Times.Once());
}
((ISupportInitialize)child).EndInit();
Assert.Equal("foo", child.Tag);
}
[Fact]
public void Name_Cannot_Be_Set_After_Added_To_Logical_Tree()
{
using (AvaloniaLocator.EnterScope())
{
var root = new TestRoot();
var child = new Border();
AvaloniaLocator.CurrentMutable.BindToSelf<IStyler>(new Styler());
var root = new TestRoot();
var child = new Border();
root.Child = child;
root.Child = child;
Assert.Throws<InvalidOperationException>(() => child.Name = "foo");
}
Assert.Throws<InvalidOperationException>(() => child.Name = "foo");
}
[Fact]
@ -328,7 +330,7 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Style_Is_Removed_When_Control_Removed_From_Logical_Tree()
{
var app = UnitTestApplication.Start(TestServices.RealStyler);
var app = UnitTestApplication.Start();
var target = new Border();
var root = new TestRoot
{

21
tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs

@ -19,7 +19,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
Assert.Null(target.Template);
@ -37,7 +36,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_To_Derived_Class_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = new DerivedThemedControl
{
Theme = CreateTheme(),
@ -58,7 +56,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Detached_When_Theme_Property_Cleared()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var root = CreateRoot(target);
@ -71,8 +68,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Detached_From_Template_Controls_When_Theme_Property_Cleared()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var theme = new ControlTheme
{
TargetType = typeof(ThemedControl),
@ -105,7 +100,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = new ThemedControl();
var root = CreateRoot(target);
@ -124,7 +118,6 @@ public class StyledElementTests_Theming
[Fact]
public void BasedOn_Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget(CreateDerivedTheme());
Assert.Null(target.Template);
@ -163,7 +156,6 @@ public class StyledElementTests_Theming
[Fact]
public void Implicit_Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var root = CreateRoot(target);
Assert.NotNull(target.Template);
@ -178,21 +170,19 @@ public class StyledElementTests_Theming
[Fact]
public void Implicit_Theme_Is_Cleared_When_Removed_From_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var root = CreateRoot(target);
Assert.NotNull(((IStyleable)target).GetEffectiveTheme());
Assert.NotNull(target.GetEffectiveTheme());
root.Child = null;
Assert.Null(((IStyleable)target).GetEffectiveTheme());
Assert.Null(target.GetEffectiveTheme());
}
[Fact]
public void Nested_Style_Can_Override_Property_In_Inner_Templated_Control()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = new ThemedControl2
{
Theme = new ControlTheme(typeof(ThemedControl2))
@ -236,7 +226,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
Assert.Null(target.Theme);
@ -248,16 +237,15 @@ public class StyledElementTests_Theming
Assert.NotNull(target.Template);
var border = Assert.IsType<Border>(target.VisualChild);
Assert.Equal(border.Background, Brushes.Red);
Assert.Equal(Brushes.Red, border.Background);
target.Classes.Add("foo");
Assert.Equal(border.Background, Brushes.Green);
Assert.Equal(Brushes.Green, border.Background);
}
[Fact]
public void Theme_Can_Be_Changed_By_Style_Class()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var theme1 = CreateTheme();
var theme2 = new ControlTheme(typeof(ThemedControl));
@ -290,7 +278,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Can_Be_Set_To_LocalValue_While_Updating_Due_To_Style_Class()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var theme1 = CreateTheme();
var theme2 = new ControlTheme(typeof(ThemedControl));

1
tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs

@ -22,7 +22,6 @@ namespace Avalonia.Benchmarks.Styling
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
styler: new Styler(),
theme: () => CreateTheme(),
threadingInterface: new NullThreadingPlatform(),
fontManagerImpl: new MockFontManagerImpl(),

47
tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs

@ -1,47 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Styling;
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.TryAttach(_control, UnitTestApplication.Current);
((IStyleable)_control).DetachStyles();
}
public void Dispose()
{
_app.Dispose();
}
}
}

126
tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Styling
{
[MemoryDiagnoser]
public class Style_Apply_Detach_Complex : IDisposable
{
private readonly IDisposable _app;
private readonly TestRoot _root;
private readonly TextBox _control;
public Style_Apply_Detach_Complex()
{
_app = UnitTestApplication.Start(
TestServices.StyledWindow.With(
renderInterface: new NullRenderingPlatform(),
threadingInterface: new NullThreadingPlatform()));
// Simulate an application with a lot of styles by creating a tree of nested panels,
// each with a bunch of styles applied.
var (rootPanel, leafPanel) = CreateNestedPanels(10);
// We're benchmarking how long it takes to apply styles to a TextBox in this situation.
_control = new TextBox();
leafPanel.Children.Add(_control);
_root = new TestRoot(true, rootPanel)
{
Renderer = new NullRenderer(),
};
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining)]
public void Apply_Detach_Styles()
{
// Styles will have already been attached when attached to the logical tree, so remove
// the styles first.
if ((string)_control.Tag != "TextBox")
throw new Exception("Invalid benchmark state");
((IStyleable)_control).DetachStyles();
if (_control.Tag is not null)
throw new Exception("Invalid benchmark state");
// Then re-apply the styles.
_control.ApplyStyling();
}
public void Dispose()
{
_app.Dispose();
}
private static (Panel, Panel) CreateNestedPanels(int count)
{
var root = new Panel();
var last = root;
for (var i = 0; i < count; ++i)
{
var panel = new Panel();
panel.Styles.AddRange(CreateStyles());
last.Children.Add(panel);
last = panel;
}
return (root, last);
}
private static IEnumerable<IStyle> CreateStyles()
{
var types = new[]
{
typeof(Border),
typeof(Button),
typeof(ButtonSpinner),
typeof(Carousel),
typeof(CheckBox),
typeof(ComboBox),
typeof(ContentControl),
typeof(Expander),
typeof(ItemsControl),
typeof(Label),
typeof(ListBox),
typeof(ProgressBar),
typeof(RadioButton),
typeof(RepeatButton),
typeof(ScrollViewer),
typeof(Slider),
typeof(Spinner),
typeof(SplitView),
typeof(TextBox),
typeof(ToggleSwitch),
typeof(TreeView),
typeof(Viewbox),
typeof(Window),
};
foreach (var type in types)
{
yield return new Style(x => x.OfType(type))
{
Setters = { new Setter(Control.TagProperty, type.Name) }
};
yield return new Style(x => x.OfType(type).Class("foo"))
{
Setters = { new Setter(Control.TagProperty, type.Name + " foo") }
};
yield return new Style(x => x.OfType(type).Class("bar"))
{
Setters = { new Setter(Control.TagProperty, type.Name + " bar") }
};
}
}
}
}

1
tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs

@ -46,7 +46,6 @@ namespace Avalonia.Benchmarks.Themes
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
styler: new Styler(),
theme: () => LoadFluentTheme(),
threadingInterface: new NullThreadingPlatform(),
fontManagerImpl: new MockFontManagerImpl(),

1
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@ -1056,6 +1056,7 @@ namespace Avalonia.Controls.UnitTests
control.Items = CreateSimpleStringArray();
TextBox textBox = GetTextBox(control);
var window = new Window {Content = control};
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
Dispatcher.UIThread.RunJobs();

63
tests/Avalonia.Controls.UnitTests/ContentControlTests.cs

@ -37,11 +37,19 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Templated_Children_Should_Be_Styled()
{
var root = new TestRoot();
var root = new TestRoot
{
Styles =
{
new Style(x => x.Is<Control>())
{
Setters = { new Setter(Control.TagProperty, "foo") }
}
}
};
var target = new ContentControl();
var styler = new Mock<IStyler>();
AvaloniaLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
target.Content = "Foo";
target.Template = GetTemplate();
root.Child = target;
@ -49,10 +57,8 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
styler.Verify(x => x.ApplyStyles(It.IsAny<ContentControl>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<Border>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<ContentPresenter>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
foreach (Control child in target.GetTemplateChildren())
Assert.Equal("foo", child.Tag);
}
[Fact]
@ -332,40 +338,37 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Set_Child_LogicalParent_After_Removing_And_Adding_Back_To_Logical_Tree()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var target = new ContentControl();
var root = new TestRoot
{
var target = new ContentControl();
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<ContentControl>())
{
new Style(x => x.OfType<ContentControl>())
Setters =
{
Setters =
{
new Setter(ContentControl.TemplateProperty, GetTemplate()),
}
new Setter(ContentControl.TemplateProperty, GetTemplate()),
}
},
Child = target
};
}
},
Child = target
};
target.Content = "Foo";
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.Content = "Foo";
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Equal(target, target.Presenter.Child.LogicalParent);
Assert.Equal(target, target.Presenter.Child.LogicalParent);
root.Child = null;
root.Child = null;
Assert.Null(target.Template);
Assert.Null(target.Template);
target.Content = null;
root.Child = target;
target.Content = "Bar";
target.Content = null;
root.Child = target;
target.Content = "Bar";
Assert.Equal(target, target.Presenter.Child.LogicalParent);
}
Assert.Equal(target, target.Presenter.Child.LogicalParent);
}
private FuncControlTemplate GetTemplate()

10
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@ -29,6 +29,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -61,6 +62,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -130,6 +132,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -158,6 +161,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -186,6 +190,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -207,6 +212,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -390,7 +396,8 @@ namespace Avalonia.Controls.UnitTests
var sp = new StackPanel { Children = { target1, target2 } };
var window = new Window { Content = sp };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -594,6 +601,7 @@ namespace Avalonia.Controls.UnitTests
windowImpl.Setup(x => x.CreateRenderer(It.IsAny<IRenderRoot>())).Returns(renderer.Object);
var w = new Window(windowImpl.Object) { Content = content };
w.ApplyStyling();
w.ApplyTemplate();
w.Presenter.ApplyTemplate();
return w;

4
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { "Foo", "Bar", "Baz " };
var theme = new ControlTheme();
var theme = new ControlTheme(typeof(ListBoxItem));
var target = new ListBox
{
Template = ListBoxTemplate(),
@ -121,7 +121,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { "Foo", "Bar", "Baz " };
var theme = new ControlTheme();
var theme = new ControlTheme(typeof(ListBoxItem));
var target = new ListBox
{
Template = ListBoxTemplate(),

3
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -193,6 +193,7 @@ namespace Avalonia.Controls.UnitTests
var target = new MenuItem();
var contextMenu = new ContextMenu { Items = new AvaloniaList<MenuItem> { target } };
var window = new Window { Content = new Panel { ContextMenu = contextMenu } };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -232,6 +233,7 @@ namespace Avalonia.Controls.UnitTests
var flyout = new MenuFlyout { Items = new AvaloniaList<MenuItem> { target } };
var button = new Button { Flyout = flyout };
var window = new Window { Content = button };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -271,6 +273,7 @@ namespace Avalonia.Controls.UnitTests
var parentMenuItem = new MenuItem { Items = new AvaloniaList<MenuItem> { target } };
var contextMenu = new ContextMenu { Items = new AvaloniaList<MenuItem> { parentMenuItem } };
var window = new Window { Content = new Panel { ContextMenu = contextMenu } };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
contextMenu.Open();

1
tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs

@ -50,6 +50,7 @@ namespace Avalonia.Controls.UnitTests
var control = CreateControl();
TextBox textBox = GetTextBox(control);
var window = new Window { Content = control };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
Dispatcher.UIThread.RunJobs();

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -51,6 +51,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
target.ApplyTemplate();
@ -177,6 +178,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
target.ApplyTemplate();

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -569,6 +569,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
windowImpl.Setup(x => x.CreateRenderer(It.IsAny<IRenderRoot>())).Returns(renderer.Object);
var window = new Window(windowImpl.Object);
window.ApplyStyling();
window.ApplyTemplate();
var target = new Popup()
@ -1090,6 +1091,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
private Window PreparedWindow(object content = null)
{
var w = new Window { Content = content };
w.ApplyStyling();
w.ApplyTemplate();
return w;
}

12
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -998,6 +998,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Order_Of_Setting_Items_And_SelectedIndex_During_Initialization_Should_Not_Matter()
{
using var app = Start();
var items = new[] { "Foo", "Bar" };
var target = new SelectingItemsControl();
@ -1015,6 +1016,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Order_Of_Setting_Items_And_SelectedItem_During_Initialization_Should_Not_Matter()
{
using var app = Start();
var items = new[] { "Foo", "Bar" };
var target = new SelectingItemsControl();
@ -1847,6 +1849,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
public void Preserves_SelectedItem_When_Items_Changed()
{
// Issue #4048
using var app = Start();
var target = new SelectingItemsControl
{
Items = new[] { "foo", "bar", "baz"},
@ -1867,6 +1870,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Setting_SelectedItems_Raises_PropertyChanged()
{
using var app = Start();
var target = new TestSelector
{
Items = new[] { "foo", "bar", "baz" },
@ -1895,6 +1899,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Setting_Selection_Raises_SelectedItems_PropertyChanged()
{
using var app = Start();
var target = new TestSelector
{
Items = new[] { "foo", "bar", "baz" },
@ -2050,6 +2055,13 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
private static IDisposable Start()
{
return UnitTestApplication.Start(new TestServices(
fontManagerImpl: new MockFontManagerImpl(),
textShaperImpl: new MockTextShaperImpl()));
}
private static void Prepare(SelectingItemsControl target)
{
var root = new TestRoot

249
tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@ -170,36 +170,41 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Templated_Children_Should_Be_Styled()
{
using (UnitTestApplication.Start(TestServices.MockStyler))
{
TestTemplatedControl target;
TestTemplatedControl target;
var root = new TestRoot
var root = new TestRoot
{
Styles =
{
Child = target = new TestTemplatedControl
new Style(x => x.Is<Control>())
{
Template = new FuncControlTemplate((_, __) =>
Setters =
{
return new StackPanel
{
Children =
new Setter(Control.TagProperty, "foo")
}
}
},
Child = target = new TestTemplatedControl
{
Template = new FuncControlTemplate((_, __) =>
{
return new StackPanel
{
Children =
{
new TextBlock
{
}
}
};
}),
}
};
};
}),
}
};
target.ApplyTemplate();
target.ApplyTemplate();
var styler = Mock.Get(UnitTestApplication.Current.Services.Styler);
styler.Verify(x => x.ApplyStyles(It.IsAny<TestTemplatedControl>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<StackPanel>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
}
foreach (Control child in target.GetTemplateChildren())
Assert.Equal("foo", child.Tag);
}
[Fact]
@ -351,166 +356,154 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Removing_From_LogicalTree_Should_Not_Remove_Child()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
Border templateChild = new Border();
TestTemplatedControl target;
var root = new TestRoot
{
Border templateChild = new Border();
TestTemplatedControl target;
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
Child = target = new TestTemplatedControl()
};
}
},
Child = target = new TestTemplatedControl()
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
Assert.NotNull(target.Template);
target.ApplyTemplate();
root.Child = null;
root.Child = null;
Assert.Null(target.Template);
Assert.IsType<Decorator>(target.GetVisualChildren().Single());
}
Assert.Null(target.Template);
Assert.IsType<Decorator>(target.GetVisualChildren().Single());
}
[Fact]
public void Re_adding_To_Same_LogicalTree_Should_Not_Recreate_Template()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
TestTemplatedControl target;
var root = new TestRoot
{
TestTemplatedControl target;
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
Child = target = new TestTemplatedControl()
};
}
},
Child = target = new TestTemplatedControl()
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
var expected = (Decorator)target.GetVisualChildren().Single();
Assert.NotNull(target.Template);
target.ApplyTemplate();
var expected = (Decorator)target.GetVisualChildren().Single();
root.Child = null;
root.Child = target;
target.ApplyTemplate();
root.Child = null;
root.Child = target;
target.ApplyTemplate();
Assert.Same(expected, target.GetVisualChildren().Single());
}
Assert.Same(expected, target.GetVisualChildren().Single());
}
[Fact]
public void Re_adding_To_Different_LogicalTree_Should_Recreate_Template()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
{
TestTemplatedControl target;
TestTemplatedControl target;
var root = new TestRoot
var root = new TestRoot
{
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
Child = target = new TestTemplatedControl()
};
}
},
Child = target = new TestTemplatedControl()
};
var root2 = new TestRoot
var root2 = new TestRoot
{
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
};
}
},
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
Assert.NotNull(target.Template);
target.ApplyTemplate();
var expected = (Decorator)target.GetVisualChildren().Single();
var expected = (Decorator)target.GetVisualChildren().Single();
root.Child = null;
root2.Child = target;
target.ApplyTemplate();
root.Child = null;
root2.Child = target;
target.ApplyTemplate();
var child = target.GetVisualChildren().Single();
Assert.NotNull(target.Template);
Assert.NotNull(child);
Assert.NotSame(expected, child);
}
var child = target.GetVisualChildren().Single();
Assert.NotNull(target.Template);
Assert.NotNull(child);
Assert.NotSame(expected, child);
}
[Fact]
public void Moving_To_New_LogicalTree_Should_Detach_Attach_Template_Child()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
TestTemplatedControl target;
var root = new TestRoot
{
TestTemplatedControl target;
var root = new TestRoot
Child = target = new TestTemplatedControl
{
Child = target = new TestTemplatedControl
{
Template = new FuncControlTemplate((_, __) => new Decorator()),
}
};
Template = new FuncControlTemplate((_, __) => new Decorator()),
}
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
Assert.NotNull(target.Template);
target.ApplyTemplate();
var templateChild = (ILogical)target.GetVisualChildren().Single();
Assert.True(templateChild.IsAttachedToLogicalTree);
var templateChild = (ILogical)target.GetVisualChildren().Single();
Assert.True(templateChild.IsAttachedToLogicalTree);
root.Child = null;
Assert.False(templateChild.IsAttachedToLogicalTree);
root.Child = null;
Assert.False(templateChild.IsAttachedToLogicalTree);
var newRoot = new TestRoot { Child = target };
Assert.True(templateChild.IsAttachedToLogicalTree);
}
var newRoot = new TestRoot { Child = target };
Assert.True(templateChild.IsAttachedToLogicalTree);
}
[Fact]

28
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -227,28 +227,24 @@ namespace Avalonia.Controls.UnitTests
};
var template = new FuncControlTemplate<TabItem>((x, __) => new Decorator());
using (UnitTestApplication.Start(TestServices.RealStyler))
var root = new TestRoot
{
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<TabItem>())
{
new Style(x => x.OfType<TabItem>())
Setters =
{
Setters =
{
new Setter(TemplatedControl.TemplateProperty, template)
}
new Setter(TemplatedControl.TemplateProperty, template)
}
},
Child = new TabControl
{
Template = TabControlTemplate(),
Items = collection,
}
};
}
},
Child = new TabControl
{
Template = TabControlTemplate(),
Items = collection,
}
};
Assert.Same(collection[0].Template, template);
Assert.Same(collection[1].Template, template);

8
tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

@ -51,6 +51,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = panel;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -114,6 +115,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -140,6 +142,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -183,6 +186,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -215,6 +219,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = decorator;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -237,6 +242,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = decorator;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -261,6 +267,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = decorator;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -286,6 +293,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();

2
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls.UnitTests
public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present()
{
TreeView target;
var theme = new ControlTheme();
var theme = new ControlTheme(typeof(TreeViewItem));
var root = new TestRoot
{

25
tests/Avalonia.Controls.UnitTests/UserControlTests.cs

@ -13,26 +13,23 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Be_Styled_As_UserControl()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var target = new UserControl();
var root = new TestRoot
{
var target = new UserControl();
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<UserControl>())
{
new Style(x => x.OfType<UserControl>())
Setters =
{
Setters =
{
new Setter(TemplatedControl.TemplateProperty, GetTemplate())
}
new Setter(TemplatedControl.TemplateProperty, GetTemplate())
}
},
Child = target,
};
}
},
Child = target,
};
Assert.NotNull(target.Template);
}
Assert.NotNull(target.Template);
}
private FuncControlTemplate GetTemplate()

21
tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs

@ -23,11 +23,8 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture1 = new KeyGesture(Key.A, KeyModifiers.Control);
var gesture2 = new KeyGesture(Key.B, KeyModifiers.Control);
@ -67,13 +64,11 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var commandResult = 0;
var expectedParameter = 1;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -112,12 +107,10 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var isExecuted = false;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -154,12 +147,10 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var clickExecutedCount = 0;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -208,13 +199,11 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var clickExecutedCount = 0;
var commandExecutedCount = 0;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);

6
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reactive.Subjects;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
@ -26,6 +27,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{
public class CompiledBindingExtensionTests
{
static CompiledBindingExtensionTests()
{
RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
}
[Fact]
public void ResolvesClrPropertyBasedOnDataContextType()
{

7
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -720,7 +720,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
//ensure binding is set and operational first
Assert.Equal(100.0, tracker.Tag);
Assert.Equal("EndInit 0", tracker.Order.Last());
// EndInit should be second-to-last operation, as last operation will be
// caused by styling being applied on EndInit.
Assert.Equal("EndInit 0", tracker.Order[tracker.Order.Count - 2]);
// Caused by styling.
Assert.Equal("Property Foreground Changed", tracker.Order[tracker.Order.Count - 1]);
}
}

14
tests/Avalonia.UnitTests/TestServices.cs

@ -23,7 +23,6 @@ namespace Avalonia.UnitTests
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
styler: new Styler(),
theme: () => CreateSimpleTheme(),
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true),
fontManagerImpl: new MockFontManagerImpl(),
@ -39,9 +38,6 @@ namespace Avalonia.UnitTests
public static readonly TestServices MockPlatformWrapper = new TestServices(
platform: Mock.Of<IRuntimePlatform>());
public static readonly TestServices MockStyler = new TestServices(
styler: Mock.Of<IStyler>());
public static readonly TestServices MockThreadingInterface = new TestServices(
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true));
@ -58,9 +54,6 @@ namespace Avalonia.UnitTests
fontManagerImpl: new MockFontManagerImpl(),
textShaperImpl: new MockTextShaperImpl());
public static readonly TestServices RealStyler = new TestServices(
styler: new Styler());
public static readonly TestServices TextServices = new TestServices(
assetLoader: new AssetLoader(),
renderInterface: new MockPlatformRenderInterface(),
@ -80,7 +73,6 @@ namespace Avalonia.UnitTests
IRenderTimer renderLoop = null,
IScheduler scheduler = null,
ICursorFactory standardCursorFactory = null,
IStyler styler = null,
Func<IStyle> theme = null,
IPlatformThreadingInterface threadingInterface = null,
IFontManagerImpl fontManagerImpl = null,
@ -101,7 +93,6 @@ namespace Avalonia.UnitTests
TextShaperImpl = textShaperImpl;
Scheduler = scheduler;
StandardCursorFactory = standardCursorFactory;
Styler = styler;
Theme = theme;
ThreadingInterface = threadingInterface;
WindowImpl = windowImpl;
@ -121,7 +112,6 @@ namespace Avalonia.UnitTests
public ITextShaperImpl TextShaperImpl { get; }
public IScheduler Scheduler { get; }
public ICursorFactory StandardCursorFactory { get; }
public IStyler Styler { get; }
public Func<IStyle> Theme { get; }
public IPlatformThreadingInterface ThreadingInterface { get; }
public IWindowImpl WindowImpl { get; }
@ -140,8 +130,7 @@ namespace Avalonia.UnitTests
IRenderTimer renderLoop = null,
IScheduler scheduler = null,
ICursorFactory standardCursorFactory = null,
IStyler styler = null,
Func<Styles> theme = null,
Func<IStyle> theme = null,
IPlatformThreadingInterface threadingInterface = null,
IFontManagerImpl fontManagerImpl = null,
ITextShaperImpl textShaperImpl = null,
@ -162,7 +151,6 @@ namespace Avalonia.UnitTests
textShaperImpl: textShaperImpl ?? TextShaperImpl,
scheduler: scheduler ?? Scheduler,
standardCursorFactory: standardCursorFactory ?? StandardCursorFactory,
styler: styler ?? Styler,
theme: theme ?? Theme,
threadingInterface: threadingInterface ?? ThreadingInterface,
windowingPlatform: windowingPlatform ?? WindowingPlatform,

5
tests/Avalonia.UnitTests/UnitTestApplication.cs

@ -68,14 +68,13 @@ namespace Avalonia.UnitTests
.Bind<IPlatformThreadingInterface>().ToConstant(Services.ThreadingInterface)
.Bind<IScheduler>().ToConstant(Services.Scheduler)
.Bind<ICursorFactory>().ToConstant(Services.StandardCursorFactory)
.Bind<IStyler>().ToConstant(Services.Styler)
.Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
var theme = Services.Theme?.Invoke();
if (theme is Styles styles)
if (theme is Style styles)
{
Styles.AddRange(styles);
Styles.AddRange(styles.Children);
}
else if (theme is not null)
{

Loading…
Cancel
Save