File: System\Windows\Controls\RadioButton.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;
 
using System.Windows.Automation;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using MS.Utility;
using MS.Internal.Telemetry.PresentationFramework;
 
// Disable CS3001: Warning as Error: not CLS-compliant
#pragma warning disable 3001
 
namespace System.Windows.Controls
{
    /// <summary>
    ///     RadioButton implements option button with two states: true or false
    /// </summary>
    [Localizability(LocalizationCategory.RadioButton)]
    public class RadioButton : ToggleButton
    {
        #region Constructors
 
        static RadioButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(RadioButton), new FrameworkPropertyMetadata(typeof(RadioButton)));
            _dType = DependencyObjectType.FromSystemTypeInternal(typeof(RadioButton));
 
            KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(typeof(RadioButton), new FrameworkPropertyMetadata(MS.Internal.KnownBoxes.BooleanBoxes.FalseBox));
 
            ControlsTraceLogger.AddControl(TelemetryControls.RadioButton);
        }
 
        /// <summary>
        ///     Default RadioButton constructor
        /// </summary>
        /// <remarks>
        ///     Automatic determination of current Dispatcher. Use alternative constructor
        ///     that accepts a Dispatcher for best performance.
        /// </remarks>
        public RadioButton() : base()
        {
        }
 
        #endregion
 
        #region private helpers
 
        private static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RadioButton radioButton = (RadioButton)d;
            string groupName = e.NewValue as string;
            string currentlyRegisteredGroupName = _currentlyRegisteredGroupName.GetValue(radioButton);
 
            if (groupName != currentlyRegisteredGroupName)
            {
                // Unregister the old group name if set
                if (!string.IsNullOrEmpty(currentlyRegisteredGroupName))
                    Unregister(currentlyRegisteredGroupName, radioButton);
 
                // Register the new group name is set
                if (!string.IsNullOrEmpty(groupName))
                    Register(groupName, radioButton);
            }
        }
 
        private static void Register(string groupName, RadioButton radioButton)
        {
            if (_groupNameToElements == null)
                _groupNameToElements = new Hashtable(1);
 
            lock (_groupNameToElements)
            {
                ArrayList elements = (ArrayList)_groupNameToElements[groupName];
 
                if (elements == null)
                {
                    elements = new ArrayList(1);
                    _groupNameToElements[groupName] = elements;
                }
                else
                {
                    // There were some elements there, remove dead ones
                    PurgeDead(elements, null);
                }
 
                elements.Add(new WeakReference(radioButton));
            }
            _currentlyRegisteredGroupName.SetValue(radioButton, groupName);
        }
 
        private static void Unregister(string groupName, RadioButton radioButton)
        {
            if (_groupNameToElements == null)
                return;
 
            lock (_groupNameToElements)
            {
                // Get all elements bound to this key and remove this element
                ArrayList elements = (ArrayList)_groupNameToElements[groupName];
 
                if (elements != null)
                {
                    PurgeDead(elements, radioButton);
                    if (elements.Count == 0)
                    {
                        _groupNameToElements.Remove(groupName);
                    }
                }
            }
            _currentlyRegisteredGroupName.SetValue(radioButton, null);
        }
 
        private static void PurgeDead(ArrayList elements, object elementToRemove)
        {
            for (int i = 0; i < elements.Count; )
            {
                WeakReference weakReference = (WeakReference)elements[i];
                object element = weakReference.Target;
                if (element == null || element == elementToRemove)
                {
                    elements.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }
        }
 
        private void UpdateRadioButtonGroup()
        {
            string groupName = GroupName;
            if (!string.IsNullOrEmpty(groupName))
            {
                Visual rootScope = KeyboardNavigation.GetVisualRoot(this);
                if (_groupNameToElements == null)
                    _groupNameToElements = new Hashtable(1);
                lock (_groupNameToElements)
                {
                    // Get all elements bound to this key and remove this element
                    ArrayList elements = (ArrayList)_groupNameToElements[groupName];
                    for (int i = 0; i < elements.Count; )
                    {
                        WeakReference weakReference = (WeakReference)elements[i];
                        RadioButton rb = weakReference.Target as RadioButton;
                        if (rb == null)
                        {
                            // Remove dead instances
                            elements.RemoveAt(i);
                        }
                        else
                        {
                            // Uncheck all checked RadioButtons different from the current one
                            if (rb != this && (rb.IsChecked == true) && rootScope == KeyboardNavigation.GetVisualRoot(rb) && rootScope != null)
                                rb.UncheckRadioButton();
                            i++;
                        }
                    }
                }
            }
            else // Logical parent should be the group
            {
                DependencyObject parent = this.Parent;
                if (parent != null)
                {
                    // Traverse logical children
                    IEnumerable children = LogicalTreeHelper.GetChildren(parent);
                    IEnumerator itor = children.GetEnumerator();
                    while (itor.MoveNext())
                    {
                        RadioButton rb = itor.Current as RadioButton;
                        if (rb != null && rb != this && string.IsNullOrEmpty(rb.GroupName) && (rb.IsChecked == true))
                            rb.UncheckRadioButton();
                    }
                }
            }
        }
 
        private void UncheckRadioButton()
        {
            SetCurrentValueInternal(IsCheckedProperty, MS.Internal.KnownBoxes.BooleanBoxes.FalseBox);
        }
 
        #endregion
 
        #region Properties and Events
 
        /// <summary>
        /// The DependencyID for the GroupName property.
        /// Default Value:      "String.Empty"
        /// </summary>
        public static readonly DependencyProperty GroupNameProperty = DependencyProperty.Register(
            "GroupName",
            typeof(string),
            typeof(RadioButton),
            new FrameworkPropertyMetadata(String.Empty, new PropertyChangedCallback(OnGroupNameChanged)));
 
        /// <summary>
        /// GroupName determine mutually excusive radiobutton groups
        /// </summary>
        [DefaultValue("")]
        [Localizability(LocalizationCategory.NeverLocalize)] // cannot be localized
        public string GroupName
        {
            get
            {
                return (string)GetValue(GroupNameProperty);
            }
 
            set
            {
                SetValue(GroupNameProperty, value);
            }
        }
 
        #endregion
 
        #region Override methods
 
        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            return new System.Windows.Automation.Peers.RadioButtonAutomationPeer(this);
        }
 
        /// <summary>
        ///     This method is invoked when the IsChecked becomes true.
        /// </summary>
        /// <param name="e">RoutedEventArgs.</param>
        protected override void OnChecked(RoutedEventArgs e)
        {
            // If RadioButton is checked we should uncheck the others in the same group
            UpdateRadioButtonGroup();
            base.OnChecked(e);
        }
 
        /// <summary>
        /// This override method is called from OnClick().
        /// RadioButton implements its own toggle behavior
        /// </summary>
        protected internal override void OnToggle()
        {
            SetCurrentValueInternal(IsCheckedProperty, MS.Internal.KnownBoxes.BooleanBoxes.TrueBox);
        }
 
        /// <summary>
        /// The Access key for this control was invoked.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnAccessKey(AccessKeyEventArgs e)
        {
            if (!IsKeyboardFocused)
            {
                Focus();
            }
 
            base.OnAccessKey(e);
        }
 
 
        #endregion
 
        #region Accessibility
 
        #endregion Accessibility
 
        #region DTypeThemeStyleKey
 
        // Returns the DependencyObjectType for the registered ThemeStyleKey's default
        // value. Controls will override this method to return approriate types.
        internal override DependencyObjectType DTypeThemeStyleKey
        {
            get { return _dType; }
        }
 
        private static DependencyObjectType _dType;
 
        #endregion DTypeThemeStyleKey
 
        #region private data
 
        [ThreadStatic] private static Hashtable _groupNameToElements;
        private static readonly UncommonField<string> _currentlyRegisteredGroupName = new UncommonField<string>();
 
        #endregion private data
    }
}