Description

This code sample demonstrates how to dynamically create buttons that are created from information from a SQL-Server table that when the button is clicked returns the primary key to the underlying data in the database which permits retrieval of fields in the same table and also data from child tables. How this differs from the majority of samples on the web is that those samples create click events on the fly in a form with no good method to use information for a button.

The following screenshot shows a window’s form after loading a primary key and customer name from a customer table from a modified version of Microsoft’s North Wind database.

On the left the buttons are placed into a panel, to the right of the panel is an invisible DataGridView. Beneath the those two is a Checkbox which is used to toggle between two different methods for displaying data. The first method displays a modal child form that data binds information returned from calling a method from a delegate to return several fields for that record and upon return provides you with any changed data. The changed data could be pushed back to the database table but that is outside the scope of the code sample. Note the primary key is displayed in the modal form's caption so if you care to can validate the right record was returned although that should be apparent from the call to get the data.


Here is the code in the main form that responses from a delegate in the class which created our buttons.

 

C#
Edit|Remove
void button_Clicker(object sender, IdentifierButtonEventArgs e) 
{ 
    dataGridView1.Visible = false; 
    dataGridView1.DataSource = null; 
 
    var Customer = dataOps.GetCustomer(e.Identifier); 
    if (checkBox1.Checked) 
    { 
        customerEditor f = new customerEditor(Customer); 
        try 
        { 
            if (f.ShowDialog() == DialogResult.OK) 
            { 
                MessageBox.Show(f.Customer.ToString()); 
            } 
        } 
        finally 
        { 
            f.Dispose(); 
        } 
    } 
    else 
    { 
        //MessageBox.Show(Customer + $"\nButton Name: {((Button)sender).Name}"); 
        dataGridView1.DataSource = dataOps.GetOrders(e.Identifier); 
        dataGridView1.Visible = true; 
    } 
             
}
The following method is from the class responsible for generating the buttons that works in tangent with the event above. The important part here is the subcription to the newly created button click event.

C#
Edit|Remove
public void CreateButtonsFromTable(DataTable sender, string Identifier, string fieldName) 
{ 
 
    Buttons = new List<Button>(); 
 
    this.ButtonCount = sender.Rows.Count - 1; 
 
    sender.AsEnumerable().Select((row) => new 
        { 
            ID = Convert.ToInt32(row[Identifier]), 
            Name = row[fieldName].ToString() 
        }) 
        .ToList().ForEach((item) =>  
            { 
                Button b = new Button 
                { 
                    Name = string.Concat(ButtonBaseName, item.Name), 
                    Text = item.Name, 
                    Tag = item.ID, 
                    Size = ButtonSize, 
                    Location = new Point(25this.Base), 
                    Parent = ParentControl, 
                    Visible = true 
                }; 
 
        b.Click += (object s, EventArgs e) => { 
            ClickedHandler(s, new IdentifierButtonEventArgs(Convert.ToInt32(((Button)s).Tag))); 
        }; 
 
        this.ParentControl.Controls.Add(b); 
        Buttons.Add(b); 
        Base += BaseAddition; 
    }); 
}
Here is our class that allows us to pass the primary key back to the main form.

C#
Edit|Remove
using System; 
 
namespace CreateDynamicTextBoxes_CS 
{ 
    public class IdentifierButtonEventArgs : EventArgs 
    { 
        public IdentifierButtonEventArgs(int id) 
        { 
            Identifier = id; 
        } 
 
        public int Identifier { get; set; } 
    } 
} 
The following class is used to strong type data passed to the modal form.

C#
Edit|Remove
public class Customer 
{ 
    public int CustomerIdentifier { get; set; } 
    public string CompanyName { get; set; } 
    public string ContactName { get; set; } 
    public string ContactTitle { get; set; } 
    public override string ToString() 
    { 
        return $"{CompanyName}, {ContactName}, {ContactTitle}"; 
    } 
}
In the following screenshot of the child form both buttons have their DialogResult set so there is no need to close the form via the form's Close method.


 
 
The alternate demo to the above dialog is to show data from a child table which is done by toggling the check box on the main form.

 
With that let's look at the call to our class which creates the buttons.
C#
Edit|Remove
CreateButtons MakeButtons = new CreateButtons() 
{ 
    ParentControl = panel2, 
    Base = 10, 
    ButtonBaseName = "btn", 
    BaseAddition = 60, 
    ButtonSize = new Size(10050};
First we tell the class where to place the buttons via ParentControl,  Base is used to space out the buttons, ButtonBaseName is really the prefix to the button name. BaseAddition works in tangent with Base for placing buttons stacked nicely and ButtonSize indicate the button width and height.

Now let's say you have a custom button, my guess it is created off of ButtonBase the code can be modified for that control also or create a new class for that type of button.

IMPORTANT
Before you can use the code you need to run the two scripts provided to create the database, tables and stored procedures.

CreateDatabaseScript.sql and GenerateTablesProceduressAndDataScript.sql may be executed right in the script file or in SSSM.