I have created a DGV adding CheckBoxColumns and TextBoxColumns. When the user enters a text value in to a TextBoxCell it then takes 2 mouse clicks to move to the next clicked cell, is there a way to allow a single mouse click to allow the next value to be entered in the selected cell straight away?
Also trying to use the Arrow Up/Down keys after entering a value doesn't move to the relevant new cell? When i use the arrow key is seems to end the edit but doesn't move to the next cell, it looks like the DGV loses focus as the mouse cursor appears.
I need both of these to work and to allow for immediate edit/ data entry as each text cell is for user input and not being able to use mouse keys to quickly navigate or having to double click with the mouse is causing a lot of wasted time.
Any help regarding these two issue would be much appreciated!
Additional Info::
This is what the grid looks like::
Grid Layout Image
The first 2 columns are data populated from a previous form and the data is held in a DataTable, the rest of the columns are added via the program
With the 3 text columns it takes two clicks on the next cell to move to the requested cell and start to edit if something has been typed, again if something has been typed in a cell and i use the arrow keys doesn't seem to take you to the next cell, it leaves the cell put doesn't start to edit or put the cursor in the expected cell.
What i am looking for is when i have entered a value in to the Text Columns and click on the next cell it starts to edit straight away or if i use an arrow key it takes you to the relevant cell and starts the edit.
Below is the full code for the form i have an issue with, i have also added a screenshot of the DGV Properties as i bet all of this is fixed by something simple.
DGV Settings
Public Class frmAppTest
Public DTApp As DataTable = New DataTable("Application")
Private Sub frmAppTest_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Call InitializeAppTable()
'Add each column required for user input (inc dropdown option and "Select All" option)
Call Add_ChkColumn("Team Crest", "TEAM_CREST")
Call AddSAChkBox(dgvApp, 186, 57, 2, "TC")
Call AddCrestSelect(dgvApp, 165, 33, "Crest")
Call Add_ChkColumn("Chest Logo", "CHEST_LOGO")
Call AddSAChkBox(dgvApp, 246, 57, 3, "CL")
Call AddSponsorSelect(dgvApp, 225, 33, "Chest")
Call Add_ChkColumn("Back" & vbCr & "Top", "BACK_TOP_LOGO")
Call AddSAChkBox(dgvApp, 306, 57, 4, "BT")
Call AddSponsorSelect(dgvApp, 285, 33, "BackTop")
Call Add_ChkColumn("Back Bottom", "BACK_BOTTOM_LOGO")
Call AddSAChkBox(dgvApp, 366, 57, 5, "BB")
Call AddSponsorSelect(dgvApp, 345, 33, "BackBottom")
Call Add_ChkColumn("Left Sleeve", "LEFT_SLEEVE_LOGO")
Call AddSAChkBox(dgvApp, 426, 57, 6, "LS")
Call AddSponsorSelect(dgvApp, 405, 33, "LeftSlv")
Call Add_ChkColumn("Right Sleeve", "RIGHT_SLEEVE_LOGO")
Call AddSAChkBox(dgvApp, 486, 57, 7, "RS")
Call AddSponsorSelect(dgvApp, 465, 33, "RightSlv")
Call Add_TxtColumn("Player Name", "PLAYER_NAME")
Call AddColourSelect(dgvApp, 528, 33, "Name")
Call Add_TxtColumn("Player Number", "PLAYER_NUMBER")
Call AddColourSelect(dgvApp, 638, 33, "Number")
Call Add_TxtColumn("Player Initials", "PLAYER_INITIALS")
Call AddColourSelect(dgvApp, 748, 33, "Initials")
Call FormatDGVApp()
'Populate default data
Call PopulateDetails()
End Sub
Private Sub AddCrestSelect(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal CboName As String)
Dim cbo As New ComboBox
cbo.Name = "cboLogo" & CboName
'The box size
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Visible = True
cbo.Items.Clear()
cbo.Items.Add("Embro")
cbo.Items.Add("Heat")
cbo.Size = New Size(55, 14)
cbo.SelectedIndex = 0
cbo.Location = New System.Drawing.Point(XLocation, YLocation)
cbo.BackColor = Color.White
theDataGridView.Controls.Add(cbo)
End Sub
Private Sub AddSponsorSelect(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal CboName As String)
Dim cbo As New ComboBox
cbo.Name = "cboLogo" & CboName
'The box size
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Visible = True
cbo.Items.Clear()
cbo.Items.Add("Single")
cbo.Items.Add("Multi")
cbo.Size = New Size(55, 14)
cbo.SelectedIndex = 0
cbo.Location = New System.Drawing.Point(XLocation, YLocation)
cbo.BackColor = Color.White
theDataGridView.Controls.Add(cbo)
End Sub
Private Sub AddColourSelect(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal CboName As String)
Dim cbo As New ComboBox
cbo.Name = "cboColour" & CboName
'The box size
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Visible = True
cbo.Items.Clear()
cbo.Items.Add("White")
cbo.Items.Add("Black")
cbo.Items.Add("Royal")
cbo.Items.Add("Navy")
cbo.Items.Add("Yellow")
cbo.Items.Add("Red")
cbo.Size = New Size(100, 14)
cbo.SelectedIndex = 0
cbo.Location = New System.Drawing.Point(XLocation, YLocation)
cbo.BackColor = Color.White
theDataGridView.Controls.Add(cbo)
End Sub
Private Sub InitializeAppTable()
DTApp.Columns.Add("Size", GetType(String))
DTApp.Columns.Add("Price", GetType(String))
dgvApp.DataSource = DTApp
End Sub
Private Sub FormatDGVApp()
Dim xCol As New DataGridViewColumn
dgvApp.Columns("Size").Width = 70
dgvApp.Columns("Size").ReadOnly = True
dgvApp.Columns("Size").SortMode = DataGridViewColumnSortMode.NotSortable
dgvApp.Columns("Size").HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.Columns("Size").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.Columns("Price").Width = 70
dgvApp.Columns("Price").ReadOnly = True
dgvApp.Columns("Price").SortMode = DataGridViewColumnSortMode.NotSortable
dgvApp.Columns("Price").HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.Columns("Price").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.ColumnHeadersHeight = 74
dgvApp.Font = New Font("Arial", 7)
End Sub
Private Sub PopulateDetails()
Dim AppRow As DataRow
Dim i As Integer
For i = 0 To 4
AppRow = DTApp.NewRow
AppRow("Size") = "S"
AppRow("Price") = "5.00"
DTApp.Rows.Add(AppRow)
Next
End Sub
Private Sub Add_ChkColumn(ByVal Header As String, ByVal Name As String)
Dim AddColumnLabelExport As New DataGridViewCheckBoxColumn
With AddColumnLabelExport
.HeaderText = Header
.Name = Name
.Width = 60
.HeaderCell.Style.Alignment = DataGridViewContentAlignment.TopCenter
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
.SortMode = DataGridViewColumnSortMode.NotSortable
End With
dgvApp.Columns.Add(AddColumnLabelExport)
End Sub
Private _IsSelectAllChecked As Boolean
Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
Dim cbx As New CheckBox
cbx.Name = "SelectAll" & ChkName
'The box size
cbx.Size = New Size(14, 14)
cbx.Tag = TagNo
cbx.Location = New System.Drawing.Point(XLocation, YLocation)
cbx.BackColor = Color.White
theDataGridView.Controls.Add(cbx)
AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
End Sub
Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
Me._IsSelectAllChecked = True
Dim cbx As CheckBox
cbx = DirectCast(sender, CheckBox)
Dim theDataGridView As DataGridView = cbx.Parent
Dim rowId As Integer = cbx.Tag
For Each row As DataGridViewRow In dgvApp.Rows
row.Cells(rowId).Value = cbx.Checked
Next
theDataGridView.EndEdit()
Me._IsSelectAllChecked = False
End Sub
Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
dgvApp.EndEdit()
Dim dataGridView As DataGridView = DirectCast(sender, DataGridView)
If Not Me._IsSelectAllChecked Then
Select Case e.ColumnIndex
Case 2
If dataGridView.Rows(e.RowIndex).Cells(2).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllTC"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(2).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllTC"), CheckBox).Checked = isAllChecked
End If
Case 3
If dataGridView.Rows(e.RowIndex).Cells(3).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllCL"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(3).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllCL"), CheckBox).Checked = isAllChecked
End If
Case 4
If dataGridView.Rows(e.RowIndex).Cells(4).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllBT"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(4).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllBT"), CheckBox).Checked = isAllChecked
End If
Case 5
If dataGridView.Rows(e.RowIndex).Cells(5).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllBB"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(5).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllBB"), CheckBox).Checked = isAllChecked
End If
Case 6
If dataGridView.Rows(e.RowIndex).Cells(6).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllLS"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(6).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllLS"), CheckBox).Checked = isAllChecked
End If
Case 7
If dataGridView.Rows(e.RowIndex).Cells(7).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllRS"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(7).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllRS"), CheckBox).Checked = isAllChecked
End If
End Select
End If
dgvApp.CurrentCell = Nothing
End Sub
Private Sub DataGridView_CurrentCellDirtyStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim dataGridView As DataGridView = DirectCast(sender, DataGridView)
If TypeOf dataGridView.CurrentCell Is DataGridViewCheckBoxCell Then
dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
Private Sub Add_TxtColumn(ByVal Header As String, ByVal Name As String)
Dim AddColumnLabelExport As New DataGridViewTextBoxColumn
With AddColumnLabelExport
.HeaderText = Header
.Name = Name
.Width = 110
.HeaderCell.Style.Alignment = DataGridViewContentAlignment.TopCenter
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
.SortMode = DataGridViewColumnSortMode.NotSortable
End With
dgvApp.Columns.Add(AddColumnLabelExport)
End Sub
Private Sub btnCancelLine_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancelLine.Click
Me.Close()
End Sub
End Class
Well… the updated code explains numerous issues you may be having. The big picture is that you need to take a little more care when subscribing to some events. It is not difficult to create a problem if you do not take care in checking when and how often your subscribed to events are firing.
Example, in your current code you have the method…
Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
Dim cbx As New CheckBox
cbx.Name = "SelectAll" & ChkName
'The box size
cbx.Size = New Size(14, 14)
cbx.Tag = TagNo
cbx.Location = New System.Drawing.Point(XLocation, YLocation)
cbx.BackColor = Color.White
theDataGridView.Controls.Add(cbx)
AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
End Sub
This code drops a check box into the grid and is called six (6) times. Once for each of the “select all” Header check boxes. Is what is incorrect are the last two lines of code…
AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
This is odd because each time the method is called a NEW handler is added to the grid. In other words, the code is subscribing to the same GRID event’s six (6) times.
If you put a couple of debug statements at the beginning and the end of the events, you will see that the events will obligingly fire five more times than needed.
Bottom line… these GRID events only need to be subscribed to ONCE. I suggest moving the code to the Forms Load event or some method that is only called once.
Next, your current issue relates to the fact that when you type text into the other non-check box cells, the wired-up grid events are still firing and you really do not want this when editing those non-check box cells. So, a simple solution is to check and make sure that when those events fire… that we do nothing if the cell is not one of the check box cells in the grid.
Currently the code is using the grids CellValueChanged event and its CurrentCellDirtyStateChanged event. In the current cell dirty state changed event, the code simply “commits” the edit to the cell which in turn will fire the grids CellValueChanged event where the code checks the check box cells. As you note… the current cell dirty state changed event will fire BEFORE the actual cell value is changed and this is the purpose of the commit so we can immediately see the changes in the grid.
I suggest you use the grids, CellContentClick event instead of the CurrentCellDirtyStateChanged event. This event will fire when the user clicks into a cell and starts to edit the cell. It will fire when the user clicks a check box cell and changes its value. We do not need to do anything other than check if the cell is a check box cell and commit the edit. If the cell is NOT a check box cell then do nothing. This event may look something like…
Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
Return
End If
Debug.WriteLine("DataGridView_CellContentClick <- Enter")
dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
Debug.WriteLine("DataGridView_CellContentClick -> Leave")
End Sub
This will work when the check boxes are changed and it will ignore the non-check box cells, however if the user is editing a non-check box cell, then the grids CellValueChanged event will still fire if the non-check box cells value changed.… So similar to the above code, we need to check in the grids CellValueChanged event to see which cell value changed. If it is NOT a check box cell that changed, then we do not want to do anything. Therefore you could add the same check using the e.RowIndex and e.ColumnIndex variables… in the grids CellValueChanged event… Something like…
Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
Return
End If
….. ‘rest of code
Another issue which I wish I had a good answer for is that… if you call the HeaderCheckBox_Click event that fires when the user clicks one of the “select all” header check boxes, then you may note that… IF the user has clicked into a check box cell and changed its value, then this will make that cell the grids CurrentCell and for some reason, it will not change its value as expected. I was only able to code a workaround that simply changes the grids CurrentCell to some other cell, then sets it back after the code changes the check box cell values. The changes I made may look something like…
Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
Debug.WriteLine("HeaderCheckBox <- Enter")
Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
Dim cbx As CheckBox
cbx = DirectCast(sender, CheckBox)
Dim rowId As Integer = cbx.Tag
RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
For Each row As DataGridViewRow In dgvApp.Rows
row.Cells(rowId).Value = cbx.Checked
Next
AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
Debug.WriteLine("HeaderCheckBox -> Leave")
End Sub
You may note that just before the For Each loop is started through the grid rows, we remove the GRIDS CellValueChanged event to prevent it from firing. We do not want it to fire at this time since we are checking or unchecking all the check boxes, therefore no checking is needed to see if they are all checked or not checked.
In my tests, these changes allowed the non-check box cells to work as expected. I hope this makes sense and helps. You may want to consider simplifying this. Example, in each of the Select/Case sections the code is doing the same thing with a different column index. I suggest creating a simple method that checks if all the check boxes are checked in a given column and return true/false. And I hope you do not plan on any horizontal scrolling of the grid as this will create a lot of work for you to scroll the added combo boxes, labels and check boxes you added to the grid… just a heads up.
I’m trying to add a CheckBox programmatically to a DataGridVew cell if the cell next to it has a value of “1”. I’m trying to do this as the rows are added
I’m hoping someone can help me out with the correct code here. I understand one of the lines of code is incorrect but I’ve put it in to illustrate what I'm trying to do.
Thanks in advance.
Private Sub Controls_DGV_RowsAdded(sender As Object, e As Windows.Forms.DataGridViewRowsAddedEventArgs) Handles Controls_DGV.RowsAdded
If Controls_DGV.Rows(e.RowIndex).Cells(2).Value = "1" Then
Controls_DGV.Rows(e.RowIndex).Cells(1).AddCheckBox ' THIS LINE IS INCORRECT
End If
End Sub
This is the same as #miguel except for the checking the value, in this case Option Strict is On as it should be.
Public Class Form1
Private Sub dataGridView1_RowsAdded(sender As Object, e As DataGridViewRowsAddedEventArgs) _
Handles dataGridView1.RowsAdded
If CStr(dataGridView1.Rows(e.RowIndex).Cells(1).Value) <> "1" Then
dataGridView1.Rows(e.RowIndex).Cells(0).Value = False
dataGridView1.Rows(e.RowIndex).Cells(0) = New DataGridViewTextBoxCell()
dataGridView1.Rows(e.RowIndex).Cells(0).Value = ""
dataGridView1.Rows(e.RowIndex).Cells(0).ReadOnly = True
End If
End Sub
Private Sub AddRowsButton_Click(sender As Object, e As EventArgs) _
Handles AddRowsButton.Click
For index As Integer = 0 To 5
If CBool(index Mod 2) Then
dataGridView1.Rows.Add(False, "0")
Else
dataGridView1.Rows.Add(False, "1")
End If
Next
End Sub
End Class
The column number 1 that you want to display a checkbox should already be of type DataGridViewCheckBoxColumn and then if the value is not "1" you can transform the type of the cell for a DataGridViewTextBoxCell, so there is no checkbox and you can even put there some text if you want. Because you're using 3 columns, i'll try to do the same.
In your Form1_Load() you should have something like this if you are adding columns programmatically:
Dim ChkBox As New DataGridViewCheckBoxColumn
Controls_DGV.Columns.Add("TextBox1", "TextBox1")
Controls_DGV.Columns.Add(ChkBox)
Controls_DGV.Columns.Add("TextBox2", "TextBox2")
Then using your code it should be like this:
Private Sub Controls_DGV_RowsAdded(sender As Object, e As Windows.Forms.DataGridViewRowsAddedEventArgs) Handles Controls_DGV.RowsAdded
If Controls_DGV.Rows(e.RowIndex).Cells(2).Value <> "1" Then
' replace the checkbox cell by textbox cell
Controls_DGV.Rows(e.RowIndex).Cells(1) = New DataGridViewTextBoxCell()
Controls_DGV.Rows(e.RowIndex).Cells(1).Value = "(empty or some text)"
End If
End Sub
The user should not be able to input a qty where the unit is empty in the datagridview.
To make it clear, I want to make the cell readonly = true if unit column is empty.
The colUOM4 is the name of the column that if the cell of this column is empty the olNewQty2 cell will be readonly.
I tried this code but it didn't work
Public Sub UnitEmpty()
For i As Integer = 0 To dgvCount.RowCount - 1
If dgvCount.Rows(i).Cells("colUOM4").Value Is Nothing Then
MessageBox.Show("It worked!")
dgvCount.Rows(i).Cells("colNewQty2").ReadOnly = True
Else
MessageBox.Show("Nothing happened!")
Exit For
End If
Next
End Sub
I'd recommend not using a loop because that will only set the state when you execute it and not react to any changes. I'd suggest working at the row and cell level, i.e. set the default state when a row is added and then react when a specific cell changes, e.g.
Private Sub DataGridView1_RowsAdded(sender As Object, e As DataGridViewRowsAddedEventArgs) Handles DataGridView1.RowsAdded
For i = e.RowIndex To e.RowIndex + e.RowCount - 1
'Make the first cell in each new row read-only by default.
DataGridView1(0, i).ReadOnly = True
Next
End Sub
Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
'Check whether the change is in the second column.
If e.RowIndex >= 0 AndAlso e.ColumnIndex = 1 Then
Dim row = DataGridView1.Rows(e.RowIndex)
'Make the first cell in the row read-only if and only if the second cell is empty.
row.Cells(0).ReadOnly = (row.Cells(1).Value Is Nothing)
End If
End Sub
My scenario:
I have a C1FlexGrid with Column and Row headers. Additionally, the first row of the C1FlexGrid is a boolean row (checkboxes). Based on the state of these checkboxes, the desired effect is a disabling of the cells for that column. The difficulty is that, due to the first row being a boolean data type that must not be disabled, using the .Cols(index).AllowEditing property is not an option. I have already successfully implemented a work-around using the BeforeEdit event handler, to prohibit changes to the cells in the column, along with a CellStyle to grey-out the cells when disabled.
Private Sub C1FlexGrid1_BeforeEdit(ByVal sender As Object, ByVal e As C1.Win.C1FlexGrid.RowColEventArgs) Handles C1FlexGrid1.BeforeEdit
If e.Row > 1 And Me.C1FlexGrid1.Item(1, e.Col) = False Then e.Cancel = True
End Sub
Private Sub C1FlexGrid1_AfterEdit(ByVal sender As Object, ByVal e As C1.Win.C1FlexGrid.RowColEventArgs) Handles C1FlexGrid1.AfterEdit
If e.Row = 1 And Me.C1FlexGrid1.Item(1, e.Col) = False Then
Call FormatColAsDisabled(e.Col)
ElseIf e.Row = 1 And Me.C1FlexGrid1.Item(1, e.Col) = True Then
Call FormatColAsEnabled(e.Col)
End If
End Sub
Private Sub FormatColAsDisabled(ByVal col As Integer)
Dim color As C1.Win.C1FlexGrid.CellStyle
color = Me.C1FlexGrid1.Styles.Add("Gray")
color.BackColor = Drawing.Color.Gray
For row As Integer = 2 To Me.C1FlexGrid1.Rows.Count - 1
Me.C1FlexGrid1.SetCellStyle(row, col, color)
Next
End Sub
Private Sub FormatColAsEnabled(ByVal col As Integer)
For row As Integer = 2 To Me.C1FlexGrid1.Rows.Count - 1
Me.C1FlexGrid1.SetCellStyle(row, col, Me.C1FlexGrid1.Styles("Normal"))
Next
End Sub
My Question
Is there a way to modify the "Highlight" behavior for only these cells, such that the disabled cells do not highlight at all?
In order to avoid highlighting the disabled cells you can set Cancel EventArgs of BeforeSelChange event to true in case the new selected cell is a disabled one.
Private Sub C1FlexGrid1_BeforeSelChange(sender As Object, e As RangeEventArgs) Handles C1FlexGrid1.BeforeSelChange
If e.NewRange.r1 > 1 And Me.C1FlexGrid1.GetCellCheck(1, e.NewRange.c1) = CheckEnum.Unchecked Then
e.Cancel = True
End If
End Sub
I should change the color to a cell which contains the parameter 'tarjeta_fam'. I tried to change the cell default property and then invalidate the row to refresh it, but (obviously) nothing happens. It's possible to change a cell color out of the cell formatting event?
Public Sub New(user As Usuario, ByVal tarjeta_fam As String)
InitializeComponent()
gridFamiliares.DataSource = BD.getTable(a query)
If Me.gridFamiliares.Rows.Count > 0 Then
For i As Integer = 0 To Me.gridFamiliares.Rows.Count - 1
If Me.gridFamiliares.Rows(i).Cells("tarjeta_fam").Value = tarjeta_fam Then
Me.gridFamiliares.Rows(i).DefaultCellStyle.BackColor = Color.Black
Me.gridFamiliares.InvalidateRow(i)
End If
Next
End If
End Sub
The DataGridView control really wants you to use the CellFormatting event for this, so declare a form level variable to be used by that event:
Private tarjeta_fam_Value As String = String.Empty
Public Sub New(user As Usuario, ByVal tarjeta_fam As String)
InitializeComponent()
gridFamiliares.DataSource = BD.getTable(a query)
tarjeta_fam_Value = tarjeta_fam
End Sub
Private Sub gridFamiliares_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles gridFamiliares.CellFormatting
If tarjeta_fam_Value <> String.Empty Then
With gridFamiliares.Rows(e.RowIndex)
If .Cells("tarjeta_fam").Value = tarjeta_fam_Value Then
.DefaultCellStyle.BackColor = Color.Black
End If
End With
End If
End Sub