Setting backgroundcolors in DataGridViewCells depending on the value from SQL (VB.Net winforms) - vb.net

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

Related

Read data from dynamically created text-box

I'm trying to collect data after creating dynamic text-box with vb.net
Private Sub btn_OK_lines_number_Click(sender As Object, e As EventArgs)
Handles btn_OK_lines_number.Click
Dim i As Integer
Dim x As Integer
Dim Z As Integer
Z = 150
If IsNumeric(txt_lines_number.Text) Then
Int32.TryParse(txt_lines_number.Text, x)
For i = 1 To x
Dim newTB As New TextBox
Dim newLB As New Label
newLB.Name = "lbl_workstation_number_line" & i
newLB.Text = "Nbr Work Station in Line" & i
newLB.Size = New Size(190, 20)
newLB.ForeColor = Color.White
newLB.Font = New Font("consolas", 12, FontStyle.Regular, GraphicsUnit.Pixel)
newLB.Location = New Point(20, Z + i * 30)
newTB.Name = "Textbox" & i
newTB.Size = New Size(170, 20)
newTB.Location = New Point(200, Z + i * 30)
Me.Controls.Add(newTB)
Me.Controls.Add(newLB)
Next
i = i + 1
Else
MessageBox.Show("please enter a number")
txt_lines_number.Text = ""
End If
End Sub
Let's say you just have one row, and only create one TextBox. You set the name here:
newTB.Name = "Textbox" & i
where the resulting TextBox is named Textbox1. The problem is you can't just reference the identifier Textbox1 directly in your code, as you do with txt_lines_number. You can't even reference it as a member of the class (Me.Textbox1). This name didn't exist at compile time, and so it's not an identifier you can use, and it's not a member of the class at all. There was never a matching Dim statement for that name.
What you can do, though, is look again in the Controls collection where you added the TextBox to the form:
Me.Controls("Textbox1")
or
Me.Controls("Textbox1").Text
You may also need to cast the value to a TextBox:
Dim box As TextBox = DirectCast(Me.Controls("Textbox1"), TextBox)
MessageBox.Show(box.Text)
Remember that case matters here.
Further saving this in a DB is out of scope for one question. There are as many ways to do that as there are programmers in the world. You should make your own attempt first, and come back here with a new question when you run into specific problems.
Thank you,
this is my attempt and it is done !
Dim userInput As TextBox = Form1.Controls.Item("TextBox" & i.ToString)
mycommand.Parameters.AddWithValue("#workstation", userInput.Text)
:D
Because you creating dynamic amount of input controls, right tool for the job will be DataGridView control.
Create a class to represent your data
Public Class LineInfo
Public Property Number As Integer
Public Property WorkStationNumber As Integer
End Class
Create `DataGridView in the form designer.
Private Sub btn_OK_lines_number_Click(sender As Object, e As EventArgs) Handles btn_OK_lines_number.Click
Dim linesAmount As Integer
If Integer.TryParse(txt_lines_number.Text, linesAmount) = False Then
MessageBox.Show("please enter a number")
txt_lines_number.Text = ""
Exit Sub
End If
' Create class instance for every line
Dim lines =
Enumerable.Range(1, linesAmount)
.Select(Function(i) New LineInfo With { .Number = i })
.ToList()
'Set lines as DataSource to the DataGridView
Me.DataGridView1.DataSource = lines
End Sub
DataGridView will display all lines and provide input fields to update work station numbers.
You can access updated lines later by casting DataSource back to the List
Dim lines = DirectCast(Me.DataGridView1.DataSource, List(Of LineInfo))
' Now you can access all data and save it to the database
Dim parameters =
lines.Select(Function(line)
Return new SqlParameter With
{
.ParameterName = $"#workstation{line.Number}",
.SqlDbType = SqlDbType.Int,
.Value = line.WorkStationNumber
}
End Function)
.ToList()
myCommand.Parameters.AddRange(parameters)
You can freely change style, font colors of different columns in the datagridview.

Changing the format of all cells in a datagridview to combobox

I have a few data tables. I loop through the number of datatables that I have. In the loop I create a DataGridView with the datasource being the given Data Table at that index. How can I then change the columns to be of type Combobox? I know how to do this on the designer but what about programatically? I've tried:
For each column as DataGridViewColumn in table.Columns
column.CellType = DataGridViewComboBoxColumn
Next
Obviously this doesn't work. CellType is a read only property and I cannot simply set it equal to a class type. I know this is probably simple, I know I am stupid. Please help.
You don't need to define an entire column as a combo box. See my example where you have a definition for a combobox with the same value for every rows. I also included a way for you to only replace a single cell with a combobox. See how the "people" column is a textboxcolumn where I put a combobox instead. It also shows you how to assign values and set the default value. It's the same concept for a checkbox column/cell.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim NewCol As New DataGridViewComboBoxColumn
DataGridView1.Columns.Add(New DataGridViewTextBoxColumn With {.Name = "ID", .HeaderText = "ID"})
With NewCol
.Name = "ComboBoxCol"
.HeaderText = "Language"
.Items.Add("VB.NET")
.Items.Add("C#")
.Items.Add("F#")
.DefaultCellStyle.NullValue = .Items(0)
End With
DataGridView1.Columns.Add(NewCol)
DataGridView1.Columns.Add(New DataGridViewTextBoxColumn With {.Name = "PeopleCol", .HeaderText = "People"})
Dim xRow As String() = {"1"}
DataGridView1.Rows.Add(xRow)
DataGridView1.Rows.Add({"2"})
DataGridView1(1, 0).Value = "C#"
Dim xPeople1 As New DataGridViewComboBoxCell
With xPeople1
.Items.Add("Santa")
.Items.Add("Satan")
.Value = .Items(0)
End With
DataGridView1(2, 0) = xPeople1
Dim xPeople2 As New DataGridViewComboBoxCell
With xPeople2
.Items.Add("God")
.Items.Add("Allah")
.Value = .Items(1)
End With
DataGridView1(2, 1) = xPeople2
End Sub
End Class

datagrid view combobox item selection vb.net

I have a datagridview which I have furnished using the following code...
The trouble I have though is with the combobox column.
First off how can I have a preset value already selected in the combobox.
Secondly When I run my code I have to click the combobox's twice how can I change this to only having to click them once?
'CREATE DATAGRIVIEW 1 COLUMN LAYOUT...S
DataGridView1.ColumnCount = 8
DataGridView1.Columns(0).Name = "QTY"
DataGridView1.Columns(1).Name = "H"
DataGridView1.Columns(2).Name = "L"
DataGridView1.Columns(3).Name = "W"
DataGridView1.Columns(4).Name = "ANG 1"
DataGridView1.Columns(5).Name = "ANG 2"
DataGridView1.Columns(6).Name = "MAT"
DataGridView1.Columns(7).Name = "THK"
Dim ComboBoxColumn As New DataGridViewComboBoxColumn()
ComboBoxColumn.HeaderText = "TYPE"
ComboBoxColumn.Name = "ComboBoxColumn"
ComboBoxColumn.MaxDropDownItems = 5
ComboBoxColumn.Items.Add("NON")
ComboBoxColumn.Items.Add("SNG")
ComboBoxColumn.Items.Add("PER")
ComboBoxColumn.Items.Add("PAR")
ComboBoxColumn.Items.Add("AXS")
DataGridView1.Columns.Insert(8, ComboBoxColumn)
Dim checkBoxColumn As New DataGridViewCheckBoxColumn()
checkBoxColumn.HeaderText = "IDW"
checkBoxColumn.Name = "checkBoxColumn"
DataGridView1.Columns.Insert(9, checkBoxColumn)
'CREATE DATAGRIVIEW 1 COLUMN LAYOUT...E
If you mean a defaultvalue for the combobox:
Private Sub DataGridView1_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles DataGridView1.CellFormatting
If (e.ColumnIndex = 8) Then ' your combo column index
e.Value = "PER"
End If
End Sub
The selection in the combo box reflects the Value of the cell. If you want something to be selected then you have to set the Value of the cell accordingly.
Your second issue, which is unrelated and should therefore have been asked in an unrelated post, is as a result of the EditMode of the grid. Change that to the appropriate value for the behaviour you want.

Visual Basic Datagrid View change row colour

Every other change to the datagrid view works fine but for some reason the row color just wont change.
Ive debugged and my application goes through the loop to change the row color.
Also I have a button that gives the datagrid view a new list and colors the rows accordingly, when I click the button the row colors changes do work!
Public Sub New(measuredValues As List(Of MeasuredValuesModel), valueType As ValueType)
IsFiltered = False
' This call is required by the designer.
InitializeComponent()
MeasuredValuesList = measuredValues
uxGrid.DataSource = MeasuredValuesList
uxGrid.Columns("StepID").Visible = False
uxGrid.Font = New Font("Arial", 10, FontStyle.Bold)
For Each c As DataGridViewColumn In uxGrid.Columns
Dim Column As DataGridViewColumn = c
c.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
Next
If valueType = StepItem.ValueType.CalculatedValues Then
uxButtonFilter.Visible = False
uxGrid.Columns("DISPOSITION").Visible = False
End If
For Each gridRow As DataGridViewRow In uxGrid.Rows
If gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "Y" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Red
ElseIf gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "N" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Orange
End If
Next
uxStepID.Text = MeasuredValuesList.FirstOrDefault.StepID
' Add any initialization after the InitializeComponent() call.
'TODO binding
End Sub
Here is the button that works for changing row color.
Maybe it works because it's pressed after the grid has been created? Im not too sure.
Private Sub uxButtonFilter_Click(sender As Object, e As EventArgs) Handles uxButtonFilter.Click
If IsFiltered = True Then
uxGrid.DataSource = MeasuredValuesList
For Each gridRow As DataGridViewRow In uxGrid.Rows
If gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "Y" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Red
ElseIf gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "N" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Orange
End If
Next
Its the exact same loop but it seems to work fine when i use the button.
Is it possible that your datagridview isn't loaded fully when you try to recolor the rows?
Since you are setting the datasource, you should put your code that affects the grid after you can make sure that it is finished loading. The column widths change because it is not dependent on the data in the grid, but your colouring is.
Catch the uxGrid.databindingComplete event and try colouring the rows in there

Mutually Exclusive Comboboxes?

So, I've got 4 Comboboxes. They all share the same list of options, lets say Apple, Banana, Carrot, Dill.
However, once the user selects one, that selection should not be available in the other comboboxes. E.g. if they pick Apple in Combobox1, then the only selections available in Combobox2, 3, and 4 should be Banana, Carrot, and Dill.
Is there a good way to do this? I originally thought to link the boxes datasource to a list containing the options, but they need separate datasources for separate choices.
Radio and checkboxes aren't really an option in the actual program, as the list of options is much larger than 4. A Combobox seems to be the best way to represent the user's available choices here.
Here's a fairly simple way to do what you are asking. This code assumes that the datasource uses an integer value to keep track of the items. If you are using the string value itself {Apple, Banana, etc.} as the id, the code will need to be amended slightly. If you need further help let me know but hopefully this gets you on the right track.
You can test this by creating a new blank form (Form1) and drop 4 combo boxes onto it (ComboBox1, ComboBox2, ComboBox3, ComboBox4). Copy/paste this code over top of the form code and run to see how it works:
Public Class Form1
Dim oComboBoxArray(3) As ComboBox
Dim bPreventSelectedChange As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Set up an array with the combo box references to reduce code size by using loops later
oComboBoxArray(0) = Me.ComboBox1
oComboBoxArray(1) = Me.ComboBox2
oComboBoxArray(2) = Me.ComboBox3
oComboBoxArray(3) = Me.ComboBox4
' Populate all four combo boxes with the same datasource to start
For Each oCbo In oComboBoxArray
PopulateDataSource(oCbo)
Next
End Sub
Private Sub PopulateDataSource(oCombo As ComboBox, Optional nIdToSelect As Integer = 0)
bPreventSelectedChange = True ' Prevent code in ComboBox_SelectedIndexChanged from executing while we change the datasource
' Using manually populated datatable as datasource because it's quick and easy to use
Dim dt As New DataTable
Dim dr As DataRow
dt.Columns.Add("ID", GetType(Int32))
dt.Columns.Add("Name", GetType(String))
' Need to have some kind of "Please select an item:" in the list or else we will be unable to clear an already selected combo box
dr = dt.NewRow
dr("ID") = 0
dr("Name") = "Select..."
dt.Rows.Add(dr)
' If you are populating from a database or other dynamic source you will only have one of these 'if' statements within a loop
If CheckSkipItem(oCombo, 1) = False Then
dr = dt.NewRow
dr("ID") = 1
dr("Name") = "Apple"
dt.Rows.Add(dr)
End If
If CheckSkipItem(oCombo, 2) = False Then
dr = dt.NewRow
dr("ID") = 2
dr("Name") = "Banana"
dt.Rows.Add(dr)
End If
If CheckSkipItem(oCombo, 3) = False Then
dr = dt.NewRow
dr("ID") = 3
dr("Name") = "Carrot"
dt.Rows.Add(dr)
End If
If CheckSkipItem(oCombo, 4) = False Then
dr = dt.NewRow
dr("ID") = 4
dr("Name") = "Dill"
dt.Rows.Add(dr)
End If
oCombo.DataSource = dt
oCombo.DisplayMember = "Name"
oCombo.ValueMember = "ID"
oCombo.SelectedValue = nIdToSelect ' Set value to either a) the "Select..." item or b) the item that was selected previously depending on the situation
bPreventSelectedChange = False ' Allow code in ComboBox_SelectedIndexChanged to be executed by user again
End Sub
Private Function CheckSkipItem(oCombo As ComboBox, nID As Integer) As Boolean
Dim bSkip As Boolean = False
' Loop through all combo boxes and see if this id has already been chosen in another combo box
For Each oCbo In oComboBoxArray
If oCbo IsNot oCombo AndAlso oCbo.SelectedValue = nID Then
' It has been chosen already so it is not valid for the current combo box that we are checking
bSkip = True
Exit For
End If
Next
Return bSkip
End Function
Private Sub ComboBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged, ComboBox2.SelectedIndexChanged, ComboBox3.SelectedIndexChanged, ComboBox4.SelectedIndexChanged
' Jump out of this event if the bPreventSelectedChange boolean is set to true (ie. if the combo box is being repopulated)
If bPreventSelectedChange = False Then
' A value was chosen by the user. Reset all other combo box datasources and remove the recently selected value
For Each oCbo As ComboBox In oComboBoxArray
If sender IsNot oCbo Then
PopulateDataSource(oCbo, If(oCbo.SelectedValue = Nothing, 0, oCbo.SelectedValue))
End If
Next
End If
End Sub
End Class