csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.8 KiB
200 lines
6.8 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Rendering;
|
|
using Avalonia.VisualTree;
|
|
|
|
namespace Avalonia.Controls
|
|
{
|
|
/// <summary>
|
|
/// Represents a button that allows a user to select a single option from a group of options.
|
|
/// </summary>
|
|
public class RadioButton : ToggleButton
|
|
{
|
|
private class RadioButtonGroupManager
|
|
{
|
|
public static readonly RadioButtonGroupManager Default = new RadioButtonGroupManager();
|
|
static readonly ConditionalWeakTable<IRenderRoot, RadioButtonGroupManager> s_registeredVisualRoots
|
|
= new ConditionalWeakTable<IRenderRoot, RadioButtonGroupManager>();
|
|
|
|
readonly Dictionary<string, List<WeakReference<RadioButton>>> s_registeredGroups
|
|
= new Dictionary<string, List<WeakReference<RadioButton>>>();
|
|
|
|
public static RadioButtonGroupManager GetOrCreateForRoot(IRenderRoot? root)
|
|
{
|
|
if (root == null)
|
|
return Default;
|
|
return s_registeredVisualRoots.GetValue(root, key => new RadioButtonGroupManager());
|
|
}
|
|
|
|
public void Add(RadioButton radioButton)
|
|
{
|
|
lock (s_registeredGroups)
|
|
{
|
|
string groupName = radioButton.GroupName!;
|
|
if (!s_registeredGroups.TryGetValue(groupName, out var group))
|
|
{
|
|
group = new List<WeakReference<RadioButton>>();
|
|
s_registeredGroups.Add(groupName, group);
|
|
}
|
|
group.Add(new WeakReference<RadioButton>(radioButton));
|
|
}
|
|
}
|
|
|
|
public void Remove(RadioButton radioButton, string oldGroupName)
|
|
{
|
|
lock (s_registeredGroups)
|
|
{
|
|
if (!string.IsNullOrEmpty(oldGroupName) && s_registeredGroups.TryGetValue(oldGroupName, out var group))
|
|
{
|
|
int i = 0;
|
|
while (i < group.Count)
|
|
{
|
|
if (!group[i].TryGetTarget(out var button) || button == radioButton)
|
|
{
|
|
group.RemoveAt(i);
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
if (group.Count == 0)
|
|
{
|
|
s_registeredGroups.Remove(oldGroupName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetChecked(RadioButton radioButton)
|
|
{
|
|
lock (s_registeredGroups)
|
|
{
|
|
string groupName = radioButton.GroupName!;
|
|
if (s_registeredGroups.TryGetValue(groupName, out var group))
|
|
{
|
|
int i = 0;
|
|
while (i < group.Count)
|
|
{
|
|
if (!group[i].TryGetTarget(out var current))
|
|
{
|
|
group.RemoveAt(i);
|
|
continue;
|
|
}
|
|
if (current != radioButton && current.IsChecked.GetValueOrDefault())
|
|
current.IsChecked = false;
|
|
i++;
|
|
}
|
|
if (group.Count == 0)
|
|
{
|
|
s_registeredGroups.Remove(groupName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static readonly DirectProperty<RadioButton, string?> GroupNameProperty =
|
|
AvaloniaProperty.RegisterDirect<RadioButton, string?>(
|
|
nameof(GroupName),
|
|
o => o.GroupName,
|
|
(o, v) => o.GroupName = v);
|
|
|
|
private string? _groupName;
|
|
private RadioButtonGroupManager? _groupManager;
|
|
|
|
public RadioButton()
|
|
{
|
|
this.GetObservable(IsCheckedProperty).Subscribe(IsCheckedChanged);
|
|
}
|
|
|
|
public string? GroupName
|
|
{
|
|
get { return _groupName; }
|
|
set { SetGroupName(value); }
|
|
}
|
|
|
|
protected override void Toggle()
|
|
{
|
|
if (!IsChecked.GetValueOrDefault())
|
|
{
|
|
IsChecked = true;
|
|
}
|
|
}
|
|
|
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
|
{
|
|
if (!string.IsNullOrEmpty(GroupName))
|
|
{
|
|
_groupManager?.Remove(this, GroupName);
|
|
|
|
_groupManager = RadioButtonGroupManager.GetOrCreateForRoot(e.Root);
|
|
|
|
_groupManager.Add(this);
|
|
}
|
|
base.OnAttachedToVisualTree(e);
|
|
}
|
|
|
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
|
{
|
|
base.OnDetachedFromVisualTree(e);
|
|
|
|
if (!string.IsNullOrEmpty(GroupName))
|
|
{
|
|
_groupManager?.Remove(this, GroupName);
|
|
}
|
|
}
|
|
|
|
private void SetGroupName(string? newGroupName)
|
|
{
|
|
var oldGroupName = GroupName;
|
|
if (newGroupName != oldGroupName)
|
|
{
|
|
if (!string.IsNullOrEmpty(oldGroupName))
|
|
{
|
|
_groupManager?.Remove(this, oldGroupName);
|
|
}
|
|
_groupName = newGroupName;
|
|
if (!string.IsNullOrEmpty(newGroupName))
|
|
{
|
|
if (_groupManager == null)
|
|
{
|
|
_groupManager = RadioButtonGroupManager.GetOrCreateForRoot(this.GetVisualRoot());
|
|
}
|
|
_groupManager.Add(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void IsCheckedChanged(bool? value)
|
|
{
|
|
var groupName = GroupName;
|
|
if (string.IsNullOrEmpty(groupName))
|
|
{
|
|
var parent = this.GetVisualParent();
|
|
|
|
if (value.GetValueOrDefault() && parent != null)
|
|
{
|
|
var siblings = parent
|
|
.GetVisualChildren()
|
|
.OfType<RadioButton>()
|
|
.Where(x => x != this);
|
|
|
|
foreach (var sibling in siblings)
|
|
{
|
|
if (sibling.IsChecked.GetValueOrDefault())
|
|
sibling.IsChecked = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (value.GetValueOrDefault() && _groupManager != null)
|
|
{
|
|
_groupManager.SetChecked(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|