using System;
using System.Drawing;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Diagnostics;

namespace DataGridViewRadioButtonElements
{
    public class DataGridViewRadioButtonCell : DataGridViewComboBoxCell, IDataGridViewEditingCell
    {
        /// <summary>
        /// Convenient enumeration using privately for calculating preferred cell sizes.
        /// </summary>
        private enum DataGridViewRadioButtonFreeDimension
        {
            Both,
            Height,
            Width
        }

        // 4 pixels of margin on the left and right of error icons
        private const byte DATAGRIDVIEWRADIOBUTTONCELL_iconMarginWidth = 4;
        // 4 pixels of margin on the top and bottom of error icons
        private const byte DATAGRIDVIEWRADIOBUTTONCELL_iconMarginHeight = 4;
        // all icons are 12 pixels wide by default
        private const byte DATAGRIDVIEWRADIOBUTTONCELL_iconsWidth = 12;
        // all icons are 11 pixels tall by default
        private const byte DATAGRIDVIEWRADIOBUTTONCELL_iconsHeight = 11;

        // default value of MaxDisplayedItems property
        internal const int DATAGRIDVIEWRADIOBUTTONCELL_defaultMaxDisplayedItems = 5; 
        // blank pixels around each radio button entry
        private const byte DATAGRIDVIEWRADIOBUTTONCELL_margin = 2;

        // codes used for the mouseLocationCode static variable:
        private const int DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric = -3;
        private const int DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton = -2; // mouse is over bottom scroll button
        private const int DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton = -1;    // mouse is over top scroll button

        private DataGridViewRadioButtonCellLayout layout;   // represents the current layout information of the cell

        private static int mouseLocationCode = DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric; 
                                               // -3 no particular location
                                               // -2 mouse over bottom scroll button
                                               // -1 mouse over top scroll button
                                               // 0-N mouse over radio button glyph
        
        private PropertyDescriptor displayMemberProperty;   // Property descriptor for the DisplayMember property
        private PropertyDescriptor valueMemberProperty;     // Property descriptor for the ValueMember property
        private CurrencyManager dataManager;                // Currency manager for the cell's DataSource
        private int maxDisplayedItems;                      // Maximum number of radio buttons displayed by the cell
        private int selectedItemIndex;                      // Index of the currently selected radio button entry
        public int focusedItemIndex;                       // Index of the focused radio button entry
        private int pressedItemIndex;                       // Index of the currently pressed radio button entry
        private bool dataSourceInitializedHookedUp;         // Indicates whether the DataSource's Initialized event is listened to
        private bool valueChanged;                          // Stores whether the cell's value was changed since it became the current cell
        private bool handledKeyDown;                        // Indicates whether the cell handled the key down notification
        private bool mouseUpHooked;                         // Indicates whether the cell listens to the grid's MouseUp event
        
        /// <summary>
        /// DataGridViewRadioButtonCell class constructor.
        /// </summary>
        public DataGridViewRadioButtonCell()
        {
            this.maxDisplayedItems = DATAGRIDVIEWRADIOBUTTONCELL_defaultMaxDisplayedItems;
            this.layout = new DataGridViewRadioButtonCellLayout();
            this.selectedItemIndex = -1;
            this.focusedItemIndex = -1;
            this.pressedItemIndex = -1;
        }

        // Implementation of the IDataGridViewEditingCell interface starts here.

        /// <summary>
        /// Represents the cell's formatted value
        /// </summary>
        public virtual object EditingCellFormattedValue
        {
            get
            {
                return GetEditingCellFormattedValue(DataGridViewDataErrorContexts.Formatting);
            }
            set
            {
                if (this.FormattedValueType == null)
                {
                    throw new ArgumentException("FormattedValueType property of a cell cannot be null.");
                }
                if (value == null || !this.FormattedValueType.IsAssignableFrom(value.GetType()))
                {
                    // Assigned formatted value may not be of the good type, in cases where the app
                    // is feeding wrong values to the cell in virtual / databound mode.
                    throw new ArgumentException("The value provided for the DataGridViewRadioButtonCell has the wrong type.");
                }

                // Try to locate the item that corresponds to the 'value' provided.
                for (int itemIndex = 0; itemIndex < this.Items.Count; itemIndex++)
                {
                    object item = this.Items[itemIndex];
                    object displayValue = GetItemDisplayValue(item);
                    if (value.Equals(displayValue))
                    {
                        // 'value' was found. It becomes the new selected item.
                        this.selectedItemIndex = itemIndex;
                        return;
                    }
                }

                string strValue = value as string;
                if (strValue == string.Empty)
                {
                    // Special case the empty string situation - reset the selected item
                    this.selectedItemIndex = -1;
                    return;
                }

                // 'value' could not be matched against an item in the Items collection.
                throw new ArgumentException();
            }
        }

        /// <summary>
        /// Keeps track of whether the cell's value has changed or not.
        /// </summary>
        public virtual bool EditingCellValueChanged
        {
            get
            {
                return this.valueChanged;
            }
            set
            {
                this.valueChanged = value;
            }
        }

        /// <summary>
        /// Returns the current formatted value of the cell
        /// </summary>
        public virtual object GetEditingCellFormattedValue(DataGridViewDataErrorContexts context)
        {
            if (this.FormattedValueType == null)
            {
                throw new InvalidOperationException("FormattedValueType property of a cell cannot be null.");
            }
            if (this.selectedItemIndex == -1)
            {
                return null;
            }
            object item = this.Items[this.selectedItemIndex];
            object displayValue = GetItemDisplayValue(item);
            // Making sure the returned value has an acceptable type
            if (this.FormattedValueType.IsAssignableFrom(displayValue.GetType()))
            {
                return displayValue;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Called by the grid when the cell enters editing mode. 
        /// </summary>
        public virtual void PrepareEditingCellForEdit(bool selectAll)
        {
            // This cell type has nothing to do here.
        }

        // Implementation of the IDataGridViewEditingCell interface stops here.

        /// <summary>
        /// Stores the CurrencyManager associated to the cell's DataSource
        /// </summary>
        private CurrencyManager DataManager
        {
            get
            {
                CurrencyManager cm = this.dataManager;
                if (cm == null && this.DataSource != null && this.DataGridView != null && 
                    this.DataGridView.BindingContext != null && !(this.DataSource == Convert.DBNull))
                {
                    ISupportInitializeNotification dsInit = this.DataSource as ISupportInitializeNotification;
                    if (dsInit != null && !dsInit.IsInitialized)
                    {
                        // The datasource is not ready yet. Attaching to its Initialized event to be notified
                        // when it's finally ready
                        if (!this.dataSourceInitializedHookedUp)
                        {
                            dsInit.Initialized += new EventHandler(DataSource_Initialized);
                            this.dataSourceInitializedHookedUp = true;
                        }
                    }
                    else
                    {
                        cm = (CurrencyManager)this.DataGridView.BindingContext[this.DataSource];
                        this.DataManager = cm;
                    }
                }
                return cm;
            }
            set
            {
                this.dataManager = value;
            }
        }

        /// <summary>
        /// Overrides the DataGridViewComboBox's implementation of the DataSource property to 
        /// initialize the displayMemberProperty and valueMemberProperty members.
        /// </summary>
        public override object DataSource
        {
            get
            {
                return base.DataSource;
            }
            set
            {
                if (this.DataSource != value)
                {
                    // Invalidate the currency manager
                    this.DataManager = null;

                    ISupportInitializeNotification dsInit = this.DataSource as ISupportInitializeNotification;
                    if (dsInit != null && this.dataSourceInitializedHookedUp)
                    {
                        // If we previously hooked the datasource's ISupportInitializeNotification
                        // Initialized event, then unhook it now (we don't always hook this event,
                        // only if we needed to because the datasource was previously uninitialized)
                        dsInit.Initialized -= new EventHandler(DataSource_Initialized);
                        this.dataSourceInitializedHookedUp = false;
                    }

                    base.DataSource = value;

                    // Update the displayMemberProperty and valueMemberProperty members.
                    try
                    {
                        InitializeDisplayMemberPropertyDescriptor(this.DisplayMember);
                    }
                    catch
                    {
                        Debug.Assert(this.DisplayMember != null && this.DisplayMember.Length > 0);
                        InitializeDisplayMemberPropertyDescriptor(null);
                    }

                    try
                    {
                        InitializeValueMemberPropertyDescriptor(this.ValueMember);
                    }
                    catch
                    {
                        Debug.Assert(this.ValueMember != null && this.ValueMember.Length > 0);
                        InitializeValueMemberPropertyDescriptor(null);
                    }

                    if (value == null)
                    {
                        InitializeDisplayMemberPropertyDescriptor(null);
                        InitializeValueMemberPropertyDescriptor(null);
                    }
                }
            }
        }

        /// <summary>
        /// Overrides the DataGridViewComboBox's implementation of the DisplayMember property to
        /// update the displayMemberProperty member.
        /// </summary>
        public override string DisplayMember
        {
            get
            {
                return base.DisplayMember;
            }
            set
            {
                base.DisplayMember = value;
                InitializeDisplayMemberPropertyDescriptor(value);
            }
        }

        /// <summary>
        /// Overrides the base implementation to replace the 'complex editing experience'
        /// with a 'simple editing experience'.
        /// </summary>
        public override Type EditType
        {
            get
            {
                // Return null since no editing control is used for the editing experience.
                return null;
            }
        }

        /// <summary>
        /// Custom property that represents the maximum number of radio buttons shown by the cell.
        /// </summary>
        [
            DefaultValue(DATAGRIDVIEWRADIOBUTTONCELL_defaultMaxDisplayedItems)
        ]
        public int MaxDisplayedItems
        {
            get
            {
                return this.maxDisplayedItems;
            }
            set
            {
                if (value < 1 || value > 100)
                {
                    throw new ArgumentOutOfRangeException("MaxDisplayedItems");
                }
                this.maxDisplayedItems = value;

                if (this.DataGridView != null && !this.DataGridView.IsDisposed && !this.DataGridView.Disposing)
                {
                    if (this.RowIndex == -1)
                    {
                        // Invalidate and autosize column
                        this.DataGridView.InvalidateColumn(this.ColumnIndex);

                        // TODO: Add code to autosize the cell's column, the rows, the column headers 
                        // and the row headers depending on their autosize settings.
                        // The DataGridView control does not expose a public method that takes care of this.
                    }
                    else
                    {
                        // The DataGridView control exposes a public method called UpdateCellValue
                        // that invalidates the cell so that it gets repainted and also triggers all
                        // the necessary autosizing: the cell's column and/or row, the column headers
                        // and the row headers are autosized depending on their autosize settings.
                        this.DataGridView.UpdateCellValue(this.ColumnIndex, this.RowIndex);
                    }
                }
            }
        }

        /// <summary>
        /// Called internally by the DataGridViewRadioButtonColumn class to avoid the invalidation
        /// done by the MaxDisplayedItems setter above (for performance reasons).
        /// </summary>
        internal int MaxDisplayedItemsInternal
        {
            set
            {
                Debug.Assert(value >= 1 && value <= 100);
                this.maxDisplayedItems = value;
            }
        }

        /// <summary>
        /// Utility function that returns the standard thickness (in pixels) of the four borders of the cell.
        /// </summary>
        private Rectangle StandardBorderWidths
        {
            get
            {
                if (this.DataGridView != null)
                {
                    DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dgvabsEffective;
                    dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle,
                        dataGridViewAdvancedBorderStylePlaceholder,
                        false /*singleVerticalBorderAdded*/,
                        false /*singleHorizontalBorderAdded*/,
                        false /*isFirstDisplayedColumn*/,
                        false /*isFirstDisplayedRow*/);
                    return BorderWidths(dgvabsEffective);
                }
                else
                {
                    return Rectangle.Empty;
                }
            }
        }

        /// <summary>
        /// Overrides the DataGridViewComboBox's implementation of the ValueMember property to
        /// update the valueMemberProperty member.
        /// </summary>
        public override string ValueMember
        {
            get
            {
                return base.ValueMember;
            }
            set
            {
                base.ValueMember = value;
                InitializeValueMemberPropertyDescriptor(value);
            }
        }

        /// <summary>
        /// Utility function that returns the cell state inherited from the owning row and column.
        /// </summary>
        private DataGridViewElementStates CellStateFromColumnRowStates(DataGridViewElementStates rowState)
        {
            Debug.Assert(this.DataGridView != null);
            Debug.Assert(this.ColumnIndex >= 0);
            DataGridViewElementStates orFlags = DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Resizable | DataGridViewElementStates.Selected;
            DataGridViewElementStates andFlags = DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Visible;
            DataGridViewElementStates cellState = (this.OwningColumn.State & orFlags);
            cellState |= (rowState & orFlags);
            cellState |= ((this.OwningColumn.State & andFlags) & (rowState & andFlags));
            return cellState;
        }

        /// <summary>
        /// Custom implementation of the Clone method to copy over the special properties of the cell.
        /// </summary>
        public override object Clone()
        {
            DataGridViewRadioButtonCell dataGridViewCell = base.Clone() as DataGridViewRadioButtonCell;
            if (dataGridViewCell != null)
            {
                dataGridViewCell.MaxDisplayedItems = this.MaxDisplayedItems;
            }
            return dataGridViewCell;
        }

        /// <summary>
        /// Computes the layout of the cell and optionally paints it.
        /// </summary>
        private void ComputeLayout(Graphics graphics,
                                   Rectangle clipBounds,
                                   Rectangle cellBounds,
                                   int rowIndex,
                                   DataGridViewElementStates cellState,
                                   object formattedValue,
                                   string errorText,
                                   DataGridViewCellStyle cellStyle,
                                   DataGridViewAdvancedBorderStyle advancedBorderStyle,
                                   DataGridViewPaintParts paintParts,
                                   bool paint)
        {
            if (paint && DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.Border))
            {
                // Paint the borders first
                PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
            }

            // Discard the space taken up by the borders.
            Rectangle borderWidths = BorderWidths(advancedBorderStyle);
            Rectangle valBounds = cellBounds;
            valBounds.Offset(borderWidths.X, borderWidths.Y);
            valBounds.Width -= borderWidths.Right;
            valBounds.Height -= borderWidths.Bottom;

            SolidBrush backgroundBrush = null;
            try
            {
                Point ptCurrentCell = this.DataGridView.CurrentCellAddress;
                bool cellCurrent = ptCurrentCell.X == this.ColumnIndex && ptCurrentCell.Y == rowIndex;
                bool cellSelected = (cellState & DataGridViewElementStates.Selected) != 0;
                bool mouseOverCell = cellBounds.Contains(this.DataGridView.PointToClient(Control.MousePosition));

                if (DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.SelectionBackground) && cellSelected)
                {
                    backgroundBrush = new SolidBrush(cellStyle.SelectionBackColor);
                }
                else
                {
                    backgroundBrush = new SolidBrush(cellStyle.BackColor);
                }

                if (paint && DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.Background) && backgroundBrush.Color.A == 255)
                {
                    Rectangle backgroundRect = valBounds;
                    backgroundRect.Intersect(clipBounds);
                    graphics.FillRectangle(backgroundBrush, backgroundRect);
                }

                // Discard the space taken up by the padding area.
                if (cellStyle.Padding != Padding.Empty)
                {
                    valBounds.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top);
                    valBounds.Width -= cellStyle.Padding.Horizontal;
                    valBounds.Height -= cellStyle.Padding.Vertical;
                }

                Rectangle errorBounds = valBounds;
                Rectangle scrollBounds = valBounds;

                this.layout.ScrollingNeeded = GetScrollingNeeded(graphics, rowIndex, cellStyle, valBounds.Size);

                if (this.layout.ScrollingNeeded)
                {
                    this.layout.ScrollButtonsSize = ScrollBarRenderer.GetSizeBoxSize(graphics, ScrollBarState.Normal);
                    // Discard the space required for displaying the 2 scroll buttons
                    valBounds.Width -= this.layout.ScrollButtonsSize.Width;
                }

                valBounds.Inflate(-DATAGRIDVIEWRADIOBUTTONCELL_margin, -DATAGRIDVIEWRADIOBUTTONCELL_margin);

                // Layout / paint the radio buttons themselves
                this.layout.RadioButtonsSize = RadioButtonRenderer.GetGlyphSize(graphics, RadioButtonState.CheckedNormal);
                this.layout.DisplayedItemsCount = 0;
                this.layout.TotallyDisplayedItemsCount = 0;
                if (valBounds.Width > 0 && valBounds.Height > 0)
                {
                    this.layout.FirstDisplayedItemLocation = new Point(valBounds.Left + DATAGRIDVIEWRADIOBUTTONCELL_margin, valBounds.Top + DATAGRIDVIEWRADIOBUTTONCELL_margin);
                    int textHeight = cellStyle.Font.Height;
                    int itemIndex = this.layout.FirstDisplayedItemIndex;
                    Rectangle radiosBounds = valBounds;
                    while (itemIndex < this.Items.Count &&
                           itemIndex < this.layout.FirstDisplayedItemIndex + this.maxDisplayedItems &&                           
                           radiosBounds.Height > 0)
                    {
                        if (paint && DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.ContentBackground))
                        {
                            Rectangle itemRect = radiosBounds;
                            itemRect.Intersect(clipBounds);
                            if (!itemRect.IsEmpty)
                            {
                                bool itemReadOnly = (cellState & DataGridViewElementStates.ReadOnly) != 0;
                                bool itemSelected = false;
                                if (formattedValue != null)
                                {
                                    object displayValue = GetItemDisplayValue(this.Items[itemIndex]);
                                    if (formattedValue.Equals(displayValue))
                                    {
                                        itemSelected = true;
                                    }
                                }
                                PaintItem(graphics, 
                                          radiosBounds, 
                                          rowIndex, 
                                          itemIndex, 
                                          cellStyle,
                                          itemReadOnly,
                                          itemSelected, 
                                          mouseOverCell,
                                          cellCurrent && this.focusedItemIndex == itemIndex && DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.Focus));
                            }
                        }
                        itemIndex++;
                        radiosBounds.Y += textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin;
                        radiosBounds.Height -= (textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin);
                        if (radiosBounds.Height >= 0)
                        {
                            this.layout.TotallyDisplayedItemsCount++;
                        }
                        this.layout.DisplayedItemsCount++;
                    }
                    this.layout.ContentBounds = new Rectangle(this.layout.FirstDisplayedItemLocation, new Size(this.layout.RadioButtonsSize.Width, this.layout.DisplayedItemsCount * (textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin)));
                }
                else
                {
                    this.layout.FirstDisplayedItemLocation = new Point(-1, -1);
                    this.layout.ContentBounds = Rectangle.Empty;
                }

                if (this.layout.ScrollingNeeded)
                {
                    // Layout / paint the 2 scroll buttons
                    Rectangle rectArrow = new Rectangle(scrollBounds.Right - this.layout.ScrollButtonsSize.Width,
                                                        scrollBounds.Top,
                                                        this.layout.ScrollButtonsSize.Width,
                                                        this.layout.ScrollButtonsSize.Height);
                    this.layout.UpButtonLocation = rectArrow.Location;
                    if (paint && DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.ContentBackground))
                    {
                        ScrollBarRenderer.DrawArrowButton(graphics, rectArrow, GetScrollBarArrowButtonState(true, mouseOverCell ? mouseLocationCode : DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric, this.layout.FirstDisplayedItemIndex > 0 /*enabled*/));
                    }
                    rectArrow.Y = scrollBounds.Bottom - this.layout.ScrollButtonsSize.Height;
                    this.layout.DownButtonLocation = rectArrow.Location;
                    if (paint && DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.ContentBackground))
                    {
                        ScrollBarRenderer.DrawArrowButton(graphics, rectArrow, GetScrollBarArrowButtonState(false, mouseOverCell ? mouseLocationCode : DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric, this.layout.FirstDisplayedItemIndex + this.layout.TotallyDisplayedItemsCount < this.Items.Count /*enabled*/));
                    }
                }

                // Finally paint the potential error icon
                if (paint && 
                    DataGridViewRadioButtonCell.PartPainted(paintParts, DataGridViewPaintParts.ErrorIcon) &&
                    !(cellCurrent && this.DataGridView.IsCurrentCellInEditMode) && 
                    this.DataGridView.ShowCellErrors)
                {
                    PaintErrorIcon(graphics, clipBounds, errorBounds, errorText);
                }
            }
            finally
            {
                if (backgroundBrush != null)
                {
                    backgroundBrush.Dispose();
                }
            }
        }

        /// <summary>
        /// Returns whether calling the OnContentClick method would force the owning row to be unshared.
        /// </summary>
        protected override bool ContentClickUnsharesRow(DataGridViewCellEventArgs e)
        {
            Point ptCurrentCell = this.DataGridView.CurrentCellAddress;
            return ptCurrentCell.X == this.ColumnIndex &&
                   ptCurrentCell.Y == e.RowIndex &&
                   this.DataGridView.IsCurrentCellInEditMode;
        }

        /// <summary>
        /// Raised when the owning grid gets a MouseUp notification
        /// </summary>
        private void DataGridView_MouseUp(object sender, MouseEventArgs e)
        {
            // Unhook the event handler
            this.DataGridView.MouseUp -= new MouseEventHandler(DataGridView_MouseUp);
            this.mouseUpHooked = false;
            // Reset the pressed item index. Since the mouse was released, no item can be pressed anymore.
            this.pressedItemIndex = -1;
        }

        /// <summary>
        /// Raised when the cell's DataSource is initialized.
        /// </summary>
        private void DataSource_Initialized(object sender, EventArgs e)
        {
            Debug.Assert(sender == this.DataSource);
            Debug.Assert(this.DataSource is ISupportInitializeNotification);
            Debug.Assert(this.dataSourceInitializedHookedUp);

            ISupportInitializeNotification dsInit = this.DataSource as ISupportInitializeNotification;
            // Unhook the Initialized event.
            if (dsInit != null)
            {
                dsInit.Initialized -= new EventHandler(DataSource_Initialized);
            }

            // The wait is over: the DataSource is initialized.
            this.dataSourceInitializedHookedUp = false;

            // Check the DisplayMember and ValueMember values - will throw if values don't match existing fields.
            InitializeDisplayMemberPropertyDescriptor(this.DisplayMember);
            InitializeValueMemberPropertyDescriptor(this.ValueMember);
        }

        /// <summary>
        /// Returns whether calling the OnEnter method would force the owning row to be unshared.
        /// </summary>
        protected override bool EnterUnsharesRow(int rowIndex, bool throughMouseClick)
        {
            return this.focusedItemIndex == -1;
        }

        /// <summary>
        /// Custom implementation of the GetContentBounds method which delegates most of the work to the ComputeLayout function.
        /// </summary>
        protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
        {
            if (this.DataGridView == null || rowIndex < 0 || this.OwningColumn == null)
            {
                return Rectangle.Empty;
            }

            // First determine the effective border style of this cell.
            bool singleVerticalBorderAdded = !this.DataGridView.RowHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single;
            bool singleHorizontalBorderAdded = !this.DataGridView.ColumnHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single;
            DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle();

            Debug.Assert(rowIndex > -1 && this.OwningColumn != null);

            DataGridViewAdvancedBorderStyle dgvabsEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle,
                dataGridViewAdvancedBorderStylePlaceholder,
                singleVerticalBorderAdded,
                singleHorizontalBorderAdded,
                rowIndex == this.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Displayed) /*isFirstDisplayedRow*/,
                this.ColumnIndex == this.DataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Displayed).Index /*isFirstDisplayedColumn*/);

            // Next determine the state of this cell.
            DataGridViewElementStates rowState = this.DataGridView.Rows.GetRowState(rowIndex);
            DataGridViewElementStates cellState = CellStateFromColumnRowStates(rowState);
            cellState |= this.State;

            // Then the bounds of this cell.
            Rectangle cellBounds = new Rectangle(new Point(0, 0), GetSize(rowIndex));

            // Finally compute the layout of the cell and return the resulting content bounds.
            ComputeLayout(graphics,
                cellBounds,
                cellBounds,
                rowIndex,
                cellState,
                null /*formattedValue*/,            // contentBounds is independent of formattedValue
                null /*errorText*/,                 // contentBounds is independent of errorText
                cellStyle,
                dgvabsEffective,
                DataGridViewPaintParts.ContentForeground,
                false /*paint*/);

            return this.layout.ContentBounds;
        }

        /// <summary>
        /// Utility function that converts a constraintSize provided to GetPreferredSize into a 
        /// DataGridViewRadioButtonFreeDimension enum value.
        /// </summary>
        private static DataGridViewRadioButtonFreeDimension GetFreeDimensionFromConstraint(Size constraintSize)
        {
            if (constraintSize.Width < 0 || constraintSize.Height < 0)
            {
                throw new ArgumentException("InvalidArgument=Value of '" + constraintSize.ToString() + "' is not valid for 'constraintSize'.");
            }
            if (constraintSize.Width == 0)
            {
                if (constraintSize.Height == 0)
                {
                    return DataGridViewRadioButtonFreeDimension.Both;
                }
                else
                {
                    return DataGridViewRadioButtonFreeDimension.Width;
                }
            }
            else
            {
                if (constraintSize.Height == 0)
                {
                    return DataGridViewRadioButtonFreeDimension.Height;
                }
                else
                {
                    throw new ArgumentException("InvalidArgument=Value of '" + constraintSize.ToString() + "' is not valid for 'constraintSize'.");
                }
            }
        }

        /// <summary>
        /// Utility function that returns the display value of an item given the 
        /// display/value property descriptors and display/value property names.
        /// </summary>
        private object GetItemDisplayValue(object item)
        {
            Debug.Assert(item != null);
            bool displayValueSet = false;
            object displayValue = null;
            if (this.displayMemberProperty != null)
            {
                displayValue = this.displayMemberProperty.GetValue(item);
                displayValueSet = true;
            }
            else if (this.valueMemberProperty != null)
            {
                displayValue = this.valueMemberProperty.GetValue(item);
                displayValueSet = true;
            }
            else if (!string.IsNullOrEmpty(this.DisplayMember))
            {
                PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.DisplayMember, true /*caseInsensitive*/);
                if (propDesc != null)
                {
                    displayValue = propDesc.GetValue(item);
                    displayValueSet = true;
                }
            }
            else if (!string.IsNullOrEmpty(this.ValueMember))
            {
                PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.ValueMember, true /*caseInsensitive*/);
                if (propDesc != null)
                {
                    displayValue = propDesc.GetValue(item);
                    displayValueSet = true;
                }
            }
            if (!displayValueSet)
            {
                displayValue = item;
            }
            return displayValue;
        }

        /// <summary>
        /// Utility function that returns the value of an item given the 
        /// display/value property descriptors and display/value property names.
        /// </summary>
        private object GetItemValue(object item)
        {
            bool valueSet = false;
            object value = null;
            if (this.valueMemberProperty != null)
            {
                value = this.valueMemberProperty.GetValue(item);
                valueSet = true;
            }
            else if (this.displayMemberProperty != null)
            {
                value = this.displayMemberProperty.GetValue(item);
                valueSet = true;
            }
            else if (!string.IsNullOrEmpty(this.ValueMember))
            {
                PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.ValueMember, true /*caseInsensitive*/);
                if (propDesc != null)
                {
                    value = propDesc.GetValue(item);
                    valueSet = true;
                }
            }
            if (!valueSet && !string.IsNullOrEmpty(this.DisplayMember))
            {
                PropertyDescriptor propDesc = TypeDescriptor.GetProperties(item).Find(this.DisplayMember, true /*caseInsensitive*/);
                if (propDesc != null)
                {
                    value = propDesc.GetValue(item);
                    valueSet = true;
                }
            }
            if (!valueSet)
            {
                value = item;
            }
            return value;
        }

        /// <summary>
        /// Returns the code identifying the part of the cell which is underneath the mouse pointer.
        /// </summary>
        private int GetMouseLocationCode(Graphics graphics, int rowIndex, DataGridViewCellStyle cellStyle, int mouseX, int mouseY)
        {
            // First determine this cell's effective border style.
            bool singleVerticalBorderAdded = !this.DataGridView.RowHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single;
            bool singleHorizontalBorderAdded = !this.DataGridView.ColumnHeadersVisible && this.DataGridView.AdvancedCellBorderStyle.All == DataGridViewAdvancedCellBorderStyle.Single;
            bool isFirstDisplayedColumn = this.ColumnIndex == this.DataGridView.Columns.GetFirstColumn(DataGridViewElementStates.Displayed).Index;
            bool isFirstDisplayedRow = rowIndex == this.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Displayed);
            DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new DataGridViewAdvancedBorderStyle(), dataGridViewAdvancedBorderStyleEffective;
            dataGridViewAdvancedBorderStyleEffective = AdjustCellBorderStyle(this.DataGridView.AdvancedCellBorderStyle,
                                                                             dataGridViewAdvancedBorderStylePlaceholder,
                                                                             singleVerticalBorderAdded,
                                                                             singleHorizontalBorderAdded,
                                                                             isFirstDisplayedColumn,
                                                                             isFirstDisplayedRow);
            // Then its size.
            Rectangle cellBounds = this.DataGridView.GetCellDisplayRectangle(this.ColumnIndex, rowIndex, false /*cutOverflow*/);
            Debug.Assert(GetSize(rowIndex) == cellBounds.Size);

            // Recompute the layout of the cell.
            ComputeLayout(graphics,
                          cellBounds,
                          cellBounds,
                          rowIndex,
                          DataGridViewElementStates.None,
                          null /*formattedValue*/,
                          null /*errorText*/,
                          cellStyle,
                          dataGridViewAdvancedBorderStyleEffective,
                          DataGridViewPaintParts.None,
                          false /*paint*/);

            // Deduce the cell part beneath the mouse pointer.
            Point mousePosition = this.DataGridView.PointToClient(Control.MousePosition);
            Rectangle rect;
            if (this.layout.ScrollingNeeded)
            {
                // Is the mouse over the bottom scroll button?
                rect = new Rectangle(this.layout.DownButtonLocation, this.layout.ScrollButtonsSize);
                if (rect.Contains(mousePosition))
                {
                    return DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton;
                }
                // Is the mouse over the upper scroll button?
                rect = new Rectangle(this.layout.UpButtonLocation, this.layout.ScrollButtonsSize);
                if (rect.Contains(mousePosition))
                {
                    return DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton;
                }
            }
            if (this.layout.DisplayedItemsCount > 0)
            {
                Point radioButtonLocation = this.layout.FirstDisplayedItemLocation;
                int textHeight = cellStyle.Font.Height;
                int itemIndex = this.layout.FirstDisplayedItemIndex;
                Rectangle radioButtonBounds = new Rectangle(radioButtonLocation, this.layout.RadioButtonsSize);
                while (itemIndex < this.Items.Count &&
                       itemIndex < this.layout.FirstDisplayedItemIndex + this.maxDisplayedItems &&
                       itemIndex - this.layout.FirstDisplayedItemIndex < this.layout.DisplayedItemsCount)
                {
                    if (radioButtonBounds.Contains(mousePosition))
                    {
                        // The mouse is over a radio button
                        return itemIndex - this.layout.FirstDisplayedItemIndex;
                    }
                    itemIndex++;
                    radioButtonBounds.Y += textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin;
                }
            }
            return DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric;
        }

        /// <summary>
        /// Returns a ScrollBarArrowButtonState state given the current mouse location.
        /// </summary>
        private ScrollBarArrowButtonState GetScrollBarArrowButtonState(bool upButton, int mouseLocationCode, bool enabled)
        {
            if (!enabled)
            {
                if (upButton)
                {
                    return ScrollBarArrowButtonState.UpDisabled;
                }
                else
                {
                    return ScrollBarArrowButtonState.DownDisabled;
                }
            }
            if (mouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton)
            {
                // Mouse is over upper button
                if (Control.MouseButtons == MouseButtons.Left)
                {
                    if (upButton)
                    {
                        return ScrollBarArrowButtonState.UpPressed;
                    }
                    else
                    {
                        return ScrollBarArrowButtonState.DownNormal;
                    }
                }
                else
                {
                    if (upButton)
                    {
                        return ScrollBarArrowButtonState.UpHot;
                    }
                    else
                    {
                        return ScrollBarArrowButtonState.DownNormal;
                    }
                }
            }
            else if (mouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton)
            {
                // Mouse is over bottom button
                if (Control.MouseButtons == MouseButtons.Left)
                {
                    if (upButton)
                    {
                        return ScrollBarArrowButtonState.UpNormal;
                    }
                    else
                    {
                        return ScrollBarArrowButtonState.DownPressed;
                    }
                }
                else
                {
                    if (upButton)
                    {
                        return ScrollBarArrowButtonState.UpNormal;
                    }
                    else
                    {
                        return ScrollBarArrowButtonState.DownHot;
                    }
                }
            }

            else if (upButton)
            {
                return ScrollBarArrowButtonState.UpNormal;
            }
            else
            {
                return ScrollBarArrowButtonState.DownNormal;
            }
        }

        /// <summary>
        /// Custom implementation of the GetPreferredSize method.
        /// </summary>
        protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize)
        {
            if (this.DataGridView == null)
            {
                return new Size(-1, -1);
            }
            DataGridViewRadioButtonFreeDimension freeDimension = DataGridViewRadioButtonCell.GetFreeDimensionFromConstraint(constraintSize);
            Rectangle borderWidthsRect = this.StandardBorderWidths;
            int borderAndPaddingWidths = borderWidthsRect.Left + borderWidthsRect.Width + cellStyle.Padding.Horizontal;
            int borderAndPaddingHeights = borderWidthsRect.Top + borderWidthsRect.Height + cellStyle.Padding.Vertical;
            int preferredHeight = 0, preferredWidth = 0;
            // Assuming here that all radio button states use the same size.
            Size radioButtonGlyphSize = RadioButtonRenderer.GetGlyphSize(graphics, RadioButtonState.CheckedNormal);

            if (freeDimension != DataGridViewRadioButtonFreeDimension.Width)
            {
                preferredHeight = Math.Min(this.Items.Count, this.MaxDisplayedItems) * (Math.Max(cellStyle.Font.Height, radioButtonGlyphSize.Height) + DATAGRIDVIEWRADIOBUTTONCELL_margin) + DATAGRIDVIEWRADIOBUTTONCELL_margin;
                preferredHeight += 2 * DATAGRIDVIEWRADIOBUTTONCELL_margin + borderAndPaddingHeights;
            }
           
            if (freeDimension != DataGridViewRadioButtonFreeDimension.Height)
            {
                TextFormatFlags flags = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPrefix;

                if (this.Items.Count > 0)
                {
                    // Figure out the width of the longest entry
                    int maxPreferredItemWidth = -1, preferredItemWidth;
                    foreach (object item in this.Items)
                    {
                        string formattedValue = GetFormattedValue(GetItemValue(item), rowIndex, ref cellStyle, null, null, DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.PreferredSize) as string;
                        if (formattedValue != null)
                        {
                            preferredItemWidth = DataGridViewCell.MeasureTextSize(graphics, formattedValue, cellStyle.Font, flags).Width;
                        }
                        else
                        {
                            preferredItemWidth = DataGridViewCell.MeasureTextSize(graphics, " ", cellStyle.Font, flags).Width;
                        }
                        if (preferredItemWidth > maxPreferredItemWidth)
                        {
                            maxPreferredItemWidth = preferredItemWidth;
                        }
                    }
                    preferredWidth = maxPreferredItemWidth;
                }

                if (freeDimension == DataGridViewRadioButtonFreeDimension.Width)
                {
                    Size contentSize = new Size(Int32.MaxValue, constraintSize.Height - borderAndPaddingHeights);
                    if (GetScrollingNeeded(graphics, rowIndex, cellStyle, contentSize))
                    {
                        // Accommodate the scrolling buttons
                        preferredWidth += ScrollBarRenderer.GetSizeBoxSize(graphics, ScrollBarState.Normal).Width;
                    }
                }

                preferredWidth += radioButtonGlyphSize.Width + 5 * DATAGRIDVIEWRADIOBUTTONCELL_margin + borderAndPaddingWidths;
            }

            if (this.DataGridView.ShowCellErrors)
            {
                // Making sure that there is enough room for the potential error icon
                if (freeDimension != DataGridViewRadioButtonFreeDimension.Height)
                {
                    preferredWidth = Math.Max(preferredWidth,
                                              borderAndPaddingWidths + DATAGRIDVIEWRADIOBUTTONCELL_iconMarginWidth * 2 + DATAGRIDVIEWRADIOBUTTONCELL_iconsWidth);
                }
                if (freeDimension != DataGridViewRadioButtonFreeDimension.Width)
                {
                    preferredHeight = Math.Max(preferredHeight,
                                               borderAndPaddingHeights + DATAGRIDVIEWRADIOBUTTONCELL_iconMarginHeight * 2 + DATAGRIDVIEWRADIOBUTTONCELL_iconsHeight);
                }
            }

            return new Size(preferredWidth, preferredHeight);
        }

        /// <summary>
        /// Helper function that determines if scrolling buttons should be displayed
        /// </summary>
        private bool GetScrollingNeeded(Graphics graphics, int rowIndex, DataGridViewCellStyle cellStyle, Size contentSize)
        {
            if (this.Items.Count <= 1)
            {
                return false;
            }

            if (this.MaxDisplayedItems >= this.Items.Count && 
                this.Items.Count * (cellStyle.Font.Height + DATAGRIDVIEWRADIOBUTTONCELL_margin) + DATAGRIDVIEWRADIOBUTTONCELL_margin <= contentSize.Height /*- borderAndPaddingHeights*/)
            {
                // There is enough vertical room to display all the radio buttons
                return false;
            }

            // Is there enough room to display the scroll buttons?
            //Size sizeBoxSize = ScrollBarRenderer.GetSizeBoxSize(graphics, ScrollBarState.Normal);
            Size sizeBoxSize = sizeBoxSize = new System.Drawing.Size(20, 20);
            if (2 * DATAGRIDVIEWRADIOBUTTONCELL_margin + sizeBoxSize.Width > contentSize.Width ||
                2 * sizeBoxSize.Height > contentSize.Height)
            {
                // There isn't enough room to show the scroll buttons.
                return false;
            }

            // Scroll buttons are required and there is enough room for them.
            return true;
        }

        /// <summary>
        /// Helper function that sets the displayMemberProperty member based on the DataSource and the provided displayMember field name
        /// </summary>
        private void InitializeDisplayMemberPropertyDescriptor(string displayMember)
        {
            if (this.DataManager != null)
            {
                if (String.IsNullOrEmpty(displayMember))
                {
                    this.displayMemberProperty = null;
                }
                else
                {
                    BindingMemberInfo displayBindingMember = new BindingMemberInfo(displayMember);
                    // make the DataManager point to the sublist inside this.DataSource
                    this.DataManager = this.DataGridView.BindingContext[this.DataSource, displayBindingMember.BindingPath] as CurrencyManager;

                    PropertyDescriptorCollection props = this.DataManager.GetItemProperties();
                    PropertyDescriptor displayMemberProperty = props.Find(displayBindingMember.BindingField, true);
                    if (displayMemberProperty == null)
                    {
                        throw new ArgumentException("Field called '" + displayMember + "' does not exist.");
                    }
                    else
                    {
                        this.displayMemberProperty = displayMemberProperty;
                    }
                }
            }
        }

        /// <summary>
        /// Helper function that sets the valueMemberProperty member based on the DataSource and the provided valueMember field name
        /// </summary>
        private void InitializeValueMemberPropertyDescriptor(string valueMember)
        {
            if (this.DataManager != null)
            {
                if (String.IsNullOrEmpty(valueMember))
                {
                    this.valueMemberProperty = null;
                }
                else
                {
                    BindingMemberInfo valueBindingMember = new BindingMemberInfo(valueMember);
                    // make the DataManager point to the sublist inside this.DataSource
                    this.DataManager = this.DataGridView.BindingContext[this.DataSource, valueBindingMember.BindingPath] as CurrencyManager;

                    PropertyDescriptorCollection props = this.DataManager.GetItemProperties();
                    PropertyDescriptor valueMemberProperty = props.Find(valueBindingMember.BindingField, true);
                    if (valueMemberProperty == null)
                    {
                        throw new ArgumentException("Field called '" + valueMember + "' does not exist.");
                    }
                    else
                    {
                        this.valueMemberProperty = valueMemberProperty;
                    }
                }
            }
        }

        /// <summary>
        /// Helper function that invalidates the entire area of an entry
        /// </summary>
        private void InvalidateItem(int itemIndex, int rowIndex)
        {
            if (itemIndex >= this.layout.FirstDisplayedItemIndex &&
                itemIndex < this.layout.FirstDisplayedItemIndex + this.layout.DisplayedItemsCount)
            {
                DataGridViewCellStyle cellStyle = GetInheritedStyle(null, rowIndex, false /* includeColors */);
                Point radioButtonLocation = this.layout.FirstDisplayedItemLocation;
                int textHeight = cellStyle.Font.Height;
                radioButtonLocation.Y += (textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin) * (itemIndex - this.layout.FirstDisplayedItemIndex);
                Size cellSize = GetSize(rowIndex);
                this.DataGridView.Invalidate(new Rectangle(radioButtonLocation.X, radioButtonLocation.Y, cellSize.Width, Math.Max(textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin, this.layout.RadioButtonsSize.Height)));
            }
        }

        /// <summary>
        /// Helper function that invalidates the glyph section of an entry
        /// </summary>
        private void InvalidateRadioGlyph(int itemIndex, DataGridViewCellStyle cellStyle)
        {
            if (itemIndex >= this.layout.FirstDisplayedItemIndex &&
                itemIndex < this.layout.FirstDisplayedItemIndex + this.layout.DisplayedItemsCount)
            {
                Point radioButtonLocation = this.layout.FirstDisplayedItemLocation;
                int textHeight = cellStyle.Font.Height;
                radioButtonLocation.Y += (textHeight + DATAGRIDVIEWRADIOBUTTONCELL_margin) * (itemIndex - this.layout.FirstDisplayedItemIndex);
                this.DataGridView.Invalidate(new Rectangle(radioButtonLocation, this.layout.RadioButtonsSize));
            }
        }

        /// <summary>
        /// Returns whether calling the OnKeyDown method would force the owning row to be unshared.
        /// </summary>
        protected override bool KeyDownUnsharesRow(KeyEventArgs e, int rowIndex)
        {
            if (!e.Alt && !e.Control && !e.Shift)
            {
                if (this.handledKeyDown)
                {
                    return true;
                }
                if (e.KeyCode == Keys.Down && this.focusedItemIndex < this.Items.Count - 1)
                {
                    return true;
                }
                else if (e.KeyCode == Keys.Up && this.focusedItemIndex > 0)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Returns whether calling the OnKeyUp method would force the owning row to be unshared.
        /// </summary>
        protected override bool KeyUpUnsharesRow(KeyEventArgs e, int rowIndex)
        {
            if (!e.Alt && !e.Control && !e.Shift)
            {
                if (e.KeyCode == Keys.Down && this.focusedItemIndex < this.Items.Count - 1 && this.handledKeyDown)
                {
                    return true;
                }
                else if (e.KeyCode == Keys.Up && this.focusedItemIndex > 0 && this.handledKeyDown)
                {
                    return true;
                }
            }
            if (this.handledKeyDown)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Returns whether calling the OnMouseDown method would force the owning row to be unshared.
        /// </summary>
        protected override bool MouseDownUnsharesRow(DataGridViewCellMouseEventArgs e)
        {
            if (this.DataGridView == null)
            {
                return false;
            }
            if (e.Button == MouseButtons.Left)
            {
                int mouseLocationCode = GetMouseLocationCode(this.DataGridView.CreateGraphics(),
                                                             e.RowIndex,
                                                             GetInheritedStyle(null, e.RowIndex, false /* includeColors */),
                                                             e.X,
                                                             e.Y);
                switch (mouseLocationCode)
                {
                    case DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric:
                        break;
                    case DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton:
                        if (this.layout.FirstDisplayedItemIndex + this.layout.DisplayedItemsCount < this.Items.Count)
                        {
                            return true;
                        }
                        break;
                    case DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton:
                        if (this.layout.FirstDisplayedItemIndex > 0)
                        {
                            return true;
                        }
                        break;
                    default:
                        if (this.pressedItemIndex != mouseLocationCode + this.layout.FirstDisplayedItemIndex)
                        {
                            return true;
                        }
                        break;
                }
            }
            return false;
        }

        /// <summary>
        /// Returns whether calling the OnMouseLeave method would force the owning row to be unshared.
        /// </summary>
        protected override bool MouseLeaveUnsharesRow(int rowIndex)
        {
            return this.pressedItemIndex != -1 && !this.mouseUpHooked;
        }

        /// <summary>
        /// Returns whether calling the OnMouseUp method would force the owning row to be unshared.
        /// </summary>
        protected override bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e)
        {
             return e.Button == MouseButtons.Left && this.pressedItemIndex != -1;
        }

        /// <summary>
        /// Method that declares the cell dirty and notifies the grid of the value change.
        /// </summary>
        private void NotifyDataGridViewOfValueChange()
        {
            this.valueChanged = true;
            Debug.Assert(this.DataGridView != null);
            this.DataGridView.NotifyCurrentCellDirty(true);
        }

        /// <summary>
        /// Potentially updates the selected item and notifies the grid of the change.
        /// </summary>
        protected override void OnContentClick(DataGridViewCellEventArgs e)
        {
            if (this.DataGridView == null)
            {
                return;
            }
            Point ptCurrentCell = this.DataGridView.CurrentCellAddress;
            if (ptCurrentCell.X == this.ColumnIndex &&
                ptCurrentCell.Y == e.RowIndex &&
                this.DataGridView.IsCurrentCellInEditMode)
            {
                if (mouseLocationCode >= 0 && 
                    UpdateFormattedValue(this.layout.FirstDisplayedItemIndex + mouseLocationCode, e.RowIndex))
                {
                    NotifyDataGridViewOfValueChange();
                }
            }
        }

        /// <summary>
        /// Updates the property descriptors when the cell gets attached to the grid.
        /// </summary>
        protected override void OnDataGridViewChanged()
        {
            if (this.DataGridView != null)
            {
                // Will throw an error if DataGridView is set and a member is invalid
                InitializeDisplayMemberPropertyDescriptor(this.DisplayMember);
                InitializeValueMemberPropertyDescriptor(this.ValueMember);
            }
            base.OnDataGridViewChanged();
        }

        /// <summary>
        /// Makes sure that there is a focused item when the cell becomes the current one.
        /// </summary>
        protected override void OnEnter(int rowIndex, bool throughMouseClick)
        {
            if (this.focusedItemIndex == -1)
            {
                this.focusedItemIndex = this.layout.FirstDisplayedItemIndex;
            }
            base.OnEnter(rowIndex, throughMouseClick);
        }

        /// <summary>
        /// Handles the KeyDown notification when it can result in a value change.
        /// </summary>
        protected override void OnKeyDown(KeyEventArgs e, int rowIndex)
        {
            if (this.DataGridView == null)
            {
                return;
            }
            if (!e.Alt && !e.Control && !e.Shift)
            {
                if (this.handledKeyDown)
                {
                    this.handledKeyDown = false;
                }
                if (e.KeyCode == Keys.Down && this.focusedItemIndex < this.Items.Count - 1)
                {
                    this.handledKeyDown = true;
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Up && this.focusedItemIndex > 0)
                {
                    this.handledKeyDown = true;
                    e.Handled = true;
                }
            }
        }

        /// <summary>
        /// Handles the KeyUp notification to change the cell's value.
        /// </summary>
        protected override void OnKeyUp(KeyEventArgs e, int rowIndex)
        {
            if (this.DataGridView == null)
            {
                return;
            }
            if (!e.Alt && !e.Control && !e.Shift && this.handledKeyDown)
            {
                if (e.KeyCode == Keys.Down && this.focusedItemIndex < this.Items.Count - 1)
                {
                    // Handle the Down Arrow key
                    if (UpdateFormattedValue(this.focusedItemIndex+1, rowIndex))
                    {
                        NotifyDataGridViewOfValueChange();
                    }
                    else if (this.selectedItemIndex == this.focusedItemIndex+1)
                    {
                        this.focusedItemIndex++;
                    }
                    if (this.focusedItemIndex >= this.layout.FirstDisplayedItemIndex + this.layout.TotallyDisplayedItemsCount)
                    {
                        this.layout.FirstDisplayedItemIndex++;
                    }
                    while (this.focusedItemIndex < this.layout.FirstDisplayedItemIndex)
                    {
                        this.layout.FirstDisplayedItemIndex--;
                    }
                    this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex);
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Up && this.focusedItemIndex > 0)
                {
                    // Handle the Up Arrow key
                    if (UpdateFormattedValue(this.focusedItemIndex - 1, rowIndex))
                    {
                        NotifyDataGridViewOfValueChange();
                    }
                    else if (this.selectedItemIndex == this.focusedItemIndex - 1)
                    {
                        this.focusedItemIndex--;
                    }
                    if (this.focusedItemIndex < this.layout.FirstDisplayedItemIndex)
                    {
                        this.layout.FirstDisplayedItemIndex--;
                    }
                    while (this.focusedItemIndex >= this.layout.FirstDisplayedItemIndex + this.layout.TotallyDisplayedItemsCount)
                    {
                        this.layout.FirstDisplayedItemIndex++;
                    }
                    this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex);
                    e.Handled = true;
                }
            }
            // Always reset the flag that indicates if the KeyDown was handled.
            if (this.handledKeyDown)
            {
                this.handledKeyDown = false;
            }
        }

        /// <summary>
        /// Custom implementation of the MouseDown notification to update the cell's value or scroll the entries.
        /// </summary>
        protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
        {
            if (this.DataGridView == null)
            {
                return;
            }
            if (e.Button == MouseButtons.Left)
            {
                int mouseLocationCode = GetMouseLocationCode(this.DataGridView.CreateGraphics(),
                                                             e.RowIndex,
                                                             GetInheritedStyle(null, e.RowIndex, false /* includeColors */),
                                                             e.X,
                                                             e.Y);
                switch (mouseLocationCode)
                {
                    case DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric:
                        break;
                    case DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton:
                        if (this.layout.FirstDisplayedItemIndex + this.layout.TotallyDisplayedItemsCount < this.Items.Count)
                        {
                            // Scroll the entries down.
                            this.layout.FirstDisplayedItemIndex++;
                            this.DataGridView.Invalidate(new Rectangle(this.layout.DownButtonLocation, this.layout.ScrollButtonsSize));
                        }
                        break;
                    case DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton:
                        if (this.layout.FirstDisplayedItemIndex > 0)
                        {
                            // Scroll the entries up.
                            this.layout.FirstDisplayedItemIndex--;
                            this.DataGridView.Invalidate(new Rectangle(this.layout.UpButtonLocation, this.layout.ScrollButtonsSize));
                        }
                        break;
                    default:
                        if (this.pressedItemIndex != mouseLocationCode + this.layout.FirstDisplayedItemIndex)
                        {
                            // Update the value of the cell.
                            InvalidateItem(this.pressedItemIndex, e.RowIndex);
                            this.pressedItemIndex = mouseLocationCode + this.layout.FirstDisplayedItemIndex;
                            InvalidateItem(this.pressedItemIndex, e.RowIndex);
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// Makes sure the radio button gets hot when the mouse gets over it
        /// </summary>
        protected override void OnMouseEnter(int rowIndex)
        {
            if (this.DataGridView == null)
            {
                return;
            }

            if (this.pressedItemIndex != -1)
            {
                InvalidateRadioGlyph(this.pressedItemIndex, GetInheritedStyle(null, rowIndex, false /* includeColors */));
            }
        }

        /// <summary>
        /// Invalidates part of the cell as needed
        /// </summary>
        protected override void OnMouseLeave(int rowIndex)
        {
            if (this.DataGridView == null)
            {
                return;
            }

            int oldMouseLocationCode = mouseLocationCode;
            if (oldMouseLocationCode != DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric)
            {
                mouseLocationCode = DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric;
                if (oldMouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton && this.layout.FirstDisplayedItemIndex > 0)
                {
                    this.DataGridView.Invalidate(new Rectangle(this.layout.UpButtonLocation, this.layout.ScrollButtonsSize));
                }
                else if (oldMouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton && this.layout.FirstDisplayedItemIndex + this.layout.DisplayedItemsCount < this.Items.Count)
                {
                    this.DataGridView.Invalidate(new Rectangle(this.layout.DownButtonLocation, this.layout.ScrollButtonsSize));
                }
                else if (oldMouseLocationCode >= 0)
                {
                    InvalidateRadioGlyph(oldMouseLocationCode + this.layout.FirstDisplayedItemIndex, GetInheritedStyle(null, rowIndex, false /* includeColors */));
                }
            }

            if (this.pressedItemIndex != -1)
            {
                if (!this.mouseUpHooked)
                {
                    // Hookup the grid's MouseUp event so that this.pressedItemIndex can be reset when the user releases the mouse button.
                    this.DataGridView.MouseUp += new MouseEventHandler(DataGridView_MouseUp);
                    this.mouseUpHooked = true;
                }
                InvalidateRadioGlyph(this.pressedItemIndex, GetInheritedStyle(null, rowIndex, false /* includeColors */));
            }
        }

        /// <summary>
        /// Invalidates part of the cell as needed
        /// </summary>
        protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
        {
            if (this.DataGridView == null)
            {
                return;
            }

            DataGridViewCellStyle cellStyle = GetInheritedStyle(null, e.RowIndex, false /* includeColors */);
            int oldMouseLocationCode = mouseLocationCode;
            mouseLocationCode = GetMouseLocationCode(this.DataGridView.CreateGraphics(), 
                                                     e.RowIndex, 
                                                     cellStyle, 
                                                     e.X, 
                                                     e.Y);
            if (oldMouseLocationCode != mouseLocationCode)
            {
                if ((oldMouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton || mouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationTopScrollButton) && this.layout.FirstDisplayedItemIndex > 0)
                {
                    this.DataGridView.Invalidate(new Rectangle(this.layout.UpButtonLocation, this.layout.ScrollButtonsSize));
                }
                else if ((oldMouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton || mouseLocationCode == DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationBottomScrollButton) && this.layout.FirstDisplayedItemIndex + this.layout.DisplayedItemsCount < this.Items.Count)
                {
                    this.DataGridView.Invalidate(new Rectangle(this.layout.DownButtonLocation, this.layout.ScrollButtonsSize));
                }
                else
                {
                    if ((this.DataGridView.Rows.SharedRow(e.RowIndex).Cells[e.ColumnIndex].GetInheritedState(e.RowIndex) & DataGridViewElementStates.ReadOnly) != 0)
                    {
                        return;
                    }
                    if (oldMouseLocationCode >= 0)
                    {
                        InvalidateRadioGlyph(oldMouseLocationCode + this.layout.FirstDisplayedItemIndex, cellStyle);
                    }
                    if (mouseLocationCode >= 0)
                    {
                        InvalidateRadioGlyph(mouseLocationCode + this.layout.FirstDisplayedItemIndex, cellStyle);
                    }
                }
            }
        }

        /// <summary>
        /// Invalidates the potential pressed radio button. 
        /// </summary>
        protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
        {
            if (this.DataGridView == null)
            {
                return;
            }
            if (e.Button == MouseButtons.Left && this.pressedItemIndex != -1)
            {
                InvalidateItem(this.pressedItemIndex, e.RowIndex);
                this.pressedItemIndex = -1;
            }
        }

        /// <summary>
        /// Paints the entire cell.
        /// </summary>
        protected override void Paint(Graphics graphics,
            Rectangle clipBounds,
            Rectangle cellBounds,
            int rowIndex,
            DataGridViewElementStates cellState,
            object value,
            object formattedValue,
            string errorText,
            DataGridViewCellStyle cellStyle,
            DataGridViewAdvancedBorderStyle advancedBorderStyle,
            DataGridViewPaintParts paintParts)
        {
            ComputeLayout(graphics,
                          clipBounds,
                          cellBounds,
                          rowIndex,
                          cellState,
                          formattedValue,
                          errorText,
                          cellStyle,
                          advancedBorderStyle,
                          paintParts,
                          true  /*paint*/);
        }

        /// <summary>
        /// Paints a single item.
        /// </summary>
        private void PaintItem(Graphics graphics, 
                               Rectangle radiosBounds, 
                               int rowIndex,
                               int itemIndex, 
                               DataGridViewCellStyle cellStyle,
                               bool itemReadOnly,
                               bool itemSelected,
                               bool mouseOverCell,
                               bool paintFocus)
        {
            object itemFormattedValue = GetFormattedValue(GetItemValue(this.Items[itemIndex]),
                                                    rowIndex,
                                                    ref cellStyle,
                                                    null /*valueTypeConverter*/,
                                                    null /*formattedValueTypeConverter*/,
                                                    DataGridViewDataErrorContexts.Display);
            string itemFormattedText = itemFormattedValue as string;
            if (string.IsNullOrEmpty(itemFormattedText))
            {
                return;
            }
            else
            {
                //Paint the glyph & caption
                Point glyphLocation = new Point(radiosBounds.Left + DATAGRIDVIEWRADIOBUTTONCELL_margin, radiosBounds.Top + DATAGRIDVIEWRADIOBUTTONCELL_margin);
                //TextFormatFlags flags = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPrefix;
                TextFormatFlags flags = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPrefix;
                Rectangle textBounds = new Rectangle(radiosBounds.Left + 2 * DATAGRIDVIEWRADIOBUTTONCELL_margin + this.layout.RadioButtonsSize.Width, radiosBounds.Top + DATAGRIDVIEWRADIOBUTTONCELL_margin, radiosBounds.Width - (2 * DATAGRIDVIEWRADIOBUTTONCELL_margin + this.layout.RadioButtonsSize.Width), cellStyle.Font.Height + 1 /*radiosBounds.Height - 2 * DATAGRIDVIEWRADIOBUTTONCELL_margin*/);
                int localMouseLocationCode = mouseOverCell ? mouseLocationCode : DATAGRIDVIEWRADIOBUTTONCELL_mouseLocationGeneric;
                using (Region clipRegion = graphics.Clip)
                {
                    graphics.SetClip(radiosBounds);
                    RadioButtonState radioButtonState;
                    if (itemSelected)
                    {
                        if (itemReadOnly)
                        {
                            radioButtonState = RadioButtonState.CheckedDisabled;
                        }
                        else
                        {
                            if (mouseOverCell && this.pressedItemIndex == itemIndex)
                            {
                                if (localMouseLocationCode + this.layout.FirstDisplayedItemIndex == this.pressedItemIndex)
                                {
                                    radioButtonState = RadioButtonState.CheckedPressed;
                                }
                                else
                                {
                                    radioButtonState = RadioButtonState.CheckedHot;
                                }
                            }
                            else
                            {
                                if (localMouseLocationCode + this.layout.FirstDisplayedItemIndex == itemIndex && this.pressedItemIndex == -1)
                                {
                                    radioButtonState = RadioButtonState.CheckedHot;
                                }
                                else
                                {
                                    radioButtonState = RadioButtonState.CheckedNormal;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (itemReadOnly)
                        {
                            radioButtonState = RadioButtonState.UncheckedDisabled;
                        }
                        else
                        {
                            if (mouseOverCell && this.pressedItemIndex == itemIndex)
                            {
                                if (localMouseLocationCode + this.layout.FirstDisplayedItemIndex == this.pressedItemIndex)
                                {
                                    radioButtonState = RadioButtonState.UncheckedPressed;
                                }
                                else
                                {
                                    radioButtonState = RadioButtonState.UncheckedHot;
                                }
                            }
                            else
                            {
                                if (localMouseLocationCode + this.layout.FirstDisplayedItemIndex == itemIndex && this.pressedItemIndex == -1)
                                {
                                    radioButtonState = RadioButtonState.UncheckedHot;
                                }
                                else
                                {
                                    radioButtonState = RadioButtonState.UncheckedNormal;
                                }
                            }
                        }
                    }
                    // Note: The cell should only show the focus rectangle when this.DataGridView.ShowFocusCues is true. However that property is
                    // protected and can't be accessed directly. A custom grid derived from DataGridView could expose this notion publicly.
                    RadioButtonRenderer.DrawRadioButton(graphics, 
                                                        glyphLocation, 
                                                        textBounds, 
                                                        itemFormattedText, 
                                                        cellStyle.Font, 
                                                        flags, 
                                                        paintFocus && /* this.DataGridView.ShowFocusCues && */ this.DataGridView.Focused,
                                                        radioButtonState);
                    graphics.Clip = clipRegion;
                } 
            }
        }

        /// <summary>
        /// Helper function that indicates if a paintPart needs to be painted.
        /// </summary>
        private static bool PartPainted(DataGridViewPaintParts paintParts, DataGridViewPaintParts paintPart)
        {
            return (paintParts & paintPart) != 0;
        }

        /// <summary>
        /// Custom implementation that follows the standard representation of cell types.
        /// </summary>
        public override string ToString()
        {
            return "DataGridViewRadioButtonCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + this.RowIndex.ToString(CultureInfo.CurrentCulture) + " }";
        }

        /// <summary>
        /// Returns true if the provided item successfully became the selected item.
        /// </summary>
        private bool UpdateFormattedValue(int newSelectedItemIndex, int rowIndex)
        {
            if (this.FormattedValueType == null || newSelectedItemIndex == this.selectedItemIndex)
            {
                return false;
            }
            IDataGridViewEditingCell editingCell = (IDataGridViewEditingCell)this;
            Debug.Assert(newSelectedItemIndex >= 0);
            Debug.Assert(newSelectedItemIndex < this.Items.Count);
            object item = this.Items[newSelectedItemIndex];
            object displayValue = GetItemDisplayValue(item);
            if (this.FormattedValueType.IsAssignableFrom(displayValue.GetType()))
            {
                editingCell.EditingCellFormattedValue = displayValue;
                this.focusedItemIndex = this.selectedItemIndex;
                this.DataGridView.InvalidateCell(this.ColumnIndex, rowIndex);
            }
            return true;
        }
    }
}
