Click here to Skip to main content
15,888,401 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
See more:
I am trying to add and delete series to a chart initialized on the Main form from a second form.

The code below compiles, but series data is added to another chart instance, not the one on the main form. How do I reference the instance of the chart on the Main form to control it, and also control the chart from other forms?

What I have tried:

C#
// Method in Main form to add/delete series data

 public static void PLOT_PAYOFF(List<string> lstGREEK, List<string> lstSTRATEGY)         // Static method to allow access from second form
        {
            nmCHART.clsCHART oCHART = new nmCHART.clsCHART();

            Chart chrtPAYOFF = new Chart();     // creating a new instance put series data on this new chart, not the chart on Main
            chrtPAYOFF.Series.Clear();          // placing 'Main' before chrtPAYOFF gives a compile error   Main.chrtPAYOFF.Series.Clear(); 

            int S, G;
            string GREEK, STRATEGY, SeriesName;
            double[] Y = new double[20];

            // Get X Axis data
            double[] X = new double[20];
            X = oCHART.GET_X_SERIES();

            for (G = 0; G <= lstGREEK.Count - 1; G++)
            {
                for (S = 0; S <= lstSTRATEGY.Count - 1; S++)
                {
                    GREEK = lstGREEK[G];
                    STRATEGY = lstSTRATEGY[S];
                    SeriesName = STRATEGY + "-" + GREEK;
                    // Get Y Axis data
                    Y = oCHART.GET_Y_SERIES(GREEK, STRATEGY);
                    chrtPAYOFF.Series.Add(SeriesName);

                    chrtPAYOFF.Series[SeriesName].ChartType = SeriesChartType.Line;
                    chrtPAYOFF.Series[SeriesName].Points.DataBindXY(X, Y);
                }
            }

// Method on second form to call method on Main form

 private  void cmdPROCESS_Click(object sender, EventArgs e)
        {
            List<string> lstSTRATEGY = new List<string>();
            List<string> lstGREEK = new List<string>();


            foreach (object itemChecked in chkbxGREEKS.CheckedItems)
            {
                lstGREEK.Add(itemChecked.ToString());
            }

            foreach (object itemChecked in chkbxSTRATEGIES.CheckedItems)
            {
                lstSTRATEGY.Add(itemChecked.ToString());
            }

            Main.PLOT_PAYOFF(lstGREEK, lstSTRATEGY);

        }
Posted
Updated 3-Apr-16 14:36pm
v2
Comments
Trader999 3-Apr-16 18:43pm    
CHill60, thanks for formatting the code, how do you do it?

1 solution

First, move the creation of instances of the charts
C#
nmCHART.clsCHART oCHART = new nmCHART.clsCHART();
Chart chrtPAYOFF = new Chart();
outside of the static method, and put in Form scope: right now you are creating new instances of those chart objects every time you call the static method on the Main Form: those new instances are never added to the ControlCollection of the Form, and they are disposed of (you have no reference to them) outside the scope of that method in the Main form.

Second, think about what you really want to do here: imho, you want to be able to call the update method from the second form. Why not consider injecting a method body into the second form that lets you call the method on the main form directly, without this business of using 'static (which is, IMHO, a code smell).

Here's a bare-bones example for a strategy that minimizes the dependency of the two Forms (I use this type of example in teaching private students):

The Main Form:
C#
namespace YourNameSpace
{
    public partial class MainForm : Form
    {
        private Form2 f2;
        
        public MainForm()
        {
            InitializeComponent();
        
            f2 = new Form2();
            f2.SendData = DataReceiver;
            f2.Show();
        }
        
        private void DataReceiver(List<string> list1, List<string> list2)
        {
            // put a break-point here, examine the parameters    
        }
    }
}
The second Form:
C#
namespace YourNameSpace
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        public Action<List<string>, List<string>> SendData { set; get; }

        public List<string> List1 = new List<string>
        {
            "one",
            "two"
        };

        public List<string> List2 = new List<string>
        {
            "three",
            "four"
        };

        private void button1_Click(object sender, EventArgs e)
        {
            // assume both Lists have to be non-null ?
            if (SendData != null && List1 != null && List2 != null) 
SendData(List1, List2);
        }
    }
}


Notes:

1. the Action<List<string>,List<string>> used here is really a short-cut for creating a Delegate Type and declaring an instance of it. An Action is a full-fledged Delegate with an Invocation List, and multi-casting to all subscribers.

2. the Main form injects a reference to its DataReceiver method into the second Form's 'SendData Action public Property by direct assignment of a reference to a private method on the Main form ... note we didn't have to use += . We could also use a lambda expression.

Using an injected method like this is an example of creating a "call-back."

The goal of all this is to expose the minimum internal state and objects of Form1 to Form2, and Form2 to Form1: that's encapsulation, and loose-coupling.
 
Share this answer
 
v4
Comments
Trader999 3-Apr-16 22:33pm    
Thanks Bill, I agree with your method. I've tried adding you code, but I get some compile errors, so I'm probably making the wrong changes.

On the main form:

public partial class Main : Form
{

nmCHART.clsCHART oCHART = new nmCHART.clsCHART();
Chart chrtPAYOFF = new Chart(); //compile error

I get an error saying the type 'Main' already has a definition for chrtPAYOFF. The chart is already embedded in the form using the toolbox.

Is the code:

public Action<list><string>,List<string>> UpdateChart { set; get; }

correct? I get a compile error saying invalid token.

How can i format the code in this comment? I'll do some reading up on using delegates to pass method references.
BillWoodruff 4-Apr-16 2:00am    
"I get an error saying the type 'Main' already has a definition for chrtPAYOFF. The chart is already embedded in the form using the toolbox." If the control is placed on the Form at design-time, then you have an automatic reference to the instance of the Control; you should not be using 'new at run-time.

Action<List<string>,List<string>> is a Delegate: it is equivalent to a declaring a Delegate Type that takes two List<string> parameters and returns nothing (i.e., 'void).

When you declare a Delegate you create a new Type: you then declare an instance of the Delegate which you can use. Using 'Action combines both those steps into one; the compiler creates the Delegate Type and then returns an instance of it.

You can use standard HTML tags in these messages by manually typing in & lt ; and & gt ; to get the left and right angle brackets ... combine them into one three letter string
Trader999 4-Apr-16 3:33am    
Thanks Bill, I'll work through your solution and will post a comment if I get stuck.
Trader999 4-Apr-16 4:43am    
Thanks Bill,it works well. Really appreciate the help, I was going in circles.

I dropped the TransferData != null which was causing an error.
BillWoodruff 4-Apr-16 6:12am    
My error: somehow, in pasting and editing the text from Visual Studio into my response here, in Form2 I used TransferData != null. The correct code is: 'SendData != null.

Code fixed.

I'm glad you found the code useful. It is standard practice to always check the invocation of a Delegate for null before invoking it; that way you avoid null reference errors.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900