Browse Source

Merge branch 'master' into better-type-res-error

pull/11347/head
Max Katz 3 years ago
committed by GitHub
parent
commit
605cd226ca
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      nukebuild/_build.csproj
  2. 2
      samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
  3. 6
      src/Avalonia.Base/AttachedProperty.cs
  4. 22
      src/Avalonia.Base/AvaloniaProperty.cs
  5. 12
      src/Avalonia.Base/AvaloniaProperty`1.cs
  6. 41
      src/Avalonia.Base/DirectProperty.cs
  7. 6
      src/Avalonia.Base/DirectPropertyBase.cs
  8. 60
      src/Avalonia.Base/StyledElement.cs
  9. 6
      src/Avalonia.Base/StyledProperty.cs
  10. 2
      src/Avalonia.Base/Styling/ControlTheme.cs
  11. 4
      src/Avalonia.Base/Styling/DescendentSelector.cs
  12. 4
      src/Avalonia.Base/Styling/IStyleable.cs
  13. 2
      src/Avalonia.Base/Styling/NestingSelector.cs
  14. 4
      src/Avalonia.Base/Styling/Selectors.cs
  15. 2
      src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
  16. 2
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  17. 4
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  18. 4
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
  19. 2
      src/Avalonia.Controls/ItemsControl.cs
  20. 4
      src/Avalonia.Controls/MaskedTextBox.cs
  21. 16
      src/Avalonia.Controls/Selection/InternalSelectionModel.cs
  22. 4
      src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
  23. 2
      src/Avalonia.Controls/UserControl.cs
  24. 4
      src/Avalonia.Controls/Window.cs
  25. 4
      src/Avalonia.Diagnostics/Diagnostics/Controls/CommitTextBox.cs
  26. 4
      src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.cs
  27. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  28. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
  29. 4
      src/Avalonia.ReactiveUI/RoutedViewHost.cs
  30. 4
      src/Avalonia.ReactiveUI/ViewModelViewHost.cs
  31. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  32. 4
      src/Windows/Avalonia.Win32/TrayIconImpl.cs
  33. 2
      tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs
  34. 17
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs
  35. 1
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
  36. 2
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
  37. 2
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
  38. 4
      tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs
  39. 1
      tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs
  40. 2
      tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs
  41. 8
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  42. 53
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  43. 4
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  44. 4
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  45. 56
      tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs

2
nukebuild/_build.csproj

@ -15,7 +15,7 @@
<PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
<PackageReference Include="MicroCom.CodeGenerator" Version="0.11.0" />
<!-- Keep in sync with Avalonia.Build.Tasks -->
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
<PackageReference Include="SourceLink" Version="1.1.0" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Build.Framework" Version="17.3.2" PrivateAssets="All" />
<PackageReference Include="xunit.runner.console" Version="2.4.2">

2
samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
<ProjectReference Include="..\SafeAreaDemo\SafeAreaDemo.csproj" />

6
src/Avalonia.Base/AttachedProperty.cs

@ -13,16 +13,18 @@ namespace Avalonia
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The class that is registering the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="validate">A value validation callback.</param>
public AttachedProperty(
internal AttachedProperty(
string name,
Type ownerType,
Type hostType,
StyledPropertyMetadata<TValue> metadata,
bool inherits = false,
Func<TValue, bool>? validate = null)
: base(name, ownerType, metadata, inherits, validate)
: base(name, ownerType, hostType, metadata, inherits, validate)
{
IsAttached = true;
}

22
src/Avalonia.Base/AvaloniaProperty.cs

@ -39,12 +39,14 @@ namespace Avalonia
/// <param name="name">The name of the property.</param>
/// <param name="valueType">The type of the property's value.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="notifying">A <see cref="Notifying"/> callback.</param>
protected AvaloniaProperty(
private protected AvaloniaProperty(
string name,
Type valueType,
Type ownerType,
Type hostType,
AvaloniaPropertyMetadata metadata,
Action<AvaloniaObject, bool>? notifying = null)
{
@ -63,9 +65,9 @@ namespace Avalonia
Notifying = notifying;
Id = s_nextId++;
_metadata.Add(ownerType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
_metadata.Add(hostType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
_defaultMetadata = metadata.GenerateTypeSafeMetadata();
_singleMetadata = new(ownerType, metadata);
_singleMetadata = new(hostType, metadata);
}
/// <summary>
@ -74,7 +76,7 @@ namespace Avalonia
/// <param name="source">The direct property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected AvaloniaProperty(
private protected AvaloniaProperty(
AvaloniaProperty source,
Type ownerType,
AvaloniaPropertyMetadata? metadata)
@ -151,7 +153,7 @@ namespace Avalonia
/// will be true before the property change notifications are sent and false afterwards. This
/// callback is intended to support Control.IsDataContextChanging.
/// </remarks>
public Action<AvaloniaObject, bool>? Notifying { get; }
internal Action<AvaloniaObject, bool>? Notifying { get; }
/// <summary>
/// Gets the integer ID that represents this property.
@ -255,6 +257,7 @@ namespace Avalonia
var result = new StyledProperty<TValue>(
name,
typeof(TOwner),
typeof(TOwner),
metadata,
inherits,
validate);
@ -301,6 +304,7 @@ namespace Avalonia
var result = new StyledProperty<TValue>(
name,
typeof(TOwner),
typeof(TOwner),
metadata,
inherits,
validate,
@ -338,7 +342,7 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode,
coerce: coerce);
var result = new AttachedProperty<TValue>(name, typeof(TOwner), metadata, inherits, validate);
var result = new AttachedProperty<TValue>(name, typeof(TOwner), typeof(THost), metadata, inherits, validate);
var registry = AvaloniaPropertyRegistry.Instance;
registry.Register(typeof(TOwner), result);
registry.RegisterAttached(typeof(THost), result);
@ -375,7 +379,7 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode,
coerce: coerce);
var result = new AttachedProperty<TValue>(name, ownerType, metadata, inherits, validate);
var result = new AttachedProperty<TValue>(name, ownerType, typeof(THost), metadata, inherits, validate);
var registry = AvaloniaPropertyRegistry.Instance;
registry.Register(ownerType, result);
registry.RegisterAttached(typeof(THost), result);
@ -554,7 +558,7 @@ namespace Avalonia
/// </summary>
/// <param name="type">The type.</param>
/// <param name="metadata">The metadata.</param>
protected void OverrideMetadata(Type type, AvaloniaPropertyMetadata metadata)
private protected void OverrideMetadata(Type type, AvaloniaPropertyMetadata metadata)
{
_ = type ?? throw new ArgumentNullException(nameof(type));
_ = metadata ?? throw new ArgumentNullException(nameof(metadata));
@ -573,7 +577,7 @@ namespace Avalonia
_singleMetadata = null;
}
protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
private protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
private AvaloniaPropertyMetadata GetMetadataWithOverrides(Type type)
{

12
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -19,14 +19,16 @@ namespace Avalonia
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param>
protected AvaloniaProperty(
private protected AvaloniaProperty(
string name,
Type ownerType,
Type hostType,
AvaloniaPropertyMetadata metadata,
Action<AvaloniaObject, bool>? notifying = null)
: base(name, typeof(TValue), ownerType, metadata, notifying)
: base(name, typeof(TValue), ownerType, hostType, metadata, notifying)
{
_changed = new LightweightSubject<AvaloniaPropertyChangedEventArgs<TValue>>();
}
@ -37,7 +39,7 @@ namespace Avalonia
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected AvaloniaProperty(
private protected AvaloniaProperty(
AvaloniaProperty<TValue> source,
Type ownerType,
AvaloniaPropertyMetadata? metadata)
@ -66,10 +68,10 @@ namespace Avalonia
_changed.OnNext(e);
}
protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
private protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
protected BindingValue<object?> TryConvert(object? value)
private protected BindingValue<object?> TryConvert(object? value)
{
if (value == UnsetValue)
{

41
src/Avalonia.Base/DirectProperty.cs

@ -24,7 +24,7 @@ namespace Avalonia
/// <param name="getter">Gets the current value of the property.</param>
/// <param name="setter">Sets the value of the property. May be null.</param>
/// <param name="metadata">The property metadata.</param>
public DirectProperty(
internal DirectProperty(
string name,
Func<TOwner, TValue> getter,
Action<TOwner, TValue>? setter,
@ -106,45 +106,6 @@ namespace Avalonia
return result;
}
/// <summary>
/// Registers the direct property on another type.
/// </summary>
/// <typeparam name="TNewOwner">The type of the additional owner.</typeparam>
/// <param name="getter">Gets the current value of the property.</param>
/// <param name="setter">Sets the value of the property.</param>
/// <param name="unsetValue">
/// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
/// </param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="enableDataValidation">
/// Whether the property is interested in data validation.
/// </param>
/// <returns>The property.</returns>
public DirectProperty<TNewOwner, TValue> AddOwnerWithDataValidation<TNewOwner>(
Func<TNewOwner, TValue> getter,
Action<TNewOwner,TValue> setter,
TValue unsetValue = default!,
BindingMode defaultBindingMode = BindingMode.Default,
bool enableDataValidation = false)
where TNewOwner : AvaloniaObject
{
var metadata = new DirectPropertyMetadata<TValue>(
unsetValue: unsetValue,
defaultBindingMode: defaultBindingMode,
enableDataValidation: enableDataValidation);
metadata.Merge(GetMetadata<TOwner>(), this);
var result = new DirectProperty<TNewOwner, TValue>(
this,
getter,
setter,
metadata);
AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
return result;
}
/// <inheritdoc/>
internal override TValue InvokeGetter(AvaloniaObject instance)
{

6
src/Avalonia.Base/DirectPropertyBase.cs

@ -20,11 +20,11 @@ namespace Avalonia
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="metadata">The property metadata.</param>
protected DirectPropertyBase(
private protected DirectPropertyBase(
string name,
Type ownerType,
AvaloniaPropertyMetadata metadata)
: base(name, ownerType, metadata)
: base(name, ownerType, ownerType, metadata)
{
Owner = ownerType;
}
@ -35,7 +35,7 @@ namespace Avalonia
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected DirectPropertyBase(
private protected DirectPropertyBase(
DirectPropertyBase<TValue> source,
Type ownerType,
AvaloniaPropertyMetadata metadata)

60
src/Avalonia.Base/StyledElement.cs

@ -30,10 +30,12 @@ namespace Avalonia
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.
@ -217,6 +219,18 @@ namespace Avalonia
/// </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>
@ -278,6 +292,18 @@ namespace Avalonia
/// </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>
@ -309,24 +335,12 @@ namespace Avalonia
/// <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()
{
@ -669,7 +683,7 @@ namespace Avalonia
// 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;
@ -700,6 +714,22 @@ namespace Avalonia
}
}
/// <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)

6
src/Avalonia.Base/StyledProperty.cs

@ -16,6 +16,7 @@ namespace Avalonia
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="validate">
@ -23,14 +24,15 @@ namespace Avalonia
/// <para>This method is not part of the property's metadata and so cannot be changed after registration.</para>
/// </param>
/// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param>
public StyledProperty(
internal StyledProperty(
string name,
Type ownerType,
Type hostType,
StyledPropertyMetadata<TValue> metadata,
bool inherits = false,
Func<TValue, bool>? validate = null,
Action<AvaloniaObject, bool>? notifying = null)
: base(name, ownerType, metadata, notifying)
: base(name, ownerType, hostType, metadata, notifying)
{
Inherits = inherits;
ValidateValue = validate;

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

@ -46,7 +46,7 @@ namespace Avalonia.Styling
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;

4
src/Avalonia.Base/Styling/DescendentSelector.cs

@ -44,9 +44,9 @@ namespace Avalonia.Styling
{
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)
{

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

@ -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>
@ -18,6 +17,7 @@ namespace Avalonia.Styling
/// <summary>
/// Gets the type by which the control is styled.
/// </summary>
[Obsolete("Override StyledElement.StyleKeyOverride instead.")]
Type StyleKey { get; }
/// <summary>

2
src/Avalonia.Base/Styling/NestingSelector.cs

@ -23,7 +23,7 @@ namespace Avalonia.Styling
{
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;
}

4
src/Avalonia.Base/Styling/Selectors.cs

@ -76,7 +76,7 @@ namespace Avalonia.Styling
/// <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));
}
@ -171,7 +171,7 @@ namespace Avalonia.Styling
/// <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));
}

2
src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs

@ -93,7 +93,7 @@ namespace Avalonia.Styling
{
if (TargetType != null)
{
var controlType = ((IStyleable)control).StyleKey ?? control.GetType();
var controlType = StyledElement.GetStyleKey(control) ?? control.GetType();
if (IsConcreteType)
{

2
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -118,7 +118,7 @@
<Compile Include="..\Avalonia.Base\Utilities\SpanHelpers.cs" Link="Utilities\SpanHelpers.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
</ItemGroup>

4
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -7,7 +7,7 @@ using Avalonia.Styling;
namespace Avalonia.Controls.Embedding
{
public class EmbeddableControlRoot : TopLevel, IStyleable, IFocusScope, IDisposable
public class EmbeddableControlRoot : TopLevel, IFocusScope, IDisposable
{
public EmbeddableControlRoot(ITopLevelImpl impl) : base(impl)
{
@ -46,7 +46,7 @@ namespace Avalonia.Controls.Embedding
return rv;
}
Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
protected override Type StyleKeyOverride => typeof(EmbeddableControlRoot);
public void Dispose() => PlatformImpl?.Dispose();
}
}

4
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs

@ -4,7 +4,7 @@ using Avalonia.Styling;
namespace Avalonia.Controls.Embedding.Offscreen
{
class OffscreenTopLevel : TopLevel, IStyleable
class OffscreenTopLevel : TopLevel
{
public OffscreenTopLevelImplBase Impl { get; }
@ -31,7 +31,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
}
}
Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
protected override Type StyleKeyOverride => typeof(EmbeddableControlRoot);
public void Dispose()
{
PlatformImpl?.Dispose();

2
src/Avalonia.Controls/ItemsControl.cs

@ -712,7 +712,7 @@ namespace Avalonia.Controls
if (itemContainerTheme is not null &&
!container.IsSet(ThemeProperty) &&
((IStyleable)container).StyleKey == itemContainerTheme.TargetType)
StyledElement.GetStyleKey(container) == itemContainerTheme.TargetType)
{
container.Theme = itemContainerTheme;
}

4
src/Avalonia.Controls/MaskedTextBox.cs

@ -10,7 +10,7 @@ using Avalonia.Styling;
namespace Avalonia.Controls
{
public class MaskedTextBox : TextBox, IStyleable
public class MaskedTextBox : TextBox
{
public static readonly StyledProperty<bool> AsciiOnlyProperty =
AvaloniaProperty.Register<MaskedTextBox, bool>(nameof(AsciiOnly));
@ -183,7 +183,7 @@ namespace Avalonia.Controls
set => SetValue(ResetOnSpaceProperty, value);
}
Type IStyleable.StyleKey => typeof(TextBox);
protected override Type StyleKeyOverride => typeof(TextBox);
/// <inheritdoc />
protected override void OnGotFocus(GotFocusEventArgs e)

16
src/Avalonia.Controls/Selection/InternalSelectionModel.cs

@ -67,6 +67,8 @@ namespace Avalonia.Controls.Selection
var previousSource = Source;
var previousWritableSelectedItems = _writableSelectedItems;
base.OnSourceCollectionChangeStarted();
try
{
_skipSyncFromSelectedItems = true;
@ -81,8 +83,20 @@ namespace Avalonia.Controls.Selection
// We skipped the sync from WritableSelectedItems before; do it now that both
// the source and WritableSelectedItems are updated.
if (previousSource != Source || previousWritableSelectedItems != _writableSelectedItems)
if (previousWritableSelectedItems != _writableSelectedItems)
{
base.OnSourceCollectionChangeFinished();
SyncFromSelectedItems();
}
else if (previousSource != Source)
{
SyncFromSelectedItems();
base.OnSourceCollectionChangeFinished();
}
else
{
base.OnSourceCollectionChangeFinished();
}
}
private protected override void SetSource(IEnumerable? value)

4
src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs

@ -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.
@ -63,7 +63,7 @@ namespace Avalonia.Controls
/// 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.

2
src/Avalonia.Controls/UserControl.cs

@ -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
{
}

4
src/Avalonia.Controls/Window.cs

@ -65,7 +65,7 @@ namespace Avalonia.Controls
/// <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;
@ -420,7 +420,7 @@ namespace Avalonia.Controls
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.

4
src/Avalonia.Diagnostics/Diagnostics/Controls/CommitTextBox.cs

@ -7,9 +7,9 @@ using Avalonia.Styling;
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.

4
src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.cs

@ -5,7 +5,7 @@ using Avalonia.Styling;
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),
@ -42,6 +42,6 @@ namespace Avalonia.Diagnostics.Controls
set => SetValue(UseWholeWordFilterProperty, value);
}
Type IStyleable.StyleKey => typeof(TextBox);
protected override Type StyleKeyOverride => typeof(TextBox);
}
}

2
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -208,7 +208,7 @@ namespace Avalonia.Diagnostics.ViewModels
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}";
}

2
src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs

@ -23,7 +23,7 @@ namespace Avalonia.Diagnostics.ViewModels
_ => TreeNodeCollection.Empty
};
if (Visual is IStyleable styleable)
if (Visual is StyledElement styleable)
IsInTemplate = styleable.TemplatedParent != null;
}

4
src/Avalonia.ReactiveUI/RoutedViewHost.cs

@ -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.
@ -126,7 +126,7 @@ namespace Avalonia.ReactiveUI
/// </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.

4
src/Avalonia.ReactiveUI/ViewModelViewHost.cs

@ -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.
@ -78,7 +78,7 @@ namespace Avalonia.ReactiveUI
/// </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.

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@ -1 +1 @@
Subproject commit e5254eb1b2017f78a92acd466c8fa1e47401056b
Subproject commit c5d5d8b78dce42dfb7b7f320a5c345da36a58579

4
src/Windows/Avalonia.Win32/TrayIconImpl.cs

@ -170,9 +170,9 @@ namespace Avalonia.Win32
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()
{

2
tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs

@ -1,3 +1,4 @@
using Avalonia.Controls;
using Xunit;
namespace Avalonia.Base.UnitTests
@ -10,6 +11,7 @@ namespace Avalonia.Base.UnitTests
var property = new AttachedProperty<string>(
"Foo",
typeof(Class1),
typeof(Control),
new StyledPropertyMetadata<string>());
Assert.True(property.IsAttached);

17
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs

@ -31,6 +31,16 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(100, target.GetValue(Class1.AttachedProperty));
}
[Fact]
public void Coerces_Set_Value_Attached_On_Class_Not_Derived_From_Owner()
{
var target = new Class2();
target.SetValue(Class1.AttachedProperty, 150);
Assert.Equal(100, target.GetValue(Class1.AttachedProperty));
}
[Fact]
public void Coerces_Bound_Value()
{
@ -301,7 +311,7 @@ namespace Avalonia.Base.UnitTests
coerce: CoerceFoo);
public static readonly AttachedProperty<int> AttachedProperty =
AvaloniaProperty.RegisterAttached<Class1, Class1, int>(
AvaloniaProperty.RegisterAttached<Class1, AvaloniaObject, int>(
"Attached",
defaultValue: 11,
coerce: CoerceFoo);
@ -332,8 +342,9 @@ namespace Avalonia.Base.UnitTests
public static int CoerceFoo(AvaloniaObject instance, int value)
{
var o = (Class1)instance;
return Math.Clamp(value, o.MinFoo, o.MaxFoo);
return instance is Class1 o ?
Math.Clamp(value, o.MinFoo, o.MaxFoo) :
Math.Clamp(value, 0, 100);
}
protected override void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)

1
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs

@ -15,6 +15,7 @@ namespace Avalonia.Base.UnitTests
new StyledProperty<int>(
"BadDefault",
typeof(Class1),
typeof(Class1),
new StyledPropertyMetadata<int>(101),
validate: Class1.ValidateFoo));
}

2
tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs

@ -22,7 +22,7 @@ namespace Avalonia.Base.UnitTests
{
var registry = new AvaloniaPropertyRegistry();
var metadata = new StyledPropertyMetadata<int>();
var property = new AttachedProperty<int>("test", typeof(object), metadata, true);
var property = new AttachedProperty<int>("test", typeof(object), typeof(object), metadata, true);
registry.Register(typeof(object), property);
registry.RegisterAttached(typeof(AvaloniaPropertyRegistryTests), property);
property.AddOwner<Class4>();

2
tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

@ -157,7 +157,7 @@ namespace Avalonia.Base.UnitTests
private class TestProperty<TValue> : AvaloniaProperty<TValue>
{
public TestProperty(string name, Type ownerType, TestMetadata metadata = null)
: base(name, ownerType, metadata ?? new TestMetadata())
: base(name, ownerType, ownerType, metadata ?? new TestMetadata())
{
}

4
tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs

@ -9,7 +9,8 @@ namespace Avalonia.Base.UnitTests
{
var p1 = new StyledProperty<string>(
"p1",
typeof(Class1),
typeof(Class1),
typeof(Class1),
new StyledPropertyMetadata<string>());
var p2 = p1.AddOwner<Class2>();
@ -24,6 +25,7 @@ namespace Avalonia.Base.UnitTests
var p1 = new StyledProperty<string>(
"p1",
typeof(Class1),
typeof(Class1),
new StyledPropertyMetadata<string>());
var p2 = p1.AddOwner<Class2>();

1
tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs

@ -18,6 +18,7 @@ namespace Avalonia.Base.UnitTests.Utilities
TestProperties[i] = new StyledProperty<string>(
$"Test{i}",
typeof(AvaloniaPropertyDictionaryTests),
typeof(AvaloniaPropertyDictionaryTests),
new StyledPropertyMetadata<string>());
}

2
tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs

@ -172,7 +172,7 @@ internal sealed class AvaloniaPropertyValueStoreOld<TValue>
internal class MockProperty : StyledProperty<int>
{
public MockProperty(string name) : base(name, typeof(object), new StyledPropertyMetadata<int>())
public MockProperty(string name) : base(name, typeof(object), typeof(object), new StyledPropertyMetadata<int>())
{
}
}

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

@ -1031,9 +1031,9 @@ namespace Avalonia.Controls.UnitTests
textShaperImpl: new HeadlessTextShaperStub()));
}
private class ItemsControlWithContainer : ItemsControl, IStyleable
private class ItemsControlWithContainer : ItemsControl
{
Type IStyleable.StyleKey => typeof(ItemsControl);
protected override Type StyleKeyOverride => typeof(ItemsControl);
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
@ -1046,9 +1046,9 @@ namespace Avalonia.Controls.UnitTests
}
}
private class ContainerControl : ContentControl, IStyleable
private class ContainerControl : ContentControl
{
Type IStyleable.StyleKey => typeof(ContentControl);
protected override Type StyleKeyOverride => typeof(ContentControl);
}
private record Item(string Caption, string? Value = null);

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

@ -2160,6 +2160,45 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
[Fact]
public void Does_Not_Write_To_Bound_SelectedItem_When_DataContext_Changes()
{
// Issue #9438.
var vm1 = new SelectionViewModel();
vm1.Items.Add("foo");
vm1.Items.Add("bar");
vm1.SelectedItem = "bar";
var vm2 = new SelectionViewModel();
vm2.Items.Add("foo");
vm2.Items.Add("bar");
vm2.SelectedItem = "bar";
var target = new SelectingItemsControl
{
DataContext = vm1,
[!ItemsControl.ItemsSourceProperty] = new Binding("Items"),
[!SelectingItemsControl.SelectedItemProperty] = new Binding("SelectedItem"),
Template = Template(),
};
Assert.Equal("bar", target.SelectedItem);
Assert.Equal(1, target.SelectedIndex);
var selectedItemChangedRaised = 0;
vm2.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(vm2.SelectedItem))
{
++selectedItemChangedRaised;
}
};
target.DataContext = vm2;
Assert.Equal(0, selectedItemChangedRaised);
}
private static IDisposable Start()
{
return UnitTestApplication.Start(TestServices.StyledWindow);
@ -2238,6 +2277,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
private class SelectionViewModel : NotifyingBase
{
private int _selectedIndex = -1;
private object _selectedItem;
public SelectionViewModel()
{
@ -2255,6 +2295,16 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
public object SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
RaisePropertyChanged();
}
}
public ObservableCollection<string> Items { get; }
public ObservableCollection<string> SelectedItems { get; }
}
@ -2267,6 +2317,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
private class TestSelector : SelectingItemsControl
{
public new static readonly DirectProperty<SelectingItemsControl, IList> SelectedItemsProperty =
SelectingItemsControl.SelectedItemsProperty;
public TestSelector()
{

4
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -1392,9 +1392,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
public void Toggle(int index) => UpdateSelection(index, true, false, true);
}
private class TestSelectorWithContainers : TestSelector, IStyleable
private class TestSelectorWithContainers : TestSelector
{
Type IStyleable.StyleKey => typeof(TestSelector);
protected override Type StyleKeyOverride => typeof(TestSelector);
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{

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

@ -603,9 +603,9 @@ namespace Avalonia.Controls.UnitTests
public string Value { get; }
}
private class TestTabControl : TabControl, IStyleable
private class TestTabControl : TabControl
{
Type IStyleable.StyleKey => typeof(TabControl);
protected override Type StyleKeyOverride => typeof(TabControl);
public new ISelectionModel Selection => base.Selection;
}
}

56
tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs

@ -1,12 +1,10 @@
using System;
using Moq;
using Avalonia.Collections;
using System.ComponentModel;
using Avalonia.Markup.Xaml.Converters;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Avalonia.Styling;
using Moq;
using Xunit;
using System.ComponentModel;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using System.Collections.Generic;
namespace Avalonia.Markup.Xaml.UnitTests.Converters
{
@ -112,61 +110,17 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
return tdMock.Object;
}
private class Class1 : AvaloniaObject, IStyleable
private class Class1 : StyledElement
{
public static readonly StyledProperty<string> FooProperty =
AvaloniaProperty.Register<Class1, string>("Foo");
public IAvaloniaReadOnlyList<string> Classes
{
get { throw new NotImplementedException(); }
}
public string Name
{
get { throw new NotImplementedException(); }
}
public Type StyleKey
{
get { throw new NotImplementedException(); }
}
public AvaloniaObject TemplatedParent
{
get { throw new NotImplementedException(); }
}
public ControlTheme GetEffectiveTheme()
{
throw new NotImplementedException();
}
public ThemeVariant ThemeVariant
{
get { throw new NotImplementedException(); }
}
public event EventHandler ThemeVariantChanged;
public void DetachStyles()
{
throw new NotImplementedException();
}
public void DetachStyles(IReadOnlyList<IStyle> styles)
{
throw new NotImplementedException();
}
public void InvalidateStyles()
{
throw new NotImplementedException();
}
public void StyleApplied(IStyleInstance instance)
{
throw new NotImplementedException();
}
public event EventHandler ThemeVariantChanged;
}
private class AttachedOwner

Loading…
Cancel
Save