How to make cell readonly if the second cell is empty - vb.net

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

Related

Add CheckBox To DataGridView Cell Programmatically

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

How to pass the 2nd selected row in datagridview to textbox?

How do i pass my 2nd selected row in datagridview to textboxt. I only know how to put the first data. How do i pass the 2nd selected to textbox?
Dim i As Integer
i = DataGridView2.SelectedRows(0).Index
Me.txtEmployeeID.Text = DataGridView2.Item(0, i).Value
you can use this code it worked for me
Dim test As String = DataGridView1.SelectedRows(0).Cells(2).Value.ToString
You need to only change the .cell(Here write the index value of the cell)
Then you can use the string value to fill up textbox
Try using the following:
Dim secondRow as integer = 0
Private Sub DataGridView2_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView2.CellClick
'assuming that by second selected row, you mean the row after the selected row
secondRow = e.RowIndex + 1
End Sub
Private Sub RowToTextBox()
Try
For i = 0 to DataGridView2.ColumnCount - 1
txtEmployeeID.Text &= DataGridView2.Item(i, secondRow)
Next
Catch ex as Exception
MsgBox("You have selected the final row")
End Try
End Sub
I don't think Me. is needed when referring to a textbox in the same form.
Since you don't know how many rows your user will select, I think looping thought the SelectedRows collection might work. I used the Name column because that is what I happened to have in my grid. Instead of a MessageBox you could add the values to a ListBox.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
For Each GridRow As DataGridViewRow In DataGridView1.SelectedRows
MessageBox.Show($"Value is {GridRow.Cells("Name").Value}")
Next
End Sub

Change datagriview cell value on CellValidating

I would like to change the value of cell value which user inputs there and do the trim before to avoid left and right spaces over value before it will be validated. Currently this is my code without this feature:
Private Sub Grid_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) Handles Grid.CellValidating
Dim newValue = e.FormattedValue.ToString
If Not Grid.Rows(e.RowIndex).IsNewRow Then 'And Not e.RowIndex = Grid.NewRowIndex - 1 Then
Select Case e.ColumnIndex
Case 1 'Name
If String.IsNullOrEmpty(newValue) Then
e.Cancel = True
Exit Select
End If
Case Else
End Select
End If
End Sub
What I tried to do was this way, but it completely doesn't work:
Private Sub Grid_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) Handles Grid.CellValidating
Dim newValue = e.FormattedValue.ToString.Trim '<---trim added
'change value of cell without trims
Dim aaa As DataGridViewCell = CType(sender, DataGridView).Rows(e.RowIndex).Cells(e.ColumnIndex)
aaa.Value = newValue
If Not Grid.Rows(e.RowIndex).IsNewRow Then 'And Not e.RowIndex = Grid.NewRowIndex - 1 Then
Select Case e.ColumnIndex
Case 1 'Name
If String.IsNullOrEmpty(newValue) Then
e.Cancel = True
Exit Select
End If
Case Else
End Select
End If
End Sub
That's the wrong event. CellValidating is for determining whether the contents of the cell is valid or not. CellValidated or CellLeave would be more suited to your situation.
In the CellValidating event this works for me.
//remove the event
grdDetails.CellValidating -= grdDetails_CellValidating;
//update current cell
grdDetails.CurrentCell.Value = "My New Value";
//force grid to commit update
grdDetails.EndEdit();
//rehook the event
grdDetails.CellValidating += grdDetails_CellValidating;

Keep focus on row after datagridview update

I'm creating an VB windows application. The point of the application is a simple DataGridView where I'm fetching a View from a SQL Server database.
The DataGridView is refreshed every second so I could see new data income in my GridView.
The problem is keeping focus on row after the refresh. I need the solution, where after I click a row, or a cell it keeps me on it even after the refresh.
Here is my code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Refresh every 1 sec
Dim timer As New Timer()
timer.Interval = 1000
AddHandler timer.Tick, AddressOf timer_Tick
timer.Start()
'TODO: This line of code loads data into the 'XYZDataSet.view1' table. You can move, or remove it, as needed.
Me.View1TableAdapter.Fill(Me.XYZDataSet.view1)
End Sub
Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
End Sub
Private Sub DataGridView1_CellFormatting(ByVal sender As Object, ByVal e As DataGridViewCellFormattingEventArgs) Handles DataGridView1.CellFormatting
For i As Integer = 0 To Me.DataGridView1.Rows.Count - 1
If Me.DataGridView1.Rows(i).Cells("DayTillDelivery").Value <= 30 Then
Me.DataGridView1.Rows(i).Cells("DayTillDelivery").Style.ForeColor = Color.Red
End If
Next
End Sub
Private Sub timer_Tick(ByVal sender As Object, ByVal e As EventArgs)
'Calling refresh after 1 second and updating the data
Me.DataGridView1.Refresh()
Me.View1TableAdapter.Fill(Me.XYZDataSet.view1)
End Sub
End Class
I've solved a similar problem in the past by storing the indexes of the selected cell in a variable before doing the refresh, so I'm able to restore the selection by calling DataGridView.Rows(selRow).Cells(selCol).Selected = True after the update.
Edit - Sample Code:
To later readers:Please take a look at Edit#2 where I describe a better method to re-select the previous selected cell!
Sample Code:
' Variables for remembering the indexes of the selected cell
Dim selRow As Integer
Dim selCol As Integer
' Check if there is a selected cell to prevent NullPointerException
If DataGridView1.SelectedCells().Count > 0 Then
selRow = DataGridView1.CurrentCell.RowIndex
selCol = DataGridView1.CurrentCell.ColumnIndex
End If
' Dummy "update"
' don't forget to clear any existing rows before adding the new bunch (only if you always reloading all rows)!
DataGridView1.Rows.Clear()
For index = 1 To 20
DataGridView1.Rows.Add()
Next
' Check if there are "enough" rows after the update,
' to prevent setting the selection to an rowindex greater than the Rows.Count - 1 which would
' cause an IndexOutOfBoundsException
If (DataGridView1.Rows.Count - 1) > selRow Then
' Clear selection and then reselect the cell that was selected before by index
DataGridView1.ClearSelection()
' For the next line of code, there is a better solution in Edit #2!
DataGridView1.Rows(selRow).Cells(selCol).Selected = True
End If
Please note:
This procedure requires you to add the rows in the exact same order that you have added them before the update, as only the .Index of the selected row is stored in the variable. If you readding the rows in a different order, then not the same row but the row at the same position will be selected after the refresh.
You should add check if there is a selected row at all (to prevent a NullPointerException) and if there are "enough" rows in the DataGridView after the refresh, to prevent an IndexOutOfBoundsException.
This only works if the DataGridView1.SelectionMode is to something that actually selects rows, like FullRowSelect.
Don't forget to clear any existing rows before adding new ones by updating (only if you always reloading all rows).
Edit 2 - RowHeader triangle and accidental MultiSelect
As stated in the comments below, there was an odd behavior that would lead to an accidental MultiSelect, if the user holds down the mouse button past the refresh cycle. Also, the RowHeader triangle was not set to the correct row.
After some research I found a solution to this behavior. Instead of setting the .Selected-property of a given cell to True, set the .CurrentCell-property of the DataGridView to the cell you would like to select!
In code, this means changing
DataGridView1.Rows(selRow).Cells(selCol).Selected = True
to
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
and there you go. :-)
Before Fill, store the CurrentRow values and currenCell column:
Dim currentColumnIndex As Integer = 0 ;
Dim currentValues As List(Of Object) = If(DataGridView1.CurrentRow Is Nothing, Nothing, New List(Of Object)())
If currentValues IsNot Nothing Then
For i As Integer = 0 To DataGridView1.Columns.Count - 1
currentValues.Add(DataGridView1.CurrentRow.Cells(i).Value)
Next
currentColumnIndex = DataGridView1.CurrentCell.ColumnIndex;
End If
After Fill, search the row corresponding to stored values:
Dim i As Integer = 0
While i < DataGridView1.Rows.Count AndAlso currentValues IsNot Nothing
Dim areIdentical As Boolean = True
Dim j As Integer = 0
While j < DataGridView1.Columns.Count AndAlso areIdentical
areIdentical = DataGridView1.Rows(i).Cells(j).Value = currentValues(j)
j += 1
End While
If areIdentical Then
DataGridView1.CurrentCell = DataGridView1.Rows(i).Cells(currentColumnIndex)
currentValues = Nothing
End If
i += 1
End While
Note: the "For/While" loop coding is perhaps not optimal because it results from automatic conversion from C# to vb.net.
C# fix code , next reload pattern
if (dataGridView7.SelectedCells.Count > 0)
{
//MessageBox.Show(selcell + "------"+dataGridView7.CurrentCell.ColumnIndex.ToString());
if (selcell > 0 && dataGridView7.CurrentCell.ColumnIndex==0) { }else
{
selrow = dataGridView7.CurrentCell.RowIndex;
selcell = dataGridView7.CurrentCell.ColumnIndex;
}
}
loaddataJobsall();
dataGridView7.ClearSelection();
dataGridView7.Rows[selrow].Cells[selcell].Selected = true;

C1FlexGrid - Change "Highlight" behavior for a range of cells

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