diff --git a/Avalonia.sln b/Avalonia.sln
index d5419365ac..98ad0cadae 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -91,7 +91,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
- build\ApiDiff.props = build\ApiDiff.props
build\AvaloniaPublicKey.props = build\AvaloniaPublicKey.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
diff --git a/NuGet.Config b/NuGet.Config
index 7d2bd8abd2..2042fea360 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -3,8 +3,7 @@
-
-
+
diff --git a/build/ApiDiff.props b/build/ApiDiff.props
deleted file mode 100644
index b0b1942f60..0000000000
--- a/build/ApiDiff.props
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- 0.10.0
- $(PackageId)
- Avalonia
- false
-
-
-
-
-
-
-
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index a4999b8df3..ea6ba93fdb 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -538,10 +538,10 @@
{
_lastKeyHandled = false;
- [[self inputContext] handleEvent:event];
+ [self keyboardEvent:event withType:KeyDown];
if(!_lastKeyHandled){
- [self keyboardEvent:event withType:KeyDown];
+ [[self inputContext] handleEvent:event];
}
}
diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm
index d0c8d7a9db..9fe0ff3c60 100644
--- a/native/Avalonia.Native/src/OSX/automation.mm
+++ b/native/Avalonia.Native/src/OSX/automation.mm
@@ -73,6 +73,13 @@ private:
if (peer->IsRootProvider())
{
auto window = peer->RootProvider_GetWindow();
+
+ if (window == nullptr)
+ {
+ NSLog(@"IRootProvider.PlatformImpl returned null or a non-WindowBaseImpl.");
+ return nil;
+ }
+
auto holder = dynamic_cast(window);
auto view = holder->GetNSView();
return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view];
@@ -284,8 +291,8 @@ private:
- (id)accessibilityWindow
{
- id topLevel = [self accessibilityTopLevelUIElement];
- return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil;
+ auto rootPeer = _peer->GetVisualRoot();
+ return [AvnAccessibilityElement acquire:rootPeer];
}
- (BOOL)isAccessibilityExpanded
diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm
index d80df68fea..56b7ce97e0 100644
--- a/native/Avalonia.Native/src/OSX/platformthreading.mm
+++ b/native/Avalonia.Native/src/OSX/platformthreading.mm
@@ -17,7 +17,6 @@ public:
Cancelled = true;
if(Running)
{
- Running = false;
if(![NSThread isMainThread])
{
AddRef();
@@ -28,22 +27,22 @@ public:
});
return;
};
+
+ Running = false;
if(IsApp)
[NSApp stop:nil];
- else
- {
- // Wakeup the event loop
- NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
- location:NSMakePoint(0, 0)
- modifierFlags:0
- timestamp:0
- windowNumber:0
- context:nil
- subtype:0
- data1:0
- data2:0];
- [NSApp postEvent:event atStart:YES];
- }
+
+ // Wakeup the event loop
+ NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
+ location:NSMakePoint(0, 0)
+ modifierFlags:0
+ timestamp:0
+ windowNumber:0
+ context:nil
+ subtype:0
+ data1:0
+ data2:0];
+ [NSApp postEvent:event atStart:YES];
}
};
};
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index 4258572df7..ebfa325067 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -5,7 +5,7 @@
-
+
all
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 16eb09de65..9aeb71f649 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -15,7 +15,6 @@
-
diff --git a/src/Avalonia.Base/Media/RenderOptions.cs b/src/Avalonia.Base/Media/RenderOptions.cs
index 639498543b..1ac2520919 100644
--- a/src/Avalonia.Base/Media/RenderOptions.cs
+++ b/src/Avalonia.Base/Media/RenderOptions.cs
@@ -8,12 +8,13 @@ namespace Avalonia.Media
public EdgeMode EdgeMode { get; init; }
public TextRenderingMode TextRenderingMode { get; init; }
public BitmapBlendingMode BitmapBlendingMode { get; init; }
+ public bool? RequiresFullOpacityHandling { get; init; }
///
/// Gets the value of the BitmapInterpolationMode attached property for a visual.
///
/// The control.
- /// The control's left coordinate.
+ /// The value.
public static BitmapInterpolationMode GetBitmapInterpolationMode(Visual visual)
{
return visual.RenderOptions.BitmapInterpolationMode;
@@ -23,7 +24,7 @@ namespace Avalonia.Media
/// Sets the value of the BitmapInterpolationMode attached property for a visual.
///
/// The control.
- /// The left value.
+ /// The value.
public static void SetBitmapInterpolationMode(Visual visual, BitmapInterpolationMode value)
{
visual.RenderOptions = visual.RenderOptions with { BitmapInterpolationMode = value };
@@ -33,7 +34,7 @@ namespace Avalonia.Media
/// Gets the value of the BitmapBlendingMode attached property for a visual.
///
/// The control.
- /// The control's left coordinate.
+ /// The value.
public static BitmapBlendingMode GetBitmapBlendingMode(Visual visual)
{
return visual.RenderOptions.BitmapBlendingMode;
@@ -53,7 +54,7 @@ namespace Avalonia.Media
/// Gets the value of the EdgeMode attached property for a visual.
///
/// The control.
- /// The control's left coordinate.
+ /// The value.
public static EdgeMode GetEdgeMode(Visual visual)
{
return visual.RenderOptions.EdgeMode;
@@ -63,7 +64,7 @@ namespace Avalonia.Media
/// Sets the value of the EdgeMode attached property for a visual.
///
/// The control.
- /// The left value.
+ /// The value.
public static void SetEdgeMode(Visual visual, EdgeMode value)
{
visual.RenderOptions = visual.RenderOptions with { EdgeMode = value };
@@ -73,7 +74,7 @@ namespace Avalonia.Media
/// Gets the value of the TextRenderingMode attached property for a visual.
///
/// The control.
- /// The control's left coordinate.
+ /// The value.
public static TextRenderingMode GetTextRenderingMode(Visual visual)
{
return visual.RenderOptions.TextRenderingMode;
@@ -83,12 +84,32 @@ namespace Avalonia.Media
/// Sets the value of the TextRenderingMode attached property for a visual.
///
/// The control.
- /// The left value.
+ /// The value.
public static void SetTextRenderingMode(Visual visual, TextRenderingMode value)
{
visual.RenderOptions = visual.RenderOptions with { TextRenderingMode = value };
}
+ ///
+ /// Gets the value of the RequiresFullOpacityHandling attached property for a visual.
+ ///
+ /// The control.
+ /// The value.
+ public static bool? GetRequiresFullOpacityHandling(Visual visual)
+ {
+ return visual.RenderOptions.RequiresFullOpacityHandling;
+ }
+
+ ///
+ /// Sets the value of the RequiresFullOpacityHandling attached property for a visual.
+ ///
+ /// The control.
+ /// The value.
+ public static void SetRequiresFullOpacityHandling(Visual visual, bool? value)
+ {
+ visual.RenderOptions = visual.RenderOptions with { RequiresFullOpacityHandling = value };
+ }
+
public RenderOptions MergeWith(RenderOptions other)
{
var bitmapInterpolationMode = BitmapInterpolationMode;
@@ -119,12 +140,20 @@ namespace Avalonia.Media
bitmapBlendingMode = other.BitmapBlendingMode;
}
+ var requiresFullOpacityHandling = RequiresFullOpacityHandling;
+
+ if (requiresFullOpacityHandling == null)
+ {
+ requiresFullOpacityHandling = other.RequiresFullOpacityHandling;
+ }
+
return new RenderOptions
{
BitmapInterpolationMode = bitmapInterpolationMode,
EdgeMode = edgeMode,
TextRenderingMode = textRenderingMode,
- BitmapBlendingMode = bitmapBlendingMode
+ BitmapBlendingMode = bitmapBlendingMode,
+ RequiresFullOpacityHandling = requiresFullOpacityHandling
};
}
}
diff --git a/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs b/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs
index c830ca2c49..27aca436b8 100644
--- a/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs
+++ b/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs
@@ -2,5 +2,7 @@ namespace Avalonia.Rendering.Composition;
internal interface ICompositionTargetDebugEvents
{
+ int RenderedVisuals { get; }
+ void IncrementRenderedVisuals();
void RectInvalidated(Rect rc);
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs
index c1037d5c67..51414c2250 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs
@@ -38,6 +38,7 @@ partial class ServerCompositionVisual
CompositionVisualChangedFields.Size
| CompositionVisualChangedFields.SizeAnimated
| CompositionVisualChangedFields.ClipToBounds
+ | CompositionVisualChangedFields.Clip
| CompositionVisualChangedFields.ClipToBoundsAnimated;
partial void OnFieldsDeserialized(CompositionVisualChangedFields changed)
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
index 45515a37e2..aeb228282e 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
@@ -38,6 +38,7 @@ namespace Avalonia.Rendering.Composition.Server
return;
Root!.RenderedVisuals++;
+ Root!.DebugEvents?.IncrementRenderedVisuals();
var boundsRect = new Rect(new Size(Size.X, Size.Y));
@@ -182,11 +183,24 @@ namespace Avalonia.Rendering.Composition.Server
if (_clipSizeDirty || positionChanged)
{
- _transformedClipBounds = ClipToBounds
- ? new Rect(new Size(Size.X, Size.Y))
- .TransformToAABB(GlobalTransformMatrix)
- : null;
+ Rect? transformedVisualBounds = null;
+ Rect? transformedClipBounds = null;
+ if (ClipToBounds)
+ transformedVisualBounds = new Rect(new Size(Size.X, Size.Y)).TransformToAABB(GlobalTransformMatrix);
+
+ if (Clip != null)
+ transformedClipBounds = Clip.Bounds.TransformToAABB(GlobalTransformMatrix);
+
+ if (transformedVisualBounds != null && transformedClipBounds != null)
+ _transformedClipBounds = transformedVisualBounds.Value.Intersect(transformedClipBounds.Value);
+ else if (transformedVisualBounds != null)
+ _transformedClipBounds = transformedVisualBounds;
+ else if (transformedClipBounds != null)
+ _transformedClipBounds = transformedClipBounds;
+ else
+ _transformedClipBounds = null;
+
_clipSizeDirty = false;
}
diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj
index 5a31053bdc..8a36e6900a 100644
--- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj
+++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj
@@ -17,7 +17,6 @@
-
diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
index 6556ce721e..6c1e019603 100644
--- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
+++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
@@ -14,7 +14,6 @@
-
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
index 0cc620dae9..2d40721bbf 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
@@ -170,26 +170,28 @@
CornerRadius="{TemplateBinding CornerRadius}">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
index 3d3fe35d29..fb7cdd87ed 100644
--- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Avalonia.Automation.Provider;
namespace Avalonia.Automation.Peers
{
@@ -115,9 +116,14 @@ namespace Avalonia.Automation.Peers
///
/// Gets the that is the parent of this .
///
- ///
public AutomationPeer? GetParent() => GetParentCore();
+ ///
+ /// Gets the that is the root of this 's
+ /// visual tree.
+ ///
+ public AutomationPeer? GetVisualRoot() => GetVisualRootCore();
+
///
/// Gets a value that indicates whether the element that is associated with this automation
/// peer currently has keyboard focus.
@@ -247,6 +253,21 @@ namespace Avalonia.Automation.Peers
return GetAutomationControlTypeCore();
}
+ protected virtual AutomationPeer? GetVisualRootCore()
+ {
+ var peer = this;
+ var parent = peer.GetParent();
+
+ while (peer.GetProvider() is null && parent is not null)
+ {
+ peer = parent;
+ parent = peer.GetParent();
+ }
+
+ return peer;
+ }
+
+
protected virtual bool IsContentElementOverrideCore()
{
return IsControlElement() && IsContentElementCore();
diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
index c19d887230..69f267a605 100644
--- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
@@ -120,6 +120,13 @@ namespace Avalonia.Automation.Peers
return _parent;
}
+ protected override AutomationPeer? GetVisualRootCore()
+ {
+ if (Owner.GetVisualRoot() is Control c)
+ return CreatePeerForElement(c);
+ return null;
+ }
+
///
/// Invalidates the peer's children and causes a re-read from .
///
diff --git a/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs
index db16bf0a53..64727c43c5 100644
--- a/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs
@@ -28,7 +28,7 @@ namespace Avalonia.Automation.Peers
if (!_searchedForScrollable)
{
if (Owner.GetValue(ListBox.ScrollProperty) is Control scrollable)
- _scroller = GetOrCreate(scrollable) as IScrollProvider;
+ _scroller = GetOrCreate(scrollable).GetProvider();
_searchedForScrollable = true;
}
diff --git a/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs
index aea91b5e26..dab8c45567 100644
--- a/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Automation.Peers
if (Owner.Parent is Control parent)
{
var parentPeer = GetOrCreate(parent);
- return parentPeer as ISelectionProvider;
+ return parentPeer.GetProvider();
}
return null;
diff --git a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs
index 9ec65592fa..ceb695422d 100644
--- a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
+using System.Globalization;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Input;
@@ -32,7 +33,21 @@ namespace Avalonia.Automation.Peers
public AutomationPeer? GetPeerFromPoint(Point p)
{
var hit = Owner.GetVisualAt(p)?.FindAncestorOfType(includeSelf: true);
- return hit is object ? GetOrCreate(hit) : null;
+
+ if (hit is null)
+ return null;
+
+ var peer = GetOrCreate(hit);
+
+ while (peer != this && peer.GetProvider() is { } embedded)
+ {
+ var embeddedHit = embedded.GetPeerFromPoint(p);
+ if (embeddedHit is null)
+ break;
+ peer = embeddedHit;
+ }
+
+ return peer;
}
protected void StartTrackingFocus()
diff --git a/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs b/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs
new file mode 100644
index 0000000000..1b1caef182
--- /dev/null
+++ b/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs
@@ -0,0 +1,33 @@
+using System;
+using Avalonia.Automation.Peers;
+
+namespace Avalonia.Automation.Provider
+{
+ ///
+ /// Exposure methods and properties to support UI Automation client access to the root of an
+ /// automation tree hosted by another UI framework.
+ ///
+ ///
+ /// This interface is implemented by the class, and can be used
+ /// to embed an automation tree from a 3rd party UI framework that wishes to use Avalonia's
+ /// automation support.
+ ///
+ public interface IEmbeddedRootProvider
+ {
+ ///
+ /// Gets the currently focused element.
+ ///
+ AutomationPeer? GetFocus();
+
+ ///
+ /// Gets the element at the specified point, expressed in top-level coordinates.
+ ///
+ /// The point.
+ AutomationPeer? GetPeerFromPoint(Point p);
+
+ ///
+ /// Raised by the automation peer when the focus changes.
+ ///
+ event EventHandler? FocusChanged;
+ }
+}
diff --git a/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs b/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs
index ce38059559..6a266da5c5 100644
--- a/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs
+++ b/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs
@@ -4,11 +4,36 @@ using Avalonia.Platform;
namespace Avalonia.Automation.Provider
{
+ ///
+ /// Exposes methods and properties to support UI Automation client access to the root of an
+ /// automation tree.
+ ///
+ ///
+ /// This interface is implemented by the class, and should only
+ /// be implemented on true root elements, such as Windows. To embed an automation tree, use
+ /// instead.
+ ///
public interface IRootProvider
{
+ ///
+ /// Gets the platform implementation of the TopLevel for the element.
+ ///
ITopLevelImpl? PlatformImpl { get; }
+
+ ///
+ /// Gets the currently focused element.
+ ///
AutomationPeer? GetFocus();
+
+ ///
+ /// Gets the element at the specified point, expressed in top-level coordinates.
+ ///
+ /// The point.
AutomationPeer? GetPeerFromPoint(Point p);
+
+ ///
+ /// Raised by the automation peer when the focus changes.
+ ///
event EventHandler? FocusChanged;
}
}
diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj
index 304454777f..0dda861448 100644
--- a/src/Avalonia.Controls/Avalonia.Controls.csproj
+++ b/src/Avalonia.Controls/Avalonia.Controls.csproj
@@ -6,7 +6,6 @@
-
diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs
index 867dca2be1..3564ce1faa 100644
--- a/src/Avalonia.Controls/ContentControl.cs
+++ b/src/Avalonia.Controls/ContentControl.cs
@@ -40,11 +40,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty VerticalContentAlignmentProperty =
AvaloniaProperty.Register(nameof(VerticalContentAlignment));
- static ContentControl()
- {
- ContentProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e));
- }
-
///
/// Gets or sets the content to display.
///
@@ -100,6 +95,16 @@ namespace Avalonia.Controls
{
return RegisterContentPresenter(presenter);
}
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == ContentProperty)
+ {
+ ContentChanged(change);
+ }
+ }
///
/// Called when an is registered with the control.
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index e3a419d7b3..78f6c5f77d 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -114,7 +114,6 @@ namespace Avalonia.Controls
///
static ContextMenu()
{
- ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
PlacementProperty.OverrideDefaultValue(PlacementMode.Pointer);
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue(AccessibilityView.Control);
@@ -216,18 +215,23 @@ namespace Avalonia.Controls
if (e.OldValue is ContextMenu oldMenu)
{
control.ContextRequested -= ControlContextRequested;
+ control.AttachedToVisualTree -= ControlOnAttachedToVisualTree;
control.DetachedFromVisualTree -= ControlDetachedFromVisualTree;
oldMenu._attachedControls?.Remove(control);
((ISetLogicalParent?)oldMenu._popup)?.SetParent(null);
}
- if (e.NewValue is ContextMenu newMenu)
+ if (e.NewValue is ContextMenu)
{
- newMenu._attachedControls ??= new List();
- newMenu._attachedControls.Add(control);
control.ContextRequested += ControlContextRequested;
+ control.AttachedToVisualTree += ControlOnAttachedToVisualTree;
control.DetachedFromVisualTree += ControlDetachedFromVisualTree;
}
+
+ if (control.IsAttachedToVisualTree)
+ {
+ AttachControlToContextMenu(control);
+ }
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
@@ -428,11 +432,25 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+
+
+ private static void ControlOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
+ {
+ AttachControlToContextMenu(sender);
+ }
+
+ private static void AttachControlToContextMenu(object? sender)
+ {
+ if (sender is Control { ContextMenu: { } contextMenu } control)
+ {
+ contextMenu._attachedControls ??= new List();
+ contextMenu._attachedControls.Add(control);
+ }
+ }
private static void ControlDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
- if (sender is Control control
- && control.ContextMenu is ContextMenu contextMenu)
+ if (sender is Control { ContextMenu: { } contextMenu } control)
{
if (contextMenu._popup?.Parent == control)
{
@@ -440,6 +458,7 @@ namespace Avalonia.Controls
}
contextMenu.Close();
+ contextMenu._attachedControls?.Remove(control);
}
}
diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs
index f9aa5fc7a0..d823455e13 100644
--- a/src/Avalonia.Controls/Primitives/Track.cs
+++ b/src/Avalonia.Controls/Primitives/Track.cs
@@ -52,7 +52,7 @@ namespace Avalonia.Controls.Primitives
ThumbProperty.Changed.AddClassHandler