Search code examples
c#winformsdatagridviewdatagridviewcolumn

DataGridViewColumn EditingControl edited value not reflecting


I have a user control which I need to add as a column in a DataGridView. I followed the instructions as shown here.

The control gets added correctly, and when I click on the cell, the user control shows up. However, now when I click elsewhere, the edited value does not get reflected back to the DataGridView. The code is as follows. MoneyTextBox is the user control.

class MoneyTextBoxColumn : DataGridViewColumn
{
    public MoneyTextBoxColumn() : base(new MoneyTextBoxCell())
    {
    }

    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            // Ensure that the cell used for the template is a MoneyTextBoxCell.
            if (value != null && !value.GetType().IsAssignableFrom(typeof(MoneyTextBoxCell)))
            {
                throw new InvalidCastException("Must be a MoneyTextBoxCell");
            }
            base.CellTemplate = value;
        }
    }
}

public class MoneyTextBoxCell : DataGridViewTextBoxCell
{

    public MoneyTextBoxCell() : base()
    {
    }

    public override void InitializeEditingControl(int rowIndex, object
        initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        // Set the value of the editing control to the current cell value.
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
        MoneyTextBoxEditingControl ctl = DataGridView.EditingControl as MoneyTextBoxEditingControl;
        if (this.RowIndex >= 0)
        {
            if (!object.ReferenceEquals(this.Value, DBNull.Value) && this.Value != null && this.Value.ToString() != string.Empty)
            {
                ctl.Text = this.Value.ToString();
            }
            else
            {
                ctl.Text = this.DefaultNewRowValue.ToString();
            }
        }
    }


    public override Type EditType
    {
        get
        {
            // Return the type of the editing control that MoneyTextBoxCell uses.
            return typeof(MoneyTextBoxEditingControl);
        }
    }

    public override Type ValueType
    {
        get
        {
            // Return the type of the value that MoneyTextBoxCell contains.

            return typeof(string);
        }
    }

    public override object DefaultNewRowValue
    {
        get
        {
            return "0.00";
        }
    }
}

class MoneyTextBoxEditingControl : MoneyTextBox, IDataGridViewEditingControl
{
    DataGridView dataGridView;
    private bool valueChanged = false;
    int rowIndex;

    public MoneyTextBoxEditingControl()
    {
    }

    // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
    // property.
    public object EditingControlFormattedValue
    {
        get
        {
            return this.Value;
        }
        set
        {
            this.MText = (string)value;
        }
    }

    // Implements the 
    // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
    public object GetEditingControlFormattedValue(
        DataGridViewDataErrorContexts context)
    {
        return EditingControlFormattedValue;
    }

    // Implements the 
    // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
    public void ApplyCellStyleToEditingControl(
        DataGridViewCellStyle dataGridViewCellStyle)
    {
        this.Font = dataGridViewCellStyle.Font;
    }

    // Implements the IDataGridViewEditingControl.EditingControlRowIndex 
    // property.
    public int EditingControlRowIndex
    {
        get
        {
            return rowIndex;
        }
        set
        {
            rowIndex = value;
        }
    }

    // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 
    // method.
    public bool EditingControlWantsInputKey(
        Keys key, bool dataGridViewWantsInputKey)
    {
        // Let the DateTimePicker handle the keys listed.
        switch (key & Keys.KeyCode)
        {
            case Keys.Left:
            case Keys.Up:
            case Keys.Down:
            case Keys.Right:
            case Keys.Home:
            case Keys.End:
            case Keys.PageDown:
            case Keys.PageUp:
                return true;
            default:
                return !dataGridViewWantsInputKey;
        }
    }

    // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
    // method.
    public void PrepareEditingControlForEdit(bool selectAll)
    {
        // No preparation needs to be done.
    }

    // Implements the IDataGridViewEditingControl
    // .RepositionEditingControlOnValueChange property.
    public bool RepositionEditingControlOnValueChange
    {
        get
        {
            return false;
        }
    }

    // Implements the IDataGridViewEditingControl
    // .EditingControlDataGridView property.
    public DataGridView EditingControlDataGridView
    {
        get
        {
            return dataGridView;
        }
        set
        {
            dataGridView = value;
        }
    }

    // Implements the IDataGridViewEditingControl
    // .EditingControlValueChanged property.
    public bool EditingControlValueChanged
    {
        get
        {
            return valueChanged;
        }
        set
        {
            valueChanged = value;
        }
    }

    // Implements the IDataGridViewEditingControl
    // .EditingPanelCursor property.
    public Cursor EditingPanelCursor
    {
        get
        {
            return base.Cursor;
        }
    }      
}

How can I propagate the edited value back to the DataGridView??

The only way how I came close to achieving what I wanted is by manually assigning the value in the DetachEditingControl method as shown below, however, I get a System.InvalidOperationException with the message Operation is not valid due to the current state of the object, when the last row is selected.

public override void DetachEditingControl()
{
    DataGridView dataGridView = this.DataGridView;
    if (dataGridView == null || dataGridView.EditingControl == null)
    {
        throw new InvalidOperationException("Cell is detached or its grid has no editing control.");
    }
    MoneyTextBoxEditingControl ctl = DataGridView.EditingControl as MoneyTextBoxEditingControl;
    dataGridView.CurrentCell.Value = ctl.EditingControlFormattedValue;


    base.DetachEditingControl();
}

My requirement is only to get the edited value back to the DataGridView. TIA.


Solution

  • If the DataGridView is databound, you won't be able to modify the content of the cell. Instead, you should modify the databound object. You can access that object through the DataBoundItem of the DataGridViewRow :

    MyObj obj = (MyObj)dataGridView.CurrentRow.DataBoundItem;
    obj.MyProperty = newValue;
    

    The bound object should implement INotifyPropertyChanged so that the change is reflected in the DataGridView

    You may find some other solutions in this post : How to programmatically set cell value in DataGridView?