Adding forms. Interaction between forms
Sometimes it is necessary to access the elements of one form from another during program execution. For example, there is a form Form1, from it we open another Form2 and now, working in Form2, we need to access the elements of the parent form Form1. I found several ways to do this.
1st method. Passing a reference to a public variable.
Namespace WindowsApplication1 ( public partial class Form1: Form ( public Form1() ( InitializeComponent(); ) private void button1_Click(object sender, EventArgs e) ( Form2 frm = new Form2(); frm.but1 = this.button1; // send link to the button in the form Form2 frm.ShowDialog(); ) ) )
In Form2, the variable to which the link was passed will now correspond to button1 from Form1
Namespace WindowsApplication1 ( public partial class Form2: Form ( public Button but1; // this variable will contain a link to button1 from Form1 public Form2() ( InitializeComponent(); ) private void button1_Click(object sender, EventArgs e) ( but1. Text = "test"; // change the text on the button button1 of Form1 ) ) )
2nd method. Passing the link to the child form.
The essence is approximately the same in the 1st method. When opening Form2, we pass into it a link to the element that we plan to change later.
Namespace WindowsApplication1 ( public partial class Form1: Form ( public Form1() ( InitializeComponent(); ) private void button1_Click(object sender, EventArgs e) ( Form2 frm = new Form2(this.button1); // pass a link to the button to the form Form2 frm.ShowDialog(); ) ) )
Now in Form2 we need to create a variable that will contain a link to this button and through it we will access the button on Form1 (lines 5,7,9 and 15).
Namespace WindowsApplication1 ( public partial class Form2: Form ( private Button but1; // this variable will contain a link to the button button1 from the form Form1 public Form2(Button but) // get a link to the button in the variable but ( but1 = but; // now but1 will be a link to button button1 InitializeComponent(); ) private void button1_Click(object sender, EventArgs e) ( but1.Text = "test"; // change the text on button button1 of Form1 ) ) )
3rd method. Access to the entire parent form.
To do this, you need to make changes in several files, but at the same time we will have access to all elements of the parent form and do not need to pass a link to each element, as in method 1.
Step 1. In file Program.cs create a public variable f1 (line 5).
Namespace WindowsApplication1 ( static class Program ( public static Form1 f1; // variable that will contain a link to the form Form1 static void Main() ( Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1() ); ) ) )
Step 2. Opening Form1.Designer.cs and in it, for elements that will need to be accessed from another form, we change private on public. For example, let's make button1 on Form1 available for changes.
Public System.Windows.Forms.Button button1; // replaced private with public
Step 3. When creating Form1, we assign variable f1 a link to this form (line 7)
Namespace WindowsApplication1 ( public partial class Form1: Form ( public Form1() ( Program.f1 = this; // now f1 will be a link to the form Form1 InitializeComponent(); ) private void button1_Click(object sender, EventArgs e) ( Form2 frm = new Form2(); frm.ShowDialog(); ) ) )
Step 4. Now from absolutely any form or from any class you can access the button1 element located on Form1 like this: Program.f1.button1. For example, let the button on Form2 change the button text on Form1:
Namespace WindowsApplication1 ( public partial class Form2: Form ( public Form2() ( InitializeComponent(); ) private void button1_Click(object sender, EventArgs e) ( Program.f1.button1.Text = "test"; // Change the text on the form button Form1 ) ) )
Today I want to talk about how to create a Windows Forms project in C++ in the Visual Studio 2013 IDE. The fact is that, starting with VS 2012, the Windows Forms Application item was removed from the list of projects that can be created. Now I’m talking about the one in C++; you can create such a project in C# by selecting the appropriate item in the section for created projects. However, the fact that such a project cannot be selected from the list does not mean that you cannot create it yourself. This is exactly what I want to talk about in this article.
The first thing you need to do is launch Visual Studio. Once VS is launched, click File > New > Project
After this, a window will open asking you to select a project type. We need to select the CLR subsection in the Visual C++ section and select Blank CLR Project.
When the project is created, in the Solution Explorer, right-click on the created project. In the context menu that opens, select Add > Create Element and in the menu that opens, in the UI section, select Windows Forms
Once the form is added, select the MyForm.cpp file in Solution Explorer. A new tab will open in front of you with a single line of code:
#include "MyForm.h"
We need to add the following code to this file:
Using namespace System; using namespace System::Windows::Forms; void Main(array
After that in the project properties. Select a subsection System section Linker and in the Subsystem line from the drop-down menu, select Windows (/SUBSYSTEM:WINDOWS) and click Apply.
Without closing the project properties window, go to the Advanced subsection and in the line Point of entry we write Main and then press the key OK.
This completes the project settings. To edit the appearance of the form, you need to go to the MyForm.h [Designer] tab by double-clicking on the MyForm.h file in the Solution Explorer.
Despite the fact that my opinion about Microsoft's Visual Studio is still the same, sometimes you have to do something on it. If we come to terms with the fact that we write, in fact, not in C++, but in the so-called C++/CLI, working with familiar visual components will not be so different from the same Borland environments. But it can create problems, compared to Builder. Let's consider 3 typical situations of working with an application containing more than one form. The example environment is free Visual C++ 2010 Express, it is assumed that the main form has the default name Form1.
An example of constructing and programmatically calling a form
This code can be executed, for example, by clicking a button in the main form Form1.
Form^form2 = gcnew Form(); Button^ button2 = gcnew Button(); button2->Text = L"OK"; button2->Location = Point(10,10); form2->Text = L"My window"; form2->HelpButton = true; form2->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; form2->StartPosition = FormStartPosition::CenterScreen; form2->Controls->Add(button2); form2->ShowDialog();
To add a click handler for a programmatically generated button button2, just write before the last line of code:
Button2->Click += gcnew System::EventHandler(this, &Form1::button2_Click);
Before the form2->ShowDialog() or form2->Show() method is called;
In this case, the handler code is located in the current module Form1.h:
Private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) ( MessageBox::Show("Here"); )
Call another form from the main form
In the menu, select Project - Add a new element - Form - name Form2
Let's add an operator
#include "Form2.h"
before the first namespace in Form1.h (that is, at the very beginning of the file).
Let's include a pointer to the class instance in the public section of the Form1 class:
Form2^F2;
Let's add the code where we need to create and call the second form:
F2=gcnewForm2(); F2->Show();
To programmatically delete the second form, the following code is suitable:
Delete F2;
It should be noted that the pointer stores the address of only one form, the one that was created last. If we sequentially create several forms using this code, only the last one will be deleted. Alternatively, try the shape array described below.
Let's describe the necessary data in the Form1 form class (here the name and namespace of the Tabulator project, if necessary, replace with your own):
Static const int MAX_FORMS = 100; //Maximum number of forms int FormCount; //Form counter array
Then we initialize the data using the Load event of the main form:
FormCount=0; F2 = gcnew array
Then we will implement the code to create the next form
If(FormCount
and its removal:
If (FormCount) ( delete F2; FormCount--; )
If we want to create child forms not separately, but inside the parent form, then in the properties of Form1 we need to indicate that it is an “ancestor” (set the IsMdiParent property = true), and before showing the child form using the F2->Show() operator, mark it as a child Form1:
F2->MdiParent = this;
Call a parent form method from a child form
We can hardly do without using .cpp files, which is not bad - writing code in .h files generally breaks the correct C system :)
Let's describe the process step by step.
1) There are 2 forms - Form1 and Form2, on Form1 there is a Button (button1, will open the second form) and Label (label1, here we will change the text). On Form2 - button1, when clicked, the text in label1 will change.
2) Since we need to have access from the first form to the second, and from the second to the first, the problem of cross-references will arise (when Form1.h refers to Form2.h, which, in turn, again refers to Form1.h) . In order to avoid this, we will move the code of the first form (Form1), which will have access to the second form (Form2), from the .h file to the .cpp file. Thus, you need to create a file Form1.cpp.
3) Declare a public Set method in Form1.h so that you can change the text of label1 (the code can be written at the end of the file, after #pragma endregion):
Public: void Set(String^ text) ( label1->Text = text; )
4) In the Form2.h file we include Form1.h (at the beginning):
#include "Form1.h"
and create a constructor that will accept and save a link to the first form for further use:
Form2(Form1^ parent) ( InitializeComponent(); parentForm = parent; ) //you can write a link immediately below: private: Form1^ parentForm;
5) On clicking the button in Form2, we will call the Set method of the parent form:
Private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) ( parentForm->Set("hello from form2"); parentForm->Show(); this->Hide(); )
6) It remains to open the second form in the first form. To do this, we move the button click handler from Form1.h to Form1.cpp , and in the .h file we leave only its declaration.
The question addressed in this article is more likely to relate to the topic of building an application architecture, and not specifically to the problem under consideration. Transferring data from one form to another is not difficult at all. To do this, it is enough to make the control whose data we want to obtain open, that is, mark it with the public modifier. Also, another option is possible. For example, in the first form we create an object of the second form by passing a reference to ourselves to the constructor, that is, by passing a reference to the first from the first form to the second
SecondForm secondForm = new SecondForm(this);
Naturally, before doing this, you should take care of creating an overload for the constructor of the second form.
And this method is quite common. However, with its simplicity, it carries many potential problems, the main one being a violation of the principle of encapsulation. In a word, the second form should not know anything about the existence of the first and, even more so, should not be able to influence it.
The solution to this problem is quite simple. Let's look directly at the code. In the designer, we create the main form (it will be launched when the application starts). Let's put one TextBox, Label And Button.
By clicking on the button, the second form will open and the text from the text field of the main form will be transferred to the text field of the second form. Initially, the second form looks like this:
Similar to the first one, it has the same controls. We don't need any more. The application entry point launches the main form:
Using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace From1FormTo2 ( static class Program ( // The main entry point for the application. static void Main() ( Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); ) ) )
The main form code looks like this:
Using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace From1FormTo2 ( public partial class MainForm: Form ( //second form SecondForm secondForm; //constructor public MainForm() ( InitializeComponent(); ) //data transfer event handler //from the main form to the second private void btn_mainForm_Click(object sender, EventArgs e) ( secondForm = new SecondForm(tb_mainForm.Text.Trim()); secondForm.ShowDialog(); if (secondForm.DialogResult == DialogResult.OK) tb_mainForm.Text = secondForm.ReturnData(); ) ) )
Accordingly, do not forget to connect the button to the event Click. Here in the main form class there is a field SecondForm secondForm, representing the second form object. When you click on the “Submit” button, a second form is created (the overloaded constructor is called, we will create it later) and launched using the method ShowDialog(). In this case, this method is suitable for us. Moreover, after this we make sure to check whether the second form was closed, but clicked on its button. If a button was clicked on the second form, then the first form should accept data from the second. This happens by calling the method ReturnData() in the second form.
Now the most interesting part is the code of the second form:
Using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace From1FormTo2 ( public partial class SecondForm: Form ( //overloaded constructor public SecondForm(string data) ( InitializeComponent(); tb_secondForm.Text = data; ) //data transfer event handler //from the second form to the main one private void btn_secondForm_Click (object sender, EventArgs e) ( this.DialogResult = DialogResult.OK; ) //public method for accessing //the text field of this form public string ReturnData() ( return (tb_secondForm.Text.Trim()); ) ) )
As you can see, there is a single constructor overload that accepts a string type. Remember that we are trying to transfer text from a TextBox. In the constructor, the components are scheduled to be initialized and the text of the text field is set to the transmitted value from the first form. Next, by subscribing to the event Click for the button of the second form, we created a handler btn_secondForm_Click, which simulates the operation of the “Ok” button of any dialog box. Thus, by clicking on the “Submit” button (of the second form), we execute the condition
(secondForm .DialogResult == DialogResult .OK)
The first form, therefore, calling the method secondForm.ReturnData(), we set the text field of the first form to the value of the text field of the second form.
The operation of this method, I think, no longer requires explanation. It simply returns the text from a single text field, while keeping it private.
As a result, we transferred data to the second form from the first and from the second to the first without violating the principles of encapsulation.
Try entering the text “aaa” into the text field of the first form and clicking the button. You will see this text in the text field in the second form that opens. Try changing the text to “aaa ppp” and click on the button. You will see how, after closing the second form, this text will appear in the text field of the main form.
Now, I think you will be able to transfer data between forms more correctly. In the next article we will talk about how to do it in ASP.NET applications.