I realize this has been partly covered, although I believe the problem I have has not been fully covered:
I have an event that creates an object of a Form when the event occurs:
private void hostView_AfterSelect(object sender, TreeViewEventArgs e)
{
string selectedNodeText = e.Node.Text;
if (selectedNodeText == "Internal Hosts" || selectedNodeText == "External Hosts")
{
// ignore, parent nodes have no corrosponding tab!
}
else
{
Form1 Form1Object = new Form1(selectedNodeText);
Form1Object.Show();
}
}
Although I need to create many objects of Form1 depending on the "selectedNodeText" string value.
For example: If "selectedNodeText" was == "Cars" I would like the object name of Form1 to be called something like "Form1ObjectCars" and if it was "Dogs" the object name would be "Form1ObjectDogs".
You can use a Dictionary<TKey,TValue> class for this, for example
//Initialize
Dictionary<string, Form1> forms = new Dictionary<string, Form1>();
//Add objects
if(selectedNodeText == "foo")
forms.Add("foo", new Form1("foo"));
if(selectedNodeText == "bar")
forms.Add("bar", new Form1("bar"));
//Get objects
Form1 bar = forms["bar"];
It sounds like you're using the same form, but just want to title or caption it differently.
string caption = string.Format("Form1Object{0}", selectedNodeText);
Form1 Form1Object = new Form1(caption);
// in the Form's constructor you are doing a this.Text = caption?
Form1Object.Show();
Or to avoid passing it to the form:
Form1 Form1Object = new Form1();
Form1Object.Text = string.Format("Form1Object{0}", selectedNodeText);
Form1Object.Show();
Related
When I call the Method idload() from another instance it does not update the list view list its supposed to. I know the method is getting called correctly because I placed a MessageBox after each statement in idload() and it shown. if idload() is called from Form2.cs [the form that it's in] it works fine but If I call it from Form4.cs it does not update the listview.
I used MessageBox.Show(xmlReader.GetAttribute("id")); and when idload() gets called from Form2.cs it loops through every id in the xml once and updates the list view as expected. when it gets called from Form4.cs it loops through everything twice and does not update the listview.
Here are the Relevant parts of code:
Form4.cs
public void myMethod()
{
Form2 form2 = new Form2();
form2.idload();
}
public void idwrite()
{
XElement xml = XElement.Load("settings.xml");
xml.Add(new XElement("Chat",
new XAttribute("id", textBox1.Text),
new XAttribute("name", textBox2.Text)));
xml.Save("settings.xml");
myMethod();
this.Close();
}
Form2.cs
public void idload()
{
listView1.Items.Clear();
XmlReader xmlReader = XmlReader.Create("settings.xml");
while (xmlReader.Read())
{
if ((xmlReader.NodeType == XmlNodeType.Element) && (xmlReader.Name == "Chat"))
{
if (xmlReader.HasAttributes)
{
// listView1.Items.Add(xmlReader.GetAttribute("id"));
// listView1.Items.Add(xmlReader.GetAttribute("name"));
string[] arr = new string[4];
ListViewItem itm;
arr[0] = (xmlReader.GetAttribute("id"));
arr[1] = (xmlReader.GetAttribute("name"));
itm = new ListViewItem(arr);
MessageBox.Show(xmlReader.GetAttribute("id"));
listView1.Items.Add(itm);
}
}
}
xmlReader.Close();
}
Here is the Project if needed: https://ufile.io/8dc20
Really confused why this is happening as there are no errors when debugging so any help is appreciated.
Thanks.
This is a common problem that is rooted in the OOP concept of instance. You have already an instance of Form2 displayed, but your code creates a NEW instance of Form2 and never shows it. Now the code calls the method using that new instance and works with the its ListView.
Of course, being the instance never shown, you don't see any change.
If you call form2.Show after the call to idload you will see your changes on the different instance.
The simplest workaround is to ask the Winforms engine to give you back a reference to the already shown instance of Form2 through the Application.OpenForms collection. Here you can select the reference to the Form2 instance with the extension OfType and getting the first form of that type available. If there is none then create and show it.
public void myMethod()
{
Form2 form2 = Application.OpenForms.OfType<Form2>().FirstOrDefault();
if(form2 != null)
form2.idload();
else
{
form2 = new Form2();
form2.Show();
form2.idload();
}
}
Say I have a list called listOfFruits in my main form. In a second form I've made I want the user to be able to remove items from that list to a second list called removedFruits. Currently I know I can access these lists in my second form simply passing them as parameters in the form constructor. However c# can't do pointers (correct?) so how can I effect the main form's copy of these lists from my second form? Because currently any changes to those lists in my second form don't effect the main form's original copy of the lists. If I were to remove 5 fruits from the listOfFruits passed to my second form then after finishing my work the main form would still still have a full listOfFruits and an empty removedFruits. Is there a simple fix to this? Maybe a get/set or a way to add/remove items from the original lists from the second form? Maybe the answer is in some sort of accessor stuff?
EDIT: To clarify; I want to add to one list, and remove from another. Not add/remove to the same list. Not sure if this matters entirely but I figured I'd be specific here in case it does.
EDIT2: I think the issue is I'm copying the original list from the first form and not editing it directly. Can someone fix my code so I can access the original list from my second form instead of making a copy of the list?
public partial class ListSelector : Form
{
private string windowName = Form1.typeOfModuleAdded;
public List<IOModule> innerIOList;
IOModule cardAdded = null;
public ListSelector(List<IOModule> cardList)
{
this.Text = windowName;
innerIOList = cardList;
InitializeComponent();
InitializeList();
}
private void InitializeList()
{
if (windowName == "Drive")
{
string[] listDrives = { "ACS880", "test" };
listBox1.Items.AddRange(listDrives);
}
else if (windowName == "IOBlock")
{
if (!innerIOList.Any())
{
MessageBox.Show("No cards loaded! Please import cards from IO List.", "Error Empty Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.Close();
}
foreach (IOModule card in innerIOList)
{
cardAdded = card;
listBox1.Items.Add(card.name);
}
}
else if (windowName == "Local Card")
{
string[] listLocals = { "1756-EN2T", "test" };
listBox1.Items.AddRange(listLocals);
}
else if (windowName == "Processor")
{
string[] listProcessors = { "1756-L71S", "test" };
listBox1.Items.AddRange(listProcessors);
}
}
private void addBtn_Click(object sender, EventArgs e)
{
if (listBox1.SelectedItem != null)
{
Form1.SetModule(listBox1.SelectedItem.ToString());
Form1.confirmedAdd = true;
this.Close();
}
else if (cardAdded != null)
{
innerIOList.Remove(cardAdded);
}
else
{
MessageBox.Show("No module selected!");
}
}
and here's how I pass the list to that form from my first form:
ListSelector test = new ListSelector(ioList);
test.ShowDialog();
where ListSelector is the name of my second form, and ioList is the list im passing to it.
EDIT3: added more code
"However c# can't do pointers (correct?) so how can I effect the main form's copy of these lists from my second form?"
No, not correct. Any object reference (for instance, of a List<Fruit>) is still very much a pointer to a place in memory, and if you pass the same List<Fruit> object to both Forms, they share the same List.
I don't know why your changes to your listOfFruits don't chow up in your first Form. I would check the following things:
Are you 100% sure you use the same List<Fruit> object in both Forms. (If you create a new List like this: new List<Fruit>(listOfFruits) it is NOT the same List)
Does the first Form have any way of finding out, that the List has changed? Possible using a Timer with recurring checks, or (my favorite) triggering an event when you change something, and subscribe an EventHandler in the first Form to the event.
I assume that you have created a second list in your second form that is filled with the items of the first form's list. Then changes on the second list aren't reflected in the first list. You have to use the same reference of the list.
public Form2(List<Fruit> listOfFruits)
{
this._listOfFruits = listOfFruits;
}
private List<Fruit> _listOfFruits;
Instead using a public field, try to use property and on creating your new ListSelector pass the list to the property.
public partial class ListSelector : Form
{
private string windowName = Form1.typeOfModuleAdded;
private List<IOModule> innerIOList;
IOModule cardAdded = null;
public List<IOModule> CardList
{
get
{
return innerIOList;
}
set
{
innerIOList = value;
InitializeList();
}
}
public ListSelector()
{
this.Text = windowName;
InitializeComponent();
}
When creating your new ListSelector object
ListSelector ls = new ListSelector();
ls.CardList = your mainform list of IOModule here
ls.ShowDialog();
Hello I have datagridview in form1 and through form1 I open form2 and through form2 I open form3 and string named vyber_ID_K placed in Form1 needs to be accessed in Form3 (I need to get its value in Form3)
this is placed on button click in form1
form2 a = new form2 ("Novy");
string vyber_IDK = (string)dataGridView1.CurrentRow.Cells["ID_K"].Value.ToString();
a.vyber_ID_K = vyber_IDK;
a.Show();
a.Closed += klient_Closed;
I would like to access vyber_ID_K in form 3, how it can be done? I tried to set public string vyber_ID_K in form2 and pass it similary to form3 but I get null. Am I doing it right? Is there any other better solution please?
Thanks in advance.
My step-by-step according to Servy:
button click in Form 1
Func vyberIDKGetter = () => dataGridView1.CurrentRow.Cells["ID_K"].Value.ToString();
try
{
form2 = new form2 ("Novy");
a.vyberIDKGetter = () => dataGridView1.CurrentRow.Cells["ID_K"].Value.ToString();
a.Show();
}
button click in form2
public Func vyberIDKGetter;
private void button1_Click(object sender, EventArgs e)
{
nova_platba b = new nova_platba("novy");
b.vyberIDKGetter();
b.Show();
b.Closed += klient_Closed;
}
In form3
Func<string> vyberIDKGetter = veberIDK;
string vyberIDK = vyberIDKGetter();
SqlCommand sc = new SqlCommand(#"
INSERT INTO kliplat (datum,text,castka,akce,subkey,priznak,rocnik)
VALUES (#datum,#text,#castka,#akce,#subkey,#priznak,#rocnik);
SELECT scope_identity();
", spojeni);
sc.Parameters.AddWithValue("#subkey", vyberIDK);
So the issue here is that the value that you want doesn't exist yet when you're constructing Form2, or even Form3 for that matter. It needs to have some means of accessing the data at some point in the future. We can get this behavior by leveraging delegates.
Rather than passing a string to Form2, when that form is constructed (since we don't know what the string will be yet) pass a Func<string>. That object will be a method that, when invoked, will provide a string that represents the needed value. Form1 can define it like this:
Func<string> vyberIDKGetter =
() => dataGridView1.CurrentRow.Cells["ID_K"].Value.ToString();
Then in Form3 when it's holding onto the function that was passed it can get the string out by simply invoking that delegate:
Func<string> vyberIDKGetter = [...];
string vyberIDK = vyberIDKGetter();
This approach to solving the problem is particularly adventageous in that Form3 doesn't need to know anything about Form1 or Form2. If there is some other caller that wants to use it they can provide their own delegate instead. If there is a developer handling the coding of each form they don't need to communicate all of the internal details of each form to each other, they can just handle the passing of this delegate and then be able to treat the caller/callee as a black box.
You have to make a public getter/setter around the string:
public string Vyber_ID_K
get
{
return vyber_ID_K;
}
set
{
vyber_ID_K = value
}
That you need a reference from Form 1 in Form 2, and from Form 2 in Form 3. So you can access
each Form.
You can't use a string as Referenced Parameter, becuase it is an immutable class. String C#
That is a really odd that you pass a parameter via the constructor
form2 a = new form2 ("Novy");
and in the same time you pass another parameter via the property
a.vyber_ID_K = vyber_IDK;
Why don't you instead pass all parameters via the constructor?
string vyber_IDK = (string)dataGridView1.CurrentRow.Cells["ID_K"].Value.ToString();
form2 a = new form2 ("Novy", vyber_IDK);
and in Form2
public class form2
{
private string Name { get; set; }
private int vyber_IDK { get; set; }
public form2( string Name, int vyber )
{
this.Name = Name;
this.vyber_IDK = vyber_IDK;
}
Then, passing anything to form3 from form2 works in the same way
form3 f = new form3( this.vyber_IDK );
I have a windows form application. On the main form a user will enter a number of item, etc and then click a button which will open a new form (either a small form or a large form depending on a checkbox). Now on my main application I have a file menu - under which is settings - change background colour. This opens the colordialog. If a user does not pick anything the background colours will stay default. However if they change it on the main entry form i change the background of a few textboxes - code below.
private void warning1ToolStripMenuItem_Click(object sender, EventArgs e)
{
colorDialog1.ShowDialog();
Warn1Color = colorDialog1.Color.ToString();
if (Warn1Color != null)
{
tbWarn1Hrs.BackColor = colorDialog1.Color;
tbWarn1Mins.BackColor = colorDialog1.Color;
tbWarn1Secs.BackColor = colorDialog1.Color;
tbWarn1Msg.BackColor = colorDialog1.Color;
}
}
Now my problem is how to I get this to then change the background in the other form that opens. I was hoping I could pass the string across in the new form constructor as i do with a number of other values.
i.e - here is my code in the new form....(note - string Warn1Color was passed across in constructor and then made = to the string _Warn1Color. If it is null then background will be default yellow but it cant convert type string to system.drawing.color. Does anyone see an easy solution to this or what I could do to get this working easily.
if (_Warn1Color == null)
{
this.BackColor = System.Drawing.Color.Yellow;
}
else
this.BackColor = _Warn1Color;
Pass the Color on via the Constructor not a string. If this is not possibly for whatever reason, you could create a ColorConfigClass that holds the required Color and you can set it when used.
You should create a static class to store your configuration data such as this colour style. You can then set this value once you have prompted the user for the change and you can also call the Color value from any other form when you need to use it.
Your static class should look something like this...
public static class StyleSettings{
private static Color _warn1Color = Color.FromArgb(255, 0, 0);//default colour
public static Color Warn1Color {
get { return _warn1Color; }
set { _warn1Color = value; }
}
}
Then you can use this in your example method like...
private void warning1ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
{
StyleSettings.Warn1Color = colorDialog1.Color;
tbWarn1Hrs.BackColor = StyleSettings.Warn1Color;
tbWarn1Mins.BackColor = StyleSettings.Warn1Color;
tbWarn1Secs.BackColor = StyleSettings.Warn1Color;
tbWarn1Msg.BackColor = StyleSettings.Warn1Color;
}
}
I assume you used a string because you wanted to be able to pass null, and System.Drawing.Color being a struct can not be null.
In which case either use Nullable ( http://msdn.microsoft.com/en-us/library/b3h38hb0%28v=vs.80%29.aspx ) which can be null or you can consider some other value as "default", say alpha=0.
To pass a value in your constructor simply go to the code file for the form (the one where you code the stuff for the events) and find the constructor function (has the same name as the form) e.g.:
namespace MyApp
{
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
...
And add the parameters to it:
namespace MyApp
{
public partial class MyForm : Form
{
public MyForm(System.drawing.color background)
{
InitializeComponent();
...do whatever you want with background...
}
...
Of course you also need to edit the places you create this form, e.g. change
form = new MyForm();
form.Show();
to
form = new MyForm(backgroundColour);
form.Show();
I have a main form that has most of the functionality in it. I was just wondering how would I pass on a variable from say a pop up form, to that main form.
Like for instance:
I have a main form that needs some connection info. So when you click the button "Enter Connection Info", it opens up a new form that the user can type in the IP Address he wants to use for his connection.
On this new form, I have a textbox and a button and once you enter the information it should close and pass on the string that contains the ip back to the original form.
Any suggestions? Do you think there is a better method of accomplishing this than using a windows form, and just going ahead and using a windows form or something? I'm quite perplexed on this issue at the moment.
Expose the textbox text as a public read only property. Show the connection form as a dialog and when it closes, get the connection from the property and then dispose the form:
in open form handler (button click, menu, whatever)
string connectionString = null;
using (ConnectionForm form = new ConnectionForm())
{
DialogResult result = form.ShowDialog();
if (result == DialogResult.Ok)
connectionString = form.ConnectionString
}
In you connection form:
public class ConnectionForm: Form
{
....
public string ConnectionString { get { return textBox1.Text; } }
}
You can create a public property in your main form and pass main form instance in pop-up constructor. In this way you can change the main form property.
You can also create an event in your pop-up form and hook it in your main form.
I like to use a pattern sort of like this (bear with me, c# is not my first language):
public class ValueForm: Form
{
public static string GetFromUser(string originalValue)
{
using (ConnectionForm form = new ConnectionForm())
{
form.TheValue = originalValue;
var result = form.ShowDialog();
if (result == DialogResult.Ok)
return form.TheValue;
else
return originalValue;
}
}
public string TheValue {
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
/* also some code behind your OK & cancel buttons to set
DialogResult appropriately,
and do any validation that you need to do
*/
}
and then you would use this like:
string newValue = ValueForm.GetFromUser(oldValue);
Reference Bind the controls on the dialog Form to properties of the Parent Form.
public dlgDbConnProps ( Form Owner )
{
// TODO: Complete member initialization
InitializeComponent ( );
owner = Owner;
}
private void cbo_ProviderLst_SelectedIndexChanged ( object sender, EventArgs e )
{
owner.Provider = cboLst.Text;
}
Here is another method that I have implemented:
... pass a Func to the child form constructor:
public dlgRequestLogin ( Func<string, string, bool> LoginMethod )
{
InitializeComponent ( );
p_loginMethod = LoginMethod;
}
... then handle on button click (or other appropriate event):
private void cmd_SendLoginCredentials_Click ( object sender, EventArgs e )
{
bool res = p_loginMethod.Invoke ( txt_UserID.Text, txt_UserPassword.Text );
}