2

I am trying to get access to Form1’s public method on another form Form2 as below. I have a textbox6 control on form1 and there is public method to bind it. But I want to bind it by form2 as below.

Form1

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 f2 = new Form2();
        f2.Show();
    }

    public void amount_sum()
    {
        string connstr = " server=.;initial catalog=maa;uid=mah;pwd=mah";
        SqlConnection con = new SqlConnection(connstr);
        con.Open();
        string sql = " select sum(amount)as amount from method";
        SqlDataAdapter dap = new SqlDataAdapter(sql, con);
        DataSet ds = new DataSet();
        dap.Fill(ds);
        for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
        {
            textBox6.Text = Convert.ToString(ds.Tables[0].Rows[i]["amount"]);
        }    
    }
}

Form2

private void button1_Click(object sender, EventArgs e)
{
    Form1 f1 = new Form1();
    f1.amount_sum();
    this.Close();
}

The above method-call is wrong. Please suggest how to correct it.

I want to bind Form1’s textBox6 control from Form2's Button_Click event-handler by calling the public method, and when Form2 is closed, then Form1’s textbox6 should be bound. Is that possible by calling the public method from Form2?

wattostudios
  • 8,666
  • 13
  • 43
  • 57
mahesh
  • 1,370
  • 9
  • 36
  • 61

4 Answers4

11

In Form2 you have

Form1 f1 = new Form1();
f1.amount_sum();

This seems to be a common mistake to create a new Form1 when you want to pass the answer back between forms. The new keyword does just that, it creates a new Form1, calls the method, does not show the form, the original instance of Form1 is unaffected.

I'll show some steps how to fix this.

1 - Pass Form1 to Form2

The first thing you can do is to simply pass the existing Form1 to Form2 so that Form2 know which Form1 it should update.
public class Form2
{
    private readonly Form1 _form1;
    public Form2(Form1 form1)
    {
        _form1 = form1;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        _form1.amount_sum(); // now this updates the existing form1 instance
        this.Close();
    }
}

In Form1

private void button1_Click(object sender, EventArgs e)
{
    Form2 f2 = new Form2(this); // pass this form1 instance to form2
    f2.Show();
}

One issue with this is that is creates a strong coupling between Form1 and Form2. If you change something in Form1 it is easy to break Form2 and the other way around.

2 - Pass a delegate

Instead of passing the whole Form1 to Form2 we can simple pass a delegate to an update method that Form2 can run. This creates less coupling between Form1 and Form2, if you call Form2 from Form3 you can pass in the update method of Form3 instead and Form3 will be updated. The same Form2 can be reused without modification.
public class Form2
{
    private readonly Action _ammountUpdater;
    public Form2(Action ammountUpdater)
    {
        _ammountUpdater = ammountUpdater;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        _ammountUpdater(); // now this updates the existing form1 instance
        this.Close();
    }
}

In Form1

private void button1_Click(object sender, EventArgs e)
{
    Form2 f2 = new Form2(this.amount_sum); // pass the update method to form2
    f2.Show();
}

Now you can change amount_sum to private since it is now really an internal affair of Form1.

Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
  • @Albin Do you know "return theAnswer" mention on "Marc's" answer. can you explain it please?. – mahesh Nov 14 '10 at 09:59
  • @Albin, Your step 2, gives compile Error Like "Using the generic type 'System.Action requires'1' type arguments" possibility due to i miss something like different namespace. – mahesh Nov 14 '10 at 11:40
  • @mahesh, `Action` with no type-parameters was introduced in .NET 3.5, if you use .NET 2.0 you can declare it your self `public delegate void Action();` – Albin Sunnanbo Nov 14 '10 at 14:05
  • @Albin, Your idea is working now but little confusion against your answer in step2. You have mentioned on it that this code is less coupling means there is a chances of coupling between forms. right? – mahesh Nov 14 '10 at 14:18
  • ... if so than how can you satisfy with it?. – mahesh Nov 14 '10 at 14:19
  • @mahesh, "less coupling" means that the "contract" between form1 and form2 is only about the "shape"(Action-delegate) of the call, not that is has to be shaped exactly like Form1. The benefit is that code changes later on in Form1 are less likely to require corresponding rewrite of Form1, Form3, etc. This is important once your project starts to grow, with 2 forms it does not really matter. – Albin Sunnanbo Nov 14 '10 at 14:28
  • @mahesh, an alternative to passing a delegate would be to use either events of Form2 that Form1 listens to as in acoolaum's answer, or create an interface that Form1 implements. – Albin Sunnanbo Nov 14 '10 at 14:30
  • @Albin, there is one more confusion against using of your step2 code. – mahesh Nov 15 '10 at 08:23
  • ... if form3 wants to communicate to form2 but there is no method to update in form3 than I think acoolaum's way is better to use. or anyother way is there with concern to your step to code. – mahesh Nov 15 '10 at 08:33
3

I would make your amount_sum method a utility method that returns the value, for example:

public static decimal GetTotalFoobarAmount() 
{ // decimal is just a guess here
    // omitted: sql code
    return theAnswer;
}

Then both blocks of code can call this method, for example.

textBox6.Text = YourClass.GetTotalFoobarAmount().ToString();

As a side-benefit, it also reduces coupling between your UI and DB, which is generally considered a good thing. If the query requires values currently from the form, make those parameters in the method. Note also that for returning a single value you might want to look at ExecuteScalar; it isn't going to make a massive difference, but it is more direct than populating a DataTable and looping over the single row.

Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • @Marc, I am using VS-2005 and Note that the "retrun theAnswer" not supported on it. it's gives compiling Error like "An object of type convertible to Decimal required" against utilize "return" instead. – mahesh Nov 14 '10 at 09:01
  • @Marc What is "theAnswer" please explain it. – mahesh Nov 14 '10 at 09:08
  • @Marc, I guess that "theAnswer" is the parameter right? if yes than how i can apply it with "textbox6" for return the value. i am confuse please explain it. – mahesh Nov 14 '10 at 10:52
  • @Marc, Sorry your answer is not acceptable even though it is good by theory but in practically it is incomplete. – mahesh Nov 14 '10 at 11:15
  • @mahesh, `theAnswer` would be something of type decimal, either as a static field in the `YourClass` or a variable in the `GetTotalFoobarAmount` method. – Albin Sunnanbo Nov 14 '10 at 14:14
  • @mahesh - `theAnswer` would be the value of the thing you are executing in TSQL. That is merely illustrative code. And yes: it should work fine in VS2005. It is deliberately incomplete because there is no point duplicate unnecessary code from your question, when that isn't what the question is about, *and* I don't agree with the code *posted* in the question. I can't give an **exact** answer (in the way I would like) because I don't know your schema and what `amount` is declare as. – Marc Gravell Nov 15 '10 at 03:27
  • @Marc, If you want to help new comer than please complete your code. if there is need further modification in my question code than tell me.I will do it for knowledge. after all our aim should be to share knowledge. – mahesh Nov 15 '10 at 06:26
  • @Marc, The amount field in database is declared as decimal your guess is right. – mahesh Nov 15 '10 at 06:31
2

Also you may use events:

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 f2 = new Form2();
        f2.ButtonClickAction += f2_ButtonClickAction;
        f2.Show();
    }

    void f2_ButtonClickAction()
    {
        amount_sum();
    }

Form2:

    public Form2()
    {
        InitializeComponent();
    }

    public event Action ButtonClickAction;

    private void button1_Click(object sender, EventArgs e)
    {
        Action a = ButtonClickAction;
        if (a != null)
            a();
        this.Close();
    }
acoolaum
  • 2,132
  • 2
  • 15
  • 24
1

Marc Gravell's answer is probably sufficient, but in general, if you want to call an instance method on a specific instance of a class, you can't just create a new one and call it on that instance. For your example, you need to call the method on the Form1 instance that already exists. The best way to do that is to have a member variable on the Form2 class of type Form1. You can define a constructor or a property on Form2 which takes a value of type Form1 and set the member variable in it. When Form1 creates an instance of Form2, it can call the constructor and pass in this or set the property to this. Then when the button is clicked on Form2, instead of creating a new instance of Form1, it can call the amount_sum() method on the Form1 instance that is already stored.

Mike Dour
  • 3,616
  • 2
  • 22
  • 24