Hi all!

I have a program that runs as a large form (Form1) with smaller forms that open inside of it (PeopleBox). It's for friends/contacts - each person gets their own row in the database (PeopleDB). At runtime I'd like it to open a PeopleBox for each person already existing inside the DB.

I've managed to get it to open a PeopleBox for each row in the DB, but each box is showing the information for the first row only (rather than cycling through the rows). I'm sure I need a foreach statement, but I'm unsure how to phrase it. Any help would be greatly appreciated!

Code below:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlServerCe;


namespace MaryAnne
{
    public partial class Form1 : Form
    {

        public Form1() 
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
            //  DB connection
            string fileName = "PeopleDB.sdf";
            string connectionString = string.Format("DataSource=\"{0}\";", fileName);

            //  SQL Command
            string sql = "select * from PeopleTable";

            SqlCeConnection cn = null;
            try
            {
                cn = new SqlCeConnection(connectionString);

                SqlCeCommand cmd = new SqlCeCommand(sql, cn);

                //  Checking to make sure no concurrent connections exist
                if (cn.State == ConnectionState.Open)  
                    cn.Close();

                //  Opening the connection
                cn.Open();

                SqlCeDataReader scdr = cmd.ExecuteReader();

                while (scdr.Read())
                {
                    //  Opening a PeopleBox for each row in the DB
                    PeopleBox childForm = new PeopleBox();
                    childForm.MdiParent = this;
                    childForm.Show();
                }
                scdr.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                if (cn != null)
                    cn.Close();
            }
                        
        }

    }
}

The relevant bit of code is:

while (scdr.Read())
                {
                    //  Opening a PeopleBox for each row in the DB
                    PeopleBox childForm = new PeopleBox();
                    childForm.MdiParent = this;
                    childForm.Show();
                }

I know that the while statement is where I'm going wrong. I'm just not having any luck with foreach statements - everything I've tried has thrown up a series of errors.

Thanks for reading!
-MaryAnne

Dani AI

Generated

As showed, the loop was creating a child form for each row but never wiring the row values into each instance. was right that the reader contains the data, and 's constructor/property ideas are fine — a slightly cleaner pattern is to map each row into a small, strongly‑typed Person object (a DTO) and give that DTO to PeopleBox through a single loader method. That keeps parsing, conversion and null checks in one place and keeps the form code focused on presentation.

Example of the small DTO and a loader method on PeopleBox:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string Email { get; set; }
}

public void LoadPerson(Person p)
{
    lblName.Text = (p.FirstName ?? "").Trim();
    lblEmail.Text = (p.Email ?? "");
    // populate other controls here
}

Fill the DTOs from the database (DataAdapter/DataTable or the DataReader) and call the loader before showing the form. Using a DataTable makes a simple foreach over rows possible; using the reader is fine too — just read fields into the DTO instead of passing raw IDataRecord objects directly into UI controls.

A few practical tips:

  • Always use explicit column lists (avoid SELECT *) and convert/check DBNull (e.g., row.IsNull("Email") or reader.IsDBNull).
  • Dispose connections/commands/readers with using blocks so resources are closed even on exceptions.
  • For large result sets, avoid creating dozens of MDI children at once — consider a master list (ListView/DataGrid) and open a single detail PeopleBox on demand or lazy-load details to keep the UI responsive.
  • If using a DataReader in a tight loop, get ordinals once (GetOrdinal) and use typed getters (GetInt32, GetString) for a small performance win.

This approach ties back to the suggestions from and but separates concerns and makes the per-row wiring explicit and easy to test.

Recommended Answers

All 6 Replies

You can't use foreach in this situation (You can only use foreach on object collections that implement IEnumerable).

You are reading data into the scdr object, but you never use it. Everything about the row is there, and that's what you need to be looking at.

Hi Momerath!

How would I go about pulling the row information from scdr and passing it to each instance of PeopleBox? With a DataRow construct?

I'm not asking you to write it up for me, but if you could point me to a tutorial that'll explain what needs to be done in newbie terms, I'd be eternally greatful!

Thanks!
-MaryAnne

You can't use foreach in this situation (You can only use foreach on object collections that implement IEnumerable).

You are reading data into the scdr object, but you never use it. Everything about the row is there, and that's what you need to be looking at.

Hi,

scdr when it is in the while loop contains the data you need. Just access it like:
scdr("ID") (although you might want to test it for null if it is nullable).

I suggest you then pass the information via a custom constuctor to the PeopleBox. In this example the constructor is expecting an int but it could be a data object.

Inside the loop the code could be:

PeopleBox childForm = new PeopleBox(scdr("ID"));

Then the new PeopleBox might look utilise something like:

    private readonly int _id;
    public Form2(int ID)
    {
        // New constructor
        InitializeComponent();
        _id = ID;
    }
    private void Form2_Load(object sender, EventArgs e)
    {
        // Do what you need with the ID
        this.Text = _id.ToString();
    }

Hope this helps.

Nicholas

Hi Nicholas!

So let me make sure I'm understanding this: I don't just need to create the relevant code in Form1, but I also need to write some code into PeopleBox that will listen for the information being passed from Form1?

-MaryAnne

Hi Nicholas!

So let me make sure I'm understanding this: I don't just need to create the relevant code in Form1, but I also need to write some code into PeopleBox that will listen for the information being passed from Form1?

-MaryAnne

Yes, in some fashion or form PeopleBox has to do something with what it gets given.

Using the constructor keeps everything in one place but you could use properties:

private int _id;
        public int UserID
        {
            get { return _id; }
            set { _id = value; this.Text = _id.ToString();}
        }

That gets accessed like:

PeopleBox childForm = new PeopleBox();
childForm.MdiParent = this;
childForm.UserID = scdr("ID");
childForm.Show();
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.