Skip to content

Commit

Permalink
Deprecate IStyleable .
Browse files Browse the repository at this point in the history
This was signalled for removal in #9553, but I was hesitant to do it because it will be in use by a lot of code in order to override `StyleKey`. Instead of removing it, deprecate it and provide a virtual `StyledElement.StyleKeyOverride` property as the supported way of overriding a control's style key.
  • Loading branch information
grokys committed May 15, 2023
1 parent 6277b3e commit 474d78b
Show file tree
Hide file tree
Showing 25 changed files with 91 additions and 107 deletions.
60 changes: 45 additions & 15 deletions src/Avalonia.Base/StyledElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ public class StyledElement : Animatable,
ILogical,
IThemeVariantHost,
IStyleHost,
IStyleable,
ISetLogicalParent,
ISetInheritanceParent,
ISupportInitialize
ISupportInitialize,
#pragma warning disable CS0618 // Type or member is obsolete
IStyleable
#pragma warning restore CS0618 // Type or member is obsolete
{
/// <summary>
/// Defines the <see cref="DataContext"/> property.
Expand Down Expand Up @@ -217,6 +219,18 @@ public object? DataContext
/// </remarks>
public Styles Styles => _styles ??= new Styles(this);

/// <summary>
/// Gets the type by which the element is styled.
/// </summary>
/// <remarks>
/// Usually controls are styled by their own type, but there are instances where you want
/// an element to be styled by its base type, e.g. creating SpecialButton that
/// derives from Button and adds extra functionality but is still styled as a regular
/// Button. To change the style for a control class, override the <see cref="StyleKeyOverride"/>
/// property
/// </remarks>
public Type StyleKey => StyleKeyOverride;

/// <summary>
/// Gets or sets the styled element's resource dictionary.
/// </summary>
Expand Down Expand Up @@ -278,6 +292,18 @@ protected internal IAvaloniaList<ILogical> LogicalChildren
/// </summary>
protected IPseudoClasses PseudoClasses => Classes;

/// <summary>
/// Gets the type by which the element is styled.
/// </summary>
/// <remarks>
/// Usually controls are styled by their own type, but there are instances where you want
/// an element to be styled by its base type, e.g. creating SpecialButton that
/// derives from Button and adds extra functionality but is still styled as a regular
/// Button. Override this property to change the style for a control class, returning the
/// type that you wish the elements to be styled as.
/// </remarks>
protected virtual Type StyleKeyOverride => GetType();

/// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree.
/// </summary>
Expand Down Expand Up @@ -309,24 +335,12 @@ protected internal IAvaloniaList<ILogical> LogicalChildren
/// <inheritdoc/>
IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;

/// <summary>
/// Gets the type by which the styled element is styled.
/// </summary>
/// <remarks>
/// Usually controls are styled by their own type, but there are instances where you want
/// a styled element to be styled by its base type, e.g. creating SpecialButton that
/// derives from Button and adds extra functionality but is still styled as a regular
/// Button.
/// </remarks>
Type IStyleable.StyleKey => GetType();

/// <inheritdoc/>
bool IStyleHost.IsStylesInitialized => _styles != null;

/// <inheritdoc/>
IStyleHost? IStyleHost.StylingParent => (IStyleHost?)InheritanceParent;


/// <inheritdoc/>
public virtual void BeginInit()
{
Expand Down Expand Up @@ -669,7 +683,7 @@ internal virtual void OnTemplatedParentControlThemeChanged()
// 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;
var key = GetStyleKey(this);

if (this.TryFindResource(key, out var value) && value is ControlTheme t)
_implicitTheme = t;
Expand Down Expand Up @@ -700,6 +714,22 @@ internal virtual void InvalidateStyles(bool recurse)
}
}

/// <summary>
/// Internal getter for <see cref="IStyleable.StyleKey"/> so that we only need to suppress the obsolete
/// warning in one place.
/// </summary>
/// <param name="e">The element</param>
/// <remarks>
/// <see cref="IStyleable"/> is obsolete and will be removed in a future version, but for backwards
/// compatibility we need to support code which overrides <see cref="IStyleable.StyleKey"/>.
/// </remarks>
internal static Type GetStyleKey(StyledElement e)
{
#pragma warning disable CS0618 // Type or member is obsolete
return ((IStyleable)e).StyleKey;
#pragma warning restore CS0618 // Type or member is obsolete
}

private static void DataContextNotifying(AvaloniaObject o, bool updateStarted)
{
if (o is StyledElement element)
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Styling/ControlTheme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal SelectorMatchResult TryAttach(StyledElement target, FrameType type)
if (TargetType is null)
throw new InvalidOperationException("ControlTheme has no TargetType.");

if (HasSettersOrAnimations && TargetType.IsAssignableFrom(((IStyleable)target).StyleKey))
if (HasSettersOrAnimations && TargetType.IsAssignableFrom(StyledElement.GetStyleKey(target)))
{
Attach(target, null, type);
return SelectorMatchResult.AlwaysThisType;
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/Styling/DescendentSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent,
{
c = c.LogicalParent;

if (c is IStyleable)
if (c is StyledElement s)
{
var match = _parent.Match((StyledElement)c, parent, subscribe);
var match = _parent.Match(s, parent, subscribe);

if (match.Result == SelectorMatchResult.Sometimes)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/Styling/IStyleable.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using Avalonia.Collections;
using Avalonia.Metadata;

namespace Avalonia.Styling
{
/// <summary>
/// Interface for styleable elements.
/// </summary>
[NotClientImplementable]
[Obsolete("This interface may be removed in 12.0. Use StyledElement, or override StyledElement.StyleKeyOverride to override the StyleKey for a class.")]
public interface IStyleable : INamed
{
/// <summary>
Expand All @@ -18,6 +17,7 @@ public interface IStyleable : INamed
/// <summary>
/// Gets the type by which the control is styled.
/// </summary>
[Obsolete("Override StyledElement.StyleKeyOverride instead.")]
Type StyleKey { get; }

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Styling/NestingSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent,
{
if (theme.TargetType is null)
throw new InvalidOperationException("ControlTheme has no TargetType.");
return theme.TargetType.IsAssignableFrom(((IStyleable)control).StyleKey) ?
return theme.TargetType.IsAssignableFrom(StyledElement.GetStyleKey(control)) ?
SelectorMatch.AlwaysThisType :
SelectorMatch.NeverThisType;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/Styling/Selectors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static Selector Is(this Selector? previous, Type type)
/// <typeparam name="T">The type.</typeparam>
/// <param name="previous">The previous selector.</param>
/// <returns>The selector.</returns>
public static Selector Is<T>(this Selector? previous) where T : IStyleable
public static Selector Is<T>(this Selector? previous) where T : StyledElement
{
return previous.Is(typeof(T));
}
Expand Down Expand Up @@ -171,7 +171,7 @@ public static Selector OfType(this Selector? previous, Type type)
/// <typeparam name="T">The type.</typeparam>
/// <param name="previous">The previous selector.</param>
/// <returns>The selector.</returns>
public static Selector OfType<T>(this Selector? previous) where T : IStyleable
public static Selector OfType<T>(this Selector? previous) where T : StyledElement
{
return previous.OfType(typeof(T));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent,
{
if (TargetType != null)
{
var controlType = ((IStyleable)control).StyleKey ?? control.GetType();
var controlType = StyledElement.GetStyleKey(control) ?? control.GetType();

if (IsConcreteType)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Avalonia.Controls.Embedding
{
public class EmbeddableControlRoot : TopLevel, IStyleable, IFocusScope, IDisposable
public class EmbeddableControlRoot : TopLevel, IFocusScope, IDisposable
{
public EmbeddableControlRoot(ITopLevelImpl impl) : base(impl)
{
Expand Down Expand Up @@ -46,7 +46,7 @@ protected override Size MeasureOverride(Size availableSize)
return rv;
}

Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
protected override Type StyleKeyOverride => typeof(EmbeddableControlRoot);
public void Dispose() => PlatformImpl?.Dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Avalonia.Controls.Embedding.Offscreen
{
class OffscreenTopLevel : TopLevel, IStyleable
class OffscreenTopLevel : TopLevel
{
public OffscreenTopLevelImplBase Impl { get; }

Expand All @@ -31,7 +31,7 @@ private void EnsureInitialized()
}
}

Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
protected override Type StyleKeyOverride => typeof(EmbeddableControlRoot);
public void Dispose()
{
PlatformImpl?.Dispose();
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/ItemsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ internal void PrepareItemContainer(Control container, object? item, int index)

if (itemContainerTheme is not null &&
!container.IsSet(ThemeProperty) &&
((IStyleable)container).StyleKey == itemContainerTheme.TargetType)
StyledElement.GetStyleKey(container) == itemContainerTheme.TargetType)
{
container.Theme = itemContainerTheme;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Controls/MaskedTextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Avalonia.Controls
{
public class MaskedTextBox : TextBox, IStyleable
public class MaskedTextBox : TextBox
{
public static readonly StyledProperty<bool> AsciiOnlyProperty =
AvaloniaProperty.Register<MaskedTextBox, bool>(nameof(AsciiOnly));
Expand Down Expand Up @@ -183,7 +183,7 @@ public bool ResetOnSpace
set => SetValue(ResetOnSpaceProperty, value);
}

Type IStyleable.StyleKey => typeof(TextBox);
protected override Type StyleKeyOverride => typeof(TextBox);

/// <inheritdoc />
protected override void OnGotFocus(GotFocusEventArgs e)
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Avalonia.Controls
/// the secondary part opens a flyout.
/// </summary>
[PseudoClasses(pcChecked)]
public class ToggleSplitButton : SplitButton, IStyleable
public class ToggleSplitButton : SplitButton
{
/// <summary>
/// Raised when the <see cref="IsChecked"/> property value changes.
Expand Down Expand Up @@ -63,7 +63,7 @@ public bool IsChecked
/// Both <see cref="ToggleSplitButton"/> and <see cref="SplitButton"/> share
/// the same exact default style.
/// </remarks>
Type IStyleable.StyleKey => typeof(SplitButton);
protected override Type StyleKeyOverride => typeof(SplitButton);

/// <summary>
/// Toggles the <see cref="IsChecked"/> property between true and false.
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/UserControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Avalonia.Controls
/// <summary>
/// Provides the base class for defining a new control that encapsulates related existing controls and provides its own logic.
/// </summary>
public class UserControl : ContentControl, IStyleable
public class UserControl : ContentControl
{

}
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Controls/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public enum SystemDecorations
/// <summary>
/// A top-level window.
/// </summary>
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot
public class Window : WindowBase, IFocusScope, ILayoutRoot
{
private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>();
private bool _isExtendedIntoWindowDecorations;
Expand Down Expand Up @@ -420,7 +420,7 @@ public PixelPoint Position
public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => PlatformImpl?.BeginResizeDrag(edge, e);

/// <inheritdoc/>
Type IStyleable.StyleKey => typeof(Window);
protected override Type StyleKeyOverride => typeof(Window);

/// <summary>
/// Fired before a window is closed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
namespace Avalonia.Diagnostics.Controls
{
//TODO: UpdateSourceTrigger & Binding.ValidationRules could help removing the need for this control.
internal sealed class CommitTextBox : TextBox, IStyleable
internal sealed class CommitTextBox : TextBox
{
Type IStyleable.StyleKey => typeof(TextBox);
protected override Type StyleKeyOverride => typeof(TextBox);

/// <summary>
/// Defines the <see cref="CommittedText" /> property.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Avalonia.Diagnostics.Controls
{
internal class FilterTextBox : TextBox, IStyleable
internal class FilterTextBox : TextBox
{
public static readonly StyledProperty<bool> UseRegexFilterProperty =
AvaloniaProperty.Register<FilterTextBox, bool>(nameof(UseRegexFilter),
Expand Down Expand Up @@ -42,6 +42,6 @@ public bool UseWholeWordFilter
set => SetValue(UseWholeWordFilterProperty, value);
}

Type IStyleable.StyleKey => typeof(TextBox);
protected override Type StyleKeyOverride => typeof(TextBox);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ private static string GetVisualSelector(Visual visual)
var classes = string.Concat(visual.Classes
.Where(c => !c.StartsWith(":"))
.Select(c => '.' + c));
var typeName = ((IStyleable)visual).StyleKey.Name;
var typeName = StyledElement.GetStyleKey(visual);

return $"{typeName}{name}{classes}";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public VisualTreeNode(AvaloniaObject avaloniaObject, TreeNode? parent, string? c
_ => TreeNodeCollection.Empty
};

if (Visual is IStyleable styleable)
if (Visual is StyledElement styleable)
IsInTemplate = styleable.TemplatedParent != null;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.ReactiveUI/RoutedViewHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace Avalonia.ReactiveUI
/// ReactiveUI routing documentation website</see> for more info.
/// </para>
/// </remarks>
public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger, IStyleable
public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger
{
/// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="Router"/> property.
Expand Down Expand Up @@ -126,7 +126,7 @@ public object? DefaultContent
/// </summary>
public IViewLocator? ViewLocator { get; set; }

Type IStyleable.StyleKey => typeof(TransitioningContentControl);
protected override Type StyleKeyOverride => typeof(TransitioningContentControl);

/// <summary>
/// Invoked when ReactiveUI router navigates to a view model.
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.ReactiveUI/ViewModelViewHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Avalonia.ReactiveUI
/// the ViewModel property and display it. This control is very useful
/// inside a DataTemplate to display the View associated with a ViewModel.
/// </summary>
public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger, IStyleable
public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger
{
/// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="ViewModel"/> property.
Expand Down Expand Up @@ -78,7 +78,7 @@ public object? DefaultContent
/// </summary>
public IViewLocator? ViewLocator { get; set; }

Type IStyleable.StyleKey => typeof(TransitioningContentControl);
protected override Type StyleKeyOverride => typeof(TransitioningContentControl);

/// <summary>
/// Invoked when ReactiveUI router navigates to a view model.
Expand Down
4 changes: 2 additions & 2 deletions src/Windows/Avalonia.Win32/TrayIconImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ private enum CustomWindowsMessage : uint
WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024,
}

private class TrayIconMenuFlyoutPresenter : MenuFlyoutPresenter, IStyleable
private class TrayIconMenuFlyoutPresenter : MenuFlyoutPresenter
{
Type IStyleable.StyleKey => typeof(MenuFlyoutPresenter);
protected override Type StyleKeyOverride => typeof(MenuFlyoutPresenter);

public override void Close()
{
Expand Down
Loading

0 comments on commit 474d78b

Please sign in to comment.