My DataView's Table attribute is assigned to one of my DataTables. The DataView is then set as my DataGridView's DataSource. Like so:
View.Table = DataSet1.Tables("dtArticles")
dgvArticles.DataSource = View
My goal is to perform some formatting on some given rows. Unfortunately, the only way I've found to do so is using the CellFormatting event. This is quite long since it must go through each row and verify if the row needs some formatting (bold font, backcolor).
CellFormatting
Private Sub dgvArticles_CellFormatting(sender As Object, e As System.Windows.Forms.DataGridViewCellFormattingEventArgs) Handles dgvArticles.CellFormatting
Dim drv As DataRowView
If e.RowIndex >= 0 Then
If e.RowIndex <= DataSet1.Tables("dtArticles").Rows.Count - 1 Then
drv = View.Item(e.RowIndex)
Dim c As Color
'Bolds if it is standard, makes it regular if it's not standard and already bold
If drv.Item("Standard").ToString = "Yes" Then
dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font = New Font("Microsoft Sans Sherif", 8, FontStyle.Bold)
End If
'Test if Standard is "No" and if the row is currently bold, if it is, put it back to regular
If drv.Item("Standard").ToString = "No" And Not dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font Is Nothing Then
If dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font.Bold Then
dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font = New Font("Microsoft Sans Sherif", 8, FontStyle.Regular)
End If
End If
'Puts a red color to the rows who are not available
If drv.Item("Available").ToString = "No" Then
'Change back color
c = Color.LightSalmon
e.CellStyle.BackColor = c
End If
End If
End If
End Sub
What this does is for each row's cells added to the DataGridView; it checks if my Standard column's current value is equal to Yes or No. Depending on the result, it will bold / un bold the row. The same principal goes for my Available column. If the Available value is equal to "No" then I put a light red back color to the row. This event is raised many times (10,000 articles with a DataTable of 10 or so columns).
About 2.2-2.7 seconds on average
Iterating through the DataView
Dim i As Integer = 0
For Each dt As BillMat.dtArticlesRow In View.Table.Rows
If dt.Standard = "Oui" Then dgvArticles.Rows(i).DefaultCellStyle.Font = New Font("Microsoft Sans Sherif", 8, FontStyle.Bold)
If dt.Available = "Non" Then dgvArticles.Rows(i).DefaultCellStyle.BackColor = Color.LightSalmon
i += 1
Next
About 1.5-1.7 seconds on average
Is it possible to use LINQ to select the RowIndex of which Standard column is equal to "Yes" and which Available column is equal to "No" ? If it is, wouldn't using the index to apply the formatting be much quicker than iterating through the whole 10,000 articles?
I don't know if using Link is the best solution for your problem.
You should first try to simplify the code in DataGridView.CellFormatting event. Some possibles ways:
Create one private instance of regular and bold Font to avoid to create multiples instance in the method
I guess you don't need to test the second condition if first is passed. So If End If + If End If could be replaced to If Else End If (don't need to test the two expressions)
Use If x AndAlso y Then instead of If x And y Then : if first expression is false, the second won't be tested
I don't understand the purpose of this If e.RowIndex <= DataSet1.Tables("dtArticles").Rows.Count - 1 Then. If the goal is not test if the row is the NewRow (with AllowUserToAddRows property set to True), simplier would be this
If DataGridView1.Rows(e.RowIndex).IsNewRow then
Private _RegularFont As New Font("Microsoft Sans Sherif", 8, FontStyle.Regular)
Private _BoldFont As New Font("Microsoft Sans Sherif", 8, FontStyle.Bold)
Private Sub dgvArticles_CellFormatting(sender As Object, e As System.Windows.Forms.DataGridViewCellFormattingEventArgs) Handles dgvArticles.CellFormatting
Dim drv As DataRowView
If e.RowIndex >= 0 Then
If Not dgvArticles.Rows(e.RowIndex).IsNewRow Then
drv = View.Item(e.RowIndex)
'Bolds if it is standard, makes it regular if it's not standard and already bold
If drv.Item("Standard").ToString = "Yes" Then
dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font = _BoldFont
Else 'First condition tested, test second
'Test if Standard is "No" and if the row is currently bold, if it is, put it back to regular
If drv.Item("Standard").ToString = "No" AndAlso Not dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font Is Nothing Then
If dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font.Bold Then
dgvArticles.Rows(e.RowIndex).DefaultCellStyle.Font = _RegularFont
End If
End If
End If
'Puts a red color to the rows who are not available
If drv.Item("Available").ToString = "No" Then
'Change back color
Dim c As Color
c = Color.LightSalmon
e.CellStyle.BackColor = c
End If
End If
End If
End Sub
Using link, you could do something like this (for your purpose, you should not iterate into DataView.Rows to retrieve index, but directly in DataGridView.Rows):
Dim _BoldFont As New Font("Microsoft Sans Sherif", 8, FontStyle.Regular)
Dim q = From r In Me.DataGridView1.Rows
Where CType(r, DataGridViewRow).Cells("Standard").Value.ToString = "Oui"
Select r
For Each item In q
CType(item, DataGridViewRow).DefaultCellStyle.Font = _BoldFont
Next
Dim q = From r In Me.DataGridView1.Rows
Where CType(r, DataGridViewRow).Cells("Available").Value.ToString = "Non"
Select r
For Each item In q
CType(item, DataGridViewRow).DefaultCellStyle.BackColor = Color.LightSalmon
Next
Related
How to change the Font Style of all Rows of a DataGridView based on a conditions?
My condition is if Cell Value Equal 0 then FontStyle = Strikeout otherwise Regular
Private Function DetailGridViewSetStyle()
Dim dgv As DataGridView = DetailDataGridView
Dim dgvInd As Integer = dgv.CurrentRow.Index
For cc As Integer = 0 To dgv.ColumnCount - 1
If dgv.Item(4, dgvInd).Value = 0 Then
dgv.DefaultCellStyle.Font = New Font("ARIAL", 8, FontStyle.Strikeout)
ElseIf dgv.Item(4, dgvInd).Value = 1 Then
dgv.DefaultCellStyle.Font = New Font("ARIAL", 8, FontStyle.Regular)
End If
Next
End Function
Only use "Function" if you need to return something, that's not your case, use Sub instead.
dgv.DefaultCellStyle.Font will set font style for all your datagrid, you need to set font style from a specify row, then use row.DefaultCellStyle.Font
If I understood your question, here's the code that you need:
Private Sub DetailGridViewSetStyle()
Dim dgv As DataGridView = DetailDataGridView
For Each row As DataGridViewRow In dgv.Rows
Dim Value As Boolean = CBool(row.Cells(4).Value)
If Value = False Then
row.DefaultCellStyle.Font = New Font("ARIAL", 8, FontStyle.Strikeout)
Else
row.DefaultCellStyle.Font = New Font("ARIAL", 8, FontStyle.Regular)
End If
Next
End Sub
If your DataGridView is bound to a data source, you may want to format the grid's Rows when the DataBinding is complete.
Subscribe to the DataBindingComplete event.
After that, you need to handle Cell value changes, to format the Row based on the value of a Column in the current Row.
Subscribe to the CellValueChanged event.
I'm using a helper method to determine what is the current what is the value of the Cell that determines the Font Style change, what is the current Font of its OwningRow, then change the Font Style to the Style specified if all criteria are met, otherwise, revert to default Font Style.
Note: the DataGridViewRow.DefaultCellStyle may be null, since the Row may inherit the Font from the DataGridView.DefaultCellStyle, so we need to check the DataGridViewRow.InheritedStyle value.
There's an assignment that uses a coalesce expression in the helper method:
Dim rowFont = If(row.DefaultCellStyle.Font, row.InheritedStyle.Font)
If this syntax is not available in your VB.Net version, use an extended form as:
Dim rowFont = If(row.DefaultCellStyle.Font Is Nothing, row.InheritedStyle.Font, row.DefaultCellStyle.Font)
Or do nothing if the current Cell value is null (Nothing) or is DbNull.Value: it can be if the DataGridView.DataSource is set to a DataTable, for example.
If you want to interpret null Cell values as 0, change the code accordingly.
Private Sub DetailDataGridView_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DetailDataGridView.CellValueChanged
If e.ColumnIndex = 4 Then
ToggleRowFontStyle(Of Integer)(DetailDataGridView(4, e.RowIndex), 0, FontStyle.Strikeout)
End If
End Sub
Private Sub DetailDataGridView_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles DetailDataGridView.DataBindingComplete
For Each row As DataGridViewRow In DetailDataGridView.Rows
ToggleRowFontStyle(Of Integer)(DetailDataGridView(4, row.Index), 0, FontStyle.Strikeout)
Next
End Sub
Helper method:
Private Sub ToggleRowFontStyle(Of T)(cell As DataGridViewCell, toggleValue As T, toggleFontStyle As FontStyle)
If cell.Value Is Nothing OrElse cell.Value Is DBNull.Value Then Return
Dim row As DataGridViewRow = cell.OwningRow
Dim cellValue As T = CType(Convert.ChangeType(cell.Value, GetType(T)), T)
Dim rowFont = If(row.DefaultCellStyle.Font, row.InheritedStyle.Font)
If cellValue.Equals(toggleValue) Then
If rowFont.Style <> toggleFontStyle Then
rowFont = New Font(rowFont, toggleFontStyle)
End If
Else
If rowFont.Style = toggleFontStyle Then
rowFont = New Font(rowFont, FontStyle.Regular)
End If
End If
row.DefaultCellStyle.Font = rowFont
End Sub
I am trying to add multiple labels to a userform at runtime
It's for the player names of a board game; and until the game starts the number of players are not known. I have managed to figure out for myself how to use the dynamic array function to create the list of players. I used a For.....Next loop to add the player names. I thought I could do that to add the labels to the form, but it only adds one. Depending on where the new control type is declared, it either adds the first player only, or the last player
This code produces one label only within the groupbox, the last player
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Players_Num As Integer = InputBox("Enter the number of players")
Dim Players(Players_Num) As String
Dim newText As New Label
For i = 0 To Players_Num - 1
Players(i) = InputBox("Enter player name")
Next
'This piece of code was jsut for me to test that I was successfully using a For...Loop
'to add the players names, and will be deleted later on
For x = 0 To Players_Num - 1
MessageBox.Show(Players(x))
Next
For z = 0 To Players_Num - 1
newText.Name = "txt" & Players(z)
newText.Text = Players(z)
newText.Size = New Size(170, 20)
newText.Location = New Point(12 + 5, 12 + 5)
GroupBox1.Controls.Add(newText)
Next
End Sub
End Class
This one places only the first player
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Players_Num As Integer = InputBox("Enter the number of players")
Dim Players(Players_Num) As String
For i = 0 To Players_Num - 1
Players(i) = InputBox("Enter player name")
Next
'This piece of code was jsut for me to test that I was successfully using a For...Loop
'to add the players names, and will be deleted later on
For x = 0 To Players_Num - 1
MessageBox.Show(Players(x))
Next
For z = 0 To Players_Num - 1
Dim newText As New Label
newText.Name = "txt" & Players(z)
newText.Text = Players(z)
newText.Size = New Size(170, 20)
newText.Location = New Point(12 + 5, 12 + 5)
GroupBox1.Controls.Add(newText)
Next
End Sub
End Class
I've tried this in vs 2015 and 2019 Community
Where is it going wrong?
From the looks of the code, you are correctly creating the controls but their location is the same for all of them, essentially, they are being place one of top of the other, the first is hidden with the second, which is hidden with the third.
The line
newText.Location = New Point(12 + 5, 12 + 5)
needs to be modified to place the labels at different locations.
Perhaps, something like:
newText.Location = New Point(12 + 5, 12 + (z * 25))
This will vertically align the labels with a gap of 25 between them
You are placing them all in the same location
newText.Location = New Point(12 + 5, 12 + 5)
Use your 'z' index to place them at different locations in order to be able to see them
For me it is easier to contain controls in a TableLayoutPanel then add the TLP to what ever control collection, such as a GroupBox This way you can couple a Label with TextBox, for example. Here's an example how you can create controls from a DataTable. In your case you would only need 1 ColumnStyle for labels, I just thought I would show you a good practice for future shortcuts. (I rarely use the designer to place controls)
'Start test data
Dim DtTable As New DataTable
With DtTable
Dim NewDtRow As DataRow = .NewRow
For i As Integer = 0 To 25
Dim DtCol As New DataColumn With {.ColumnName = "Col" & i, .DataType = GetType(String)}
.Columns.Add(DtCol)
NewDtRow(DtCol.ColumnName) = "Test" & i
Next
.Rows.Add(NewDtRow)
End With
'End test data
Dim TLP1 As New TableLayoutPanel With {.Name = "TlpFields"}
With TLP1
.BorderStyle = BorderStyle.Fixed3D
.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset
.AutoScroll = True
.AutoSize = True
.RowStyles.Clear()
.ColumnStyles.Clear()
.Dock = DockStyle.Fill
.ColumnCount = 2
.ColumnStyles.Add(New ColumnStyle With {.SizeType = SizeType.AutoSize})
End With
For Each DtCol As DataColumn In DtTable.Columns
With TLP1
.RowCount += 1
.RowStyles.Add(New RowStyle With {.SizeType = SizeType.AutoSize})
'create labels
.Controls.Add(New Label With {
.Text = DtCol.ColumnName,
.Anchor = AnchorStyles.Right}, 0, .RowCount)
'create textboxs
Dim TxtBox As New TextBox With {
.Name = "TextBox" & DtCol.ColumnName,
.Size = New Size(170, 20),
.Anchor = AnchorStyles.Left}
'add binding
TxtBox.DataBindings.Add("Text", DtTable, DtCol.ColumnName)
.Controls.Add(TxtBox, 1, .RowCount)
End With
Next
Controls.Add(TLP1)
Before anyone classes this as a duplicate, I have indeed searched both around StackOverflow and the Google for the solution to my problem, neither have proven to be successful.
To summarise: I am building a game as a part of a unit in my CS course "Object Oriented Programming". The aim of the game is to randomly generate planes, who's fuel level deducts using a timer and then events such as crash are fired upon the fuel reaching 0. We then have to land the planes and assign them runways, and so on...
This all works fine, however, I am at the stage where I have to list all the planes that are being generated and then give the user the option to land them.
I initially used a ListView but quickly switched to a DataGridView because I wanted to have a button as a column.
So, intially as the planes were being generated, and furthermore displayed within the DataGridView, whenever I clicked the "Land Plane" button within a row of my choosing the selection would simply jump back to the first row. I asked my lecturer for assitance, and he stated that it's because the fuel's value is constantly being updated as it counts down. When the DataGridView is cleared, the selected row resets.
To resolve this, we added two local variables to store the selected row and the selected column. We checked to see if the selected column still remained within the collection, when the "Land Plane" button was clicked. This is because when the fuel reaches 0 it simply removes the row from the DataGridView.
The problem I am now having, which my lecturer was puzzled by was this:
An unhandled exception of type 'System.ArgumentOutOfRangeException'
occurred in mscorlib.dll
Additional information: Index was out of range. Must be non-negative
and less than the size of the collection.
The code that handles the DataGridView is as follows:
Public Sub populateDataGV()
Dim p As New Aircraft
selRow = -1
'Populate the data grid view
If Not (IsNothing(DataGridView1.CurrentCell)) Then
selRow = DataGridView1.CurrentCell.RowIndex
selCol = DataGridView1.CurrentCell.ColumnIndex
End If
DataGridView1.Rows.Clear()
DataGridView1.ColumnCount = 2
DataGridView1.Columns(0).Name = "Flight No"
DataGridView1.Columns(1).Name = "Fuel"
For Each p In airport.planeCollection
Dim row As String() = New String() {p.name, p.getFuelLevel()}
DataGridView1.Rows.Add(row)
Next
Dim RowsToDelete As New List(Of DataGridViewRow)()
For Each rows As DataGridViewRow In DataGridView1.Rows
If rows.Cells(1).Value IsNot Nothing AndAlso rows.Cells(1).Value.ToString = "0" Then
RowsToDelete.Add(rows)
If selRow = rows.Index Then
selRow = -1
End If
End If
Next
For Each rows As DataGridViewRow In RowsToDelete
DataGridView1.Rows.Remove(rows)
Next
RowsToDelete.Clear()
If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If
'Add button column
Dim btn As DataGridViewButtonColumn = New DataGridViewButtonColumn()
btn.HeaderText = "Action"
btn.Text = "Land Plane"
btn.UseColumnTextForButtonValue = True
DataGridView1.Columns.Add(btn)
End Sub
This is where the error is thrown:
If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If
If anyone can shed some light on what is actually causing this error I'd be most grateful.
Your range check is open to errors.
First, Your current code allows selRow to be less than -2:
If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If
Second, you do not need to set the column... instead, just set it to 0 or 1.
You should change
If selRow <> -1 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(selCol)
End If
to
If selRow >= 0 And selRow <= DataGridView1.Rows.Count - 1 Then
DataGridView1.CurrentCell = DataGridView1.Rows(selRow).Cells(1)
End If
This new code properly range checks both values.
Also, you need to remove from the top:
selRow = -1
Finally, to address the problem of selecting the right row, I suggest changing:
DataGridView1.Rows.Clear()
DataGridView1.ColumnCount = 2
DataGridView1.Columns(0).Name = "Flight No"
DataGridView1.Columns(1).Name = "Fuel"
For Each p In airport.planeCollection
Dim row As String() = New String() {p.name, p.getFuelLevel()}
DataGridView1.Rows.Add(row)
Next
to:
' DataGridView1.Rows.Clear()
If DataGridView1.ColumnCount = 0 Then
DataGridView1.ColumnCount = 2
DataGridView1.Columns(0).Name = "Flight No"
DataGridView1.Columns(1).Name = "Fuel"
End If
For Each p In airport.planeCollection
Dim updated As Boolean = False
For Each rows As DataGridViewRow In DataGridView1.Rows
If rows.Cells(0).Value = p.name Then
rows.Cells(1).Value = p.getFuelLevel
updated = True
Exit For
End If
Next
If Not updated Then
Dim row As String() = New String() {p.name, p.getFuelLevel()}
DataGridView1.Rows.Add(row)
End If
Next
You should add/update rather than simply clearing and adding.
I have a DataGridView dgv showing the content of an underlying DataView dv, which I use for filtering the row entries. (dgv.DataSource = dv)
Two of the columns in the DataView are Boolean types and show up as the default checkbox formatting of VB, however, I would like them to show as squares (or rectangles) in colors red and green for False and True respectively.
I know that for a DataGridView with a column of the type DataGridViewImageColumn I could simply generate an image and show it with something similar to this:
bmp = New Bitmap(20, 10)
Using g As Graphics = Graphics.FromImage(bmp)
If VarIsValid Then
g.FillRectangle(Brushes.GreenYellow, 0, 0, bmp.Width - 1, bmp.Height - 1)
Else
g.FillRectangle(Brushes.Red, 0, 0, bmp.Width - 1, bmp.Height - 1)
End If
g.DrawRectangle(Pens.Black, 0, 0, bmp.Width - 1, bmp.Height - 1)
End Using
row.Cells("VarIsValid").Value = bmp
But I have no Idea how to do something similar with a column originating from a linked DataView; much less when the column is not even an image column.
I considered changing the underlying DataView to contain an image, but then I don't know how I would filter by the value of that column. Thus I hope there is some way to simply change the visualization without changing the underlying structure.
Use Cell Formatting event handler
Private Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgv.CellFormatting
If e.RowIndex < 0 OrElse e.ColumnIndex < 0 Then Exit Sub
'You can check that column is right by ColumnIndex
'I prefer using a name of the DataGridViewColumn,
'because indexes can be changed while developing
If Me.dgv.Columns(e.ColumnIndex).Name.Equals("PredefinedColumnName") = True Then
If CBool(e.Value) = True Then
e.Value = My.Resources.GreenSquare 'image saved in the resources
Else
e.Value = My.Resources.RedSquare
End If
End If
End Sub
For a simple color fill operation, there is no need to use a bitmap. Just subscribe to the CellPainting event on the DatagridView.
Private Sub dgv1_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgv1.CellPainting
Const checkboxColumnIndex As Int32 = 0 ' set this to the needed column index
If e.ColumnIndex = checkboxColumnIndex AndAlso e.RowIndex >= 0 Then
Dim br As Brush
If CBool(e.Value) Then
br = Brushes.GreenYellow
Else
br = Brushes.Red
End If
e.Graphics.FillRectangle(br, e.CellBounds)
e.Paint(e.ClipBounds, DataGridViewPaintParts.Border)
e.Handled = True
End If
End Sub
For more information, see: Customizing the Windows Forms DataGridView Control
As the title says.
I collect a bunch of values from a SQL Compact DB, and puts them into a DataGridView. One cell ("Status" in this case) contains a varchar field. I want to set the background color of this field depending on its value.
For example, if this value is == "5" or "V", i want it to be red, if it's "4" or "B" i want it green or something like that.
I've tried with a loop that checks every value in this cell and sets the background color, but when i click in the DataGridView headers to change the order of the values the colors disapears.
And... It does not feel right to achieve this result by looping the values afterwards since there's quite much data.
I collect the values with something like this:
Dim Source as Bindingsource = GetBinding()
Form1.ClientsDataGrid.Columns("CustomerNr").DataPropertyName = "CustNR"
Form1.ClientsDataGrid.Columns("CustomerName").DataPropertyName = "Name"
Form1.ClientsDataGrid.Columns("Status").DataPropertyName = "Status"
Form1.ClientsDataGrid.DataSource = Source
'Just in case, this is how i set the colors now
For Each TblRow As DataGridViewRow In Form1.ClientsDataGrid.Rows
If TblRow.Cells(3).Value.ToString = "5" Or TblRow.Cells(3).Value.ToString = "V" Then
TblRow.Cells(3).Style.BackColor = Color.Red
ElseIf (TblRow.Cells(3).Value.ToString = "4" Or TblRow.Cells(3).Value.ToString = "B" Then
TblRow.Cells(3).Style.BackColor = Color.Green
End If
Next
and the GetBinding() looks something like this:
'blah blah make connections
Dim Com As New SqlCeCommand("SELECT Customernumber AS CustNR, Customername AS Name, Customerstatus AS Status FROM Mytable", Con)
Dim dataAdapter As SqlCeDataAdapter
dataAdapter = New SqlCeDataAdapter(Com)
Dim dataSet As New DataSet
dataAdapter.Fill(dataSet, "Mytable")
Dim bind As BindingSource
bind = New BindingSource(dataSet, "Mytable")
Con.Close()
Com = Nothing
Return bind
Is there a way to set these rules directly to the DataGridView? I can of course do the looping every time i sort the list, but it does'nt feel right?
Do that in rowprepaint event .. apply to the row ..
Private Sub ClientsDataGrid_RowPrePaint(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowPrePaintEventArgs) Handles ClientsDataGrid.RowPrePaint
Dim tblRow as DataGridViewRow = ClientsDataGrid.Rows(e.RowIndex)
If (TblRow.Cells(3).Value.ToString = "5" Or TblRow.Cells(3).Value.ToString = "V" Then
tblRow.DefaultCellStyle.BackColor = Color.Red
ElseIf (TblRow.Cells(3).Value.ToString = "4" Or TblRow.Cells(3).Value.ToString = "B" Then
tblRow.DefaultCellStyle.BackColor = Color.Green
End If
End Sub