Resolved Can't figure out why DataGridView isn't showing all the records inside table from SQL Server database on _Load()

accol

New member
Joined
Jun 19, 2023
Messages
4
Programming Experience
Beginner
We are making a form where a user can log in and in turn upload listings for cars to sell, however, the data grid view for the listings isn't showing every entry even though it is in the database when it is being first loaded. I also notice too that updating the data grid view and refreshing it doesn't work after uploading an entry to it, only after you close the form and reopen it again does it actually update with the new listing (but only for those of seller ID 1 still).

SQL:
/* the table's properties in the query that was used to create it*/
CREATE TABLE [dbo].[Listing](
    [ListingID] [int] IDENTITY(1,1) NOT NULL,
    [SellerID] [int] NOT NULL,
    [CarVIN] [varchar](50) NOT NULL,
    [Description] [varchar](500) NULL,
    [CreationDateTime] [smalldatetime] NOT NULL,
PRIMARY KEY CLUSTERED
(
    [ListingID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

/**/

1687218303287.png

1687218315640.png

Below is the code that I reference the data grid view in some form.
C#:
private void frmCarsForSale_Load(object sender, EventArgs e)
    {
        // TODO: This line of code loads data into the 'groupFinal266DataSet.Listing' table. You can move, or remove it, as needed.
        this.listingTableAdapter.Fill(this.groupFinal266DataSet.Listing);
    }

// this is for filtering but it shouldn't be impacting how the data grid view is being loaded when the form's just being loaded?
private void viewAllToolStripButton_Click(object sender, EventArgs e)
        {
            /*...*/
           this.groupFinal266DataSet.Listing.Rows.Clear();

            /*...*/
            List<Listing> listings = ListingDB.GetAllListings(Program.sqlConnection);

            string carVINs = String.Join("|", cars.Select(x => x.carVIN).ToArray());
            carVINs = carVINs.Trim(new Char[] { '|' });

            listings.RemoveAll(l => l.carVIN.Any(c => !Regex.IsMatch(l.carVIN, carVINs)));

            foreach (Listing listing in listings)
                this.groupFinal266DataSet.Listing.Rows.Add(listing.listingID, listing.sellerID, listing.carVIN, listing.description, listing.creationDateTime);
        }

// all of the code that references the data grid view itself asides the _Load() function only happens if you click something, so I don't think the code is the problem?
private void btnUpload_Click(object sender, EventArgs e)
        {
            modifyingCarComponents |= ModifyingCarComponents.IS_MODIFYING_LISTING;

            AssignBusinessObjectDataToUpload();

            if (!ValidateBusinessObjectData())
                return;

            if (!listingDB.Upload(listing, Program.sqlConnection)) {
                MessageBox.Show(listingDB.MsgText, listingDB.MsgCaption);
                return;
            }

            // Why don't these work? That's also been an issue?
            listingDataGridView.Update();
            listingDataGridView.Refresh();
        }

        public void AssignBusinessObjectDataToDelete(int rowIndex)
        {
            if (modifyingCarComponents == ModifyingCarComponents.IS_MODIFYING_LISTING)
            {
                listing.listingID = Convert.ToInt32(listingDataGridView.Rows[rowIndex].Cells[0].Value);
                listing.sellerID = Convert.ToInt32(listingDataGridView.Rows[rowIndex].Cells[1].Value);
                listing.carVIN = Convert.ToString(listingDataGridView.Rows[rowIndex].Cells[2].Value);
                listing.description = Convert.ToString(listingDataGridView.Rows[rowIndex].Cells[3].Value);
                listing.creationDateTime = Convert.ToDateTime(listingDataGridView.Rows[rowIndex].Cells[4].Value);
            }
        }

private void listingDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 5)
            {
                modifyingCarComponents = ModifyingCarComponents.IS_MODIFYING_LISTING;

                AssignBusinessObjectDataToDelete(e.RowIndex);

                if (!ValidateBusinessObjectData())
                    return;

                if (!listingDB.Delete(listing, Program.sqlConnection)) {
                    MessageBox.Show(listingDB.MsgText, listingDB.MsgCaption);
                    return;
                }

                listingDataGridView.Rows.RemoveAt(e.RowIndex);
            }
        }
I have linked the entire frmCarsForSale code if needed (the names are the hyperlinks). There is a method that handled filtering but it isn't called on load, but when a button is clicked so that shouldn't be impacting anything. Also the frmUsers as well, which is the parent form that houses the login screen.
1687218472178.png

I have been trying to figure this out and I don't know what to do anymore. From what I notice and have tested, it only shows the listings for SellerID 1 even though no one in the group has specified to filter out the listings by seller ID then populating the data grid view based on that. The query builder shows that `Fill()` for the adapter is working and the data grid view is clearly bound otherwise it wouldn't be showing anything.
1687218360958.png

1687218372436.png


I have tried calling `listingBindingSource.ResetBindings(true);` to update the data grid view inside the cars for sale form automatically after adding a record for instance and that didn't work, tried setting it to false too (I think there's something wrong with how the `listingDataGridView` is loading in general. The data set was set up via the Data Source Configuration Wizard with an already existing connection string. In frmUsers I've tried commenting out the entirety of the pagination code because it does clear the dataset, tested that and that didn't fix the issue. I don't know what the cause is, if I commented out `this.listingTableAdapter.Fill(this.groupFinal266DataSet.Listing);` it's not going to fill at all, but since the query shows that you should be getting back all the records from `Listing`, maybe something's going on between when it calls `Fill` and when the data set is being created?

Sorry for the code by the way, I know that there's a lot of redundancies and probably better optimisation and refactoring could be done, but we're on a short time limit. Thank you for any response. I really have no clue what's going on anymore and my brain is tired.
 
We are making a form where a user can log in and in turn upload listings for cars to sell, however, the data grid view for the listings isn't showing every entry even though it is in the database when it is being first loaded. I also notice too that updating the data grid view and refreshing it doesn't work after uploading an entry to it, only after you close the form and reopen it again does it actually update with the new listing (but only for those of seller ID 1 still).

SQL:
/* the table's properties in the query that was used to create it*/
CREATE TABLE [dbo].[Listing](
    [ListingID] [int] IDENTITY(1,1) NOT NULL,
    [SellerID] [int] NOT NULL,
    [CarVIN] [varchar](50) NOT NULL,
    [Description] [varchar](500) NULL,
    [CreationDateTime] [smalldatetime] NOT NULL,
PRIMARY KEY CLUSTERED
(
    [ListingID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

/**/

View attachment 2828
View attachment 2829
Below is the code that I reference the data grid view in some form.
C#:
private void frmCarsForSale_Load(object sender, EventArgs e)
    {
        // TODO: This line of code loads data into the 'groupFinal266DataSet.Listing' table. You can move, or remove it, as needed.
        this.listingTableAdapter.Fill(this.groupFinal266DataSet.Listing);
    }

// this is for filtering but it shouldn't be impacting how the data grid view is being loaded when the form's just being loaded?
private void viewAllToolStripButton_Click(object sender, EventArgs e)
        {
            /*...*/
           this.groupFinal266DataSet.Listing.Rows.Clear();

            /*...*/
            List<Listing> listings = ListingDB.GetAllListings(Program.sqlConnection);

            string carVINs = String.Join("|", cars.Select(x => x.carVIN).ToArray());
            carVINs = carVINs.Trim(new Char[] { '|' });

            listings.RemoveAll(l => l.carVIN.Any(c => !Regex.IsMatch(l.carVIN, carVINs)));

            foreach (Listing listing in listings)
                this.groupFinal266DataSet.Listing.Rows.Add(listing.listingID, listing.sellerID, listing.carVIN, listing.description, listing.creationDateTime);
        }

// all of the code that references the data grid view itself asides the _Load() function only happens if you click something, so I don't think the code is the problem?
private void btnUpload_Click(object sender, EventArgs e)
        {
            modifyingCarComponents |= ModifyingCarComponents.IS_MODIFYING_LISTING;

            AssignBusinessObjectDataToUpload();

            if (!ValidateBusinessObjectData())
                return;

            if (!listingDB.Upload(listing, Program.sqlConnection)) {
                MessageBox.Show(listingDB.MsgText, listingDB.MsgCaption);
                return;
            }

            // Why don't these work? That's also been an issue?
            listingDataGridView.Update();
            listingDataGridView.Refresh();
        }

        public void AssignBusinessObjectDataToDelete(int rowIndex)
        {
            if (modifyingCarComponents == ModifyingCarComponents.IS_MODIFYING_LISTING)
            {
                listing.listingID = Convert.ToInt32(listingDataGridView.Rows[rowIndex].Cells[0].Value);
                listing.sellerID = Convert.ToInt32(listingDataGridView.Rows[rowIndex].Cells[1].Value);
                listing.carVIN = Convert.ToString(listingDataGridView.Rows[rowIndex].Cells[2].Value);
                listing.description = Convert.ToString(listingDataGridView.Rows[rowIndex].Cells[3].Value);
                listing.creationDateTime = Convert.ToDateTime(listingDataGridView.Rows[rowIndex].Cells[4].Value);
            }
        }

private void listingDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 5)
            {
                modifyingCarComponents = ModifyingCarComponents.IS_MODIFYING_LISTING;

                AssignBusinessObjectDataToDelete(e.RowIndex);

                if (!ValidateBusinessObjectData())
                    return;

                if (!listingDB.Delete(listing, Program.sqlConnection)) {
                    MessageBox.Show(listingDB.MsgText, listingDB.MsgCaption);
                    return;
                }

                listingDataGridView.Rows.RemoveAt(e.RowIndex);
            }
        }
I have linked the entire frmCarsForSale code if needed (the names are the hyperlinks). There is a method that handled filtering but it isn't called on load, but when a button is clicked so that shouldn't be impacting anything. Also the frmUsers as well, which is the parent form that houses the login screen.
View attachment 2832
I have been trying to figure this out and I don't know what to do anymore. From what I notice and have tested, it only shows the listings for SellerID 1 even though no one in the group has specified to filter out the listings by seller ID then populating the data grid view based on that. The query builder shows that `Fill()` for the adapter is working and the data grid view is clearly bound otherwise it wouldn't be showing anything.
View attachment 2830
View attachment 2831

I have tried calling `listingBindingSource.ResetBindings(true);` to update the data grid view inside the cars for sale form automatically after adding a record for instance and that didn't work, tried setting it to false too (I think there's something wrong with how the `listingDataGridView` is loading in general. The data set was set up via the Data Source Configuration Wizard with an already existing connection string. In frmUsers I've tried commenting out the entirety of the pagination code because it does clear the dataset, tested that and that didn't fix the issue. I don't know what the cause is, if I commented out `this.listingTableAdapter.Fill(this.groupFinal266DataSet.Listing);` it's not going to fill at all, but since the query shows that you should be getting back all the records from `Listing`, maybe something's going on between when it calls `Fill` and when the data set is being created?

Sorry for the code by the way, I know that there's a lot of redundancies and probably better optimisation and refactoring could be done, but we're on a short time limit. Thank you for any response. I really have no clue what's going on anymore and my brain is tired.

So it turns out that listingBindingSource had the data source being the Sellers table and the data member a FK_Listing_Sellers, I have no clue why that happened.
 
OK, so here's how it's supposed to work. I suspect how you had things originally, if you'd clicked on the BMW M3, then you'd have seen a single seller Enna Bashkim in your top grid

I've replicated your setup, but simpler:


There are two tables, and they're in a 1:M relationship. 1 seller lists Many cars. A given car has only 1 seller:

1687276849517.png


Note, my relationship is named differently to yours because I rename my rels so they're like "1parent_Mchildren" - SSMS by default calls them "listings_sellers" -> I call that "seller_listings"

I have two sellers:

1687276959389.png


I have 4 cars, helpfully named according to who is selling them to make it easy to visualize something later:

1687277346886.png



I've made a winforms app (make sure your project is framework, not core) with a dataset. Into the dataset I've created tableadapters for Sellers and Listings. A DataRelation has been created mimicking the foreign key setup:

1687277544650.png



When it comes to placing these things on a form to create datagridviews we can use the Data Sources window. I've fully expanded the nodes in it:

1687277647096.png


The listings node appears twice. Once as a head node, once as a child of sellers. These behave differently

If we drag the head node #1 out of data sources and onto the form, we get a datagridview bound to "listingsBindingSource", and that bindingsource is bound to the dataset instance, member "listings"

1687277819129.png


This grid will always show all listings present in dataset1's listings table



-------


If we drag the child listings node onto the form (#2) we end up with a data grid view that is bound to a listingsBindingSource (that i've named 2, because it's the second one I've put on my form) that is bould to the *data relation* exposed by the parent binding source, in this case "SellersBindingSource", which is also on the form:

1687278048569.png



So thats:

Sellers Grid, bound to Sellers BS, which is bound to Sellers table in dataSet1
Listings Grid bound to Listings BS 2, bound to FK_Seller_listings DataRelation exposed by Sellers BS

This is subtly different to the previous, where Listings Grid bound to Listings BS, which is bound to Listings table in dataset1

----

The data relation is a filtering device. When a particular parent seller is chosen in the sellers grid, the Listings grid only shows cars owned by that seller, because the bindingsource performs a filter operation


Look when we run the project:


1687278310266.png


Listings 1 shows all the listings in the DB

Listing2 only shows "JohnCar" make of cars, because these cars are owned by Seller ID 1, which is John. John is selected in the Seller grid (the black > arrow in the row header is on the john row)

Look what happens when I select Tim seller:

1687278402511.png


The listing 2 grid has filtered automatically. Listing 1 grid doesn't filter



So, that's how it's intended to work; displaying related data, selectign a parent, seeing only relevant child records. All the data is in the dataset but it is shown selectively



-----

And finally, why did you only see one seller?

It's perfectly possible to create a DataRelation the other way up, and nominate listings as the parent table and seller as the child. Then if you have all your listings shown in a grid and you select one of them, the seller grid will show only the seller that relates to that listing


I suspect this is what you managed to do, and maybe it's logical in some ways to have the datarelation that way up. Usually though we'd have it so we can select one seller and see many cars for that seller.

If we were doing things the other way round ("i know the listing, i want to know the related seller") we might use a DataColumn in our datatable that had an Expression that retrieved parent data(because any given child has only one parent) so to find the seller of the BMW M3, we wouldn't need a datarelation with "parent listing, child seller", we would have a listing table datacolumn with expression "Parent.SellerName". We might also use a Combobox that was configured so it looks up the Listings.SellerID value in the Sellers.SellerId table column. This can also be used as a device to edit which seller owns a car
 
Other tips for your table adapter life:

Don't make queries that download all of a table's contents out of the DB, unless the table really is a lookup table (like a c# enum)
Have the main query in a tableadapter be one that selects rows by PK ID and call it FillById
Have more queries that download relevant data that you want (like if you can search cars by make, create a SELECT * FROM Listings WHERE CarMake = @carMake query, and call it FillByCarMake)
If you need to know the sellers of those cars, either iterate the results and call Sellers.FillById on each distinct seller ID, or create a query that one-shots it like SELECT * FROM Sellers WHERE Id IN (SELECT SellerID FROM Listings WHERE CarMake = @carMake) and call that with the same criteria as you called ListingsTableAdapter.FillByCarMake

Strive to keep only records you want to use, and for a short amount of time


And, vitally, don't store passwords in plain text. Ever


If you have other questions regarding working with related data using tableadapters etc, fire away
 
Other tips for your table adapter life:

Don't make queries that download all of a table's contents out of the DB, unless the table really is a lookup table (like a c# enum)
Have the main query in a tableadapter be one that selects rows by PK ID and call it FillById
Have more queries that download relevant data that you want (like if you can search cars by make, create a SELECT * FROM Listings WHERE CarMake = @carMake query, and call it FillByCarMake)
If you need to know the sellers of those cars, either iterate the results and call Sellers.FillById on each distinct seller ID, or create a query that one-shots it like SELECT * FROM Sellers WHERE Id IN (SELECT SellerID FROM Listings WHERE CarMake = @carMake) and call that with the same criteria as you called ListingsTableAdapter.FillByCarMake

Strive to keep only records you want to use, and for a short amount of time


And, vitally, don't store passwords in plain text. Ever


If you have other questions regarding working with related data using tableadapters etc, fire away

Thank you. The thing is that this is meant to be a project for our professor to grade, and we have to demo it, should we take out the stored passwords then because I originally had them just to remember what they were (I have a goldfish memory).

Again, I'll try to incorporate what you said if I have time, part of the issue is that there's 3 of us that's supposed to be working on this, but due to circumstances I have been the only one working on it.

I'll keep this knowledge in mind though for the future, I really appreciate your feedback and my brain is fried, I don't entirely remember how I set up the database's relations on top of my head so I'd have to look at that. Finals are today though, so I need to look at that later.
 
Last edited:
Simplest way I find, in SSMS, is to set up your tables then add a database diagram, add the tables to the diagram, then drag the pk column out of Seller and drop it on Listings' SellerId. The create relationship window appears. The only thing I do after that, because SSMS always names relations like FK_Children_Parents, is rename it to FK_Parent_Children (because it makes more sense to me that way)
 
should we take out the stored passwords

Accepted practice is to perform a one way hashing of the password and store the hash. To check the password the user types in future, hash it and compare the two hashes

At its most primitive level, for this purpose, using GetHashCode on the password string would work. It's unreliable across machines but it's quick and demos that you understand the concept of password hashing
 
OK, so here's how it's supposed to work. I suspect how you had things originally, if you'd clicked on the BMW M3, then you'd have seen a single seller Enna Bashkim in your top grid

I've replicated your setup, but simpler:


There are two tables, and they're in a 1:M relationship. 1 seller lists Many cars. A given car has only 1 seller:

View attachment 2833

Note, my relationship is named differently to yours because I rename my rels so they're like "1parent_Mchildren" - SSMS by default calls them "listings_sellers" -> I call that "seller_listings"

I have two sellers:

View attachment 2834

I have 4 cars, helpfully named according to who is selling them to make it easy to visualize something later:

View attachment 2835


I've made a winforms app (make sure your project is framework, not core) with a dataset. Into the dataset I've created tableadapters for Sellers and Listings. A DataRelation has been created mimicking the foreign key setup:

View attachment 2836


When it comes to placing these things on a form to create datagridviews we can use the Data Sources window. I've fully expanded the nodes in it:

View attachment 2837

The listings node appears twice. Once as a head node, once as a child of sellers. These behave differently

If we drag the head node #1 out of data sources and onto the form, we get a datagridview bound to "listingsBindingSource", and that bindingsource is bound to the dataset instance, member "listings"

View attachment 2838

This grid will always show all listings present in dataset1's listings table



-------


If we drag the child listings node onto the form (#2) we end up with a data grid view that is bound to a listingsBindingSource (that i've named 2, because it's the second one I've put on my form) that is bould to the *data relation* exposed by the parent binding source, in this case "SellersBindingSource", which is also on the form:

View attachment 2839


So thats:

Sellers Grid, bound to Sellers BS, which is bound to Sellers table in dataSet1
Listings Grid bound to Listings BS 2, bound to FK_Seller_listings DataRelation exposed by Sellers BS

This is subtly different to the previous, where Listings Grid bound to Listings BS, which is bound to Listings table in dataset1

----

The data relation is a filtering device. When a particular parent seller is chosen in the sellers grid, the Listings grid only shows cars owned by that seller, because the bindingsource performs a filter operation


Look when we run the project:


View attachment 2840

Listings 1 shows all the listings in the DB

Listing2 only shows "JohnCar" make of cars, because these cars are owned by Seller ID 1, which is John. John is selected in the Seller grid (the black > arrow in the row header is on the john row)

Look what happens when I select Tim seller:

View attachment 2841

The listing 2 grid has filtered automatically. Listing 1 grid doesn't filter



So, that's how it's intended to work; displaying related data, selectign a parent, seeing only relevant child records. All the data is in the dataset but it is shown selectively



-----

And finally, why did you only see one seller?

It's perfectly possible to create a DataRelation the other way up, and nominate listings as the parent table and seller as the child. Then if you have all your listings shown in a grid and you select one of them, the seller grid will show only the seller that relates to that listing


I suspect this is what you managed to do, and maybe it's logical in some ways to have the datarelation that way up. Usually though we'd have it so we can select one seller and see many cars for that seller.

If we were doing things the other way round ("i know the listing, i want to know the related seller") we might use a DataColumn in our datatable that had an Expression that retrieved parent data(because any given child has only one parent) so to find the seller of the BMW M3, we wouldn't need a datarelation with "parent listing, child seller", we would have a listing table datacolumn with expression "Parent.SellerName". We might also use a Combobox that was configured so it looks up the Listings.SellerID value in the Sellers.SellerId table column. This can also be used as a device to edit which seller owns a car

Sorry for responding so late, I just wanted to reply in order to not leave this hanging. Thank you for being willing to answer my questions about databases, I'll definitely create a new thread in the future for that.

I think in our case it probably would've been more convenient to have used Expressions.

So from what I remember, the relationships are all one to many: Sellers (Parent) and Listing (Child), Cars (Parent) and Listing (Child), Listing (Parent) and Comments (Child).

(I actually don't know why we had a Buyers table, our group had severe communication issues).

The person who originally did the database had the Listing as the parent and the Cars as the child so I remember I had to flip it back. But now looking back at it, I think it should've been a 1 to 1? Now it doesn't really make sense (at least to me) that 2 listings would be able to have a listing for the same car, so that was my bad.
1689626456170.png


Anyways, thank you so much for this information, I'll definitely store this in mind for the future. And never store passwords in plain text. Have a good rest of your day. (y) Sorry, it's been a month so I don't remember much of what I did, and I was toobusy.
 

Latest posts

Back
Top Bottom