Browse Source

fix(controls): fix radio MenuItem grouping in MenuFlyout (#21356)

pull/21371/head
yoyo 5 months ago
committed by GitHub
parent
commit
60db99ac89
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
  2. 9
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  3. 16
      src/Avalonia.Controls/MenuItem.cs
  4. 17
      src/Avalonia.Controls/RadioButtonGroupManager.cs
  5. 41
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

9
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml

@ -54,6 +54,15 @@
IsHitTestVisible="False" />
</MenuItem.Icon>
</MenuItem>
<MenuItem GroupName="Radio-ableMenuItem"
Header="Menu Item with _RadioButton1"
IsChecked="True"
StaysOpenOnClick="True"
ToggleType="Radio" />
<MenuItem GroupName="Radio-ableMenuItem"
Header="Menu Item with _RadioButton2"
StaysOpenOnClick="True"
ToggleType="Radio" />
</MenuFlyout>
</Border.ContextFlyout>
<TextBlock Text="Defined in XAML" />

9
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -43,6 +43,15 @@
IsHitTestVisible="False" />
</MenuItem.Icon>
</MenuItem>
<MenuItem GroupName="Radio-ableMenuItem"
Header="Menu Item with _RadioButton1"
IsChecked="True"
StaysOpenOnClick="True"
ToggleType="Radio" />
<MenuItem GroupName="Radio-ableMenuItem"
Header="Menu Item with _RadioButton2"
StaysOpenOnClick="True"
ToggleType="Radio" />
<MenuItem Header="Menu Item that won't close on click" StaysOpenOnClick="True" />
</ContextMenu>
</Border.ContextMenu>

16
src/Avalonia.Controls/MenuItem.cs

@ -14,6 +14,7 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -387,7 +388,9 @@ namespace Avalonia.Controls
/// <inheritdoc/>
IEnumerable<IMenuItem> IMenuElement.SubItems => LogicalChildren.OfType<IMenuItem>();
private IMenuInteractionHandler? MenuInteractionHandler => this.FindLogicalAncestorOfType<MenuBase>()?.InteractionHandler;
private IMenuInteractionHandler? MenuInteractionHandler =>
this.FindLogicalAncestorOfType<MenuBase>()?.InteractionHandler ??
this.FindAncestorOfType<MenuBase>()?.InteractionHandler;
/// <summary>
/// Opens the submenu.
@ -470,6 +473,7 @@ namespace Avalonia.Controls
base.OnAttachedToVisualTree(e);
TryUpdateCanExecute();
RegisterInMenuInteractionHandler();
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
@ -908,6 +912,16 @@ namespace Avalonia.Controls
SelectedItem = null;
}
private void RegisterInMenuInteractionHandler()
{
if (ToggleType != MenuItemToggleType.Radio || MenuInteractionHandler is not DefaultMenuInteractionHandler handler)
{
return;
}
handler.OnGroupOrTypeChanged(this, null);
}
void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => CanExecuteChangedHandler(sender, e);
void IClickableControl.RaiseClick()

17
src/Avalonia.Controls/RadioButtonGroupManager.cs

@ -41,6 +41,23 @@ internal class RadioButtonGroupManager
_registeredGroups.Add(groupName, group);
}
int i = 0;
while (i < group.Count)
{
if (!group[i].TryGetTarget(out var current))
{
group.RemoveAt(i);
continue;
}
if (current == radioButton)
{
return;
}
i++;
}
group.Add(new WeakReference<IRadioButton>(radioButton));
}
}

41
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -560,6 +560,47 @@ namespace Avalonia.Controls.UnitTests
Assert.True(menuItem3.IsChecked);
}
[Fact]
public void Radio_MenuItem_In_Same_Group_In_MenuFlyout_Is_Unchecked()
{
using var app = Application();
var menuItem1 = new MenuItem
{
GroupName = "A",
IsChecked = true,
StaysOpenOnClick = true,
ToggleType = MenuItemToggleType.Radio,
};
var menuItem2 = new MenuItem
{
GroupName = "A",
IsChecked = false,
StaysOpenOnClick = true,
ToggleType = MenuItemToggleType.Radio,
};
var flyout = new MenuFlyout
{
Items =
{
menuItem1,
menuItem2,
}
};
var button = new Button { ContextFlyout = flyout };
var window = new Window { Content = button };
window.Show();
flyout.ShowAt(button);
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded, TestContext.Current.CancellationToken);
menuItem2.IsChecked = true;
Assert.False(menuItem1.IsChecked);
Assert.True(menuItem2.IsChecked);
}
[Fact]
public void Radio_Menu_Group_Can_Be_Changed_In_Runtime()
{

Loading…
Cancel
Save