Jimmy
Jimmy

Reputation: 5241

DataGridView: Object of type 'System.DBNull' cannot be converted to type 'System.Decimal'

I have a c# DataGridView that is bound to a c# BindingList. A user-modifiable text column is bound to a decimal value. When the decimal value of the bound object contains 0, an empty cell is correctly displayed as empty rather than showing 0 (i.e., CellFormatting handler displays "" instead of 0). If the user enters 0, then that gets passed on to the bound object and an empty cell is displayed, so that all works fine.

I would like the behavior to be the same if the user clears the cell; however, I get an exception:

System.ArgumentException: Object of type 'System.DBNull' cannot be converted to type 'System.Decimal'.
   at System.ComponentModel.ReflectPropertyDescriptor.SetValue(Object component, Object value)
   at System.Windows.Forms.DataGridView.DataGridViewDataConnection.PushValue(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex, Object value)

In the CellValidating event I do:

string priceString = e.FormattedValue.ToString();
if (String.IsNullOrWhiteSpace(priceString))
{
  boundObject.decimalValue = 0;
}

So that works in the sense that the bound object does get its decimal value correctly set to 0, but I'd like to avoid that exception.

e.FormattedValue is read-only so I can't modify that; I also tried using

dataGridView[e.ColumnIndex, e.RowIndex].Value = 0m;

but that doesn't solve the problem.

Can I do anything in CellValidating to avoid this issue? Or do I need to modify the value somehwere else (CellEndEdit? CellValueChanged? Somewhere else?)

Thanks!

Upvotes: 0

Views: 568

Answers (1)

Loathing
Loathing

Reputation: 5266

The new value needs to be handled in the OnCellParsing(...) method. E.g:

public class Form5 : Form {
    
    public Form5() {
        List<Person> list = new List<Person>();
        list.Add(new Person { Name = "A", Age = 1 });
        list.Add(new Person {Name = "B", Age = 2 });
        list.Add(new Person {Name = "Z", Age = 0 });

        DGV dgv = new DGV { Dock = DockStyle.Fill };
        var bindingList = new BindingList<Person>(list);
        dgv.DataSource = bindingList;
        dgv.BindingContext = new BindingContext();

        Controls.Add(dgv);
    }

    public class DGV : DataGridView {
        ErrorProvider errorProvider = new ErrorProvider { BlinkStyle = ErrorBlinkStyle.NeverBlink };
        public DGV() {
            AllowUserToAddRows = false;
        }

        protected override void OnCellFormatting(DataGridViewCellFormattingEventArgs e) {
            base.OnCellFormatting(e);
            if (e.Value is decimal && ((decimal) e.Value) == 0m)
                e.Value = "";
        }

        protected override void OnCellParsing(DataGridViewCellParsingEventArgs e) {
            base.OnCellParsing(e);
            String s = e.Value.ToString();
            if (String.IsNullOrWhiteSpace(s)) {
                e.Value = 0m;
                e.ParsingApplied = true; // important, otherwise the value reverts back to the original value
            }
            else {
                decimal val = 0m;
                if (decimal.TryParse(s, out val))
                    e.Value = val;
            }
        }

        protected override void OnDataError(bool displayErrorDialogIfNoHandler, DataGridViewDataErrorEventArgs e) {
            base.OnDataError(false, e);
            Control c = this.EditingControl;
            errorProvider.SetError(c, e.Exception.Message);
            errorProvider.SetIconAlignment(c, ErrorIconAlignment.MiddleLeft);
            errorProvider.SetIconPadding(c, -20);
        }

        protected override void OnCellEndEdit(DataGridViewCellEventArgs e) {
            base.OnCellEndEdit(e);
            errorProvider.Clear();
        }
    }

    public class Person {
        public String Name { get; set; }
        public decimal Age { get; set; }
    }
}

Upvotes: 2

Related Questions