Filling ComboBox Column in DatagridView VB.Net - vb.net

i have a datagridview with 2 columns as combobox and i want to fill the second one depending on the first one.
Ex. I have a table in my database with stations
TableStations
Station 1
Station 2
And each stations has a different amount of outputs
Ex.
Station 1 Station 2
OutP1 OutP5
OutP2 OutP6
OutP7
What i want to do in the datagridview is that when the user selects from the first combobox a station the next combobox gets filled with the outputs for that station, my problem comes when the user adds a second row in the datagridview if he selects a diferent station the info in the first row will be modified.
Is there any solution for this or any other way to do what i want?
Thanks in advance
EDIT: this is the code im using
Con.Open()
cmd.Parameters.Clear()
With cmd
.CommandText = "Select output From List_outputs where station=#station"
.Parameters.AddWithValue("#station", datagridview1.Item(0, e.RowIndex).Value)
.Connection = Con
reader = .ExecuteReader
End With
combobox2.Items.Clear()
While reader.Read
combobox2.Items.Add(reader("output "))
End While
reader.Close()
This code is under the cellclick event of my datagridview.

This is a bit tricky since you can't set the column's data source. Setting the column's data source affects the entire column. You must set the data source of each cell separately. I'll show you how to do it.
First add a DataGridView in an empty form. Don't add the columns, we're going to add the columns by code. You don't have to add the columns by code in your real project, but please follow what I did in this example. I add comments to make the code easy to understand. I choose to create two classes to hold Station and Output. This is also optional, you can just use a DataReader and add them manually. Hope this helps you.
Public Class Form1
Dim outputs As List(Of Output) ' this holds the fake output data.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Replace this section with the code to retrieve stations from database.
Dim stations As New List(Of Station) From {
New Station() With {.StationName = "Station 1"},
New Station() With {.StationName = "Station 2"}
}
' Add stations to first combobox
Dim firstColumn = New DataGridViewComboBoxColumn()
For Each station In stations
firstColumn.Items.Add(station.StationName)
Next
' Populate fake data, replace this section with the code to retrive outputs from database.
outputs = New List(Of Output) From {
New Output() With {.OutputName = "OutP1", .StationName = "Station 1"},
New Output() With {.OutputName = "OutP2", .StationName = "Station 1"},
New Output() With {.OutputName = "OutP5", .StationName = "Station 2"},
New Output() With {.OutputName = "OutP6", .StationName = "Station 2"},
New Output() With {.OutputName = "OutP7", .StationName = "Station 2"}
}
' add combobox columns to datagridview
DataGridView1.Columns.Add(firstColumn)
DataGridView1.Columns.Add(New DataGridViewComboBoxColumn())
End Sub
Private Sub DataGridView1_CellBeginEdit(sender As Object, e As DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit
' Only process if the column is the second combobox.
' You will need to change the index according to your second combobox index.
' e.ColumnIndex = 1 because the second combobox index is 1 in this sample.
If e.ColumnIndex = 1 Then
' Filter the outputs by selected Station in the row.
' Change the ColumnIndex to your first combobox index.
' DataGridView1(0, e.RowIndex) because the first combobox index is 0 in this sample.
Dim outputByStation = outputs.Where(Function(x) x.StationName = DataGridView1(0, e.RowIndex).Value.ToString())
' Get current cell, we're going to populate the combobox
Dim currentCell = CType(DataGridView1(e.ColumnIndex, e.RowIndex), DataGridViewComboBoxCell)
' Populate the cell's combobox.
currentCell.Items.Clear()
For Each output In outputByStation
currentCell.Items.Add(output.OutputName)
Next
End If
End Sub
End Class
Public Class Station
Public Property StationName As String
End Class
Public Class Output
Public Property OutputName() As String
Public Property StationName() As String
End Class
Screenshot:

Related

How would you structure data with different datatypes in VB.NET

Right now I have many locations with this structure. At the moment I have: name as string and x,y,z positions as single. So it's a mix of data types and I might want to add both more data in the future and also other data types. I must be able to easily extract any part of this data.
Example of how I'll work with this data is: When I choose South Wales from a combobox then I want to get its properties, x,y,z populated in a textbox. So they need to be "linked". If I choose London then it'll have its x,y,z properties etc.
My initial idea is just to dim every single data such as in the first example below. This should be the easiest way with 100% control of what's what, and I could easily extract any single data but at the same time might get tedious I assume, or am I wrong? Is it a good way to approach this?
Dim SW_FP As String = "South Wales"
Dim SW_FP_X As Single = "489,1154"
Dim SW_FP_Y As Single = "-8836,795"
Dim SW_FP_Z As Single = "109,6124"
The next example below is something i just googled up. Is this a good method?
Dim dt As DataTable = New DataTable
dt.Columns.Add("South Wales", GetType(String))
dt.Columns.Add("489,1154", GetType(Single))
dt.Columns.Add("-8836,795", GetType(Single))
dt.Columns.Add("109,6124", GetType(Single))
OR should I use something else? Arrays, Objects with properties... and this is where my ideas end. Are there other methods? XML?
I want to do it in a smart way from start instead of risking to rewrite/recreate everything in the future. So my main question is: Which method would you suggest to be the smartest to choose? and also if you could provide a super tiny code example.
You mentioned that when you choose an item you want to get it's properties. This shows that you are looking for objects. If not using a database one example could be to make Location objects and have a List of these to be added or removed from. Then you have a lot of different ways to get the data back from the List. For example:
Class:
Public Class Location
Public Property Name As String
Public Property X As Single
Public Property Y As Single
Public Property Z As Single
End Class
List:
Dim locations As New List(Of Location)
Dim location As New Location With {
.Name = "South Wales",
.X = 1.1,
.Y = 1.2,
.Z = 1.3
}
locations.Add(location)
LINQ to get result:
Dim result = locations.SingleOrDefault(Function(i) i.Name = "South Wales")
This is just an example for use within your program, hope it helps.
Disclaimer: Untested code. It's more to guide you than copy-paste into your project.
First, create a Class that will represent the structured data:
Public Class Location
Public Property Name As String
Public Property PositionX As Single
Public Property PositionY As Single
Public Property PositionZ As Single
Public Sub New()
Me.New (String.Empty, 0, 0, 0)
End Sub
Public Sub New(name As String, x As Single, y As Single, z As Single)
Me.Name = name
Me.PositionX = x
Me.PositionY = y
Me.PositionZ = z
End Sub
Now, you can create a List(Of Location) and use that List to bind to a ComboBox, like this:
Dim list As New List(Of Location) = someOtherClass.ReadLocations ' Returns a List(Of Location) from your database, or file, or whatever.
cboLocations.DataSource = list
cboLocations.DisplayMember = "Name" ' The name of the Location class' Property to display.
cboLocations.ValueMember = "Name" ' Use the same Name Property since you have no ID.
You can also forego the list variable declaration like the following, but I wanted to show the declaration of list above:
cboLocations.DataSource = someOtherClass.ReadLocations
Function someOtherClass.ReadLocations() may populate the List(Of Locations) in a way similar to this. Note I'm not including data access code; this is just an example to show how to add Location objects to the List(Of Location):
Dim list As List(Of Location)
' Some loop construct
For each foo in Bar
Dim item As New Location(foo.Name, foo.X, foo.Y, foo.Z)
list.Add(item)
' End loop
Return list
The "magic" happens when you select an option from the ComboBox. I forget the ComboBox event offhand, so that's homework for you :-) You take the selected Object of the ComboBox and cast it back to the native type, in this case Location:
Dim item As Location = DirectCast(cboLocations.SelectedItem, Location)
txtName.Text = item.Name
txtPosX.Text = item.PositionX.ToString
txtPosY.Text = item.PositionY.ToString
txtPosZ.Text = item.PositionZ.ToString
Here is one way, using a DataTable as you mentioned. This is a stand alone example project just to show code used.
This example loads data from file is found and saves data on exit.
Form1 Image
' Stand alone example
' needs DataGridView1, Label1 and
' ComboBox1 on the Designer
' copy/replace this code with default
Option Strict On
Option Explicit On
Public Class Form1
Dim dt As New DataTable("Freddy")
Dim bs As New BindingSource
'edit path/filename to use as test data path
Dim filepath As String = "C:\Users\lesha\Desktop\TestData.xml"
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
dt.WriteXml(filepath)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
With dt
dt.Columns.Add("Country", GetType(String))
dt.Columns.Add("x", GetType(String))
dt.Columns.Add("y", GetType(String))
dt.Columns.Add("z", GetType(String))
' add extra column to hold concatenated
' location (could be a hidden column)
dt.Columns.Add("CombLoc", GetType(String), "'x = ' + x + ' y = ' + y + ' z = ' + z")
If IO.File.Exists(filepath) Then
' saved file found so load it
dt.ReadXml(filepath)
Else
' no saved file so make one test row
.Rows.Add("South Wales", 489.1154, -8836.795, 109.6124)
End If
End With
bs.DataSource = dt
DataGridView1.DataSource = bs
' set any properties for DataGridView1
With DataGridView1
' to hide Combined Location column
.Columns("CombLoc").Visible = False
' dontwant row headers
.RowHeadersVisible = False
End With
set up ComboBox
With ComboBox1
.DataSource = bs
' displayed item
.DisplayMember = "Country"
' returned item
.ValueMember = "CombLoc"
If .Items.Count > 0 Then .SelectedIndex = 0
End With
' default Label text
Label1.Text = "No items found"
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
no items in list so exit sub
If ComboBox1.SelectedIndex < 0 Then Exit Sub
send returneditem to Label
Label1.Text = ComboBox1.SelectedValue.ToString
End Sub
End Class

vb.net combobox displaymember saves to database not valuemember

I've searched regarding displaymember and valuemember of combobox, but I think none of them fits my need.
Here's my scenario
I have two table, emp201 and department_misc
emp201 contains empno, name, department, etc.
department_misc contains code, description.
Here's my code to fill combobox:
Public Sub Auto1(ByVal cmb As ComboBox, ByVal strTbl As String)
Dim locDa As New DataSet
rs = cn.Execute("SELECT Code, Description FROM " & strTbl)
locDaOle.Fill(locDa, rs, strTbl)
cmb.DisplayMember = "department"
cmb.ValueMember = "Code"
cmb.DataSource = locDa.Tables(strTbl)
End Sub
After that, this is my code to bind using bindingsource
Public Sub bindControls(ByVal fFormName As Form, ByVal strFormName As String, ByVal strTable As String)
Dim ctrl As Control
Try
ds = New DataSet()
sda = New OleDbDataAdapter("SELECT * FROM " + strTable, oleDBConn)
sda.Fill(ds)
bsSource = New BindingSource()
bsSource.DataSource = ds.Tables(0)
sda.Fill(ds, strTable)
For Each ctrl In fFormName.Controls
If ctrl.AccessibleName = "" Then Continue For
If (ctrl.GetType() Is GetType(TextBox)) Or _
(ctrl.GetType() Is GetType(ComboBox)) Then
ctrl.DataBindings.Clear()
ctrl.DataBindings.Add(New Binding("Text", bsSource, ctrl.AccessibleName))
ElseIf (ctrl.GetType() Is GetType(CheckBox)) Then
ctrl.DataBindings.Clear()
ctrl.DataBindings.Add(New Binding("Checked", bsSource, ctrl.AccessibleName))
MAINFORM.BindingNavigator1.BindingSource = bsSource
End If
Next
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
I used accessiblename to store the field in database, since datafield in .net is not available.
Then after I bound the combobox using bindingsource, it works, but when I browse the data saved to database, it saved the displaymember. Is there a way i can save the code not the description? or maybe I am wrong using the displaymember and valuemember?
None of the code you have shown will cause anything to be saved to the database. I'm assuming that you have your emp201 table bound to other controls, including that ComboBox. If you're expecting the Code of the selected department to be saved into a another record then you need to bind your ComboBox to that record. That would look something like this:
cmb.DataBindings.Add("SelectedValue", myBindingSource, "department")
When you select an item in a ComboBox, the SelectedValue property will return the value from the column/property whose name is assigned to the ValueMember.

DataGridView Cascading/Dependent ComboBox Columns

So I work from time to time in Winforms on a legacy app and am not familiar with best practices at all times with binding objects. Basically I have a three part set where I have two people, they may have only one product, but that product could cause the possibility to have different sets of SKUs. Is there a way to trigger an event and population of a combobox from the values of a first combobox? I have been looking around and I am either finding basic data of just how to bind a combobox(I can do that fine) or do something with how you bind it. Not binding after a dependent parent change is triggered and changing the dataset. Example below:
POCOS:
Public Class Person
Public Property PersonID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property ProductId As Integer
Public Property SkuId As Integer
End Class
Public Class Product
Public Property ProductId As Integer
Public Property Description As String
End Class
Public Class Sku
Public Property SKUId As Integer
Public Property ProductId As Integer
Public Property Description As String
End Class
Main code example (basic UI really only has a Datset labeled 'ds' that matches nearly identically the Person and Product POCOS with datatables. A datagridview 'dgv' whose columns are bound to data in Person EXCEPT for a column called SKU that has no binding as I want to bind it after the fact and that is where I am failing miserably at.
Update 9-13-2016
I can get the below code to work EXCEPT in certain large scale solutions(the whole reason I did this). It basically will NOT execute the line that casts the cell() to a datagridviewcomboboxcell and ignores it and jumps over the line. No reason why, it just jumps over it. I am wondering if in larger classes the datagrid views can become corrupt or something.
Main Code:
Private _people As List(Of Person) = New List(Of Person)
Private _products As List(Of Product) = New List(Of Product)
Private _SKUs As List(Of Sku) = New List(Of Sku)
Private _initialLoadDone = False
Private _currentRow As Integer? = Nothing
Private Sub DynamicComboBoxDoubleFill_Load(sender As Object, e As EventArgs) Handles MyBase.Load
_products = New List(Of Product)({
New Product With {.ProductId = 1, .Description = "Offline"},
New Product With {.ProductId = 2, .Description = "Online"}
})
Dim s = ""
For Each o In _products
Dim row As DataRow = ds.Tables("tProducts").NewRow
row("ProductId") = o.ProductId
row("Description") = o.Description
ds.Tables("tProducts").Rows.Add(row)
Next
_SKUs = New List(Of Sku)({
New Sku With {.SKUId = 1, .ProductId = 1, .Description = "Mail"},
New Sku With {.SKUId = 2, .ProductId = 1, .Description = "Magazine"},
New Sku With {.SKUId = 3, .ProductId = 2, .Description = "Email"},
New Sku With {.SKUId = 4, .ProductId = 2, .Description = "APIRequest"}
})
Dim items = _SKUs
_people = New List(Of Person)({
New Person With {.PersonID = 1, .FirstName = "Emily", .LastName = "X", .ProductId = 1, .SkuId = 1},
New Person With {.PersonID = 2, .FirstName = "Brett", .LastName = "X", .ProductId = 2, .SkuId = 3}
})
For Each p In _people
Dim row As DataRow = ds.Tables("tPeople").NewRow
row("PersonId") = p.PersonId
row("FirstName") = p.FirstName
row("LastName") = p.LastName
row("ProductId") = p.ProductId
row("SkuId") = p.SkuId
ds.Tables("tPeople").Rows.Add(row)
Next
For Each row As DataGridViewRow In dgv.Rows
ArrangeValuesForSKUComboBox(row)
Next
_initialLoadDone = True
End Sub
Private Sub ArrangeValuesForSKUComboBox(row As DataGridViewRow)
Dim productId = CInt(row.Cells("ProductId")?.Value)
Dim skus = _SKUs.Where(Function(x) x.ProductId = productId).ToList().Select(Function(x) New With {Key .SkuId = x.SKUId, .SkuDesc = x.Description}).ToList()
Dim cell = row.Cells("SKU")
'Yeah I don't always work. In this example I do, in others I won't.
'For this reason I just want more ideas. I don't care if you completely blow up how the binding is done and do something else entirely.
Dim combobox = CType(cell, DataGridViewComboBoxCell)
combobox.DataSource = skus
combobox.ValueMember = "SKUId"
combobox.DisplayMember = "SkuDesc"
combobox.Value = skus.FirstOrDefault()?.SkuId
End Sub
Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
If _initialLoadDone Then
Dim headerText As String = TryCast(sender, DataGridView).Columns(e.ColumnIndex).HeaderText
If headerText = "PRODUCT" Then
ArrangeValuesForSKUComboBox(dgv?.CurrentRow)
End If
End If
End Sub
To have dependent (cascading or master/slave) ComboBox columns in DataGridView, you can follow this steps:
Set DataSource of slave column to all available values.
Goal: Here the goal is prevent rendering errors at first load, so all slave combo boxes can show value correctly.
Hanlde EditingControlShowing event of the grid and check if the current cell is slave combo cell, then get the editing control using e.Control which is of type DataGridViewComboBoxEditingControl. Then check the value of master combo cell and set the DataSource property of editing control to a suitable subset of values based on the value of master combo cell. If the value of master cell is null, set the data source to null.
Goal: Here the goal is setting data source of slave combo to show only suitable values when selecting values from slave combo.
Handle CellValueChanged and check if the current cell is master combo, then set the value for dependent cell to null.
Note: Instead of setting the value of slave cell to null, you can set it to first available valid value based on master cell value.
Goal: Here the goal is prevent the slave combo to have invalid values after the value of master combo changed, so we reset the value.
Following above rules you can have as many dependent combo boxes as you need.
Example
In below example I have a Country (Id, Name) table, a State(Id, Name, CountryId) table and a Population(CountryId, StateId, Population) table. And I want to perform data entry for Population table using 2 combo columns for country and state and a text column for population. I know this is not a normal db design, but it's just for example of having master/slave (dependent) combo box columns in grid:
Private Sub EditingControlShowing(sender As Object, _
e As DataGridViewEditingControlShowingEventArgs) _
Handles PopulationDataGridView.EditingControlShowing
Dim grid = DirectCast(sender, DataGridView)
If (grid.CurrentCell.ColumnIndex = 1) Then 'State column
Dim combo = DirectCast(e.Control, DataGridViewComboBoxEditingControl)
If (grid.CurrentRow.Cells(0).Value IsNot DBNull.Value) Then
Dim data = Me.DataSet1.State.AsDataView()
data.RowFilter = "CountryId = " + grid.CurrentRow.Cells(0).Value.ToString()
combo.DataSource = data
Else
combo.DataSource = Nothing
End If
End If
End Sub
Private Sub CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) _
Handles PopulationDataGridView.CellValueChanged
Dim grid = DirectCast(sender, DataGridView)
If (e.ColumnIndex = 0 And e.RowIndex >= 0) Then 'Country Column
grid.Rows(e.RowIndex).Cells(1).Value = DBNull.Value 'State Column
End If
End Sub

Display DataGridView values in text boxes based on matching values - vb.net

I have an SQL query that returns 2 columns of values:
country | number
NA | 1
IN | 2
CN | 3
DE | 4
And so on.
I am trying to do one of the following:
Assign these values to variables I can copy to an excel workbook
Or just use the DGV as a medium to copy values to text boxes.
For example, I have a form with country labels and textboxes next to them. I would want to click a button and have the data copied to the matching text box.
DGV number value where DGV row value = CN would be 3 and that value would be copied to the CN value text box.
If you are only using the DGV as a medium, but not actually displaying it, use a dictionary instead. Output the SQLDataReader using the SQLcommand(cmd) with the country code being the key and the number being the value. Then its as simple as:
Dim dc As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim cmd As SqlCommand = "your query string"
cmd.Connection.ConnectionString = "your con string"
Using sqlrdr As SqlDataReader = cmd.ExecuteReader()
While (sqlrdr.Read())
dc.Add(sqlrdr(0), sqlrdr(1))
End While
End Using
Then just output to the textbox:
txtNA.Text = dc.Item("NA")
If the countries are fixed as your question refers, then you could use something like this:
First, use the names of the countries to name the TextBoxes (txtNA, txtIN, txtCN,...)
Then you can put this code:
Try
For i = 0 To DataGridView1.RowCount - 1
Me.Controls("txt" & DataGridView1.Rows(i).Cells(0).Value.ToString).Text = _
DataGridView1.Rows(i).Cells(1).Value.ToString()
Next
Catch
End Try
The following will not work 'as is' if using classes via dragging tables from the data source window in the ide. We would need to cast objects not to a DataTable but to the class definition in a TableAdapter, DataSet and BindingSource.
Here is a method which reads from SQL-Server database table, places data into a DataTable, data table become the data source for a BindingSource which becomes the data source for the DataGridView. Using a BindingSource we now use the BindingSource to access row data rather than the DataGridView and it's always best to go to the data source rather than the user interface.
BindingSource.Current is a DataRowView, drill down to Row property then field language extension method to get strongly typed data for the current row if there is a current row as you will note I am using two forms of assertions, first, do we have a data source and is the data source populated then we see if there is indeed a current row.
From here we can set variable, properties or control text to the field values of the current row.
Note in form load I seek a specific country (totally optional) and then if found go to that row.
Least but not last, I like using xml literals when doing SQL in code so there is no string concatenation and we can format the statement nicely.
Public Class Form1
''' <summary>
''' Permits obtaining row data in DataGridView
''' </summary>
''' <remarks></remarks>
Dim bsCountries As New BindingSource
Public Property Country As String
Public Property CountryNumber As Integer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt As New DataTable
Using cn As New SqlClient.SqlConnection With
{
.ConnectionString = My.Settings.KarenDevConnectionString
}
Using cmd As New SqlClient.SqlCommand With {.Connection = cn}
' xml literal to make command text
cmd.CommandText =
<SQL>
SELECT [ID],[Country],[Number]
FROM [Countries]
</SQL>.Value
cn.Open()
dt.Load(cmd.ExecuteReader)
dt.Columns("ID").ColumnMapping = MappingType.Hidden
bsCountries.DataSource = dt
DataGridView1.DataSource = bsCountries
' let's try and move to a country
Dim index As Integer = bsCountries.Find("Country", "CN")
If index > -1 Then
bsCountries.Position = index
End If
End Using
End Using
End Sub
''' <summary>
''' Put field values into TextBoxes
''' </summary>
''' <remarks></remarks>
Private Sub DoWork()
If bsCountries.DataSource IsNot Nothing Then
If bsCountries.Current IsNot Nothing Then
Dim row As DataRow = CType(bsCountries.Current, DataRowView).Row
TextBox1.Text = row.Field(Of String)("Country")
TextBox2.Text = row.Field(Of Integer)("Number").ToString
' we can also do this
Me.Country = row.Field(Of String)("Country")
Me.CountryNumber = row.Field(Of Integer)("Number")
End If
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
DoWork()
End Sub
End Class

Passing data to DataGridView

How can I pass data from my textboxes (in form1) to my DataGridView (in form2)?
here is the code i used.
DataGridView1.ColumnCount = 2
DataGridView1.Columns(0).Name = "Customer"
DataGridView1.Columns(1).Name = "Information"
Dim FNAME As String = Form1.TBFNAME.Text.ToString
Dim row As String() = New String() {"FULLNAME", FNAME}
DataGridView1.Rows.Add(row)
row = New String() {"EMAIL", Form1.TBEADD.Text}
DataGridView1.Rows.Add(row)
row = New String() {"ADDRESS", Form1.TBADDRESS.Text}
DataGridView1.Rows.Add(row)
row = New String() {"CONTACT NO.", Form1.TBCONTACT.Text}
the dgv have two columns. and the passed data will appear to the 2nd column but whenever i already input the data it's still blank in the dgv
Your code is missing
DataGridView1.Rows.Add(row)
for the "CONTACT NO" row.
Other than that, it seems fine. Are FNAME,TBEADD,TBADDRESS, and TBCONTACT all standard textboxes? Where is this code called from?
Some troubleshooting suggestions:
Try creating a minimal project with 4 textboxes, a DataGridView, and a button. Put your code above into the Click event of the button. Then try it with the DataGridView and button on a second form. Compare that to your existing project.