diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml
index a6f463fdcb..353e01dca7 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml
+++ b/samples/IntegrationTestApp/MainWindow.axaml
@@ -153,6 +153,9 @@
+
+
+
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 2c9efc7767..d89d6f3690 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -664,14 +664,12 @@ namespace Avalonia
/// The property that has changed.
/// The old property value.
/// The new property value.
- /// The priority of the binding that produced the value.
protected void RaisePropertyChanged(
DirectPropertyBase property,
- Optional oldValue,
- BindingValue newValue,
- BindingPriority priority = BindingPriority.LocalValue)
+ T oldValue,
+ T newValue)
{
- RaisePropertyChanged(property, oldValue, newValue, priority, true);
+ RaisePropertyChanged(property, oldValue, newValue, BindingPriority.LocalValue, true);
}
///
@@ -720,7 +718,7 @@ namespace Avalonia
///
/// True if the value changed, otherwise false.
///
- protected bool SetAndRaise(AvaloniaProperty property, ref T field, T value)
+ protected bool SetAndRaise(DirectPropertyBase property, ref T field, T value)
{
VerifyAccess();
diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs
index 2cdb973174..5b8dac2f53 100644
--- a/src/Avalonia.Base/StyledElement.cs
+++ b/src/Avalonia.Base/StyledElement.cs
@@ -524,13 +524,7 @@ namespace Avalonia
NotifyResourcesChanged();
}
-#nullable disable
- RaisePropertyChanged(
- ParentProperty,
- new Optional(old),
- new BindingValue(Parent),
- BindingPriority.LocalValue);
-#nullable enable
+ RaisePropertyChanged(ParentProperty, old, Parent);
}
}
diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs
index 87bb1d3790..8b0cc06136 100644
--- a/src/Avalonia.Base/Visual.cs
+++ b/src/Avalonia.Base/Visual.cs
@@ -573,7 +573,7 @@ namespace Avalonia
/// The new visual parent.
protected virtual void OnVisualParentChanged(Visual? oldParent, Visual? newParent)
{
- RaisePropertyChanged(VisualParentProperty, oldParent, newParent, BindingPriority.LocalValue);
+ RaisePropertyChanged(VisualParentProperty, oldParent, newParent);
}
internal override ParametrizedLogger? GetBindingWarningLogger(
diff --git a/src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs
new file mode 100644
index 0000000000..42b15eec96
--- /dev/null
+++ b/src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs
@@ -0,0 +1,22 @@
+using Avalonia.Automation.Peers;
+
+namespace Avalonia.Controls.Automation.Peers
+{
+ public class SliderAutomationPeer : RangeBaseAutomationPeer
+ {
+ public SliderAutomationPeer(Slider owner) : base(owner)
+ {
+ }
+
+ override protected string GetClassNameCore()
+ {
+ return "Slider";
+ }
+
+ override protected AutomationControlType GetAutomationControlTypeCore()
+ {
+ return AutomationControlType.Slider;
+ }
+
+ }
+}
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 065c4ff2e5..2ee32b0dda 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -345,10 +345,7 @@ namespace Avalonia.Controls.Primitives
if (_oldSelectedItems != SelectedItems)
{
- RaisePropertyChanged(
- SelectedItemsProperty,
- new Optional(_oldSelectedItems),
- new BindingValue(SelectedItems));
+ RaisePropertyChanged(SelectedItemsProperty, _oldSelectedItems, SelectedItems);
_oldSelectedItems = SelectedItems;
}
}
@@ -909,10 +906,7 @@ namespace Avalonia.Controls.Primitives
else if (e.PropertyName == nameof(InternalSelectionModel.WritableSelectedItems) &&
_oldSelectedItems != (Selection as InternalSelectionModel)?.SelectedItems)
{
- RaisePropertyChanged(
- SelectedItemsProperty,
- new Optional(_oldSelectedItems),
- new BindingValue(SelectedItems));
+ RaisePropertyChanged(SelectedItemsProperty, _oldSelectedItems, SelectedItems);
_oldSelectedItems = SelectedItems;
}
else if (e.PropertyName == nameof(ISelectionModel.Source))
diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs
index 828bf2a1fb..7de726a932 100644
--- a/src/Avalonia.Controls/Slider.cs
+++ b/src/Avalonia.Controls/Slider.cs
@@ -10,6 +10,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Utilities;
using Avalonia.Automation;
+using Avalonia.Controls.Automation.Peers;
namespace Avalonia.Controls
{
@@ -380,6 +381,11 @@ namespace Avalonia.Controls
}
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new SliderAutomationPeer(this);
+ }
+
///
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
diff --git a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
index 5ca4ef63bf..48ebd4068e 100644
--- a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
+++ b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
@@ -20,7 +20,6 @@ namespace Avalonia.Win32.Automation
IRawElementProviderSimple,
IRawElementProviderSimple2,
IRawElementProviderFragment,
- IRawElementProviderAdviseEvents,
IInvokeProvider
{
private static Dictionary s_propertyMap = new()
@@ -47,14 +46,31 @@ namespace Avalonia.Win32.Automation
private static ConditionalWeakTable s_nodes = new();
private readonly int[] _runtimeId;
- private int _raiseFocusChanged;
- private int _raisePropertyChanged;
public AutomationNode(AutomationPeer peer)
{
_runtimeId = new int[] { 3, GetHashCode() };
Peer = peer;
s_nodes.Add(peer, this);
+ peer.ChildrenChanged += Peer_ChildrenChanged;
+ peer.PropertyChanged += Peer_PropertyChanged;
+ }
+
+ private void Peer_ChildrenChanged(object? sender, EventArgs e)
+ {
+ ChildrenChanged();
+ }
+
+ private void Peer_PropertyChanged(object? sender, AutomationPropertyChangedEventArgs e)
+ {
+ if (s_propertyMap.TryGetValue(e.Property, out var id))
+ {
+ UiaCoreProviderApi.UiaRaiseAutomationPropertyChangedEvent(
+ this,
+ (int)id,
+ e.OldValue as IConvertible,
+ e.NewValue as IConvertible);
+ }
}
public AutomationPeer Peer { get; protected set; }
@@ -86,14 +102,6 @@ namespace Avalonia.Win32.Automation
0);
}
- public void PropertyChanged(AutomationProperty property, object? oldValue, object? newValue)
- {
- if (_raisePropertyChanged > 0 && s_propertyMap.TryGetValue(property, out var id))
- {
- UiaCoreProviderApi.UiaRaiseAutomationPropertyChangedEvent(this, (int)id, oldValue, newValue);
- }
- }
-
[return: MarshalAs(UnmanagedType.IUnknown)]
public virtual object? GetPatternProvider(int patternId)
{
@@ -188,32 +196,6 @@ namespace Avalonia.Win32.Automation
void IRawElementProviderSimple2.ShowContextMenu() => InvokeSync(() => Peer.ShowContextMenu());
void IInvokeProvider.Invoke() => InvokeSync((AAP.IInvokeProvider x) => x.Invoke());
- void IRawElementProviderAdviseEvents.AdviseEventAdded(int eventId, int[] properties)
- {
- switch ((UiaEventId)eventId)
- {
- case UiaEventId.AutomationPropertyChanged:
- ++_raisePropertyChanged;
- break;
- case UiaEventId.AutomationFocusChanged:
- ++_raiseFocusChanged;
- break;
- }
- }
-
- void IRawElementProviderAdviseEvents.AdviseEventRemoved(int eventId, int[] properties)
- {
- switch ((UiaEventId)eventId)
- {
- case UiaEventId.AutomationPropertyChanged:
- --_raisePropertyChanged;
- break;
- case UiaEventId.AutomationFocusChanged:
- --_raiseFocusChanged;
- break;
- }
- }
-
protected void InvokeSync(Action action)
{
if (Dispatcher.UIThread.CheckAccess())
@@ -266,15 +248,6 @@ namespace Avalonia.Win32.Automation
throw new NotSupportedException();
}
- protected void RaiseFocusChanged(AutomationNode? focused)
- {
- if (_raiseFocusChanged > 0)
- {
- UiaCoreProviderApi.UiaRaiseAutomationEvent(
- focused,
- (int)UiaEventId.AutomationFocusChanged);
- }
- }
private AutomationNode? GetRoot()
{
diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
index 3d8b4995ad..ff8ff69d5e 100644
--- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
+++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
@@ -10,8 +10,11 @@ namespace Avalonia.Win32.Automation
{
[RequiresUnreferencedCode("Requires .NET COM interop")]
internal class RootAutomationNode : AutomationNode,
- IRawElementProviderFragmentRoot
+ IRawElementProviderFragmentRoot,
+ IRawElementProviderAdviseEvents
{
+ private int _raiseFocusChanged;
+
public RootAutomationNode(AutomationPeer peer)
: base(peer)
{
@@ -42,6 +45,36 @@ namespace Avalonia.Win32.Automation
return GetOrCreate(focus);
}
+ void IRawElementProviderAdviseEvents.AdviseEventAdded(int eventId, int[] properties)
+ {
+ switch ((UiaEventId)eventId)
+ {
+ case UiaEventId.AutomationFocusChanged:
+ ++_raiseFocusChanged;
+ break;
+ }
+ }
+
+ void IRawElementProviderAdviseEvents.AdviseEventRemoved(int eventId, int[] properties)
+ {
+ switch ((UiaEventId)eventId)
+ {
+ case UiaEventId.AutomationFocusChanged:
+ --_raiseFocusChanged;
+ break;
+ }
+ }
+
+ protected void RaiseFocusChanged(AutomationNode? focused)
+ {
+ if (_raiseFocusChanged > 0)
+ {
+ UiaCoreProviderApi.UiaRaiseAutomationEvent(
+ focused,
+ (int)UiaEventId.AutomationFocusChanged);
+ }
+ }
+
public void FocusChanged(object? sender, EventArgs e)
{
RaiseFocusChanged(GetOrCreate(Peer.GetFocus()));
diff --git a/tests/Avalonia.IntegrationTests.Appium/SliderTests.cs b/tests/Avalonia.IntegrationTests.Appium/SliderTests.cs
new file mode 100644
index 0000000000..7fa5eb83ee
--- /dev/null
+++ b/tests/Avalonia.IntegrationTests.Appium/SliderTests.cs
@@ -0,0 +1,35 @@
+using System;
+using OpenQA.Selenium.Appium;
+using OpenQA.Selenium.Interactions;
+using Xunit;
+
+namespace Avalonia.IntegrationTests.Appium
+{
+ [Collection("Default")]
+ public class SliderTests
+ {
+ private readonly AppiumDriver _session;
+
+ public SliderTests(TestAppFixture fixture)
+ {
+ _session = fixture.Session;
+
+ var tabs = _session.FindElementByAccessibilityId("MainTabs");
+ var tab = tabs.FindElementByName("SliderTab");
+ tab.Click();
+ }
+
+ [Fact]
+ public void Changes_Value_When_Clicking_Increase_Button()
+ {
+ var slider = _session.FindElementByAccessibilityId("Slider");
+
+ // slider.Text gets the Slider value
+ Assert.True(double.Parse(slider.Text) == 30);
+
+ new Actions(_session).Click(slider).Perform();
+
+ Assert.Equal(50, Math.Round(double.Parse(slider.Text)));
+ }
+ }
+}