2

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.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Fahad
  • 1,364
  • 3
  • 20
  • 40
  • 1
    I believe the correct answer is *notify the grid of changes by calling `NotifyCurrentCellDirty(true)` of the grid in editing control* and the accepted answer is completely misleading! – Reza Aghaei May 23 '16 at 14:53
  • To make sure if you take a look at source code of all standard editing control types you will see a part of code doing the same job. For example in `DataGridViewComboBoxEditingControl`, there is an override of [`OnSelectedIndexChange`](http://referencesource.microsoft.com/System.Windows.Forms/winforms/Managed/System/WinForms/DataGridViewComboBoxEditingControl.cs.html#d82a226eee5ab67c) and in the method you can see a method call containing `this.dataGridView.NotifyCurrentCellDirty(true);` – Reza Aghaei May 23 '16 at 14:54
  • Also you can take a look at [How to: Host Controls in Windows Forms DataGridView Cells](https://msdn.microsoft.com/en-us/library/7tas5c80(v=vs.110).aspx) and see how the method `this.EditingControlDataGridView.NotifyCurrentCellDirty(true);` is called in editing control in the sample. – Reza Aghaei May 23 '16 at 14:54

1 Answers1

2

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?

Community
  • 1
  • 1
YouneS
  • 390
  • 4
  • 10