Prendo spunto da
un post sul forum dove si chiedeva cosa fare per avere un controllo complesso nell'edit in una GridView in un applicativo WindowsForm.
Anche se cerco di "guidare" sempre l'utente nelle maschere di programmi con dei controlli specifici per i singoli campi (classico master-details) a volte è comodo lasciar modificare direttamente in griglia dei valori, ma la griglia in sè permette un edit semplificato che a volte non è molto User-Friendly.
Fare apparire al momento dell'Edit un controllo specifico è una funzionalità che molte griglie di terze parti forniscono egregiamente, ma forse non tutti sanno che è possibile farlo anche con la DataGridView standard del Framework, implementando l'interfaccia IDataGridViewEditingControl, quindi ad esempio è possibile includere un DateTimePicker (come l'oggetto del post sul forum) o qualsiasi altro controllo .net che riteniamo più opportuno.
* E' possibile fare in maniera più complessa di quella che segue, ma questa a mio avviso è già sufficiente a raggiungere l'obbiettivo in maniera veloce
Quello che serve sono 3 oggetti, ed un po' di codice per fare in modo che venga visualizzato quando si va in Edit il nostro controllo.
Prendiamo ad esempio una colonna che contiene valori DateTime, che all'Edit dell'utente (click dentro alla cella) visualizzi un DateTimePicker per la scelta della data corretta di quella cella:
1) Creare la cella che eredita da DataGridViewTextBoxCell ma che visualizzerà il nostro controllo (MioDatePicker):
public class CalendarCell : DataGridViewTextBoxCell
{
public override Type EditType
{
get { return typeof(MioDatePicker); }
}
public override Type ValueType
{
get { return typeof(DateTime);
}
public override Type FormattedValueType
{
get { return typeof(DateTime); }
}
//--- questo serve perchè in realtà stiamo usando una DataGridViewTextBoxCell
//--- quindi si aspetta appunto una stringa da visualizzare nella cella
protected override object GetFormattedValue(object value, int rowIndex,
ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter,
TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
{
if (value != null)
return value.ToString();
else
return null;
}
}
2) Creare una colonna che estende una DataGridViewTextBoxColumn, e che il CellTemplate sia la nostra cella:
public class CalendarColumn : DataGridViewTextBoxColumn
{
public override DataGridViewCell CellTemplate
{
get { return new CalendarCell(); }
}
}
3) Ed infine la parte più "delicata" ossia estendere un controllo facendogli implementare l'interfaccia IDataGridViewEditingControl:
public class MioDatePicker : DateTimePicker, IDataGridViewEditingControl
{
#region IDataGridViewEditingControl Members
int rowIndex;
DataGridView dataGridView;
bool valueChanged;
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
this.Font = dataGridViewCellStyle.Font;
}
public DataGridView EditingControlDataGridView
{
get { return dataGridView; }
set { dataGridView = value; }
}
public object EditingControlFormattedValue
{
get { return this.Value; }
set { this.Value = (DateTime)value; }
}
public int EditingControlRowIndex
{
get { return rowIndex; }
set { rowIndex = value; }
}
public bool EditingControlValueChanged
{
get { return valueChanged; }
set { valueChanged = value; }
}
public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
{
return true;
}
public Cursor EditingPanelCursor
{
get { return Cursors.Default; }
}
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
return this.EditingControlFormattedValue;
}
public void PrepareEditingControlForEdit(bool selectAll)
{
this.Value = (DateTime)this.EditingControlDataGridView.CurrentCell.Value;
}
public bool RepositionEditingControlOnValueChange
{
get { return false; }
}
#endregion
//--- aggiornamento del valore della proprietà nell'oggetto sottostante
protected override void OnLeave(System.EventArgs e)
{
if (this.EditingControlDataGridView != null)
{
valueChanged = true;
this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
this.EditingControlDataGridView.CurrentCell.Value = this.Value;
}
}
}
Come detto in precedenza è necessario implementare l'interfaccia che la griglia si aspetta dal controllo, e soprattutto dare alla griglia indicazione che il valore è stato cambiato in modo che essa possa aggiornare l'oggetto sottostante (tipicamente Collection oppure DataTable etc...) con il metodo "NotifyCurrentCellDirty".
Creati queste 3 classi (che di fatto poi danno vita ad una sola colonna) è possibile avere questi tipi di colonne nostri, tra la lista delle colonne disponibili nel designer di Visual Studio:
* se non compaiono subito provare a ricompilare l'applicativo
E quello che compare all'utente quando clicca per andare in Edit su quella cella è questo:
Questa tecnica è utilizzabile con praticamente tutti i controlli .net, un esempio che usa oltre alla CalendarColumn, una MaskedColumn (che guida l'utente ad inserire solo numeri), uno SliderColumn ed un ColorColum (visualizza uno Usercontrol del progetto per la selezione di un colore) è disponibile qui: