简体   繁体   中英

DataGridView - cell validation - prevent CurrentRow/Cell change

I have WinForms DataGridView with source set to SortableBindingList . In this form, there's column Comment and I need to prevent user from inserting some characters, thus validation.

What I want to do is, whenever user enters invalid value, system will notify him ( OnNotification( 'You entered wrong comment'); ) and force him/her to stay in edit mode.

So far I build solution like this:

void MyDataGridView_CellEndEdit( object sender, DataGridViewCellEventArgs e )
{
    if (e.ColumnIndex == ColumnComment.Index) {
        object data = Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
        if( (data != null) && (!CommentIsValid( data.ToString()))){
            CurrentCell = Rows[e.RowIndex].Cells[e.ColumnIndex];
            BeginEdit( true );

            // My notification method
            OnNotification( String.Format( "Comment `{0}` contains invalid characters) );
            return;
        }
    }
}

I have following issues with this:

  • OnCellValidating is triggered only when whole form is closing or when current row is changed, not after I finish editing of single cell , so I've put check into CellEndEdit .
  • When I used Enter / Esc to end editing, it works as expected and desired.
  • When I use mouse and click to another row, cell stays in edit mode, but another row gets selected.
  • When I try to use Enter (displays notification on invalid comment) and then Esc (to cancel edit) it uses value pushed by Enter (because edit mode has finished).

So my questions are :

  • How can I fire CellValidating after each cell edit, not when form is closing
  • How can I prevent CurrentRow and CurrentCell change even after mouse click?
  • How can I force cell to stay in edit mode?

When I use mouse and click to another row, cell stays in edit mode, but another row gets selected.

Here I would use a global Boolean, bool isInvalidState say and a global DataGridViewCell = invalidCell object. In the default state you can set isInvalidState = false and invalidCell = null . Then using

private bool OnNotification(string cellValue)
{
    // Check for error.
    if (error)
        return false;
}

Then in the above method

void MyDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == ColumnComment.Index) {
        object data = Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
        if((data != null) && (!CommentIsValid(data.ToString()))){
            CurrentCell = Rows[e.RowIndex].Cells[e.ColumnIndex];
            BeginEdit(true);

            // My notification method
            isInvalidState = OnNotification(
                String.Format("Comment `{0}` contains invalid characters));
            if (isInvalidState)
                invalidCell = MyDataGridView[e.RowIndex, e.ColumnIndex];
            return;
        }
    }
}

Now, wire-up an event CellContentClick on your DataGridView and check if isInvalidState == true

private void MyDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    if (isInvlaidState)
    {
        isInvalidState = false;
        MyDataGridView.CurrentCell = invalidCell;
        invalidCell = null;
        return;
    }
    // Do other stuff here.
}

When I try to use Enter (displays notification on invalid comment) and then Esc (to cancel edit) it uses value pushed by Enter (because edit mode has finished).

I am not sure about this problem; it is likely you will have to handle the KeyDown event and capture the escape key - handling it differently.

I hope this helps.

Try something like this. It shall work.

private void datagridview1_dataGridview_CellValidating
(object sender, DataGridViewCellValidatingEventArgs e) 
{
    if (datagridview1_dataGridview.Rows[e.RowIndex].Cells[2].Value.Equals(""))
    {
         MessageBox.Show("Product name should not be empty", "Error");
         datagridview1_dataGridview.CurrentCell = datagridview1_dataGridview.Rows[e.RowIndex].Cells[2];
         datagridview1_dataGridview.CurrentCell.Selected = true;
    }
}

Unfortunately, MoonKnight's solution didn't work fully for me as the code in CellContentClick event handler never set the control back to the cell that was being validated for its value, when it had an invalid value. Nevertheless, considering his valuable hint of using global variables isInvalidState and invalidCell helped me constructing the following solution that works exactly as asked in the OP.

Using the combination of CellValidating and CellValidated in the right way solves the problem as follows:

Do your data validation inside the CellValidating event handler. Set the isInvalidState flag and the cellWithInvalidUserInput variable (NOTE: I renamed the invalidCell to cellWithInvalidUserInput ):

private void MyDataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
    var cellUnderConsideration = MyDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex];

    if (!ValidateCurrentCellValue(cellUnderConsideration)) 
    {               
       OnNotification( String.Format( "Comment `{0}` contains invalid characters) );
       //Or MessageBox.Show("your custom message");

       isInvalidState = true;
       cellWithInvalidUserInput = cellUnderConsideration;
       e.Cancel = true;                    
    }
}

The data validation function:

bool isInvalidState;
DataGridViewCell cellWithInvalidUserInput;
private bool ValidateCurrentCellValue(DataGridViewCell cellToBeValidated)
{
    //return 'true' if valid, 'false' otherwise
}

Perform desired actions on UI controls inside the CellValidated event handler:

private void MyDataGridView_CellValidated(object sender, DataGridViewCellEventArgs e)
{
    if (isInvalidState)
    {
        isInvalidState = false;

        if (cellWithInvalidUserInput != null && cellWithInvalidUserInput.RowIndex > -1)
        {
            MyDataGridView.CurrentCell = cellWithInvalidUserInput;
            MyDataGridView.CurrentCell.Selected = true;
            MyDataGridView.BeginEdit(true);
        }

        cellWithInvalidUserInput = null;
    }
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM