I struggle with selecting proper item in winforms combobox. Previously I described in detail here but I think that problem remain unsolved cause I use one form for add/edit record. so on form load I have
private void AddEditForm_Load(object sender, EventArgs e)
{
PopulateComboBoxLanguage();
}
private void PopulateComboBoxLanguage()
{
comboBoxLang.DataSource = Enum.GetValues(typeof(Book.EnumLang));
}
and on edit action I want to populate form with existing data and everything is populated as it should except combobox where first item from EnumLang is always displayed.
from my second constructor I call PopulateWithExisingData(book) where I use
comboBoxLang.SelectedItem = book.Language;
but even when passed book.Language is set to German SelectedItem is always null on debug mode.
p.s. I tried with comboBoxLang.SelectedItem = (book.EnumLang)book.Language; also with SelectedValue but remains the same.
Once more I guess that problem is on populating combobox on page load but I don't know is it and how to fix that.
Please ask for more info.
Declare an instance of the object type you are adding/editing in your form.
Add a bool isEdit to the form and set it to false
Add a method public void Initialize(ObjectType name)
Your Initialize method should set the form instance equal to the parameter and it should set a boolean flag isEdit = true.
Put all your code that loads data/populates controls (like your combobox) in your forms load event.
At the bottom of your load event, after your controls are populated do
if (isEdit)
{
//Set your controls selected values from the object you are editing
}
Now, for new objects, just make your form and call a Show or a ShowDialog on it. This will cause the Load event to fire and your controls will populate.
For edits, make your form, call Initialize, THEN do the Show/ShowDialog. Since your Initialize method sets isEdit = true, the if(isEdit) block of code at the bottom of your load event will be hit and the controls values will be set equal to the properties of the object you are editing.
Here is some very simple example code:
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//This is simulating an add...First Language will be displayed on form2,
//which is English
Form2 form = new Form2();
form.ShowDialog();
}
private void button2_Click(object sender, EventArgs e)
{
//This is simulating an edit...this will display french
//(or whatever is passed in)
Form2 form = new Form2();
form.Initialize(Languages.French);
form.ShowDialog();
}
Languages editValue;
bool isEdit = false;
public Form2()
{
InitializeComponent();
}
public void Initialize(Languages var)
{
editValue = var;
isEdit = true;
}
private void Form2_Load(object sender, EventArgs e)
{
comboBox1.DataSource = Enum.GetValues(typeof(Languages));
if (isEdit)
{
comboBox1.SelectedItem = editValue;
}
}
public enum Languages
{
English = 0,
French = 1,
Spanish = 2,
German = 3
}
When you set DataSource, you pass an array of objects.
When you set SelectedItem, you pass an enum value, so it gets boxed to an object again. ComboBox searches for your item among the DataSource values, using method IndexOf, which uses method Object.Equals to compare those values against your new value. And since they are different objects (the references differ), your item is never found in the DataSource collection, so the selection is not changed.
Related
I'm working on a GUI for an admin interface for management of a student complex. Currently the GUI has a listbox with predefined 6 rules for the students. In the beginning of the code, I add them to a list
private void Form1_Load(object sender, EventArgs e)
{
foreach (string rule in lbRules.Items)
ruleList.Add(rule);
}
Then, the GUI provides the admin with an option to modify the rules. To do so he selects a rule from the listbox and clicks a "Modify" button, which opens another form:
private void BtnModify_Click(object sender, EventArgs e)
{
if (lbRules.SelectedItems.Count > 0)
{
selectedRule = lbRules.SelectedItem.ToString();
selectedIndex = lbRules.SelectedIndex;
selectedRuleNumber = selectedRule.Substring(0, 3);
selectedRule = selectedRule.Substring(6);
var rulesForm = new Rules();
rulesForm.Show();
}
}
On the second form load event I get the rule's text and number:
private void Rules_Load(object sender, EventArgs e)
{
tbRule.Text = Form1.selectedRuleNumber;
tbModifyRule.Text = Form1.selectedRule;
}
The text gets added to a RichTextBox, from where the rule can be edited.
Then the admin clicks a "Save" button, which gets the edited text from the RichTextBox(tbModifyRule) and adds it to a static ruleList in form1, sets a static boolean from form1 to true. Afterwards the second form gets closed:
private void BtnSave_Click(object sender, EventArgs e)
{
saveRule = Form1.selectedRuleNumber + " - " + tbModifyRule.Text;
Form1.ruleList.Insert(Form1.selectedIndex, saveRule);
Form1.ruleList.RemoveAt(Form1.selectedIndex+1);
Form1.formOpen = true;
this.Dispose();
}
At this point we are back to form1, in which we have a timer with timer_tick event. In there we check whether the boolean formOpen is true (which it is set before closing form2). Inside the if statement we clear the listbox and add each rule from the ruleList (previously edited in form2) to the listbox, then sets the formOpen back to false so it doesn't get executed all the time:
if (formOpen)
{
lbRules.Items.Clear();
foreach (string item in ruleList)
lbRules.Items.Add(item);
}
formOpen = false;
Now this is really weird, and at this point makes absolutely no sense to me, since I tried debugging it for over an hour, trying different ways, which also led me to mysterious wonders of WHY TF IT WORKS WHENEVER IT WANTS...
So this works randomly, like it would work the first time, the second and third times it won't. Or vice versa. It's all random.
Strangely, I tried adding a breakpoint on the
lbRules.Items.Add(item);
in the foreach loop, so it stops on each item. And I actually saw the changed rule getting added from the ruleList into the listBox, however in the end it was not there.
And weirdly enough, I also tried adding the text from form2 in the listBox in form1, without using a list, but for whatever odd reason, I use the int selectedIndex, which gets the index of the selected item from the BtnModify_Click event to insert the text in that particular index, but this very index gets RANDOMLY set to bloody 0 after form2 closes.
hence, it again works from time to time, because at some tries it doesn't get set to 0 and it works.
if (formOpen)
{
selectedRule = Rules.saveRule;
lbRules.Items.Insert(selectedIndex, selectedRule);
lbRules.Items.RemoveAt(selectedIndex+1);
}
formOpen = false;
I don't assign value to this integer ANYWHERE else in the code.
I really tried digging some sense, but I hit a solid hard rock.
Any help appreciated!
And thanks for the time!
edit1:
as requested - rest of the timer method
private void Timer1_Tick(object sender, EventArgs e)
{
foreach (string text in ws.messages)
message = text;
if (ws.messages.Count > 0)
{
if (message.Contains("comp"))
{
Complaints();
message = String.Empty;
ws.messages.Clear();
}
}
if (formOpen)
{
lbRules.Items.Clear();
foreach (string item in ruleList)
lbRules.Items.Add(item);
}
formOpen = false;
}
I would change your code to the following:
if (formOpen)
{
formOpen = false;
lbRules.Items.Clear();
foreach (string item in ruleList)
lbRules.Items.Add(item);
}
The issue with having the formOpen = false; outside the if statement is that there is a chance that once the user clicks the Save button the timer could be about to execute the formOpen = false instruction setting it to false making the code inside the If statement to never be executed.
I truly believe this is not random but just a timing issue due to complicated logic.
If I were you, I'd do a couple things:
Use a separate class for data exchange between forms, avoid using public static (I assume) form members for this.
Instead of a timer, subscribe to the Form.Closed event of RulesForm
This might make code flow a bit more predictable and allow you to find errors more easily.
Better yet, use the following pattern:
class Form1
{
private void BtnModify_Click(object sender, EventArgs e)
{
var ruleData = ..... //get current rule data
var rulesForm = new Rules();
rulesForm.SetData(ruleData); //pass initial state to the form
rulesForm.SaveChanges = this.ApplyRules; //pass a method which will be called on save
rulesForm.Show();
}
private bool ApplyRules(RuleData ruleData)
{
//do whatever you like with the rules here
return true;
}
}
class RuleForm
{
public void SetData(RuleData ruleData)
{
//initialize fields, etc
}
public Func<RuleData, bool> SaveChanges { get; set; }
private void BtnSave_Click(object sender, EventArgs e)
{
var ruleData = .... //get data from form fields
if(this.SaveChanges(ruleData))
this.Close();
}
}
class RuleData
{
//whatever data you need
}
I have two forms.
1st one is frmStudentDetails. It has a datagrid
2nd one is frmStudentRegistration. It has some text boxes and Add button
When user enter some information and press "Add" button, I want to add those to the datagrid one by one
For accomplish that 1st I created following method in frmStudentDetails
public void AddRecord(string StID, string Name)
{
DataGridViewRow row = (DataGridViewRow)dgvStDetails.Rows[0].Clone();
row.Cells[0].Value = StID;
row.Cells[1].Value = Name;
dgvStDetails.Rows.Add(row);
}
I called it on frmStudentRegistration form's add button -->
private void btnAdd_Click(object sender, EventArgs e)
{
frmStudentDetailsForm frm = new frmStudentDetailsForm();
frm.AddRecord(txtStudentID.Text, txtStName.Text);
frm.ShowDialog();
}
Then the problem is, itz generating new forms to show every new record.
But i want to add all records in one form.
please somebody help me for that
In your "frmStudentRegistration" Class, add a "public frmStudentDetailsForm StudentDetailsForm { get; set; };" Property Declaration at the Class level.
Set it equal to the Instance of your "frmStudentDetailsForm" Class. There are several ways you can do so (i.e. in "frmStudentRegistration" Class's custom Constructor or its "Load" Event Handler), but for a novice, I would recommend just setting it after you Instantiate your "frmStudentRegistration" Class and before you call that Instance Variable's "Show" Method. NOTE: If you call "frmStudentRegistration"'s "ShowDialog" Method, any updates to other Forms (i.e. "frmStudentDetailsForm") won't show on screen until you exit "ShowDialog" or you explicity call the other Form's "Show" or "ShowDialog" Methods.
On a side note, I'm curious as to why you're calling "frmStudentDetailsForm"'s "ShowDialog" Method from "frmStudentRegistration". In my experience, either: a) the grid's Form would call "ShowDialog" on the add-item's Form with the "Add" option (i.e. via Button, Context Menu item, Insert Key and/or Enter Key (after filling in a new template Row)) being on the grid's Form or b) both Forms would remain open at the same time (via Modeless "Show" Method Calls) with the "Add" option on the add-item's form. Btw, in my experience your "frmStudentRegistration" Form would be called the "frmStudentDetailsForm" and your "frmStudentDetailsForm" would be called something like "frmStudentsForm", "frmStudentsListForm" or "frmStudentSummariesForm", etc. Also, btw, the .NET naming convention would be "var studentDetailsForm = new StudentDetailsForm()" (i.e. suffix vs. prefix/both and no abbrev.). Actually, if were up to me, it would be "var StudentDetailsFrmObj = new StudentDetailsFrmCls()". ;)
Ex.
In "frmStudentRegistration":
public frmStudentDetailsForm StudentDetailsForm { get; set; }
public void main ()
{
var studentRegistration = new frmStudentRegistration();
var studentDetailsForm = new frmStudentDetailsForm();
studentRegistration.StudentDetailsForm = studentDetailsForm;
studentRegistration.Show();
}
In "frmStudentRegistration":
private void btnAdd_Click(object sender, EventArgs e)
{
StudentDetailsForm.AddRecord(txtStudentID.Text, txtStName.Text);
StudentDetailsForm.ShowDialog();
}
I've just about got this but I'm not doing something right. I'm trying to pass a value from form1 to form2. On form2 I've got a property set up allowing access to one of it's text boxes. On form1 I've got it set to open an instance of form2 and pass a value from an object in a listbox to form2's text box. It seems like I've got things set up almost right because I tested it by posting the object value in a messagebox.show and it displayed the different object values just how I planned. For some reason though when I actually run it form2 will open but it will not set the value I passed to the textbox in the form, it's just a blank form. I've got no errors but I'm thinking it has something to do with the data not being passed directly to my new instance of form2. I hope I explained it well enough. Any help is appreciated.
form 1
private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
{
frmProperties editProperties = new frmProperties();
editProperties.ShowDialog();
Employee person = (Employee)lstBoxEmployees.Items[lstBoxEmployees.SelectedIndex];
editProperties.TextFirstName = person.EmployeeFirstName;
}
form 2
public string TextFirstName
{
get { return txtFirstName.Text; }
set { txtFirstName.Text = value; }
}
You have to set the textbox before you show the dialog.
private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
{
frmProperties editProperties = new frmProperties();
Employee person = (Employee)lstBoxEmployees.Items[lstBoxEmployees.SelectedIndex];
editProperties.TextFirstName = person.EmployeeFirstName;
editProperties.ShowDialog();
}
private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
{
frmProperties editProperties = new frmProperties();
editProperties.ShowDialog();
Employee person = new Employee ();
person.EmployeeFirstName = lstBoxEmployees.Items[lstBoxEmployees.SelectedIndex];
editProperties.TextFirstName = person.EmployeeFirstName;
}
Please, what is wrong with this:
Form2_Closing:
Form1.DataGridView1.Rows[0].Cells[1].Value = "323";
Error: Index was out of range. Must be non-negative and less than the
size of the collection. Parameter name: index
DGV on Form1 has 10 Rows and 14 Columns
Create a new Winforms Project and add a button & its click handler and a TextBox [make it accessible, such that the child can set value. I have made it public in the designer for now] too. Then add the following code on this Form. Additionally, add a new Form (Form2) in the Project.
private void button1_Click(object sender, EventArgs e)
{
var child = new Form2();
child.FormClosing += new FormClosingEventHandler(ChildFormClosing);
this.Enabled = false;
child.Show(this);
}
void ChildFormClosing(object sender, FormClosingEventArgs e)
{
var child = sender as Form2;
if (child != null)
{
if (child.DialogResult == DialogResult.None)
{
// do data grid view manipulation here
// for ex:
(child.Owner as Form1).textBox1.Text = "Hi";
}
}
Enabled = true;
}
From your comments it looks like you are trying to create a custom Dialog that will manipulate a particular value within a DataGridView on the calling form. I suggest looking at this example of creating a custom message box.
You'll be able to return say the value that you want the DataGridViewCell updated to, then set it on your Form1.
I want to get some data to fill a listview control, but this data it's determined in other form. This is what I code in form1 (Nuevo_Credito):
private void combo_cliente_SelectionChangeCommitted(object sender, EventArgs e)
{
Credito_Grupo ventana = new Credito_Grupo(combo_cliente.SelectedItem);
ventana.ShowDialog();
}
public void AgregaIntegrantes(string id, string nombre, string monto)
{
ListViewItem elem = new ListViewItem(id);
elem.SubItems.Add(nombre);
elem.SubItems.Add(monto);
listView_integrantes.Items.Add(elem);
}
I'm invoking form2 (Credito_grupo) as show dialog window, then I want to retrieve some values and pass them to Form1 using the public method "AgregaIntegrantes". So in form2 I did the following:
public Credito_Grupo(dynamic item)
{
this.id = item.IDCliente;
this.nombre = item.NomComp;
InitializeComponent();
}
private void Credito_Grupo_Load(object sender, EventArgs e)
{
text_nombre.Text = this.nombre;
}
private void button_AgregaCliente_Click(object sender, EventArgs e)
{
Nuevo_Credito obj = new Nuevo_Credito();
obj.AgregaIntegrantes(id.ToString(), nombre, text_monto.Text);
this.Close();
}
When the event button_AgregaCliente_click is triggered I need to add the data to listview in form1 using the method described above, but none data is added. I found a solution using delegates here 3077677, is there an approach using objects?
You have an error in button_AgregaCliente_Click method (the last one in the listing). You create a new Nuevo_Credito form there, and pass the data to listview. It looks OK. But this newly created Nuevo_Credito form does exist only in the local variable, so then you throw it away without displaying it when button_AgregaCliente_Click finishes.
I think you need to delete this line: Nuevo_Credito obj = new Nuevo_Credito();
You need to get your real Nuevo_Credito form, not create a new one here.
You can send this from your Nuevo_Credito to the constructor of the Credito_Grupo form. Then you can use it to call back to the original Nuevo_Credito. This approach is based only on objects, and not delegates. As you wanted. :-)