File: winforms\Managed\System\WinForms\RadioButton.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="RadioButton.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 */
namespace System.Windows.Forms {
    using System.Runtime.InteropServices;
 
    using System.Diagnostics;
 
    using System;
    using System.Security.Permissions;
    using System.Windows.Forms.ButtonInternal;
 
    using System.ComponentModel;
    using System.ComponentModel.Design;
 
    using System.Drawing;
    using System.Windows.Forms.Internal;
 
    using System.Drawing.Drawing2D;
    using System.Windows.Forms.Layout;
    using Microsoft.Win32;
 
    /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Encapsulates a
    ///       standard
    ///       Windows radio button (option button).
    ///    </para>
    /// </devdoc>
    [
    ComVisible(true),
    ClassInterface(ClassInterfaceType.AutoDispatch),
    DefaultProperty("Checked"),
    DefaultEvent("CheckedChanged"),
    DefaultBindingProperty("Checked"),
    ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign),
    Designer("System.Windows.Forms.Design.RadioButtonDesigner, " + AssemblyRef.SystemDesign),
    SRDescription(SR.DescriptionRadioButton)
    ]
    public class RadioButton : ButtonBase {
 
        private static readonly object EVENT_CHECKEDCHANGED = new object();
        private static readonly ContentAlignment anyRight  = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight;
 
        // Used to see if we need to iterate through the autochecked items and modify their tabstops.
        private bool firstfocus = true;
        private bool isChecked;
        private bool autoCheck = true;
        private ContentAlignment checkAlign = ContentAlignment.MiddleLeft;
        private Appearance appearance        = System.Windows.Forms.Appearance.Normal;
 
        private const int FlatSystemStylePaddingWidth = 24;
        private const int FlatSystemStyleMinimumHeight = 13;
 
        internal int flatSystemStylePaddingWidth = FlatSystemStylePaddingWidth;
        internal int flatSystemStyleMinimumHeight = FlatSystemStyleMinimumHeight;
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButton"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Windows.Forms.RadioButton'/>
        ///       class.
        ///    </para>
        /// </devdoc>
        public RadioButton() : base() {
            if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                flatSystemStylePaddingWidth = LogicalToDeviceUnits(FlatSystemStylePaddingWidth);
                flatSystemStyleMinimumHeight = LogicalToDeviceUnits(FlatSystemStyleMinimumHeight);
            }
 
            // Radiobuttons shouldn't respond to right clicks, so we need to do all our own click logic
            SetStyle(ControlStyles.StandardClick, false);
 
            TextAlign = ContentAlignment.MiddleLeft;
            TabStop = false;
            SetAutoSizeMode(AutoSizeMode.GrowAndShrink);
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.AutoCheck"]/*' />
        /// <devdoc>
        /// <para>Gets or sets a value indicating whether the <see cref='System.Windows.Forms.RadioButton.Checked'/>
        /// value and the appearance of
        /// the control automatically change when the control is clicked.</para>
        /// </devdoc>
        [
        DefaultValue(true),
        SRCategory(SR.CatBehavior),
        SRDescription(SR.RadioButtonAutoCheckDescr)
        ]
        public bool AutoCheck {
            get {
                return autoCheck;
            }
 
            set {
                if (autoCheck != value) {
                    autoCheck = value;
                    PerformAutoUpdates(false);
                }
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.Appearance"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the appearance of the radio
        ///       button
        ///       control is drawn.
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(Appearance.Normal),
        SRCategory(SR.CatAppearance),
        Localizable(true),
        SRDescription(SR.RadioButtonAppearanceDescr)
        ]
        public Appearance Appearance {
            get {
                return appearance;
            }
 
            set {
                if (appearance != value) {
                    //valid values are 0x0 to 0x1
                    if (!ClientUtils.IsEnumValid(value, (int)value, (int)Appearance.Normal, (int)Appearance.Button)){
                        throw new InvalidEnumArgumentException("value", (int)value, typeof(Appearance));
                    }
 
                    using (LayoutTransaction.CreateTransactionIf(AutoSize, this.ParentInternal, this, PropertyNames.Appearance)) {                                                         
                        appearance = value;
                        if (OwnerDraw) {
                            Refresh();
                        }
                        else {
                            UpdateStyles();
                        }
                        
                        OnAppearanceChanged(EventArgs.Empty);
                    }
                }
            }
        }
 
        private static readonly object EVENT_APPEARANCECHANGED = new object();
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.AppearanceChanged"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnAppearanceChangedDescr)]
        public event EventHandler AppearanceChanged {
            add {
                Events.AddHandler(EVENT_APPEARANCECHANGED, value);
            }
 
            remove {
                Events.RemoveHandler(EVENT_APPEARANCECHANGED, value);
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.CheckAlign"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or
        ///       sets the location of the check box portion of the
        ///       radio button control.
        ///       
        ///    </para>
        /// </devdoc>
        [
        Localizable(true),
        SRCategory(SR.CatAppearance),
        DefaultValue(ContentAlignment.MiddleLeft),
        SRDescription(SR.RadioButtonCheckAlignDescr)
        ]
        public ContentAlignment CheckAlign {
            get {
                return checkAlign;
            }
            set {
                if (!WindowsFormsUtils.EnumValidator.IsValidContentAlignment(value)) {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment));
                }
 
                checkAlign = value;
                if (OwnerDraw) {
                    Invalidate();
                }
                else {
                    UpdateStyles();
                }
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.Checked"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the
        ///       control is checked or not.
        ///       
        ///    </para>
        /// </devdoc>
        [
        Bindable(true),
        SettingsBindable(true),
        DefaultValue(false),
        SRCategory(SR.CatAppearance),
        SRDescription(SR.RadioButtonCheckedDescr)
        ]
        public bool Checked {
            get {
                return isChecked;
            }
 
            set {
                if (isChecked != value) {
                    isChecked = value;
 
                    if (IsHandleCreated) SendMessage(NativeMethods.BM_SETCHECK, value? 1: 0, 0);
                    Invalidate();
                    Update();
                    PerformAutoUpdates(false);
                    OnCheckedChanged(EventArgs.Empty);
                }
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.DoubleClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event EventHandler DoubleClick {
            add {
                base.DoubleClick += value;
            }
            remove {
                base.DoubleClick -= value;
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.MouseDoubleClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public new event MouseEventHandler MouseDoubleClick {
            add {
                base.MouseDoubleClick += value;
            }
            remove {
                base.MouseDoubleClick -= value;
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.CreateParams"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
                cp.ClassName = "BUTTON";
                if (OwnerDraw) {
                    cp.Style |= NativeMethods.BS_OWNERDRAW;
                }
                else {
                    cp.Style |= NativeMethods.BS_RADIOBUTTON;
                    if (Appearance == Appearance.Button) {
                        cp.Style |= NativeMethods.BS_PUSHLIKE;
                    }
                    
                    // Determine the alignment of the radio button
                    //
                    ContentAlignment align = RtlTranslateContent(CheckAlign);                              
                    if ((int)(align & anyRight) != 0) {
                        cp.Style |= NativeMethods.BS_RIGHTBUTTON;
                    }
                }
                return cp;
            }
        }
        
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.DefaultSize"]/*' />
        /// <devdoc>
        ///     Deriving classes can override this to configure a default size for their control.
        ///     This is more efficient than setting the size in the control's constructor.
        /// </devdoc>
        protected override Size DefaultSize {
            get {
                return new Size(104, 24);
            }
        }
 
        /// <summary>
        /// When overridden in a derived class, handles rescaling of any magic numbers used in control painting.
        /// For RadioButton controls, scale the width of the system-style padding and height of the radio button image.
        /// Must call the base class method to get the current DPI values. This method is invoked only when 
        /// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has 
        /// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on.
        /// </summary>
        /// <param name="deviceDpiOld">Old DPI value</param>
        /// <param name="deviceDpiNew">New DPI value</param>
        protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) {
            base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);
 
            if (DpiHelper.EnableDpiChangedHighDpiImprovements) {
                flatSystemStylePaddingWidth = LogicalToDeviceUnits(FlatSystemStylePaddingWidth);
                flatSystemStyleMinimumHeight = LogicalToDeviceUnits(FlatSystemStyleMinimumHeight);
            }
        }
 
        internal override Size GetPreferredSizeCore(Size proposedConstraints) {
            if(FlatStyle != FlatStyle.System) {
                return base.GetPreferredSizeCore(proposedConstraints);
            }
 
            Size textSize = TextRenderer.MeasureText(this.Text, this.Font);
            Size size = SizeFromClientSize(textSize);
            size.Width += flatSystemStylePaddingWidth;
            size.Height = DpiHelper.EnableDpiChangedHighDpiImprovements ? Math.Max(size.Height + 5, flatSystemStyleMinimumHeight) : size.Height + 5; // ensure minimum height to avoid truncation of RadioButton circle or text
            return size;                
        }
 
        internal override Rectangle OverChangeRectangle {
            get {
                if (Appearance == Appearance.Button) {
                    return base.OverChangeRectangle;
                }
                else {
                    if (FlatStyle == FlatStyle.Standard) {
                        // this Rectangle will cause no Invalidation
                        // can't use Rectangle.Empty because it will cause Invalidate(ClientRectangle)
                        return new Rectangle(-1, -1, 1, 1);
                    }
                    else {
                        return Adapter.CommonLayout().Layout().checkBounds;
                    }
                }
            }
        }
 
        internal override Rectangle DownChangeRectangle {
            get {
                if (Appearance == Appearance.Button || FlatStyle == FlatStyle.System) {
                    return base.DownChangeRectangle;
                }
                else {
                    return Adapter.CommonLayout().Layout().checkBounds;
                }
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.TabStop"]/*' />
        [DefaultValue(false)]
        new public bool TabStop {
            get {
                return base.TabStop;
            }
            set {
                base.TabStop = value;
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.TextAlign"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the value indicating whether the user can give the focus to this
        ///       control using the TAB key.
        ///       
        ///    </para>
        /// </devdoc>
        [
        Localizable(true),
        DefaultValue(ContentAlignment.MiddleLeft)
        ]
        public override ContentAlignment TextAlign {
            get {
                return base.TextAlign;
            }
            set {
                base.TextAlign = value;
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.CheckedChanged"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Occurs when the
        ///       value of the <see cref='System.Windows.Forms.RadioButton.Checked'/>
        ///       property changes.
        ///    </para>
        /// </devdoc>
        [SRDescription(SR.RadioButtonOnCheckedChangedDescr)]
        public event EventHandler CheckedChanged {
            add {
                Events.AddHandler(EVENT_CHECKEDCHANGED, value);
            }
            remove {
                Events.RemoveHandler(EVENT_CHECKEDCHANGED, value);
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.CreateAccessibilityInstance"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Constructs the new instance of the accessibility object for this control. Subclasses
        ///       should not call base.CreateAccessibilityObject.
        ///    </para>
        /// </devdoc>
        protected override AccessibleObject CreateAccessibilityInstance() {
            return new RadioButtonAccessibleObject(this);
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.OnHandleCreated"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override void OnHandleCreated(EventArgs e) {
            base.OnHandleCreated(e);
            //Since this is protected override, this can be called directly in a overriden class
            //and the handle doesn't need to be created.
            //So check for the handle to improve performance
            if (IsHandleCreated) {
                SendMessage(NativeMethods.BM_SETCHECK, isChecked? 1: 0, 0);
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.OnCheckedChanged"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Windows.Forms.CheckBox.CheckedChanged'/>
        ///       event.
        ///    </para>
        /// </devdoc>
        protected virtual void OnCheckedChanged(EventArgs e) {
            AccessibilityNotifyClients(AccessibleEvents.StateChange, -1);
            AccessibilityNotifyClients(AccessibleEvents.NameChange, -1);
            EventHandler handler = (EventHandler)Events[EVENT_CHECKEDCHANGED];
            if (handler != null) handler(this, e);
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.OnClick"]/*' />
        /// <devdoc>
        ///     We override this to implement the autoCheck functionality.
        /// </devdoc>
        /// <internalonly/>
        protected override void OnClick(EventArgs e) {
            if (autoCheck) {
                Checked = true;
            }
            base.OnClick(e);
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.OnEnter"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        protected override void OnEnter(EventArgs e) {
            // Just like the Win32 RadioButton, fire a click if the
            // user arrows onto the control..
            //
            if (MouseButtons == MouseButtons.None) {
                if (UnsafeNativeMethods.GetKeyState((int)Keys.Tab) >= 0) {
                    //We enter the radioButton by using arrow keys
                    //Paint in raised state...
                    //
                    ResetFlagsandPaint();
                    if(!ValidationCancelled){ 
                        OnClick(e);
                    }
                }
                else {
                    //we enter the radioButton by pressing Tab
                    PerformAutoUpdates(true);
                    //reset the TabStop so we can come back later
                    //notice that PerformAutoUpdates will set the 
                    //TabStop of this button to false
                    TabStop = true;
                }
            }
            base.OnEnter(e);
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.PerformAutoUpdates"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        private void PerformAutoUpdates(bool tabbedInto) {
            if (autoCheck) {
                if (firstfocus) {
                    WipeTabStops(tabbedInto);
                }
                TabStop = isChecked;
                if (isChecked) {
                    Control parent = ParentInternal;
                    if (parent != null) {
                        Control.ControlCollection children = parent.Controls;
                        for (int i = 0; i < children.Count; i++) {
                            Control ctl = children[i];
                            if (ctl != this && ctl is RadioButton) {
                                RadioButton button = (RadioButton)ctl;
                                if (button.autoCheck && button.Checked) {
                                    PropertyDescriptor propDesc = TypeDescriptor.GetProperties(this)["Checked"];
                                    propDesc.SetValue(button, false);
                                }
                            }
                        }
                    }
                }
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.WipeTabStops"]/*' />
        /// <devdoc>
        ///     Removes tabstops from all radio buttons, other than the one that currently has the focus.
        /// </devdoc>
        /// <internalonly/>
        private void WipeTabStops(bool tabbedInto) {
		    Control parent = ParentInternal;
            if (parent != null) {
                Control.ControlCollection children = parent.Controls;
                for (int i = 0; i < children.Count; i++) {                  
                    Control ctl = children[i];
                    if (ctl is RadioButton) {
                        RadioButton button = (RadioButton) ctl;
                        if (!tabbedInto) {
                            button.firstfocus = false;
                        }
                        if (button.autoCheck) {
                            button.TabStop = false;
                        }
                    }
                }
            }
        }
 
        internal override ButtonBaseAdapter CreateFlatAdapter() {
            return new RadioButtonFlatAdapter(this);
        }
 
        internal override ButtonBaseAdapter CreatePopupAdapter() {
            return new RadioButtonPopupAdapter(this);
        }
            
        internal override ButtonBaseAdapter CreateStandardAdapter() {
            return new RadioButtonStandardAdapter(this);
        }
 
        private void OnAppearanceChanged(EventArgs e) {
            EventHandler eh = Events[EVENT_APPEARANCECHANGED] as EventHandler;
            if (eh != null) {
                eh(this, e);
            }
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.OnMouseUp"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Windows.Forms.ButtonBase.OnMouseUp'/> event.
        ///       
        ///    </para>
        /// </devdoc>
        protected override void OnMouseUp(MouseEventArgs mevent) {
            if (mevent.Button == MouseButtons.Left && GetStyle(ControlStyles.UserPaint)) {
                if (base.MouseIsDown) {
                    Point pt = PointToScreen(new Point(mevent.X, mevent.Y));
                    if (UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) {
                        //Paint in raised state...
                        //
                        ResetFlagsandPaint();
                        if (!ValidationCancelled) {
                            OnClick(mevent);
                            OnMouseClick(mevent);
                        }
                        
                    }
                }
            }
            base.OnMouseUp(mevent);
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.PerformClick"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Generates a <see cref='System.Windows.Forms.Control.Click'/> event for the
        ///       button, simulating a click by a user.
        ///    </para>
        /// </devdoc>
        public void PerformClick() {
            if (CanSelect) {
                //Paint in raised state...
                //
                ResetFlagsandPaint();
                if (!ValidationCancelled) {
                    OnClick(EventArgs.Empty);
                }
            }
                
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.ProcessMnemonic"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>        
        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
        protected internal override bool ProcessMnemonic(char charCode) {
            if (UseMnemonic && IsMnemonic(charCode, Text) && CanSelect) {
                if (!Focused) {
                    FocusInternal();    // This will cause an OnEnter event, which in turn will fire the click event
                }
                else {
                    PerformClick();     // Generate a click if already focused
                }
                return true;
            }
            return false;
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.ToString"]/*' />
        /// <devdoc>
        ///     Returns a string representation for this control.
        /// </devdoc>
        /// <internalonly/>
        public override string ToString() {
 
            string s = base.ToString();
            return s + ", Checked: " + Checked.ToString();
        }
 
        /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButtonAccessibleObject"]/*' />
        /// <internalonly/>        
        /// <devdoc>
        /// </devdoc>
        [System.Runtime.InteropServices.ComVisible(true)]        
        public class RadioButtonAccessibleObject : ButtonBaseAccessibleObject {
 
            /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButtonAccessibleObject.RadioButtonAccessibleObject"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public RadioButtonAccessibleObject(RadioButton owner) : base(owner) {
            }
 
            /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButtonAccessibleObject.DefaultAction"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public override string DefaultAction {
                get {
                    string defaultAction = Owner.AccessibleDefaultActionDescription;
                    if (defaultAction != null) {
                        return defaultAction;
                    }
 
                    return SR.GetString(SR.AccessibleActionCheck);
                }
            }
 
            /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButtonAccessibleObject.Role"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public override AccessibleRole Role {
                get {
                    AccessibleRole role = Owner.AccessibleRole;
                    if (role != AccessibleRole.Default) {
                        return role;
                    }
                    return AccessibleRole.RadioButton;
                }
            }
 
            /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButtonAccessibleObject.State"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public override AccessibleStates State {
                get {
                    if (((RadioButton)Owner).Checked) {
                        return AccessibleStates.Checked | base.State;
                    }
                    return base.State;
                }
            }
 
            /// <include file='doc\RadioButton.uex' path='docs/doc[@for="RadioButton.RadioButtonAccessibleObject.DoDefaultAction"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void DoDefaultAction() {
                ((RadioButton)Owner).PerformClick();
            }
        }
 
    }
}