diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index c5d624da64..f8838e8cfb 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -46,6 +46,8 @@ namespace Avalonia private Styles _styles; private IResourceDictionary _resources; private bool _notifyingResourcesChanged; + private Action> _stylesAdded; + private Action> _stylesRemoved; /// /// Defines the property. @@ -202,6 +204,18 @@ namespace Avalonia /// public IApplicationLifetime ApplicationLifetime { get; set; } + event Action> IGlobalStyles.GlobalStylesAdded + { + add => _stylesAdded += value; + remove => _stylesAdded -= value; + } + + event Action> IGlobalStyles.GlobalStylesRemoved + { + add => _stylesRemoved += value; + remove => _stylesRemoved -= value; + } + /// /// Initializes the application by loading XAML etc. /// @@ -217,10 +231,12 @@ namespace Avalonia void IStyleHost.StylesAdded(IReadOnlyList styles) { + _stylesAdded?.Invoke(styles); } void IStyleHost.StylesRemoved(IReadOnlyList styles) { + _stylesRemoved?.Invoke(styles); } /// diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index cabadac7d6..f477379777 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -50,6 +50,7 @@ namespace Avalonia.Controls private readonly IAccessKeyHandler _accessKeyHandler; private readonly IKeyboardNavigationHandler _keyboardNavigationHandler; private readonly IPlatformRenderInterface _renderInterface; + private readonly IGlobalStyles _globalStyles; private Size _clientSize; private ILayoutManager _layoutManager; @@ -94,6 +95,7 @@ namespace Avalonia.Controls _inputManager = TryGetService(dependencyResolver); _keyboardNavigationHandler = TryGetService(dependencyResolver); _renderInterface = TryGetService(dependencyResolver); + _globalStyles = TryGetService(dependencyResolver); Renderer = impl.CreateRenderer(this); @@ -112,6 +114,13 @@ namespace Avalonia.Controls _keyboardNavigationHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this); + + if (_globalStyles is object) + { + _globalStyles.GlobalStylesAdded += ((IStyleHost)this).StylesAdded; + _globalStyles.GlobalStylesRemoved += ((IStyleHost)this).StylesRemoved; + } + styler?.ApplyStyles(this); ClientSize = impl.ClientSize; @@ -215,10 +224,7 @@ namespace Avalonia.Controls /// double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1; - IStyleHost IStyleHost.StylingParent - { - get { return AvaloniaLocator.Current.GetService(); } - } + IStyleHost IStyleHost.StylingParent => _globalStyles; IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget(); @@ -267,6 +273,12 @@ namespace Avalonia.Controls /// protected virtual void HandleClosed() { + if (_globalStyles is object) + { + _globalStyles.GlobalStylesAdded -= ((IStyleHost)this).StylesAdded; + _globalStyles.GlobalStylesRemoved -= ((IStyleHost)this).StylesRemoved; + } + var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null); ((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs); diff --git a/src/Avalonia.Styling/Styling/IGlobalStyles.cs b/src/Avalonia.Styling/Styling/IGlobalStyles.cs index 51393ef0b3..6152755a05 100644 --- a/src/Avalonia.Styling/Styling/IGlobalStyles.cs +++ b/src/Avalonia.Styling/Styling/IGlobalStyles.cs @@ -1,6 +1,11 @@ // 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.Collections.Generic; + +#nullable enable + namespace Avalonia.Styling { /// @@ -8,5 +13,14 @@ namespace Avalonia.Styling /// public interface IGlobalStyles : IStyleHost { + /// + /// Raised when styles are added to or a nested styles collection. + /// + public event Action> GlobalStylesAdded; + + /// + /// Raised when styles are removed from or a nested styles collection. + /// + public event Action> GlobalStylesRemoved; } } diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 91592b1ff7..26bf604187 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -9,6 +9,7 @@ using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Platform; +using Avalonia.Styling; using Avalonia.UnitTests; using Moq; using Xunit; @@ -269,6 +270,44 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Reacts_To_Changes_In_Global_Styles() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var impl = new Mock(); + impl.SetupGet(x => x.Scaling).Returns(1); + + var child = new Border { Classes = { "foo" } }; + var target = new TestTopLevel(impl.Object) + { + Template = CreateTemplate(), + Content = child, + }; + + target.LayoutManager.ExecuteInitialLayoutPass(target); + + Assert.Equal(new Thickness(0), child.BorderThickness); + + var style = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Border.BorderThicknessProperty, new Thickness(2)) + } + }; + + Application.Current.Styles.Add(style); + target.LayoutManager.ExecuteInitialLayoutPass(target); + + Assert.Equal(new Thickness(2), child.BorderThickness); + + Application.Current.Styles.Remove(style); + + Assert.Equal(new Thickness(0), child.BorderThickness); + } + } + private FuncControlTemplate CreateTemplate() { return new FuncControlTemplate((x, scope) =>