Home > Back-end >  Adding a new button to form2 by clicking a button on form1
Adding a new button to form2 by clicking a button on form1

Time:07-05

I'm trying to write a simple application that opens two forms and displays a "newButton" on Form2 in two different places depending on the number of clicks of "button1" in Form1.

int ButtonClick1 = 0;

private void button1_Click(object sender, EventArgs e)
{      
      ButtonClick1;
    Button newButton1 = new Button();
    newButton1.Name = "NewButton1";
    newButton1.Text = "1";
    Form2 frm2 = new Form2();

    switch (ButtonClick1)
    {
              
        case 1:
            button1.BackColor = Color.Red;    
            frm2.flowLayoutPanel1.Controls.Add(newButton1);
            frm2.Show();
            break;

        case 2:
            button1.BackColor = Color.Green;
            frm2.flowLayoutPanel1.Controls.Remove(newButton1);
            frm2.flowLayoutPanel2.Controls.Add(newButton1);
            frm2.Show();
            break;

        case 3:
            button1.BackColor = Color.Gainsboro;
            frm2.flowLayoutPanel2.Controls.Remove(newButton1);
            frm2.Show();
            ButtonClick1 = 0;
            break;
    }
}

Every time I click on "button1" it's creating a new instance of Form2 because of frm2.Show() but only with that line, I can see that it's actually doing something. I don't know how to change it so that it would just update Form2 without opening new instances.

CodePudding user response:

Here is my take on what you are trying to achieve:

Form 1:

public partial class testform1 : Form
{
    testform2 form2;
    int cnt = 0;
    public testform1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
          cnt;
        if (form2 == null)
        {
            form2 = new testform2();
        }
        form2.Show();
        Button btn = new Button()
        {
            Text = "1",
            Name = "NewButton1",
        };
        form2.AddButton(btn, (Button)sender, ref cnt);
    }
}

Form 2:

public partial class testform2 : Form
{
    public testform2()
    {
        InitializeComponent();
    }
    public void AddButton(Button NewButton, Button ParentButton, ref int counter)
    {
        switch (counter)
        {
            case 1:
                flowLayoutPanel1.Controls.Add(NewButton);
                ParentButton.BackColor = Color.Green;
                break;
            case 2:
                flowLayoutPanel1.Controls.Clear();
                flowLayoutPanel2.Controls.Add(NewButton);
                ParentButton.BackColor = Color.Red;
                break;
            case 3:
                flowLayoutPanel2.Controls.Clear();
                ParentButton.BackColor = Color.Gainsboro;
                counter = 0;
                break;
        }
    }
}

You can pass the counter as reference in the AddButton method to update the value once it reaches 3.

CodePudding user response:

You have identified two things that need to happen to achieve your desired outcome:

  1. Manage the visibility of Form 2
  2. Have Form 2 respond to property changes in Form 1.

To keep the clutter to a minimum, I'll provide a link where you can browse the example code. This answer will focus on how to achieve the outcome in the FlowLayoutPanel controls in Form2 when the button is clicked in Form1.

shows the three count states


Property Change for ClickCount

One clean approach is to make a bindable ClickCount property in Form1 and a Button to increment it. Whenever that property changes, it fires a PropertyChanged event:

public partial class Form1 : Form , INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    int _ClickCount = 0;
    public int ClickCount
    {
        get => _ClickCount;
        set
        {
            // Detect changes and notify when that happens
            if(_ClickCount != value)
            {
                _ClickCount = value == 3 ? 0 : value;
                PropertyChanged?.Invoke(
                    this,
                    new ClickCountPropertyChangedEventArgs(
                        nameof(ClickCount), 
                        ClickCount));
            }
        }
    }
}

The constructor for Form2 will bind this event source to a PropertyChanged handler:

public Form2(INotifyPropertyChanged binding)
{
    InitializeComponent();
    binding.PropertyChanged  = Binding_PropertyChanged;
}

One really nice thing about doing it this way is that Form1 doesn't need to know anything about Form2 (like whether it does or doesn't have FlowLayoutPanel controls or what Form2 might plan to do with the information) and also would be unaffected by future changes to those things.


Handling the ClickCount changed event

When the PropertyChanged handler in Form2 detects that the count has changed, it calls a specialized handler:

private void onClickCountChanged(int count)
{
    labelClickCount.Text = $"Click Count = {count}";
    buttonShowOrHideA.Visible = count == 1;
    buttonShowOrHideB.Visible = count == 2;
}

You're probably wondering what happened to the "add" and "remove" statements for the buttons? Well here's the thing: why not let FlowLayoutPanel do its job of rearranging the layout when buttons are shown or hidden? This way, all you have to do is place the buttons in the designer and you can subscribe to their click events just one time instead of having to do that every time the button is added or removed. It takes less overhead and there are fewer things to go wrong. Here are the handlers for the Red and Green buttons:

private void buttonShowOrHideA_Click(object sender, System.EventArgs e) =>
    MessageBox.Show("Red one clicked");

private void buttonShowOrHideB_Click(object sender, System.EventArgs e) =>
    MessageBox.Show("Green one clicked");

enter image description here


Showing and hiding Form2

Being able to show and hide Form2 repeatedly and keep it out in front of Form1 means making sure the Form2 gets disposed properly (in the end) but not prematurely and I would encourage you to browse the full code for this. The main highlight is that when the ShowForm checkbox is true for the first time, it will instantiate Form2 by passing in a reference to itself.

if (checkBoxShowForm2.Checked)
{
    if (_form2 == null)
    {
        _form2 = new Form2(binding: this);  
    }
    _form2.Location = new Point(Location.X   Width   10, Location.Y);
    // Passing 'this' ensures Form 2 will stay in front of Form 1
    _form2.Show(this); 
}
else _form2.Hide();
  • Related