cool hit counter [C#] Make DataGridView input update the calculated columns in the data source in real time_Intefrankly

[C#] Make DataGridView input update the calculated columns in the data source in real time

This article applies to Winform development, and the DataGridView data source for the DataTable / DataView case.

Understand the premise. Good knowledge of DataTable, DataView

Request. Better Solutions

Consider the following scenario.

Column B of a DataTable (hereinafter called dt) is a calculated column (set the Expression property), which is calculated based on the data in column A. The dt is bound to a DataGridView (hereinafter called dgv), and both columns A and B are to be displayed in dgv, where column A is editable (ReadOnly=false). The requirement is for column B to change in real time when an edit is made to column A (input or delete). For example, the following example.

The [Target File Name] is calculated based on [Model Number] and [Color Number] (concatenated string), and the target file name can change in real time when editing the model number/color number.

Any ape familiar with dgv knows that the above effect cannot be achieved without special handling. The reason is that dgv by default waits for the focus to leave the edit cell (CurrentCell) before committing changes to the data source, and even if the focus leaves, if the focus is still on the same row (i.e. CurrentCell changes, but CurrentRow doesn't)should go sources go Also still in edit status(DataRowView.IsEdit because oftrue), The calculation column is also not updated。 It has to be the focus off this go( Go to another go, Or other controls), The calculation column will only be updated。—— This is a slightly more informative passage, unfamiliardgv Apes who submit the mechanism may have to resort to the following further explanation to understand~ Old birds, please go around.。 Let's start by getting to know a few concepts:

  • dgv cell: DataGridViewCell
  • dgv go:DataGridViewRow
  • Source row for dgv row: DataRowView. When dgv is bound to a data source, each row of it corresponds to a row (or item, as it's called) in the data source, which is what I call a [source row]. It can be obtained through the DataGridViewRow.DataBoundItem property, which is of type object. When the data source of dgv is DataTable or DataView (hereinafter called dv), the real type of DataBoundItem is DataRowView, which can be understood as the row of DataView. In turn, dv is based on dt, so behind dv corresponds to another dt, so behind DataRowView also corresponds to a DataRow, which can be obtained through DataRowView.Row. A simple representation is, DataGridViewRow (access DataBoundItem property) → DataRowView (access Row property) → DataRow
  • dgv has the concept of a cell and an entity class (DataGridViewCell), but dt and dv do not, the latter only up to the row level, although the value of the cell can be accessed through DataRow[x] or DataRowView[x], but there is no entity class like DataCell that represents the cell at the class level, that is, the edit/submit etc. operations of dt and dv are in [row] as a cell

The following is the regular submission process for dgv.

① edit dgv cells → ② complete editing (out of focus) → ③ submit the data source (the source row is still in the editing state) → ④ focus away from the dgv row → ⑤ end of the source row editing state → ⑥ source row update calculation column (in fact, the complete process also includes other links, such as cell data validation, but here only the link directly related to the submission).

It can be seen that there are two keys to calculating the column getting updated.

  1. The data in cell dgv is to be submitted to the corresponding cell of the data source
  2. origin go End of editing status

Following the regular submission process. It is necessary to keep the focus off the cell where the go(only leave the cell are not oh) to achieve the purpose, and our needs are, the editing process to be updated in real time, not to mention leaving the line, even the cell do not want to leave.

I. Solving the problem of updating calculated columns in real time

This can be achieved with the CurrentCellDirtyStateChanged event of dgv.

private void dgv_CurrentCellDirtyStateChanged(object sender, EventArgs e)
     // Determine if there are uncommitted changes in the current cell, and continue only if they exist.
     //This judgment is necessary because the following dgv.CommitEdit will also trigger the event, but by this time IsCurrentCellDirty will already be false
     // If no judgment is made, it will be repeatedly entered, causing unnecessary consumption
    if (dgv.IsCurrentCellDirty)
         //Submit the cell values to the data source, dgv. EndEdit() can also do a commit, but that will end the cell in an edited state
         //and dgv.CommitEdit() will keep the edit state
         // The parameters are provided to events such as DataError for a reason
         // Manually ends the edit state of the source line. Only then will the calculated column of the source row be updated
        (dgv.CurrentRow.DataBoundItem as DataRowView).EndEdit();
         // Or execute EndEdit() of DataRow to achieve the same thing
        //(dgv.CurrentRow.DataBoundItem as DataRowView).Row.EndEdit();

This event does the two things you want to do above, namely (i) update the dgv cell value to the data source, and (ii) end the source row edit state. It is said to get it right here, and does in fact make the calculated column reflect the input in real time, but there is another experience level problem that the cell will select the content in full after each keystroke, as shown in the figure.

That is, if you want to type continuously, you must use the mouse or arrow keys to unselect all and position the cursor to the correct location after each entry ~ that's not a pain in the balls, it has to be fixed! First of all the reason why it was all chosen is unknown. I guessis due to updates to the data source in turn affecting the dgv. Tried using CellEnter, CellBeginEdit, EditingControlShowing, dgv. EditingControl and other things were not ideal, either it didn't work at all or the input focus was wrong, so all in all it was a real toss up, but in the end it finally worked out another way to solve it perfectly.

II. Solve the problem of automatic full selection after typing

I got the idea from the control message block, the cell of dgv actually hosts some kind of edit control (such as TextBox, CheckBox), so it doesn't matter what the reason is to select all, it should always end up receiving some kind of message before it selects all, then I used spy++ to intercept the message, and sure enough, I found that

At a cursory glance, it's EM_SETSEL, which, upon understanding, is EM_SETSEL, so the next thing to do is to customize a text edit control to ignore this message, and finish making this control the text edit control in the dgv cell. Learn a bit about the following sets.

  1. Write the bearer controls. Need to inherit from the base control and implement the System.Windows.Forms. Since I just want to block a message from an existing control, and not to write a functional control from scratch, it is enough to inherit directly from the DataGridViewCell hosting the textbox control DataGridViewTextBoxEditingControl, which already implements the above interface.
public class DataGridViewTextBoxUnSelectableEditingControl : DataGridViewTextBoxEditingControl
    protected override void WndProc(ref Message m)
        //EM_SETSEL The constants of the message are0xb1
        if (m.Msg == 0xb1) { return; }

        base.WndProc(ref m);
  1. Write the DataGridViewCell that hosts the above control. Need to inherit from DataGridViewCell or its subclasses. Again, for this example I simply inherit from DataGridViewTextBoxCell:.
public class DataGridViewTextBoxUnSelectableCell : DataGridViewTextBoxCell
     // Simply override the property to specify the type of control that hosts it
    public override Type EditType
            return typeof(DataGridViewTextBoxUnSelectableEditingControl);
  1. Set the CellTemplate property of the dgv column (DataGridViewColumn) that you want to use the above cell as an instance of the above cell, multiple columns can be set to the same instance. CellTemplate preferably set early, e.g. in the form constructor, immediately after the InitializeComponent() method.

var cell = new DataGridViewTextBoxUnSelectableCell();
 dgv. Columns[0]. CellTemplate = cell;//CellTemplate of the column that will use the special cell is specified as a cell instance
 dgv. Columns [1]. CellTemplate = cell;//Multiple columns can share a single instance

For this example, do the above to solve the problem of dgv cells selected all. For the complete set of custom cell controls please refer to your ownMSDN

At the request of ape friends, put up demos of.


1、Regulating AI Artificial intelligence with human consciousness will emerge in the next 20 years
2、My 2017
3、Seven days Let this language which spans the arts and sciences and business take you across the door to data analysis
4、Send a highfashion friend in Python a simple application of image processing with code
5、How to play with the world of the second generation of the lady is really goodlooking

    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送