How to handle different event handlers with same parent?

116 views Asked by At

The following program is actually not my project, but it makes it easier to understand what I want later in my real program.

Here a .gif to see the current functionality:

What I want? I want add a label total points. Which includes the points of the checkboxes and textboxes, this should be also calculate in real time. For each group (panel)

Where is the problem? I dont know how to manage this, because I'm working with two different event handlers.

Here is source:

namespace Test1
{
    public partial class Form1 : Form
    {
        int chkBoxX = 10;
        int chkBoxY = 30;
        int txtBoxX = 10;
        int txtBoxY = 50;
        int panelX = 0;
        int panelY = 0;


        public Form1()
        {
            InitializeComponent();

            for (int k = 1; k <= 3; k++)
            {

                Panel panel = new Panel();
                panel.Width = 550;
                panel.Height = 100;
                panel.Location = new Point(panelX, panelY);
                panel.BackColor = Color.RoyalBlue;


                panelY += 110;

                 this.Controls.Add(panel);
                  AddElements(panel);

            }                                          

        }

        void AddElements(Panel panel) {

            Label labelChkBoxPoints = new Label();
            labelChkBoxPoints.Name = "labelChkBoxPoints";
            labelChkBoxPoints.Location = new Point(400, 30);
            labelChkBoxPoints.AutoSize = true;
            labelChkBoxPoints.BackColor = Color.White;
            labelChkBoxPoints.Font = new Font(labelChkBoxPoints.Font.FontFamily, 12, FontStyle.Bold);

            panel.Controls.Add(labelChkBoxPoints);

            Label labelTxtBoxPoints = new Label();
            labelTxtBoxPoints.Name = "labelTxtBoxPoints";
            labelTxtBoxPoints.Location = new Point(400, 50);
            labelTxtBoxPoints.AutoSize = true;
            labelTxtBoxPoints.BackColor = Color.White;
            labelTxtBoxPoints.Font = new Font(labelChkBoxPoints.Font.FontFamily, 12, FontStyle.Bold);

            panel.Controls.Add(labelTxtBoxPoints);

            Label labeltotalPoints = new Label();
            labeltotalPoints.Location = new Point(430, 40);
            labeltotalPoints.Font = new Font(labeltotalPoints.Font.FontFamily, 14, FontStyle.Bold);
      //      labeltotalPoints.Text = "10XXXXXXXXXXXX0";
            labeltotalPoints.AutoSize = true;
            labeltotalPoints.BackColor = Color.White;
                    //   labeltotalPoints.;
            panel.Controls.Add(labeltotalPoints);

            for (int i = 1; i <= 5; i++)
            {
                CheckBox checkBox = new CheckBox();
                checkBox.Name = String.Format("checkBox{0}", i);
                checkBox.Text = "";
                checkBox.Width = 20;
                checkBox.Height = 15;
                checkBox.Location = new Point(chkBoxX, chkBoxY);
                chkBoxX += 26;

                checkBox.CheckedChanged += checkBox_CheckedChanged;

                panel.Controls.Add(checkBox);

            }

            for (int j = 1; j <= 5; j++)
            {
                TextBox tb = new TextBox();
                tb.Name = String.Format("textBox{0}", j);
                tb.Width = 60;
                tb.Location = new Point(txtBoxX, txtBoxY);
                txtBoxX += 80;
                tb.TextChanged += txtBox_CheckedChanged;
                panel.Controls.Add(tb);
            }

            chkBoxX -= (5 * 26);
            txtBoxX -= (5 * 80);

        }

        private void txtBox_CheckedChanged(object sender, EventArgs e)
        {

            int total = 0; int points = 0; 

            foreach (object tb in ((TextBox)sender).Parent.Controls)
            {

                if (tb is TextBox)
                {
                    if (!string.IsNullOrEmpty(((TextBox)tb).Text))
                    {
                        if (!int.TryParse(((TextBox)tb).Text, out points))
                        {
                            ((TextBox)tb).Text = "";
                        }

                            total += points;
                    }
                }
            }

            (((TextBox)sender).Parent.Controls["labelTxtBoxPoints"]).Text = Convert.ToString(total);



        }

        private void checkBox_CheckedChanged(object sender, EventArgs e)
        {

            int counter = 0;

            foreach (object cb in ((CheckBox)sender).Parent.Controls)
            {
                if (cb is CheckBox)
                {
                    if (((CheckBox)cb).Checked)
                    {
                        counter++;
                    }
                }

            }

            (((CheckBox)sender).Parent.Controls["labelChkBoxPoints"]).Text = Convert.ToString(counter);

        }
    }
}
3

There are 3 answers

7
George Vovos On BEST ANSWER

Why don't you just calculate the sum of your "point" labels at the end of both events?
It is nice and simple(You could do it more efficiently but i don't this there is a reason... )
Just call TotalPoints() at the end of checkBox_CheckedChanged,txtBox_CheckedChanged

int TotalPoints()
{
        int total = (from i in GetControlsWithPoint(this)
                     where i.Text != null && i.Text != string.Empty
                     select Convert.ToInt32(i.Text)).Sum();
       // MessageBox.Show("total" + total);
        return total;
}

List<Control> GetControlsWithPoint(Control parent)
{
        List<Control> results = new List<Control>();
        if (parent == null)
            return results;

        foreach (Control item in parent.Controls)
        {
            if (item.Name == "labelChkBoxPoints" || item.Name == "labelTxtBoxPoints")
                results.Add(item);
            results.AddRange(GetControlsWithPoint(item));
        }

        return results;

 }

Update

If you only want the sum per panel all you have to do is change the TotalPoint method to search only inside your panel

 int TotalPoints(Panel p)
 {
        int total = (from i in GetControlsWithPoint(p) where i.Text != null && i.Text != string.Empty select Convert.ToInt32(i.Text)).Sum();
       // MessageBox.Show("total" + total);
        return total;
  }

And at the end of your events

Panel p = (((Control)sender).Parent) as Panel;
TotalPoints(p);
1
Enigmativity On

I would use Microsoft's Reactive Framework for this (NuGet "Rx-Main" & "Rx-WinForms"/"Rx-WPF").

It let's me create a couple of functions to do what you want in a fairly compact kind of way.

For the check boxes you need this function:

Func<IEnumerable<CheckBox>, IObservable<int>> makeCheckBoxCounter = cbs =>
    cbs
        .Select(cb => Observable.FromEventPattern(h => cb.Click += h, h => cb.Click -= h))
        .Merge()
        .Select(ep => cbs.Where(cb2 => cb2.Checked == true).Count());

This lets me pass a list of check boxes to the function and it returns an IObservable<int> that gives me an observable sequence the sum of checked check boxes.

You use it like this:

IDisposable checkBoxesSubscription =
    makeCheckBoxCounter(new[] { checkBox1, checkBox2, checkBox3, checkBox4, checkBox5, })
        .Subscribe(x => label1.Text = x.ToString());

I've tested this code and it works perfectly.

You can shut it down by calling the following:

checkBoxesSubscription.Dispose();

The text box version is slightly more complicated owing to the need to parse the text. It looks like this:

Func<IEnumerable<TextBox>, IObservable<int>> makeTextBoxCounter = tbs =>
    tbs
        .Select(tb =>
            Observable
                .FromEventPattern(h => tb.TextChanged += h, h => tb.TextChanged -= h))
        .Merge()
        .Select(ep =>
        {
            var total = 0;
            foreach (var tb2 in tbs)
            {
                var value = 0;
                if (int.TryParse(tb2.Text, out value))
                {
                    total += value;
                }
            }
            return total;
        });

And the subscription is this:

var textBoxesSubscription =
    makeTextBoxCounter(new[] { textBox1, textBox2, textBox3, textBox4, textBox5, })
        .Subscribe(x => label2.Text = x.ToString());

I've tested both of these.

5
AudioBubble On

I would use a TextBoxand a static int totalTotalPoints =0;

Add a the TextBlock with each group of labels:

 void AddElements(Panel panel) {
    TextBlock textblock = new TextBlock();
    TextBlock.Name = "TBlock";

Then in each of your methods where there are changes to the points, update the TBlock.text();

 private void txtBox_CheckedChanged(object sender, EventArgs e)
 { ....
    total += points;
    // Implement a method to determine if points increase or decrease
    // of points change.
    // So if ( points -ve or if points +ve
    totalTotalPoints += points or -=points change;
     (((TextBlock)sender).Parent.Controls["TBlock"]).Text = Convert.ToString(totalTotalPoints);
 }

 private void checkBox_CheckedChanged(object sender, EventArgs e)
 { ...
    counter++;

    totalTotalPoints++;
    (((CheckBox)sender).Parent.Controls["TBlock"]).Text = Convert.ToString( totalTotalPoints);

 }

Ok. This should work for you.

A side note:

When creating your panels allocate them names dynamically:

        for (int k = 1; k <= 3; k++)
        {
            Panel panel = new Panel();
            panel.Name = k.ToString();
            ...
        }