I have a form populated with an array of userControls that is created from the main form. I need to be able to access this array of userControls from the main form once the popup has been closed when a button is pressed. If I fill out the forms and then press the button on the main form without closing the popup, the values are present. However, if I close the popup window, the values are not present. My main form is static so I can use it's variables in other forms.
Code for the popup:
public ScanChannel[] controls;
public ScanListSetup()
{
InitializeComponent();
int numChans = Convert.ToInt32(Form1.Self.numChannels.Text);
controls = new ScanChannel[numChans];
// Create the UserControls
for(int i = 0; i < numChans; i++)
{
controls[i] = new ScanChannel();
}
// Place them
for (int i = 0; i < numChans; i++)
{
controls[i].Location = new Point(13,(35+25*(i)));
this.Controls.Add(controls[i]);
}
doneButton.Location = new Point(82, 35 + (25 * (numChans + 1)));
this.Size =new Size(280, 110 + (25 * (numChans + 1)));
}
private void doneButton_Click(object sender, EventArgs e)
{
Form1.Self.setChannelsToScan(controls);
}
I need to access the controls
array in the main form. The code for the main form is as follows:
private ScanChannel[] channelsToScan;
private void configureScanListButton_Click(object sender, EventArgs e)
{
var form = new ScanListSetup();
form.Show(this);
scanListConfigured = true;
this.channelsToScan = new ScanChannel[Convert.ToInt32(numChannels.Text)];
}
public void setChannelsToScan(ScanChannel[] arr)
{
for (int i = 0; i < arr.Length; i++)
{
this.channelsToScan[i] = arr[i];
}
}
private void scanButton_Click(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Test: " + this.channelsToScan[0].getDeviceType());
// THIS PRINTS AN EMPTY STRING
}
So, the Debug writeLine outputs the correct value if I click the scanButton
while the popup form is still open. However, if I close the form after clicking the doneButton
on the popup form, the Debug writeLine outputs Test:
with nothing else.
Any help with this would be greatly appreciated. Thanks.
Your problem essentially boils down to sending data from a secondary window (your 'pop-up' window) to the main window from where it was created. It doesn't matter whether you're working with Windows Control
objects or simple data types like string
, so I'm going to use a simple example to illustrate how to handle such a situation.
Let's assume you have a Main
form that looks like this. It has an OPEN
button and a TextBox
.
When you click OPEN
, it opens up this secondary input window (your pop-up) which looks like this:
Now the idea is this. You click OPEN
and opens the Input
form, and lets the user enter some text into the TextBox
there. Once you click the OK
button, it should close the Input
window, and display the text entered by the user in the Main
window. Remember that at this point the Input
window is closed, which is equivalent to your situation.
So I'd make use of Delegates to accomplish this goal. A delegate
lets you transfer data between windows which is what you want.
In my Main
I'd declare a public
delegate with a signature like this:
public delegate void DataTransfer(string data);
That is, this delegate
represents a method that takes in a single string
parameter, and has void
return type. The idea is to let the secondary Input
window 'call' a method in the Main
, and that method takes in a string
parameter. So, if there was a way for us to call a method that resides in the Main
from Input
, and pass a string
, we can then take the user input text in the Input
window, and pass it to the Main
window. With me so far?
Now, if I write a method like this in the Main
, and let it be called from Input
, that should accomplish our goal. Here, txtDisplay
is the TextBox
in the Main
form.
public void ReceiveInput(string data)
{
txtDisplay.Text = data;
}
To accomplish this, I would define a delegate
of type DataTransfer
in the Main
form like below, and register the ReceiveInput()
method to it. Your Main
form code behind should look like this:
public delegate void DataTransfer(string data);
public partial class MainForm : Form
{
public DataTransfer transferDelegate;
InputForm inputForm = null;
public MainForm()
{
InitializeComponent();
transferDelegate += new DataTransfer(ReceiveInput);
}
public void ReceiveInput(string data)
{
txtDisplay.Text = data;
}
private void BtnOpen_Click(object sender, EventArgs e)
{
inputForm = new InputForm(transferDelegate);
inputForm.Show();
}
}
BtnOpen
is the OPEN
button in the Main
form, and when it's clicked, it passes the delegate to the Input
form, then opens it. So, accordingly, we need to now modify our Input
form:
public partial class InputForm : Form
{
DataTransfer transferDel;
public InputForm(DataTransfer del)
{
InitializeComponent();
transferDel = del;
}
private void BtnOK_Click(object sender, EventArgs e)
{
string data = txtInput.Text;
transferDel.Invoke(data);
Close();
}
}
Here, we modify the constructor so that it takes in a delegate
of type DataTransfer
, and sets it to the local instance of the same type. Then, at the click of BtnOK
on the Input
form, we take in the text input by user, and pass that text to the said delegate
and invoke it. 'Invoking' is the same as calling the method in the Main
form. At this point, you can Clsoe()
the Input
window as shown above, and you'd still have access to the user input string
data from your Main
form.
You can use this same approach, and instead of strings you can pass around Controls
. However, it's not the best approach to pass around a bunch of controls back and forth, so ideally you would extract the data you need from those controls in your pop-up, and pass only the said data instead of the whole controls.
EDIT: After OP posted the erroneous code.
OK, so here's your issue. The testUserControl
class is not a regular class but a control element derived from UserControl
. In otherwise, a GUI element. You shouldn't use GUI elements to pass data around. Because, when you do your controlArr[i].getText();
, it tries to get the text from the textItem
, but textItem
is a TextBox
Control which doesn't exist at this point because you closed your window. Remember, you do the delegate.Invoke()
only once, and at that point *you must send ALL the data back to your main window*.
What you should do is, simply define a class to hold ALL the data you want to pass to your main. For example something like this:
public class DataToPass
{
public string TextBoxText { get; set; }
public string SomeOtherData { get; set; }
// Other stuff you want...
}
Now, instead of passing an array of testUserControl
, pass an array of DataToPass
. That way, at the Main
form you don't have to do the following:
controlArr[i].getText();
Instead you'd simply do something like:
controlArr[i].TextBoxText;
where controlArr
now is an array of type DataToPass
.
Simply, passing a control derived from UserControl
is not a good idea. Just create one class that is capable of holding ALL the data you want to pass and pass it back to the main once.