C# Get variable from popup form that will be closed - c#

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.

Related

Load popup form cross thread

I'm having trouble manipulating forms when from another thread.
I've overcome the issue by loading the form at runtime, showing it then hiding it. This means the form is created on the right thread and can be manipulated using invokes.
This is not the right way to do it. I have 3 problems that come from using this method
I can't spawn another popup box I have to use the one I created at runtime
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
I have to use a variable bool to hold if the popup is open or not.
If anyone could point me in the right direction It would be much appreciated. Currently my code looks like:
On Main form Load:
CallerIDfrm = new frmCallerID();
CallerIDfrm.Show();
CallerIDfrm.Hide();
to manipulate the popup Im using
delegate void StringArgReturningVoidDelegate1(string CallerIDnum, string CallerIDname, string ContactID);
private void CallerID(string CallerIDnum, string CallerIDname, string ContactID)
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate1 d = new StringArgReturningVoidDelegate1(CallerID);
CallerIDfrm.Invoke(d, new object[] { CallerIDnum, CallerIDname, ContactID });
}
else
{
if (ContactID != null || ContactID != "0")
{
CallerIDfrm.ContactID = ContactID;
}
CallerIDfrm.Mainfrm = this;
CallerIDfrm.TopLevel = true;
CallerIDfrm.TopMost = true;
CallerIDfrm.lblCallerIDname.Text = CallerIDname;
CallerIDfrm.lblCallerIDnum.Text = CallerIDnum;
CallerIDfrm.Show();
CallerIDOpen = true;
}
}
To Hide the popup until required again im using:
delegate void StringArgReturningVoidDelegate2();
private void CallerIDClose()
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate2 d = new StringArgReturningVoidDelegate2(CallerIDClose);
CallerIDfrm.Invoke(d, new object[] { });
}
else
{
try
{
CallerIDfrm.Hide();
CallerIDOpen = false;
}
catch
{
}
}
}
I've tried otherways but the Popup loads as if it is not responding and I loose access to the popup.
Ultimately I'd like to be able to spawn multiple popups and have the ability to close them from the Main Form.
What I gather from your question: You have an caller api/lib/class and you like to show CallerId on a popup form when a call is received. Have a look at Events and Event Driven programming.
The following codes has not been tested, I wrote it from top of my head. Might not compile, they are here to show an example:
Create an CallReceived event in api/lib class as follows:
public event EventHandler<CallReceivedEventArgs> CallReceived;
protected void OnCallReceived(EventArgs e)
{
var handler = CallReceived;
if (handler != null)
handler(this, e);
// Note: For C# 6.0 and later, above statements can be simplified to
// CallReceived?.Invoke(this, e);
}
Note: If you don't have access to this api/lib code, create a Gateway class and put your event in there along with mechanism to trigger it.
Also create a CallReceivedEventArgs, this will be used to transfer event data:
public class CallReceivedEventArgs : EventArgs
{
public string CallerIDnum {get; set;}
public string CallerIDname {get; set;}
public string ContactID {get; set;}
}
Now, in your api/lib class raise this event whenever a call is received:
// a call received, replace dummy values with actual values
OnCallReceived(new CallReceivedEventArgs() { CallerIDnum="5554443322", CallerIDname="SOME_NAME", ContactID="SOME_CONTACT" });
Finally in your GUI form, register to said event and process accordingly.
// inside your main form class
private CallerAPI callerApi = new CallerAPI();
// somewhere inside you main form class, register to event
// from your use case, I would place it inside Main Form's constructor
callerApi.CallReceived += callerApi_Callreceived;
// receive event
void callerApi_Callreceived(object sender, CallReceivedEventArgs e)
{
var callerIDnum = e.CallerIDnum;
// etc.
// show callerId form with details
// you need to change frmCallerID's constructor accordingly
CallerIDfrm = new frmCallerID(e.CallerIDnum, CallerIDname, ContantID);
// to be able to track opened popups, you can store them inside a list
// private List<Form> openPopupList = new List<Form>();
//
// alternatively, you can assign CallerIDnum to form's name property
// and store these inside a List<string> instead of List<Form>
openPopupList.add(CallerIDfrm);
CallerIDfrm.Show();
}
Don't forget to unregister from event.
callerApi.CallReceived -= callerApi_Callreceived;
To wrap it up:
I can't spawn another popup box I have to use the one I created at runtime
You can create and show multiple frmCallerID, independent from each other.
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
Since new approach creates CallerID forms based on events, you won't see these form flashing. It'll open whenever a CallReceived event is received.
I have to use a variable bool to hold if the popup is open or not.
A better approach would be: Register to forms FormClosed event, and remove from openPopupList accordingly.
frmCallerID.FormClosed += frmCallerID_FormClosed;
void frmCallerID_FormClosed(object sender, EventArgs e)
{
// remove form from openPopupList
frmCallerID closedPopup = (frmCallerID) sender;
openPopupList.remove(closedPopup);
}

How to pass data from one form to another form textbox in windows application?

I'm trying to pass a variable from one form to another form textbox. The 'variable' is a result of a calculation based on the user inputs.
Below is the code for the parent form(RuleInsertForm) where I'm calling the subform(Helpformula) to get the user inputs.
public partial class RuleInsertForm : Form
{
public string helpformulainputs;
}
private void RuleInsertForm_Load(object sender,EventArgs e)
{
if (helpformulainputs=="")
{
textBox_Inputs.Text = "";
}
else
{
textBox_Inputs.Text = helpformulainputs;
}
}
Below is the code for the subform(Helpformula) where i'm passing the result variable(formulainputs) to the parent form(RuleInsertForm).
public partial class HelpFormula : Form
{
public string formulainputs = string.Empty;
private void button_generateformula_Click(objectsender, EventArgs e)
{
using (RuleInsertForm insertform = new RuleInsertForm())
{
insertform.helpformulainputs = formulainputs;
this.Close();
insertform.Show();
}
}
}
Problem:
The values are getting passed to the text box but in the UI its not getting dispalyed.
so far I tried to push data back to parent form and then tried to display the data in the textbox where I failed.(I dont know where it went wrong suggest me if I can resolve the below one)
Now I need an alternative method to this for eg: instead of pushing the data back to parent form i need to make the variable available for all the forms trying to use the subform(formulainputs)
How can I acheive this process ? any suggestions are much appreciated.
The problem seems to be that insertForm.Show() does not block the execution of your button handler. Show opens the insertform as non-modal.
So after insertform is opened, the execution is continued in button_generateformula_Click and when you exit the using block, the insertform is disposed and therefore closed.
To solve this you may call insertForm.ShowDialog() instead.
For different ways of communicating between Forms look here or simply type communicate between forms into the SO search box.

Passing Content between two Windows in WPF [duplicate]

This question already has an answer here:
Pass Parameters Between Windows
(1 answer)
Closed 7 years ago.
My problem is, to pass some information from a WPF window to an existing WPF page. I would like to open a the window by pressing a button on WPF Page1.
Then, i have to get all the text stored in Textbox1 to a variable in Page1. But only when the user presses ButtonXY on the "Window". How can i solve this Problem without using the "Bindings" because there has to be abig "Switch" clause next to Page1?
If you are using MVVM then both the window and the page could have a common ViewModel and those TextBoxes should be bound to the same property with UpdateSourceTrigger set to PropertyChanged and this way no more events or notifications are needed.
Otherwise, sometimes i tend to create a global project which will be added as a references where needed. This way, you will be able to set events and their handlers to accomplish your specific issues. I think this is not an elegant solution but it helps me a lot communicating between different projects or classes.
Make a new class that will contain all your data you want to transfer between all your windows.
public YourNewClassWithSettings
{
string StringNumber1;
string StringNumber2;
}
Create an instance of this class in your main window and set the values of the properties you want.
Pass the instance of YourNewClassWithSettings by reference to your constructor of your next window.
YourNewClassWithSettings test = new YourNewClassWithSettings();
test.StringNumber1 = "mySetting";
WindowControl w = new WindowControl(ref test);
w.ShowDialog();
//See what was changed!!!!
string changedValue = test.StringNumber1; //Will be "IamDone"
In your constructor you would say:
public WindowControl(ref YourNewClassWithSettings test)
{
// example: You can now say TextBox1.Text = test.StringNumber1;
// Whatever you change here like test.
test.StringNumber1 = "IamDone";
//When you close this window, because test was passed by ref : you
// will see StringNumber1 = "IamDone" in your main window again
//when accessing the property of that class. Always pass by ref
this.Close();
}
This is the MVVM approach.
Lets assume you have a MainWindow and a SubWindow.
Your SubWindow contains a Button and a Textbox. When you click on the Button in the SubWindow you want to transfer the Text from the Textbox into the MainWindow.
Main Window.
You implment a Property (a Getter and Setter) wich will contain the transfered Text string. The MainWindow contains also the SubWindow! Don't forget to pass the MainWindow into the SubWindow Constructor (look below) so the SubWindow know the MainWindow instance
public partial class MainWindow : Window
{
public string Text { get; set; }
private Window subWindow;
public MainWindow()
{
InitializeComponent();
subWindow = new SubWindow(this);
subWindow.Show();
}
}
SubWindow
public partial class SubWindow : Window
{
private Window mainWindow;
public SubWindow(Window mainWindow)
{
this.mainWindow = mainWindow;
InitializeComponent();
}
private void ButtonTransferTheData_Click(object sender, RoutedEventArgs e)
{
mainWindow.Text = myTextBoxInSubWindow.Text;
}
}
Your SubWindow knows the MainWindow. So you can access the Text Property. On the Button click you wirte the Text from the Textbox into the Property of the MainWindow.
HINT
The mainWindow.Text = myTextBoxInSubWindow.Text; line could throw an exception. this is because you want to access a thread from another.
So you need to dispatch it. Change this line into:
Dispatcher.Invoke(() => mainWindow.Text = myTextBoxInSubWindow.Text);
Hope this helps.

Need to define control flow and functionalities

I have a project with requirements as below,
Login form opens up and asks for user name and password.
Login Successful - Login form closes and another main form opens and all the functionalities will be added here.
In the main form there is a Call button when clicked on it, it will pop a dial pad.
When the number is entered on the dial pad and clicked "ok", control comes back to the same main form.
When logout is clicked, It wil take me to the login screen again.
For this can anybody explain me the following points:
How do I tranfer the control from one form to another?
I have to make sure when user clicks on the close 'x' , I have to log out and close the windows?
Neeed some rough class information.
Thanks in advance.
This is what i used earlier to carry data from one form to other
public partial class DialPad : Form
{
public MainGUI guiObject;
public DialPad(MainGUI mG)
{
InitializeComponent();
guiObject = mG;
}
By the sounds of it your dialler form should be a dialog..
class MyDiallerDialog : Form
{
public String DialledNumber
{
get { return this.txtNumber.Text; } // Or however the form stores its number...
}
}
class MyMainForm : Form
{
void btnCall_Click(object sender, EventArgs e)
{
using (var dialog = new MyDiallerDialog())
{
if (dialog.ShowDialog() == DialogResult.OK)
{
String number = dialog.DialledNumber;
// do something interesting with the number...
}
}
}
}
For your point no. 2
I have to make sure when user clicks
on the close 'x' , I have to log out
and close the windows?
In the event FormClosing of the form, take a look at e.ClosingReason.
So if user closes using Close Button (X), then ClosingReason would be UserClosing. So check that and then write appropriate code therein.
How do I tranfer the control from one
form to another?
For e.g
If you want to get the number in main form from Dialpad form.
1st in the contructor of main form
public static MainForm instance = null;
public string numberInMainForm = null;
public MainForm()
{
instance = this;
}
now in your dialpad form, when user enters the number, you can pass the number (or any other variable.) to main form from the dialpad form directly.
In dialpad form just write:
MainForm.instance.numberInMainForm = number;
thats it. You are done !!
let us assume you 1st form is loginform,
Suppose User press OK on the login form, so on
OK_click()
event call another form.
suppose your name of the another form is MainForm.cs then you can call using...
MainForm mf = new Mainform()
Suppose you want to close the login form when user press OK of your logIn form you can keep the order in following way..
private void OK_Click(object sender, EventArgs e)
{
. . .
// your validations
//return bool (true or false ) to confirm you have complted validations
MainForm mf = new Mainform();
mf.show(); // or you can use mf.ShowDialog();
. . .
. . .
this.close();
}
When you will close the MainForm,its control will come directly to the next line after mf.show();
To close any form use
this.close() command.
I hope this will help you ands you can start working on your project now.
EDIT:
Add a new class file named commondata.cs and in that use static variables like
public static string myString = "";
You can keep all the static functions and variables in the common file like commonData.cs so that you can modify its value from anywhere and can be used from anywhere.
Before closing your current form store the information in the static myString so even if u close the current form related information will be stored in myString & you can access it in any form using commonData.myString command.
string temp = commonData.myString;
Regards,
Sangram Nandkhile.

Changing the property of a control on another form

Basically, I have a settings window, and when you click "OK", it's suppose to apply settings to the main form (eg, set font of a control, etc), and then close.
frmmain frm = new frmmain();
frm.OLVAltBackColor = Color.Aquamarine ;
I tried that, but it only applies the settings to that instance, and you can see it if you do frm.Show();
I'm trying to make it so the already opened form has it's control's properties changed.
What you are trying to do is not working because you are creating a NEW instance of your main form and updating that rather than the first instance. It is possible to update the main form by keeping a reference to it in your settings form... but...
...it sounds like you are approaching this from the wrong direction.
Don't make the settings form dependent on the main form. Instead create the settings form from the main dialog.
class SettingsForm : Form
{
// You need to ensure that this color is updated before the form exits
// either make it return the value straight from a control or set it
// as the control is updated
public Color OLVAltBackColor
{
get;
private set;
}
}
In your main form
(I'm assuming some kind of button or menu click)
private void ShowSettingsClicked(object sender, EventArgs args)
{
using (SettingsForm settings = new SettingsForm())
{
// Using 'this' in the ShowDialog parents the settings dialog to the main form
if (settings.ShowDialog(this) == DialogResult.OK)
{
// update settings in the main form
this.OLVAltBackColor = settings.OLVAltBackColor;
}
}
}
Apply the property change to the form that already exists and is already shown instead of creating a new form and changing that one.
In this code you're creating a new instance of the frmmain. Any changes you make to that new object will happen in the new object, not the one you actually want to change.:
frmmain frm = new frmmain(); //Creating a new object isn't the way.
frm.OLVAltBackColor = Color.Aquamarine ;
What you're looking for is a way to call on the already existant frmmain class and change the property of that.
Edit, for example:
using System;
class Statmethod
{
//A class method declared
static void show()
{
int x = 100;
int y = 200;
Console.WriteLine(x);
Console.WriteLine(y);
}
public static void Main()
{
// Class method called without creating an object of the class
Statmethod.show();
}
}

Categories