|
@@ -1,4 +1,5 @@
|
|
using System;
|
|
using System;
|
|
|
|
+using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Linq;
|
|
using Avalonia.Controls.Generators;
|
|
using Avalonia.Controls.Generators;
|
|
@@ -9,18 +10,19 @@ using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Layout;
|
|
using Avalonia.Layout;
|
|
using Avalonia.LogicalTree;
|
|
using Avalonia.LogicalTree;
|
|
|
|
+using Avalonia.Styling;
|
|
|
|
|
|
namespace Avalonia.Controls
|
|
namespace Avalonia.Controls
|
|
{
|
|
{
|
|
/// <summary>
|
|
/// <summary>
|
|
/// A control context menu.
|
|
/// A control context menu.
|
|
/// </summary>
|
|
/// </summary>
|
|
- public class ContextMenu : MenuBase
|
|
|
|
|
|
+ public class ContextMenu : MenuBase, ISetterValue
|
|
{
|
|
{
|
|
private static readonly ITemplate<IPanel> DefaultPanel =
|
|
private static readonly ITemplate<IPanel> DefaultPanel =
|
|
new FuncTemplate<IPanel>(() => new StackPanel { Orientation = Orientation.Vertical });
|
|
new FuncTemplate<IPanel>(() => new StackPanel { Orientation = Orientation.Vertical });
|
|
private Popup _popup;
|
|
private Popup _popup;
|
|
- private Control _attachedControl;
|
|
|
|
|
|
+ private List<Control> _attachedControls;
|
|
private IInputElement _previousFocus;
|
|
private IInputElement _previousFocus;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -74,13 +76,14 @@ namespace Avalonia.Controls
|
|
if (e.OldValue is ContextMenu oldMenu)
|
|
if (e.OldValue is ContextMenu oldMenu)
|
|
{
|
|
{
|
|
control.PointerReleased -= ControlPointerReleased;
|
|
control.PointerReleased -= ControlPointerReleased;
|
|
- oldMenu._attachedControl = null;
|
|
|
|
|
|
+ oldMenu._attachedControls?.Remove(control);
|
|
((ISetLogicalParent)oldMenu._popup)?.SetParent(null);
|
|
((ISetLogicalParent)oldMenu._popup)?.SetParent(null);
|
|
}
|
|
}
|
|
|
|
|
|
if (e.NewValue is ContextMenu newMenu)
|
|
if (e.NewValue is ContextMenu newMenu)
|
|
{
|
|
{
|
|
- newMenu._attachedControl = control;
|
|
|
|
|
|
+ newMenu._attachedControls ??= new List<Control>();
|
|
|
|
+ newMenu._attachedControls.Add(control);
|
|
control.PointerReleased += ControlPointerReleased;
|
|
control.PointerReleased += ControlPointerReleased;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -96,18 +99,22 @@ namespace Avalonia.Controls
|
|
/// <param name="control">The control.</param>
|
|
/// <param name="control">The control.</param>
|
|
public void Open(Control control)
|
|
public void Open(Control control)
|
|
{
|
|
{
|
|
- if (control is null && _attachedControl is null)
|
|
|
|
|
|
+ if (control is null && (_attachedControls is null || _attachedControls.Count == 0))
|
|
{
|
|
{
|
|
throw new ArgumentNullException(nameof(control));
|
|
throw new ArgumentNullException(nameof(control));
|
|
}
|
|
}
|
|
|
|
|
|
- if (control is object && _attachedControl is object && control != _attachedControl)
|
|
|
|
|
|
+ if (control is object &&
|
|
|
|
+ _attachedControls is object &&
|
|
|
|
+ !_attachedControls.Contains(control))
|
|
{
|
|
{
|
|
throw new ArgumentException(
|
|
throw new ArgumentException(
|
|
"Cannot show ContentMenu on a different control to the one it is attached to.",
|
|
"Cannot show ContentMenu on a different control to the one it is attached to.",
|
|
nameof(control));
|
|
nameof(control));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ control ??= _attachedControls[0];
|
|
|
|
+
|
|
if (IsOpen)
|
|
if (IsOpen)
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
@@ -126,7 +133,12 @@ namespace Avalonia.Controls
|
|
_popup.Closed += PopupClosed;
|
|
_popup.Closed += PopupClosed;
|
|
}
|
|
}
|
|
|
|
|
|
- ((ISetLogicalParent)_popup).SetParent(control);
|
|
|
|
|
|
+ if (_popup.Parent != control)
|
|
|
|
+ {
|
|
|
|
+ ((ISetLogicalParent)_popup).SetParent(null);
|
|
|
|
+ ((ISetLogicalParent)_popup).SetParent(control);
|
|
|
|
+ }
|
|
|
|
+
|
|
_popup.Child = this;
|
|
_popup.Child = this;
|
|
_popup.IsOpen = true;
|
|
_popup.IsOpen = true;
|
|
|
|
|
|
@@ -155,6 +167,17 @@ namespace Avalonia.Controls
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ void ISetterValue.Initialize(ISetter setter)
|
|
|
|
+ {
|
|
|
|
+ // ContextMenu can be assigned to the ContextMenu property in a setter. This overrides
|
|
|
|
+ // the behavior defined in Control which requires controls to be wrapped in a <template>.
|
|
|
|
+ if (!(setter is Setter s && s.Property == ContextMenuProperty))
|
|
|
|
+ {
|
|
|
|
+ throw new InvalidOperationException(
|
|
|
|
+ "Cannot use a control as a Setter value. Wrap the control in a <Template>.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
protected override IItemContainerGenerator CreateItemContainerGenerator()
|
|
protected override IItemContainerGenerator CreateItemContainerGenerator()
|
|
{
|
|
{
|
|
return new MenuItemContainerGenerator(this);
|
|
return new MenuItemContainerGenerator(this);
|
|
@@ -179,7 +202,7 @@ namespace Avalonia.Controls
|
|
SelectedIndex = -1;
|
|
SelectedIndex = -1;
|
|
IsOpen = false;
|
|
IsOpen = false;
|
|
|
|
|
|
- if (_attachedControl is null)
|
|
|
|
|
|
+ if (_attachedControls is null || _attachedControls.Count == 0)
|
|
{
|
|
{
|
|
((ISetLogicalParent)_popup).SetParent(null);
|
|
((ISetLogicalParent)_popup).SetParent(null);
|
|
}
|
|
}
|