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
{
///
/// Convenient enumeration using privately for calculating preferred cell sizes.
///
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
///
/// DataGridViewRadioButtonCell class constructor.
///
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.
///
/// Represents the cell's formatted value
///
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();
}
}
///
/// Keeps track of whether the cell's value has changed or not.
///
public virtual bool EditingCellValueChanged
{
get
{
return this.valueChanged;
}
set
{
this.valueChanged = value;
}
}
///
/// Returns the current formatted value of the cell
///
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;
}
}
///
/// Called by the grid when the cell enters editing mode.
///
public virtual void PrepareEditingCellForEdit(bool selectAll)
{
// This cell type has nothing to do here.
}
// Implementation of the IDataGridViewEditingCell interface stops here.
///
/// Stores the CurrencyManager associated to the cell's DataSource
///
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;
}
}
///
/// Overrides the DataGridViewComboBox's implementation of the DataSource property to
/// initialize the displayMemberProperty and valueMemberProperty members.
///
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);
}
}
}
}
///
/// Overrides the DataGridViewComboBox's implementation of the DisplayMember property to
/// update the displayMemberProperty member.
///
public override string DisplayMember
{
get
{
return base.DisplayMember;
}
set
{
base.DisplayMember = value;
InitializeDisplayMemberPropertyDescriptor(value);
}
}
///
/// Overrides the base implementation to replace the 'complex editing experience'
/// with a 'simple editing experience'.
///
public override Type EditType
{
get
{
// Return null since no editing control is used for the editing experience.
return null;
}
}
///
/// Custom property that represents the maximum number of radio buttons shown by the cell.
///
[
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);
}
}
}
}
///
/// Called internally by the DataGridViewRadioButtonColumn class to avoid the invalidation
/// done by the MaxDisplayedItems setter above (for performance reasons).
///
internal int MaxDisplayedItemsInternal
{
set
{
Debug.Assert(value >= 1 && value <= 100);
this.maxDisplayedItems = value;
}
}
///
/// Utility function that returns the standard thickness (in pixels) of the four borders of the cell.
///
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;
}
}
}
///
/// Overrides the DataGridViewComboBox's implementation of the ValueMember property to
/// update the valueMemberProperty member.
///
public override string ValueMember
{
get
{
return base.ValueMember;
}
set
{
base.ValueMember = value;
InitializeValueMemberPropertyDescriptor(value);
}
}
///
/// Utility function that returns the cell state inherited from the owning row and column.
///
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;
}
///
/// Custom implementation of the Clone method to copy over the special properties of the cell.
///
public override object Clone()
{
DataGridViewRadioButtonCell dataGridViewCell = base.Clone() as DataGridViewRadioButtonCell;
if (dataGridViewCell != null)
{
dataGridViewCell.MaxDisplayedItems = this.MaxDisplayedItems;
}
return dataGridViewCell;
}
///
/// Computes the layout of the cell and optionally paints it.
///
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();
}
}
}
///
/// Returns whether calling the OnContentClick method would force the owning row to be unshared.
///
protected override bool ContentClickUnsharesRow(DataGridViewCellEventArgs e)
{
Point ptCurrentCell = this.DataGridView.CurrentCellAddress;
return ptCurrentCell.X == this.ColumnIndex &&
ptCurrentCell.Y == e.RowIndex &&
this.DataGridView.IsCurrentCellInEditMode;
}
///
/// Raised when the owning grid gets a MouseUp notification
///
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;
}
///
/// Raised when the cell's DataSource is initialized.
///
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);
}
///
/// Returns whether calling the OnEnter method would force the owning row to be unshared.
///
protected override bool EnterUnsharesRow(int rowIndex, bool throughMouseClick)
{
return this.focusedItemIndex == -1;
}
///
/// Custom implementation of the GetContentBounds method which delegates most of the work to the ComputeLayout function.
///
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;
}
///
/// Utility function that converts a constraintSize provided to GetPreferredSize into a
/// DataGridViewRadioButtonFreeDimension enum value.
///
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'.");
}
}
}
///
/// Utility function that returns the display value of an item given the
/// display/value property descriptors and display/value property names.
///
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;
}
///
/// Utility function that returns the value of an item given the
/// display/value property descriptors and display/value property names.
///
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;
}
///
/// Returns the code identifying the part of the cell which is underneath the mouse pointer.
///
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;
}
///
/// Returns a ScrollBarArrowButtonState state given the current mouse location.
///
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;
}
}
///
/// Custom implementation of the GetPreferredSize method.
///
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);
}
///
/// Helper function that determines if scrolling buttons should be displayed
///
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;
}
///
/// Helper function that sets the displayMemberProperty member based on the DataSource and the provided displayMember field name
///
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;
}
}
}
}
///
/// Helper function that sets the valueMemberProperty member based on the DataSource and the provided valueMember field name
///
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;
}
}
}
}
///
/// Helper function that invalidates the entire area of an entry
///
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)));
}
}
///
/// Helper function that invalidates the glyph section of an entry
///
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));
}
}
///
/// Returns whether calling the OnKeyDown method would force the owning row to be unshared.
///
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;
}
///
/// Returns whether calling the OnKeyUp method would force the owning row to be unshared.
///
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;
}
///
/// Returns whether calling the OnMouseDown method would force the owning row to be unshared.
///
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;
}
///
/// Returns whether calling the OnMouseLeave method would force the owning row to be unshared.
///
protected override bool MouseLeaveUnsharesRow(int rowIndex)
{
return this.pressedItemIndex != -1 && !this.mouseUpHooked;
}
///
/// Returns whether calling the OnMouseUp method would force the owning row to be unshared.
///
protected override bool MouseUpUnsharesRow(DataGridViewCellMouseEventArgs e)
{
return e.Button == MouseButtons.Left && this.pressedItemIndex != -1;
}
///
/// Method that declares the cell dirty and notifies the grid of the value change.
///
private void NotifyDataGridViewOfValueChange()
{
this.valueChanged = true;
Debug.Assert(this.DataGridView != null);
this.DataGridView.NotifyCurrentCellDirty(true);
}
///
/// Potentially updates the selected item and notifies the grid of the change.
///
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();
}
}
}
///
/// Updates the property descriptors when the cell gets attached to the grid.
///
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();
}
///
/// Makes sure that there is a focused item when the cell becomes the current one.
///
protected override void OnEnter(int rowIndex, bool throughMouseClick)
{
if (this.focusedItemIndex == -1)
{
this.focusedItemIndex = this.layout.FirstDisplayedItemIndex;
}
base.OnEnter(rowIndex, throughMouseClick);
}
///
/// Handles the KeyDown notification when it can result in a value change.
///
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;
}
}
}
///
/// Handles the KeyUp notification to change the cell's value.
///
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;
}
}
///
/// Custom implementation of the MouseDown notification to update the cell's value or scroll the entries.
///
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;
}
}
}
///
/// Makes sure the radio button gets hot when the mouse gets over it
///
protected override void OnMouseEnter(int rowIndex)
{
if (this.DataGridView == null)
{
return;
}
if (this.pressedItemIndex != -1)
{
InvalidateRadioGlyph(this.pressedItemIndex, GetInheritedStyle(null, rowIndex, false /* includeColors */));
}
}
///
/// Invalidates part of the cell as needed
///
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 */));
}
}
///
/// Invalidates part of the cell as needed
///
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);
}
}
}
}
///
/// Invalidates the potential pressed radio button.
///
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;
}
}
///
/// Paints the entire cell.
///
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*/);
}
///
/// Paints a single item.
///
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;
}
}
}
///
/// Helper function that indicates if a paintPart needs to be painted.
///
private static bool PartPainted(DataGridViewPaintParts paintParts, DataGridViewPaintParts paintPart)
{
return (paintParts & paintPart) != 0;
}
///
/// Custom implementation that follows the standard representation of cell types.
///
public override string ToString()
{
return "DataGridViewRadioButtonCell { ColumnIndex=" + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) + ", RowIndex=" + this.RowIndex.ToString(CultureInfo.CurrentCulture) + " }";
}
///
/// Returns true if the provided item successfully became the selected item.
///
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;
}
}
}