// Copyright © 2006 by Wunnell Development
//
// This code may be used and distributed freely, either in part or in full,
// under the condition that this copyright notice is included unchanged.
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Wunnell.Windows.Forms
{
///
/// Displays a date/time picker in a DataGridView control.
///
///
/// The DataGridViewDateTimePickerCell class is a specialised type of DataGridViewCell used to display a date/time picker control, which is a date/time editing field combined with a drop-down calendar field. The currently selected DataGridViewDateTimePickerCell hosts a DataGridViewDateTimePickerEditingControl in which the user can edit the cell's value, assuming the cell's ReadOnly property is set to false.
///
public class DataGridViewDateTimePickerCell : DataGridViewTextBoxCell
{
#region Constants
///
/// The default value of the Checked property.
///
internal const bool DEFAULT_CHECKED = true;
///
/// The default value of the CustomFormat property.
///
internal const string DEFAULT_CUSTOM_FORMAT = null;
///
/// The default value of the Format property.
///
internal const DateTimePickerFormat DEFAULT_FORMAT = DateTimePickerFormat.Long;
///
/// The default value of the ShowCheckBox property.
///
internal const bool DEFAULT_SHOW_CHECK_BOX = false;
///
/// The default value of the ShowUpDown property.
///
internal const bool DEFAULT_SHOW_UP_DOWN = false;
///
/// The default value of the ZerTime property.
///
internal const bool DEFAULT_ZERO_TIME = false;
#endregion // Constants
#region Variables
///
/// When ShowCheckBox is true, determines if the check box is checked, indicating that the user has selected a value.
///
private bool _checked;
///
/// The custom format string used to format the date and/or time displayed in the cell.
///
private string _customFormat;
///
/// Determines whether dates and times are displayed using standard or custom formatting.
///
private DateTimePickerFormat _format;
///
/// The maximum date that can be selected.
///
private DateTime _maxDate;
///
/// The minimum date that can be selected.
///
private DateTime _minDate;
///
/// Determines whether a check box is displayed in the control. When the box is unchecked, no value is selected.
///
private bool _showCheckBox;
///
/// Indicates whether a spin box rather than a drop-down calendar is displayed for modifying the control value.
///
private bool _showUpDown;
///
/// Indicates whether the time should be zeroed to provide a value containing effectively just a date.
///
private bool _zeroTime;
#endregion // Variables
#region Properties
///
/// Gets the DataGridView's current EditingControl as a DataGridViewDateTimePickerEditingControl reference.
///
///
/// The DataGridView's current DataGridViewDateTimePickerEditingControl.
///
private DataGridViewDateTimePickerEditingControl EditingDateTimePicker
{
get
{
return this.DataGridView.EditingControl as DataGridViewDateTimePickerEditingControl;
}
}
///
/// Gets or sets a value indicating whether the Value property has been set with a valid date/time value and the displayed value is able to be updated.
///
///
/// true if the Value property has been set with a valid DateTime value and the displayed value is able to be updated; otherwise, false. The default is true.
///
public bool Checked
{
get
{
if (this.RowIndex < 0)
{
// The row is not displayed in the grid so use the Value property is not available.
return this._checked;
}
else
{
return this.Value != DBNull.Value;
}
}
set
{
if (value != (this.Value != DBNull.Value))
{
// The value has changed so set the new value and refresh the grid.
this.SetChecked(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Gets or sets the custom date/time format string.
///
///
/// A string that represents the custom date/time format. The default is a null reference.
///
public string CustomFormat
{
get
{
return this._customFormat;
}
set
{
if (value != this._customFormat)
{
// The value has changed so set the new value and refresh the grid.
this.SetCustomFormat(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Overridden. Gets the default value for a cell in the row for new records.
///
///
/// null if the cell is not in edit mode and the editing control would display an unchecked check box by default; otherwise, the current date and time.
///
public override object DefaultNewRowValue
{
get
{
if (this.IsInEditMode || !this._showCheckBox || this._checked)
{
//DateTime.Now :
// The default value for a populated cell is the current date and, optionally, time.
return this.ZeroTime ? this.MinDate : this.MinDate; // DateTime.Now : DateTime.Now;
}
else
{
// An unchecked check box in a cell that is not being edited indicates a null value.
return DBNull.Value;
}
}
}
///
/// Overridden. Gets the type of the cell's hosted editing control.
///
///
/// A Type representing the DataGridViewDateTimePickerEditingControl type.
///
public override Type EditType
{
get
{
// Return the type of the control that DataGridViewDateTimePickerCell contains.
return typeof(DataGridViewDateTimePickerEditingControl);
}
}
///
/// Gets or sets the format of the date and time displayed in the control.
///
///
/// One of the DateTimePickerFormat values. The default is Long.
///
public DateTimePickerFormat Format
{
get
{
return this._format;
}
set
{
if (value != this._format)
{
// The value has changed so set the new value and refresh the grid.
this.SetFormat(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Gets or sets the maximum date and time that can be selected in the control.
///
///
/// The maximum date and time that can be selected in the control. The default is 12/31/9998 00:00:00.
///
public DateTime MaxDate
{
get
{
return this._maxDate;
}
set
{
if (value != this._maxDate)
{
// The value has changed so set the new value and refresh the grid.
this.SetMaxDate(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Gets or sets the minimum date and time that can be selected in the control.
///
///
/// The minimum date and time that can be selected in the control. The default is 1/1/1753 00:00:00.
///
public DateTime MinDate
{
get
{
return this._minDate;
}
set
{
if (value != this._minDate)
{
// The value has changed so set the new value and refresh the grid.
this.SetMinDate(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Gets or sets a value indicating whether a check box is displayed to the left of the selected date.
///
///
/// true if a check box is displayed to the left of the selected date; otherwise, false. The default is false.
///
public bool ShowCheckBox
{
get
{
return this._showCheckBox;
}
set
{
if (value != this._showCheckBox)
{
// The value has changed so set the new value and refresh the grid.
this.SetShowCheckBox(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Gets or sets a value indicating whether a spin button control (also known as an up-down control) is used to adjust the date/time value.
///
///
/// true if a spin button control is used to adjust the date/time value; otherwise, false. The default is false.
///
public bool ShowUpDown
{
get
{
return this._showUpDown;
}
set
{
if (value != this._showUpDown)
{
// The value has changed so set the new value and refresh the grid.
this.SetShowUpDown(this.RowIndex, value);
this.OnPropertyChange();
}
}
}
///
/// Overridden. Gets the data type of the values in the cell.
///
///
/// A Type representing the DateTime type.
///
public override Type ValueType
{
get
{
// Return the type of the value that DataGridViewDateTimePickerCell contains.
return typeof(DateTime);
}
}
///
/// Gets or sets a value indicating whether the time should be zeroed to provide a value containing effectively just a date.
///
///
/// true if the time portion of the date/time value is zeroed; otherwise, false. The default is false.
///
///
/// This property always returns true when a standard date form, i.e. Long or Short, is in use. It always returns false if a standard time format, i.e. Time, is in use. It is user-configurable when a custom format is in use.
///
public bool ZeroTime
{
get
{
bool zeroTime;
switch (this.Format)
{
// The time is always zeroed when a standard date format is used.
case DateTimePickerFormat.Long:
case DateTimePickerFormat.Short:
zeroTime = true;
break;
// The time is never zeroed when a standard time format is used.
case DateTimePickerFormat.Time:
zeroTime = false;
break;
// The property is user-configurable when a custom format is used.
case DateTimePickerFormat.Custom:
default:
zeroTime = this._zeroTime;
break;
}
return zeroTime;
}
set
{
if (value != this._zeroTime)
{
// The value has changed so set the new value.
this.SetZeroTime(this.RowIndex, value);
if (this.Format == DateTimePickerFormat.Custom)
{
// Refresh the grid only if a custom format is in use, otherwise the property's return value will not have changed.
this.OnPropertyChange();
}
}
}
}
#endregion // Properties
#region Constructors
///
/// Initialises a new instance of the DataGridViewTextBoxCell class.
///
///
/// This constructor initialises the following property values.
/// Checked: true
/// CustomFormat: null reference
/// Format: Long
/// MaxDate: 31/12/9998
/// MinDate: 1/01/1753
/// ShowCheckBox: false
/// ShowUpDown: false
///
public DataGridViewDateTimePickerCell()
: base()
{
this._checked = DEFAULT_CHECKED;
this._customFormat = DEFAULT_CUSTOM_FORMAT;
this._format = DEFAULT_FORMAT;
this._maxDate = DateTimePicker.MaximumDateTime;
this._minDate = DateTimePicker.MinimumDateTime;
this._showCheckBox = DEFAULT_SHOW_CHECK_BOX;
this._showUpDown = DEFAULT_SHOW_UP_DOWN;
this._zeroTime = DEFAULT_ZERO_TIME;
}
#endregion // Constructors
#region Methods
///
/// Overridden. Creates an exact copy of this cell.
///
///
/// An Object that represents the cloned DataGridViewDateTimePickerCell.
///
public override object Clone()
{
DataGridViewDateTimePickerCell clone = base.Clone() as DataGridViewDateTimePickerCell;
if (clone != null)
{
clone.Checked = this.Checked;
clone.CustomFormat = this.CustomFormat;
clone.Format = this.Format;
clone.MaxDate = this.MaxDate;
clone.MinDate = this.MinDate;
clone.ShowCheckBox = this.ShowCheckBox;
clone.ShowUpDown = this.ShowUpDown;
clone.ZeroTime = this.ZeroTime;
}
return clone;
}
///
/// Gets the value closest to the specified value that is also within the constraints of the minimum and maximum allowable values.
///
///
/// The proposed value.
///
///
/// The valid value closest to the proposed value.
///
private DateTime Constrain(DateTime value)
{
Debug.Assert(this._minDate <= this._maxDate);
if (value < this._minDate)
{
value = this._minDate;
}
else if (value > this._maxDate)
{
value = this._maxDate;
}
return value;
}
///
/// Overrideen. Removes the cell's editing control from the DataGridView.
///
public override void DetachEditingControl()
{
DateTimePicker editingControl = this.EditingDateTimePicker;
if (editingControl != null)
{
// Update the cell's value based on the value in the editing control and whether or not it is checked.
this.Value = this.ZeroTime ? editingControl.Value.Date
: editingControl.Value;
this.Checked = editingControl.Checked;
}
base.DetachEditingControl();
}
///
/// Converts the current cell content alignment into the corresponding drop-down alignment for the editing control.
///
///
/// One of the LeftRightAlignment values.
///
///
/// If the cell alignment is not set default column alignment is used. If this is not set then the default value Left is used.
///
private LeftRightAlignment GetEditingControlDropDownAlign()
{
// Get the content alignment of the cell. If it is not set then get the default alignment for the column.
DataGridViewContentAlignment cellAlignment = this.Style.Alignment == DataGridViewContentAlignment.NotSet ? this.OwningColumn.DefaultCellStyle.Alignment
: this.Style.Alignment;
LeftRightAlignment controlAlignment;
switch (cellAlignment)
{
case DataGridViewContentAlignment.MiddleRight:
case DataGridViewContentAlignment.BottomRight:
case DataGridViewContentAlignment.TopRight:
controlAlignment = LeftRightAlignment.Right;
break;
case DataGridViewContentAlignment.MiddleLeft:
case DataGridViewContentAlignment.BottomLeft:
case DataGridViewContentAlignment.TopLeft:
default:
controlAlignment = LeftRightAlignment.Left;
break;
}
return controlAlignment;
}
///
/// Overrideen. Gets the formatted value of the cell's data.
///
///
/// The value to be formatted.
///
///
/// The index of the cell's parent row.
///
///
/// The DataGridViewCellStyle in effect for the cell.
///
///
/// A TypeConverter associated with the value type that provides custom conversion to the formatted value type, or a null reference if no such custom conversion is needed.
///
///
/// A TypeConverter associated with the formatted value type that provides custom conversion from the value type, or a null reference if no such custom conversion is needed.
///
///
/// A bitwise combination of DataGridViewDataErrorContexts values describing the context in which the formatted value is needed.
///
///
/// The value of the cell's data after formatting has been applied or a null reference if the cell is not part of a DataGridView control.
///
protected override object GetFormattedValue(object value,
int rowIndex,
ref DataGridViewCellStyle cellStyle,
System.ComponentModel.TypeConverter valueTypeConverter,
System.ComponentModel.TypeConverter formattedValueTypeConverter,
DataGridViewDataErrorContexts context)
{
object formattedValue = base.GetFormattedValue(value,
rowIndex,
ref cellStyle,
valueTypeConverter,
formattedValueTypeConverter,
context);
string formattedString = formattedValue as string;
if (!string.IsNullOrEmpty(formattedString) && value != null)
{
DateTime unformattedValue = (DateTime)value;
switch (this.Format)
{
case DateTimePickerFormat.Custom:
formattedValue = unformattedValue.ToString(this.CustomFormat);
break;
case DateTimePickerFormat.Long:
formattedValue = unformattedValue.ToLongDateString();
break;
case DateTimePickerFormat.Short:
formattedValue = unformattedValue.ToShortDateString();
break;
case DateTimePickerFormat.Time:
formattedValue = unformattedValue.ToLongTimeString();
break;
}
}
return formattedValue;
}
///
/// Overrideen. Initializes the control used to edit the cell.
///
///
/// The zero-based row index of the cell's location.
///
///
/// An Object that represents the value displayed by the cell when editing is started.
///
///
/// A DataGridViewCellStyle that represents the style of the cell.
///
///
/// After initialising the control as per the base implementation, this method sets the properties of the DateTimePicker control in accordance with the cell properties.
///
public override void InitializeEditingControl(int rowIndex,
object initialFormattedValue,
DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
DataGridViewDateTimePickerEditingControl editingControl = this.DataGridView.EditingControl as DataGridViewDateTimePickerEditingControl;
if (editingControl != null)
{
string initialFormattedString = initialFormattedValue as string;
// Set the specific properties of the DateTimePicker control.
if (string.IsNullOrEmpty(initialFormattedString))
{
editingControl.Value = (DateTime)this.DefaultNewRowValue;
}
else
{
editingControl.Value = this.Constrain(DateTime.Parse(initialFormattedString));
}
editingControl.Checked = this.Checked;
editingControl.CustomFormat = this.CustomFormat;
editingControl.DropDownAlign = this.GetEditingControlDropDownAlign();
editingControl.Format = this.Format;
editingControl.MaxDate = this.MaxDate;
editingControl.MinDate = this.MinDate;
editingControl.ShowCheckBox = this.ShowCheckBox;
editingControl.ShowUpDown = this.ShowUpDown;
}
}
/////
///// Refreshes the DataGridView as needed to account for the new property value.
/////
private void OnPropertyChange()
{
if (this.DataGridView != null && !this.DataGridView.IsDisposed && !this.DataGridView.Disposing)
{
if (this.RowIndex == -1)
{
// Invalidate and autosize the parent column
this.DataGridView.InvalidateColumn(this.ColumnIndex);
// TODO: Explicitly resize the parent row and the row and column headers as required.
}
else
{
// Invalidate and autosize the parent row and column, including headers.
this.DataGridView.UpdateCellValue(this.ColumnIndex, this.RowIndex);
}
}
}
//
//Determines whether the current cell is displaying the the grid's editing control at the specified row index.
//
//
//The zero-based index of the row to test. This is required because the current cell may be shared among multiple rows.
//
//
//false if the cell or row is detached; true if the current cell is displaying the grid's editing control in the specified row; otherwise, false.
//
private bool OwnsEditingDateTimePicker(int rowIndex)
{
bool result;
if (rowIndex == -1 || this.DataGridView == null)
{
result = false;
}
else
{
DataGridViewDateTimePickerEditingControl editingControl = this.DataGridView.EditingControl as DataGridViewDateTimePickerEditingControl;
result = editingControl != null && rowIndex == ((IDataGridViewEditingControl)editingControl).EditingControlRowIndex;
}
return result;
}
///
/// Sets a value indicating whether the Value property has been set with a valid date/time value and the displayed value is able to be updated.
///
///
/// The index of the row containing the current cell.
///
///
/// A value indicating whether the Value property has been set with a valid date/time value and the displayed value is able to be updated.
///
internal void SetChecked(int rowIndex, bool value)
{
this._checked = value;
if (this._checked)
{
if (this.Value == DBNull.Value)
{
// The cell is checked but does not contain a value so assign the default value.
this.Value = this.DefaultNewRowValue;
}
}
else
{
// The cell is not checked so set the value to null.
this.Value = DBNull.Value;
}
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.Checked = value;
}
}
///
/// Sets the custom date/time format string.
///
///
/// The index of the row containing the current cell.
///
///
/// A string that represents the custom date/time format.
///
internal void SetCustomFormat(int rowIndex, string value)
{
this._customFormat = value;
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.CustomFormat = value;
}
}
///
/// Sets the maximum date and time that can be selected in the control.
///
///
/// The index of the row containing the current cell.
///
///
/// The maximum date and time that can be selected in the control.
///
internal void SetFormat(int rowIndex, DateTimePickerFormat value)
{
Debug.Assert(Enum.GetName(typeof(DateTimePickerFormat), value) != null);
this._format = value;
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.Format = value;
}
}
///
/// Sets the format of the date and time displayed in the control.
///
///
/// The index of the row containing the current cell.
///
///
/// One of the DateTimePickerFormat values.
///
internal void SetMaxDate(int rowIndex, DateTime value)
{
Debug.Assert(value >= DateTimePicker.MinimumDateTime && value <= DateTimePicker.MaximumDateTime);
this._maxDate = value;
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.MaxDate = value;
}
}
///
/// Sets the minimum date and time that can be selected in the control.
///
///
/// The index of the row containing the current cell.
///
///
/// The minimum date and time that can be selected in the control.
///
internal void SetMinDate(int rowIndex, DateTime value)
{
Debug.Assert(value >= DateTimePicker.MinimumDateTime && value <= DateTimePicker.MaximumDateTime);
this._minDate = value;
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.MinDate = value;
}
}
///
/// Sets a value indicating whether a check box is displayed to the left of the selected date.
///
///
/// The index of the row containing the current cell.
///
///
/// A value indicating whether a check box is displayed to the left of the selected date.
///
internal void SetShowCheckBox(int rowIndex, bool value)
{
this._showCheckBox = value;
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.ShowCheckBox = value;
}
}
///
/// Sets a value indicating whether a spin button control (also known as an up-down control) is used to adjust the date/time value.
///
///
/// The index of the row containing the current cell.
///
///
/// A value indicating whether a spin button control (also known as an up-down control) is used to adjust the date/time value.
///
internal void SetShowUpDown(int rowIndex, bool value)
{
this._showUpDown = value;
if (this.OwnsEditingDateTimePicker(rowIndex))
{
// Update the property of the current editing control.
this.EditingDateTimePicker.ShowUpDown = value;
}
}
///
/// Sets a value indicating whether the time should be zeroed to provide a value containing effectively just a date.
///
///
/// The index of the row containing the current cell.
///
///
/// A value indicating whether the time should be zeroed to provide a value containing effectively just a date.
///
internal void SetZeroTime(int rowIndex, bool value)
{
this._zeroTime = value;
}
#endregion // Methods
}
}