diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
index d03746766e..8999b7ca76 100644
--- a/nukebuild/_build.csproj
+++ b/nukebuild/_build.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj b/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
index 619209892d..a3b020d531 100644
--- a/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
+++ b/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src/Avalonia.Base/AttachedProperty.cs b/src/Avalonia.Base/AttachedProperty.cs
index 4a09f2a80a..6269df2a26 100644
--- a/src/Avalonia.Base/AttachedProperty.cs
+++ b/src/Avalonia.Base/AttachedProperty.cs
@@ -13,16 +13,18 @@ namespace Avalonia
///
/// The name of the property.
/// The class that is registering the property.
+ /// The class that the property being is registered on.
/// The property metadata.
/// Whether the property inherits its value.
/// A value validation callback.
- public AttachedProperty(
+ internal AttachedProperty(
string name,
Type ownerType,
+ Type hostType,
StyledPropertyMetadata metadata,
bool inherits = false,
Func? validate = null)
- : base(name, ownerType, metadata, inherits, validate)
+ : base(name, ownerType, hostType, metadata, inherits, validate)
{
IsAttached = true;
}
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index c57131f7b5..95f8eea852 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -39,12 +39,14 @@ namespace Avalonia
/// The name of the property.
/// The type of the property's value.
/// The type of the class that registers the property.
+ /// The class that the property being is registered on.
/// The property metadata.
/// A callback.
- protected AvaloniaProperty(
+ private protected AvaloniaProperty(
string name,
Type valueType,
Type ownerType,
+ Type hostType,
AvaloniaPropertyMetadata metadata,
Action? 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);
}
///
@@ -74,7 +76,7 @@ namespace Avalonia
/// The direct property to copy.
/// The new owner type.
/// Optional overridden metadata.
- 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.
///
- public Action? Notifying { get; }
+ internal Action? Notifying { get; }
///
/// Gets the integer ID that represents this property.
@@ -255,6 +257,7 @@ namespace Avalonia
var result = new StyledProperty(
name,
typeof(TOwner),
+ typeof(TOwner),
metadata,
inherits,
validate);
@@ -301,6 +304,7 @@ namespace Avalonia
var result = new StyledProperty(
name,
typeof(TOwner),
+ typeof(TOwner),
metadata,
inherits,
validate,
@@ -338,7 +342,7 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode,
coerce: coerce);
- var result = new AttachedProperty(name, typeof(TOwner), metadata, inherits, validate);
+ var result = new AttachedProperty(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(name, ownerType, metadata, inherits, validate);
+ var result = new AttachedProperty(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
///
/// The type.
/// The metadata.
- 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 GetChanged();
+ private protected abstract IObservable GetChanged();
private AvaloniaPropertyMetadata GetMetadataWithOverrides(Type type)
{
diff --git a/src/Avalonia.Base/AvaloniaProperty`1.cs b/src/Avalonia.Base/AvaloniaProperty`1.cs
index f8c062a176..ea2f3577e5 100644
--- a/src/Avalonia.Base/AvaloniaProperty`1.cs
+++ b/src/Avalonia.Base/AvaloniaProperty`1.cs
@@ -19,14 +19,16 @@ namespace Avalonia
///
/// The name of the property.
/// The type of the class that registers the property.
+ /// The class that the property being is registered on.
/// The property metadata.
/// A callback.
- protected AvaloniaProperty(
+ private protected AvaloniaProperty(
string name,
Type ownerType,
+ Type hostType,
AvaloniaPropertyMetadata metadata,
Action? notifying = null)
- : base(name, typeof(TValue), ownerType, metadata, notifying)
+ : base(name, typeof(TValue), ownerType, hostType, metadata, notifying)
{
_changed = new LightweightSubject>();
}
@@ -37,7 +39,7 @@ namespace Avalonia
/// The property to copy.
/// The new owner type.
/// Optional overridden metadata.
- protected AvaloniaProperty(
+ private protected AvaloniaProperty(
AvaloniaProperty source,
Type ownerType,
AvaloniaPropertyMetadata? metadata)
@@ -66,10 +68,10 @@ namespace Avalonia
_changed.OnNext(e);
}
- protected override IObservable GetChanged() => Changed;
+ private protected override IObservable GetChanged() => Changed;
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
- protected BindingValue
diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
index b79fef55b9..7a0ab36050 100644
--- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
+++ b/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();
}
}
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
index b037dd9901..a87e36d00d 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
+++ b/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();
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 064716fa9b..1613bda45b 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/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;
}
diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs
index f54e8b19db..4800b7b1e4 100644
--- a/src/Avalonia.Controls/MaskedTextBox.cs
+++ b/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 AsciiOnlyProperty =
AvaloniaProperty.Register(nameof(AsciiOnly));
@@ -183,7 +183,7 @@ namespace Avalonia.Controls
set => SetValue(ResetOnSpaceProperty, value);
}
- Type IStyleable.StyleKey => typeof(TextBox);
+ protected override Type StyleKeyOverride => typeof(TextBox);
///
protected override void OnGotFocus(GotFocusEventArgs e)
diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs
index c8ad9bd88b..455700af1f 100644
--- a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs
+++ b/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)
diff --git a/src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs b/src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
index 509b58833f..ff8bd5dc0b 100644
--- a/src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
+++ b/src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Controls
/// the secondary part opens a flyout.
///
[PseudoClasses(pcChecked)]
- public class ToggleSplitButton : SplitButton, IStyleable
+ public class ToggleSplitButton : SplitButton
{
///
/// Raised when the property value changes.
@@ -63,7 +63,7 @@ namespace Avalonia.Controls
/// Both and share
/// the same exact default style.
///
- Type IStyleable.StyleKey => typeof(SplitButton);
+ protected override Type StyleKeyOverride => typeof(SplitButton);
///
/// Toggles the property between true and false.
diff --git a/src/Avalonia.Controls/UserControl.cs b/src/Avalonia.Controls/UserControl.cs
index e9339d5f4b..40c312be42 100644
--- a/src/Avalonia.Controls/UserControl.cs
+++ b/src/Avalonia.Controls/UserControl.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Controls
///
/// Provides the base class for defining a new control that encapsulates related existing controls and provides its own logic.
///
- public class UserControl : ContentControl, IStyleable
+ public class UserControl : ContentControl
{
}
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 66cce89b9d..3f04350995 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -65,7 +65,7 @@ namespace Avalonia.Controls
///
/// A top-level window.
///
- 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);
///
- Type IStyleable.StyleKey => typeof(Window);
+ protected override Type StyleKeyOverride => typeof(Window);
///
/// Fired before a window is closed.
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/CommitTextBox.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/CommitTextBox.cs
index 7870febd0a..d724318641 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Controls/CommitTextBox.cs
+++ b/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);
///
/// Defines the property.
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.cs
index 1e5674cc21..c703c73549 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.cs
+++ b/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 UseRegexFilterProperty =
AvaloniaProperty.Register(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);
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
index 4e8b4c66a2..67dbfed92b 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
+++ b/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}";
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
index c3ebb1beaf..8dbcd606b0 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
+++ b/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;
}
diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
index 2d848d4cd7..701b0d0f46 100644
--- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs
+++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
@@ -50,7 +50,7 @@ namespace Avalonia.ReactiveUI
/// ReactiveUI routing documentation website for more info.
///
///
- public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger, IStyleable
+ public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger
{
///
/// for the property.
@@ -126,7 +126,7 @@ namespace Avalonia.ReactiveUI
///
public IViewLocator? ViewLocator { get; set; }
- Type IStyleable.StyleKey => typeof(TransitioningContentControl);
+ protected override Type StyleKeyOverride => typeof(TransitioningContentControl);
///
/// Invoked when ReactiveUI router navigates to a view model.
diff --git a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
index dc45758046..3e7ed42662 100644
--- a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
+++ b/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.
///
- public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger, IStyleable
+ public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger
{
///
/// for the property.
@@ -78,7 +78,7 @@ namespace Avalonia.ReactiveUI
///
public IViewLocator? ViewLocator { get; set; }
- Type IStyleable.StyleKey => typeof(TransitioningContentControl);
+ protected override Type StyleKeyOverride => typeof(TransitioningContentControl);
///
/// Invoked when ReactiveUI router navigates to a view model.
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
index e5254eb1b2..c5d5d8b78d 160000
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
@@ -1 +1 @@
-Subproject commit e5254eb1b2017f78a92acd466c8fa1e47401056b
+Subproject commit c5d5d8b78dce42dfb7b7f320a5c345da36a58579
diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs
index d541e6b436..5069add9fd 100644
--- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs
+++ b/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()
{
diff --git a/tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs
index f461240d68..8a6e7b8679 100644
--- a/tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs
+++ b/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(
"Foo",
typeof(Class1),
+ typeof(Control),
new StyledPropertyMetadata());
Assert.True(property.IsAttached);
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs
index fe4262331f..0d0456dbda 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs
+++ b/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 AttachedProperty =
- AvaloniaProperty.RegisterAttached(
+ AvaloniaProperty.RegisterAttached(
"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)
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
index 513aeb65ab..2b3bd07fed 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
@@ -15,6 +15,7 @@ namespace Avalonia.Base.UnitTests
new StyledProperty(
"BadDefault",
typeof(Class1),
+ typeof(Class1),
new StyledPropertyMetadata(101),
validate: Class1.ValidateFoo));
}
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
index 0d62f20f7e..23e21c1645 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Base.UnitTests
{
var registry = new AvaloniaPropertyRegistry();
var metadata = new StyledPropertyMetadata();
- var property = new AttachedProperty("test", typeof(object), metadata, true);
+ var property = new AttachedProperty("test", typeof(object), typeof(object), metadata, true);
registry.Register(typeof(object), property);
registry.RegisterAttached(typeof(AvaloniaPropertyRegistryTests), property);
property.AddOwner();
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
index e44c15d962..d974351b1e 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
@@ -157,7 +157,7 @@ namespace Avalonia.Base.UnitTests
private class TestProperty : AvaloniaProperty
{
public TestProperty(string name, Type ownerType, TestMetadata metadata = null)
- : base(name, ownerType, metadata ?? new TestMetadata())
+ : base(name, ownerType, ownerType, metadata ?? new TestMetadata())
{
}
diff --git a/tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs b/tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs
index f0aa47784d..5304c74c39 100644
--- a/tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs
+++ b/tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs
@@ -9,7 +9,8 @@ namespace Avalonia.Base.UnitTests
{
var p1 = new StyledProperty(
"p1",
- typeof(Class1),
+ typeof(Class1),
+ typeof(Class1),
new StyledPropertyMetadata());
var p2 = p1.AddOwner();
@@ -24,6 +25,7 @@ namespace Avalonia.Base.UnitTests
var p1 = new StyledProperty(
"p1",
typeof(Class1),
+ typeof(Class1),
new StyledPropertyMetadata());
var p2 = p1.AddOwner();
diff --git a/tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs
index 362eee8035..4b4aeb4344 100644
--- a/tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs
@@ -18,6 +18,7 @@ namespace Avalonia.Base.UnitTests.Utilities
TestProperties[i] = new StyledProperty(
$"Test{i}",
typeof(AvaloniaPropertyDictionaryTests),
+ typeof(AvaloniaPropertyDictionaryTests),
new StyledPropertyMetadata());
}
diff --git a/tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs b/tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs
index e160c8dfa8..ef21100d1f 100644
--- a/tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs
+++ b/tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs
@@ -172,7 +172,7 @@ internal sealed class AvaloniaPropertyValueStoreOld
internal class MockProperty : StyledProperty
{
- public MockProperty(string name) : base(name, typeof(object), new StyledPropertyMetadata())
+ public MockProperty(string name) : base(name, typeof(object), typeof(object), new StyledPropertyMetadata())
{
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
index 3d4852c4ff..86249c66ff 100644
--- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
+++ b/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);
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index 9d2ffb1fa2..51745e1687 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/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 Items { get; }
public ObservableCollection SelectedItems { get; }
}
@@ -2267,6 +2317,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
private class TestSelector : SelectingItemsControl
{
+ public new static readonly DirectProperty SelectedItemsProperty =
+ SelectingItemsControl.SelectedItemsProperty;
+
public TestSelector()
{
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
index 78ac6bb3e2..a4181fa8e8 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
+++ b/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)
{
diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
index 15ba871e8c..0d3eb80ae7 100644
--- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
+++ b/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;
}
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
index d4d188f584..241ebc2d38 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
+++ b/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 FooProperty =
AvaloniaProperty.Register("Foo");
- public IAvaloniaReadOnlyList 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 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