Browse Source

Merge pull request #1827 from siegfriedpammer/dev/siegfriedpammer/radiobutton-groupname

Implement GroupName for RadioButton
pull/1835/head
Jeremy Koritzinsky 8 years ago
committed by GitHub
parent
commit
e03ed449f2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  2. 176
      src/Avalonia.Controls/RadioButton.cs
  3. 38
      tests/Avalonia.Controls.UnitTests/RadioButtonTests.cs

13
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -22,6 +22,19 @@
<RadioButton IsChecked="{x:Null}" IsThreeState="True">Three States: Option 3</RadioButton>
<RadioButton IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Disabled</RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical"
Spacing="16">
<RadioButton GroupName="A" IsChecked="True">Group A: Option 1</RadioButton>
<RadioButton GroupName="A" IsEnabled="False">Group A: Disabled</RadioButton>
<RadioButton GroupName="B">Group B: Option 1</RadioButton>
<RadioButton GroupName="B" IsChecked="{x:Null}">Group B: Option 3</RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical"
Spacing="16">
<RadioButton GroupName="A" IsChecked="True">Group A: Option 2</RadioButton>
<RadioButton GroupName="B">Group B: Option 2</RadioButton>
<RadioButton GroupName="B" IsChecked="{x:Null}">Group B: Option 4</RadioButton>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

176
src/Avalonia.Controls/RadioButton.cs

@ -2,19 +2,119 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
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
{
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())
@ -23,21 +123,77 @@ namespace Avalonia.Controls
}
}
private void IsCheckedChanged(bool? value)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
if (!string.IsNullOrEmpty(GroupName))
{
var manager = RadioButtonGroupManager.GetOrCreateForRoot(e.Root);
if (manager != _groupManager)
{
_groupManager.Remove(this, _groupName);
_groupManager = manager;
manager.Add(this);
}
}
base.OnAttachedToVisualTree(e);
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
if (!string.IsNullOrEmpty(GroupName) && _groupManager != null)
{
_groupManager.Remove(this, _groupName);
}
}
private void SetGroupName(string newGroupName)
{
var parent = this.GetVisualParent();
string oldGroupName = GroupName;
if (newGroupName != oldGroupName)
{
if (!string.IsNullOrEmpty(oldGroupName) && _groupManager != null)
{
_groupManager.Remove(this, oldGroupName);
}
_groupName = newGroupName;
if (!string.IsNullOrEmpty(newGroupName))
{
if (_groupManager == null)
{
_groupManager = RadioButtonGroupManager.GetOrCreateForRoot(this.GetVisualRoot());
}
_groupManager.Add(this);
}
}
}
if (value.GetValueOrDefault() && parent != null)
private void IsCheckedChanged(bool? value)
{
string groupName = GroupName;
if (string.IsNullOrEmpty(groupName))
{
var siblings = parent
.GetVisualChildren()
.OfType<RadioButton>()
.Where(x => x != this);
var parent = this.GetVisualParent();
foreach (var sibling in siblings)
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)
{
if (sibling.IsChecked.GetValueOrDefault())
sibling.IsChecked = false;
_groupManager.SetChecked(this);
}
}
}

38
tests/Avalonia.Controls.UnitTests/RadioButtonTests.cs

@ -32,5 +32,43 @@ namespace Avalonia.Controls.UnitTests
Assert.True(radioButton1.IsChecked);
Assert.Null(radioButton2.IsChecked);
}
[Fact]
public void RadioButton_In_Same_Group_Is_Unchecked()
{
var parent = new Panel();
var panel1 = new Panel();
var panel2 = new Panel();
parent.Children.Add(panel1);
parent.Children.Add(panel2);
var radioButton1 = new RadioButton();
radioButton1.GroupName = "A";
radioButton1.IsChecked = false;
var radioButton2 = new RadioButton();
radioButton2.GroupName = "A";
radioButton2.IsChecked = true;
var radioButton3 = new RadioButton();
radioButton3.GroupName = "A";
radioButton3.IsChecked = false;
panel1.Children.Add(radioButton1);
panel1.Children.Add(radioButton2);
panel2.Children.Add(radioButton3);
Assert.False(radioButton1.IsChecked);
Assert.True(radioButton2.IsChecked);
Assert.False(radioButton3.IsChecked);
radioButton3.IsChecked = true;
Assert.False(radioButton1.IsChecked);
Assert.False(radioButton2.IsChecked);
Assert.True(radioButton3.IsChecked);
}
}
}

Loading…
Cancel
Save