jdelange
jdelange

Reputation: 801

C++ /Cli Passing data to child form (and back) or how to pass an object to a child form

I am starting out with C++\Cli so be gentle ;-)

It seems my issue is not uncommon, but I have not been able to find a solution that works for me, i.e. that I am able to implement.

I create an object "Genome" in say Form1_Load(), which I then want to provide with data in a child Form3 that is displayed via a tool strip menu. The object is provided with default system data in the constructor. This default data needs to be send to the child form as starting values in the input fields. After the user has accepted or modified this data, the object needs to be modified accordingly, after which the child form is closed.

My feeling is that this could be done by sending a pointer / handle to the object to the child form, making the data accessible.

Two issues: 1) basic, and 2) less basic.

1) The ToolstripMenuItem_Click action does not see the object created in Form1_Load.

2) I cannot seem to figure out how to pass (a handle) to the object with the ToolStripMenuItem_click event such that the object data is accessible in the child form.

Here's some code, I've stripped out as much as I can, I hope not too much (data hiding still to do):

Genome.h

ref class Genome
{
public:
    int nTFA; // nr of Transcription Factor genes
    int nFDE; // nr of Function cell Death genes

    List<char>^ cString;    // chromosome string 
    List<Gene^>^ cStruct;   // structure consisting of a List with Gene objects

    // methods
    void loadIniGen(String^ iniFile);   // load genome values from .ini file

    Genome(void);
};

Genome.cpp (methods and lots of vars left out):

// constructor
Genome::Genome(void)
{
    nTFA    = 20; // nr of Transcription Factor genes

    cString = gcnew List<char>();
    cStruct = gcnew List<Gene^>();
}
// methods

Form1.h

#pragma endregion

private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e)
         {
         Genome^ myGenome = gcnew Genome(); // instantiate genome object
         }

private: System::Void genomeToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e)
         {
             Form3^ genomeForm = gcnew Form3(myGenome); // pass genome object to Form3 for data input
             genomeForm->Text = "Genome settings";

             genomeForm->StartPosition = FormStartPosition::CenterParent;
             genomeForm->ShowDialog(); // modal

         }

Thanks, Jan

Let's add the errors:

1) Gives this: error C2065: 'myGenome' : undeclared identifier

2) Gives this: error C3673: 'GenomeV2::Form3' : class does not have a copy-constructor (when I create the myGenome object in the ToolStrip action for testing)

To clarify, Form3 is designed, but has no functionality yet. Summary:

namespace GenomeV2 {

public ref class Form3 : public System::Windows::Forms::Form
{
public:
    Form3(void)
    {
        InitializeComponent();
        //
        //TODO: Add the constructor code here
        //
    }

protected:
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    ~Form3()
    {
        if (components)
        {
            delete components;
        }
    }
private: System::Windows::Forms::GroupBox^  groupBox1;

etc.

#pragma region Windows Form Designer generated code

etc.

#pragma endregion

private: System::Void Form3_Load(System::Object^  sender, System::EventArgs^  e) 
         {
         numericUpDown1->Minimum = 10;  // TFA nr of Transcription Factor genes
         numericUpDown1->Maximum = 100;
etc.
     }

Upvotes: 0

Views: 3112

Answers (2)

jdelange
jdelange

Reputation: 801

Well, I've got it to work. Wheter it is the proper way, or if it can be improved, let me know!

1) Making myGenome visible

I've put the variable declaration of Genome^ myGenome; in the Form1 public declaration so it is visible to all actions, and then I create the object in the Form1_Load event with myGenome = gcnew Genome(); This can later be changed to a List with Genome objects etc.

2) Passing data between forms back and forth

As advised by David I pass the (now visible) myGenome object to Form3 when instantiating genomeForm with Form3^ genomeForm = gcnew Form3(myGenome);. In the Form3 declaration I create a bGenome object as a copy to take the data passed to the form. This is done in the constructor of Form3 (see code below). This bGenome object contains a copy of the data of the myGenome object of Form1, that I can use to populate the numericUpDown fields of Form3, i.e. data transfer from Form1 to Form3.

When all data is reviewed and modified where required, the form is closed with OK. At that moment, the data entered in the numericUpDown fields is copied back to the bGenome object.

Since Form3 is #included in Form1 (Form1 is not #included in Form3 to avoid a circular reference), Form1 has access to bGenome and the data of bGenome can be copied back to myGenome, closing the loop: data transfer from Form3 back to Form1.

Note: I realise that Form1 could also read out the numericUpDown fields of Form3 as it has access, but I think it is cleaner to leave those in Form3 and only feed back one complete modified object bGenome that can then be copied to myGenome in one statement.

The object myGenome was used to populate the input fields to the user and the (revised) input is fed back to myGenome.

Code, all non-essentials stripped out.

Form1.h

public ref class Form1 : public System::Windows::Forms::Form
{

public:
    Genome^ myGenome;

etc.

private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e)
     {
         myGenome = gcnew Genome(); // instantiate genome object
     }

etc.

private: System::Void genomeToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e)
     {
         Form3^ genomeForm = gcnew Form3(myGenome); // pass genome object to Form3 for data input
         genomeForm->ShowDialog(); // modal

         myGenome->nTFA = genomeForm->bGenome->nTFA; // feed data from Form3 back to myGenome
         textBox1->Text = myGenome->nTFA.ToString();
     }

Form3.h

public ref class Form3 : public System::Windows::Forms::Form
{
public:
    Genome^ bGenome;

public:
    Form3(Genome^ aGenome)
    {
        InitializeComponent();
        bGenome = gcnew Genome();
        bGenome = aGenome;
    }

etc.

private: System::Void Form3_Load(System::Object^  sender, System::EventArgs^  e) 
     {
         numericUpDown1->Minimum = 10;  // TFA nr of Transcription Factor genes

etc.

         numericUpDown1->Value = bGenome->nTFA; // show the current Genome data in the input field
     }

private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) 
     {
         bGenome->nTFA = Convert::ToInt32(numericUpDown1->Value); // copy the (modified) input data back into the Genome object

etc.
         this->DialogResult = System::Windows::Forms::DialogResult::OK;
         this->Close();
     }

I would like to be able to do away with the local copies (aGenome and bGenome) and access myGenome directly from Form3, so if there is a way to do that, let me know. I have a feeling this could be done by passing Form1 to Form3 and #include Form1 in the .cpp version of Form3 (to avoid the circular ref.), but haven't tried that.

Upvotes: 0

David Yaw
David Yaw

Reputation: 27864

  1. Error C2065: You need to store the myGenome object somewhere that other methods can see it. Right now, it's a local variable within the Form1_Load method. Make it a class field, and other methods within that object can see it.

    ref class Genome
    {
    public:
        Genome^ myGenome;
    };
    
  2. Error C3673: If you haven't already, add a constructor to Form3 that takes a Genome^. Once that is done, and you've made the above change to the storage of myGenome, then this line should compile.

  3. "After the user has accepted or modified this data, the object needs to be modified accordingly, after which the child form is closed." You're passing the object itself, the child form will modify it directly. You shouldn't need to do any manipulation of myGenome in genomeToolStripMenuItem_Click after ShowDialog returns.

Upvotes: 1

Related Questions