Harald Coppoolse Harald Coppoolse - 11 months ago 44
C# Question

DataGridView using DataSourceNullValue: enter either nothing, or the nullValue

I have a DataGridView, which is databound to a DataSource.

One of the columns of the DataGridView is bound to a property of type decimal? (nullable decimal). This column is editable.

If the value of the property is null, then the underlying object should use a default value retrieved from another object.

Operators seem to be confused if this column shows nothing. Hence I want to show the string "

<default>
", indicating that the default value is used.

When editing this value, I'd like to give the operator the freedom to either leave an empty field, or enter something like "
<default>
", if he wants to indicate that the default value should be used. So:


  • operator enters the string "
    <default>
    ": property gets value (decimal?)null

  • operator enters empty string: property gets value (decimal?)null

  • operator enters any other value: validate if it is a decimal, and use this value.



What I tried (that's always the first that is asked for :-)

public MyForm() // constructor
{
this.InitializeComponent();

// initialize datagridview (part of this could have been done in designer)
this.dataGridView1.AutoGenerateColumns = false;
this.dataGridView1.DataSource = ...;
var valueCellStyle = this.columnNonDefault.DefaultCellStyle;
valueCellStyle.DataSourceNullValue = (decimal?)null;
valueCellStyle.NullValue = "<default>";
}


The validation event handler is as follows:

private void OnCellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (e.CollumnIndex == this.columnNonDefaultValue.Index)
{ // validating the non-default value
// allow either empty string, or cellStyle.NullValue or decimal
string proposedValue = (string)e.FormattedValue;

// check if empty:
if (String.IsNullOrEmpty(proposedValue)
{ // accept empty:
e.Cancel = false;
}
else
{ // check if "<default>"
var cellToValidate = this.dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
DataGridViewCellStyle style = cellToValidate.InheritedStyle;
if (!style.IsNullValueDefault
&& String.Equals(proposedValue, style.NullValue.ToString()))
{ // accept default value:
e.Cancel = false;
}
else if (this.IsProperDecimal(proposedValue)
{ // accept decimal value
e.Cancel = false;
}
else
{ // all others: don't accept
this.ShowInvalidData(...)
e.Cancel = true;
}
}
}
}


This works fine if people enter a proper decimal value, or "
<default>
". If an empty string is entered, then as soon as the data is pushed to the databound item I get a string format exception.

I had expected I should handle the CellValuePushed event, with VirtualMode = true. Alas, because the grid view is databound this event is not called, not even if a proper value is pushed

So what can I use to accept both "" and empty string as (decimal?) null?

Answer Source

As an option you can use CellParsing and CellFormatting events to handle conversion between entered value to data source value and displaying the data source value in a suitable format.

  • CellParsing: To convert entered text to the suitable data source value.

    Here you want to convert entered value to your default value based on some criteria (when the entered text is empty or "" or equals to the default value).

  • CellFormatting: To convert data source value to suitable text to show in cell.

    Here you want to convert your default value to some text like <Default>.

Here is an example:

public class Test
{
    public int X { get; set; }
    public decimal? Y { get; set; }
}

BindingList<Test> list;
decimal? defaultValue = 100; //Some default value
string defaultFormattedValue = "<Default>"; //Some default formatted value

private void Form1_Load(object sender, EventArgs e)
{
    list = new BindingList<Test>(new List<Test>());
    this.dataGridView1.DataSource = list;
}

private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
    var value = string.Format("{0}", e.Value);
    if (value == string.Empty || value == "\"\"")
        e.Value = defaultValue;
    e.ParsingApplied = true;
}

private void dataGridView1_CellFormatting(object sender, 
    DataGridViewCellFormattingEventArgs e)
{
    if (e.Value is int? && (int?)e.Value == defaultValue)
        e.Value = defaultFormattedValue;
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download