C#DataGridView - 检测单元格值更改

时间:2017-03-12 10:50:16

标签: c# datagridview

我终于找到了适合我的自定义NumericUpDownCell。 (链接:custom NumericUpDownCell,由Loathing创建。)一切都很好。

但现在又出现了一个新问题:如果我使用向上箭头增加要购买的产品数量,我想立即增加另一列(价格单元格)的价值(不是在离开单元格之后) 。如何创建一个在更改金额单元格值时作出反应的侦听器?

我尝试在DataGridView上使用CellValueChanged事件。但这反应很奇怪:首先当我启动应用程序时(我猜测DataGridView已经创建并且它对此做出了反应。)其次,在我取消选择我增加值的单元格后,它首先做出了反应。

我希望它在我点击向上箭头时立即做出反应。我想也许有一种方法来修改Loathing的代码来为此添加一个监听器?但我自己也没有能力做到这一点。除非有一个内置的方法,我还没有找到它?

1 个答案:

答案 0 :(得分:0)

一个简单的解决方案是使用DataGridView的EditingControlShowing事件,并为ValueChanged EditingControl事件注册一个事件处理程序,如下面的代码所示:

NumericUpDownEditingControl lastCtl = null;
EventHandler handler = null;

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    var ctl = e.Control as NumericUpDownEditingControl;
    if (ctl != null) //&& ctl != lastCtl
    {
        if (handler == null) handler = new EventHandler(myUpDownCtl_ValueChanged); //save a handler has better performance
        //Event => ValueChanged: just fires for Up/Dn btn or when press enter, TextChanged: also fired during user typing
        if (lastCtl != null) lastCtl.ValueChanged -= handler; //ensure we remove our handler. 
        lastCtl = ctl;
        lastCtl.ValueChanged += handler; //we can use myUpDownCtl_ValueChanged directly instead of that handler var
        //handler(sender, EventArgs.Empty);
    }
}

void myUpDownCtl_ValueChanged(object sender, EventArgs e)
{
    MessageBox.Show("New value: " + lastCtl.Value.ToString());
    //Grid1.CurrentRow.Cells[5].Value = lastCtl.Value * 10; //sample to change other columns based on this value
}

注意:ValueChanged事件仅针对向上/向下按钮触发,或者当用户键入某个数字并按Enter键时触发。如果您想在Numeric TextBox内的用户类型时更新,请改用TextChanged

另请注意我在回答InitializeEditingControl方法的答案时发表的评论。

更新

这是要测试的整个代码:

public partial class Form4 : Form
{
    public Form4()
    {
        InitializeComponent();
    }

    private void Form4_Load(object sender, EventArgs e)
    {
    }

    NumericUpDownEditingControl lastCtl = null;
    EventHandler handler = null;

    private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
    {
        var ctl = e.Control as NumericUpDownEditingControl;
        if (ctl != null) //&& ctl != lastCtl
        {
            if (handler == null) handler = new EventHandler(myUpDownCtl_ValueChanged); //save a handler has better performance
            //Event => ValueChanged: just fires for Up/Dn btn or when press enter, TextChanged: also fired during user typing
            if (lastCtl != null) lastCtl.ValueChanged -= handler; //ensure we remove our handler. 
            lastCtl = ctl;
            lastCtl.ValueChanged += handler; //we can use myUpDownCtl_ValueChanged directly instead of that handler var
            //handler(sender, EventArgs.Empty);
        }
    }

    void myUpDownCtl_ValueChanged(object sender, EventArgs e)
    {
        //MessageBox.Show("New value: " + lastCtl.Value.ToString());
        dataGridView1.CurrentRow.Cells[1].Value = lastCtl.Value * 10; //sample to change other columns based on this value
    }

} //end Form


//****************************************************************


public class NumericUpDownColumn : DataGridViewColumn {

    public NumericUpDownColumn() : base(new NumericUpDownCell()) {
        this.ValueType = typeof(decimal?);
    }

    public override DataGridViewCell CellTemplate {
        get {
            return base.CellTemplate;
        }
        set {
            if (!(value is NumericUpDownCell))
                throw new InvalidCastException("Must be a NumericUpDownCell");

            base.CellTemplate = value;
        }
    }
}

public class NumericUpDownCell : DataGridViewTextBoxCell {

    public override void InitializeEditingControl(int rowIndex, Object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) {
        // required to initialize the editing control:
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
        var ctl = (NumericUpDownEditingControl) DataGridView.EditingControl;
        //NumericUpDownColumn cc = (NumericUpDownColumn) this.OwningColumn;
        if (this.Value == null || this.Value == DBNull.Value) {
            ctl.Value = (ctl.Minimum <= 0 && ctl.Maximum >= 0 ? 0 : ctl.Minimum);
        }
        else {
            Object trueValue = this.Value;
            ctl.Value = Convert.ToDecimal(trueValue); //(decimal)trueValue; 
        }
    }

    public override Type EditType {
        get {
            return typeof(NumericUpDownEditingControl);
        }
    }

    public override Type ValueType {
        get {
            return base.ValueType;
        }
        set {
            base.ValueType = value;
        }
    }

    public override Object DefaultNewRowValue {
        get {
            return DBNull.Value;
        }
    }
}

[ToolboxItem(false)] //don't show this as a new control in toolbox
public class NumericUpDownEditingControl : NumericUpDown, IDataGridViewEditingControl {

    private bool Cancelling = false;

    public NumericUpDownEditingControl() {
    }

    // Implements the IDataGridViewEditingControl.EditingControlFormattedValue property.
    public Object EditingControlFormattedValue {
        get {
            // must return a String
            // it doesn't matter if the value is formatted, it will be replaced
            // by the formatting events
            String s = "" + this.Value.ToString();
            return s;
        }

        set {
            decimal val = 0;
            if (value is decimal)
                this.Value = (decimal) value;
            else {
                String s = "" + value;
                if (s.Length > 0) {
                    if (decimal.TryParse(s, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out val))
                        this.Value = val;
                }
            }
        }
    }

    protected override void OnLeave(EventArgs e) {
        if (!Cancelling) {
            var dgv = this.EditingControlDataGridView;
            var cell = (NumericUpDownCell) dgv.CurrentCell;
            cell.Value = this.Value;
        }

        base.OnLeave(e);
        Cancelling = false;
    }


    protected override void OnKeyDown(KeyEventArgs e) {
        if (e.KeyCode == Keys.Escape) {
            Cancelling = true;
            e.Handled = true;
            e.SuppressKeyPress = true;
            var dgv = this.EditingControlDataGridView;
            dgv.CancelEdit();
            dgv.EndEdit();
        }

        base.OnKeyDown(e);
    }

    // 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;
        this.ForeColor = dataGridViewCellStyle.ForeColor;
        this.BackColor = dataGridViewCellStyle.BackColor;
    }

    // Implements the IDataGridViewEditingControl.EditingControlRowIndex property. 
    public int EditingControlRowIndex { get; set; }

    // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey method. 
    public bool EditingControlWantsInputKey(Keys key, bool dataGridViewWantsInputKey) {
        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:
            case Keys.Escape:
                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; set; }

    // Implements the IDataGridViewEditingControl.EditingControlValueChanged property. 
    public bool EditingControlValueChanged { get; set; }

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