Filling a List with Objects in C# - c#

I'm trying to make a simple program with GUI in c# but unfortunately I have some difficulties. Now I'll try to explain the basic structure of my program. I have 3 classes for three different groups of people(University Professors, University Students and people who don't work or study either). I have some methods for reading information from a file(professor's title, name, university name, student's faculty number, etc.). I read the file line by line and save the information in an object of type one of the 3 classes. After that I put that object in a List. So here comes my problem. I want to read every object from the list and take its name and put in on a dynamically created labels on other windows form. Here it is a little part of my code:
private void button1_Click(object sender, EventArgs e)
{
ForeignPeople fPeople = new ForeignPeople();
UniversityProfessors uProf = new UniversityProfessors();
UniversityStudents uStudents = new UniversityStudents();
if (radioButton1.Checked == true)
{
objList1 = loadList();
Form2 f2 = new Form2();
for (int i = 0; i < objList1.Count; i++)
{
if (objList1[i] is UniversityStudents)
{
uStudents = (UniversityStudents)objList1[i];
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
Label et_tag = new Label();
et_tag.Name = "label" + i.ToString();
et_tag.Text = uStudents.getFirstName().ToString() + " " + uStudents.getLastName().ToString();
et_tag.AutoSize = true;
f2.tableLayoutPanel1.Controls.Add(et_tag, 0, i);
Label op = new Label();
op.AutoSize = true;
op.Text = "description";
f2.tableLayoutPanel1.Controls.Add(op, 1, i);
}
else if (objList1[i] is UniversityProfessors)
{
uProf = (UniversityProfessors)objList1[i];
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
Label et_tag = new Label();
Label label = new Label();
et_tag.Name = "label" + i.ToString();
et_tag.Text = uProf.getFirstName().ToString() + " " + uProf.getLastName().ToString();
et_tag.AutoSize = true;
f2.tableLayoutPanel1.Controls.Add(et_tag, 0, i);
Label op = new Label();
op.AutoSize = true;
op.Text = "description";
f2.tableLayoutPanel1.Controls.Add(op, 1, i);
}
else if (objList1[i] is ForeignPeople)
{
fPeople = (ForeignPeople)objList1[i];
String name, Name;
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
Label et_tag = new Label();
et_tag.Name = "label" + i.ToString();
et_tag.Text = fPeople.getFirstName().ToString() + " " + fPeople.getLastName().ToString();;
et_tag.AutoSize = true;
f2.tableLayoutPanel1.Controls.Add(et_tag, 0, i);
Label op = new Label();
op.AutoSize = true;
op.Text = "description";
f2.tableLayoutPanel1.Controls.Add(op, 1, i);
}
}
f2.FormClosed += new FormClosedEventHandler(childFormClosed);
f2.Show();
this.Hide();
}
But if I have two or more lines which belongs to one Object(for example I have two or more students, or two or more professors in the file) the text of all the labels becomes with the name of the last read object. I know that the problem is in the List or in the cast which I make but I can't figure out another way of doing what I want. I'll be extremely grateful if someone can help.

In addition to the change that phoog mentioned in the comments, I would also move the instantiation of your "people" objects down into the section for each within the loop. That way you can be sure that the old one is being properly destroyed. I would also add a check to make sure that the cast worked.
private void button1_Click(object sender, EventArgs e)
{
if (radioButton1.Checked == true)
{
objList1 = loadList();
Form2 f2 = new Form2();
for (int i = 0; i < objList1.Count; i++)
{
if (objList1[i] is UniversityStudents)
{
UniversityStudents uStudents = (UniversityStudents)objList1[i];
if (uStudents != null)
{
// do stuff
}
else
{
// do something sensible with the error here
}
}
// if clauses for the other "people" objects
// ...
}
f2.FormClosed += new FormClosedEventHandler(childFormClosed);
f2.Show();
this.Hide();
}
}

Related

How to access and change the property of a label that I created dynamically

I wanna change the Text properties of Label using Buttons just like in hangman; but after I created the Label, I became confused when I try to access the specific Label
// creating label
for (int i = 0; i < numericUpDown1.Value; i++)
{
Label l = new Label();
l.Text = "_";
l.Width = 20;
l.Height = 25;
l.Left = i * 20 + 510;
l.Top = 20;
l.BackColor = Color.Transparent;
groupBox2.Controls.Add(l);
}
// function to change the label text
// if I clicked the button
// the first label text will be changed to the text in the button i clicked
private void B_Click(object sender, EventArgs e)
{
var thsBtn = (Button)sender;
bool benar = false;
if (benar == false)
{
thsBtn.Text = " ";
thsBtn.Enabled = false;
}
else
{
thsBtn.Enabled = false;
}
}
You can organize created Labels into a collection, say, List<Label>:
private List<Label> m_CreatedLabels = new List<Label>();
...
// Remove all previous labels
foreach (Label lbl in m_CreatedLabels)
lbl.Dispose();
m_CreatedLabels.Clear();
// Create new ones
for (int i = 0; i < numericUpDown1.Value; i++) {
m_CreatedLabels.Add(new Label() {
Text = "_",
Width = 20,
Height = 25,
Left = i * 20 + 510,
Top = 20,
BackColor = Color.Transparent,
Parent = groupBox2
});
}
Now you have m_CreatedLabels collection to work with created Labels, e.g.
private void B_Click(object sender, EventArgs e) {
var thsBtn = sender as Button;
// you may want to add a condition into FirstOrDefault(), e.g.
// .FirstOrDefault(lbl => lbl.Text == "_")
// - first label with "_" Text
Label lblToProcess = m_CreatedLabels
.FirstOrDefault();
if (null != lblToProcess)
lblToProcess.Text = thsBtn.Text;
thsBtn.Enabled = false;
}
One option here is to give your dynamically created Label instances a Name. From there, you should be able to use ControlCollection.Find to find your Label instances by name.
private void CreateLabels()
{
for (int i = 0; i < numericUpDown1.Value; i++)
{
Label l = new Label();
l.Name = $"DynamicLabel{i}";
l.Text = "_";
l.Width = 20;
l.Height = 25;
l.Left = i * 20 + 510;
l.Top = 20;
l.BackColor = Color.Transparent;
groupBox2.Controls.Add(l);
}
}
private void DoSomethingWithADynamicLabel(int dynamicLabelIndex)
{
Label l = groupBox2.Controls.Find($"DynamicLabel{i}", true).FirstOrDefault() as Label;
if (l is null)
{
// Couldn't find the label...
return;
}
// Do something with l
}
When creating the Label instances inside CreateLabels, I'm simply appending the for loop's counter to the string "DynamicLabel". This gives you a bunch of Labels with names like "DynamicLabel0", "DynamicLable1", "DynamicLabel2", etc...
Then in DoSomethingWithADynamicLabel, assuming you have the index of the Label you want to deal with, you can use groupBox2.Controls.Find to actually find the Label you're interested in. ControlCollection.Find returns Control[], so calling FirstOrDefault will take the first item from the array or null if no Control with the given name exists.

C# - Dynamic buttons with dynamic data-entering

I have a bit of an issue here.
I'm trying to create dynamic clickevents with variable data.
for(int i = 0; i < data.Devices.Length; i++)
{
Button _button = new Button();
_button.Size = new Size(100, 15);
_button.Text = data.Devices[i].Alias;
_button.Name = "textbox" + i.ToString();
_button.Location = new Point(x,y);
x += 110;
if(x > 1850)
{
y += 50;
x = 10;
}
if (data.Devices[i].OnlineState == "Online")
{
_button.BackColor = Color.Green;
}
else
{
_button.BackColor = Color.Red;
}
_button.Click += (Sender, args) =>
{
MessageBox.Show(data.Devices[i].Alias);
};
Controls.Add(_button);
}
The idea here is that I'll create buttons until length of the list is done (The list and position of these objects vary).
What I'm looking for is to make a bunch of buttons, and when you click on the button there should you'll open another screen with some statistics attached to that object.
Since the data will vary A LOT there is no way to hardcode each scenario, but instead I'm looking to do the same thing as you can do in Android, see below.
for (int i = 1; i <= 20; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
Button btn = new Button(this);
btn.setId(i);
final int id_ = btn.getId();
btn.setText("button " + id_);
btn.setBackgroundColor(Color.rgb(70, 80, 90));
linear.addView(btn, params);
btn1 = ((Button) findViewById(id_));
btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Toast.makeText(view.getContext(), "Button clicked index = " + id_, Toast.LENGTH_SHORT).show();
}
});
Is there any way I can achieve this?
Kind Regards.
I think the problem you are experiencing is regarding closures.
In your example you are adding the event handler with a delegate that's referencing the variable i which changes each loop iteration. So when the event handler is actually executed (when the button is clicked) i is out of scope.
You could do something like this:
private void Form1_Shown(object sender, EventArgs e)
{
var items = new[] { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
for (int i = 0; i < items.Length; i++)
{
var btn = new Button
{
Text = $"Button {i + 1}",
Tag = items[i]
};
btn.Click += (object obj, EventArgs args)
=>
{
MessageBox.Show($"Hello. {((Button)obj).Tag}");
};
flowLayoutPanel1.Controls.Add(btn);
}
}
In your case - the button's tag can be set to data.Devices[i] - in other words it doesn't have to be a string it can be an object.
I start off by creating the buttons with the and placing them on the screen.
for (int i = 0; i < data.Devices.Length; i++){
Button _button = new Button();
_button.Size = new System.Drawing.Size(100, 55);
_button.Text = data.Devices[i].Alias;
_button.Name = "dynamicButton";
_button.FlatStyle = FlatStyle.Flat;
_button.Tag = data.Devices[i].Alias + "|" +
data.Devices[i].DeviceId + "|" +
data.Devices[i].LastSeen + "|" +
data.Devices[i].OnlineState + "|" +
data.Devices[i].Description + "|" +
data.Devices[i].RemotecontrolId;
_button.Location = new System.Drawing.Point(x, y);
x += 110;
if (x > 1850)
{
y += 60;
x = 30;
}
_button.Click += new EventHandler(bt_click);
Controls.Add(_button);
}
And then using the onclick event.
protected void bt_click(object sender, EventArgs e)
{
Button btn = sender as Button;
String[] information = btn.Tag.ToString().Split('|');
String present = "Namn: " + information[0]
+ "\nDeviceID: " + information[1]
+ "\nSenast uppe: " + information[2]
+ "\nStatus: " + information[3]
+ "\nEmail: " + information[4]
+ "\nTW-ID: " + information[5];
MessageBox.Show(present);
//DO THE THINGS WITH THE INFORMATION
}
And then to unload everything and eventually updating the status of the device.
for (int i = 0; i < 10; i++)
{
foreach (Control item in Controls.OfType<Control>())
{
if (item.Name == "dynamicButton")
{
Controls.Remove(item);
}
}
}
For some reason I have to loop that thing a couple of times, beacuse it won't delete them all, but instead deleting them in some pattern.
(only takes every other one each time)
I looped it 10 times for good measures.
It does exactly what I want it to do, and new devices gets added automaticly and removed devices dissapears just as fast.

Windows form receive text input

Hey guys/girls I got myself stuck and I was hoping I could get some help with it. simply said I'm trying to make a soccerpool on windows form. Because the player can put in as many team's as he/she wants I put the code that makes the betting panels in a (for)loop with the text as 0. very handy if I say so myself but now I can't retrieve the correct input from the user or without breaking the loop. any idea's?
for (int i = 0; i < hometable.Rows.Count; i++)
{
DataRow dataRowHome = hometable.Rows[i];
DataRow dataRowAway = awayTable.Rows[i];
Label lblHomeTeam = new Label();
Label lblAwayTeam = new Label();
TextBox txtHomePred = new TextBox();
TextBox txtAwayPred = new TextBox();
lblHomeTeam.TextAlign = ContentAlignment.BottomRight;
lblHomeTeam.Text = dataRowHome["TeamName"].ToString();
lblHomeTeam.Location = new Point(15, txtHomePred.Bottom + (i * 30));
lblHomeTeam.AutoSize = true;
txtHomePred.Text = "0";
txtHomePred.Location = new Point(lblHomeTeam.Width, lblHomeTeam.Top - 3);
txtHomePred.Width = 40;
txtAwayPred.Text = "0";
txtAwayPred.Location = new Point(txtHomePred.Width + lblHomeTeam.Width, txtHomePred.Top);
txtAwayPred.Width = 40;
lblAwayTeam.Text = dataRowAway["TeamName"].ToString();
lblAwayTeam.Location = new Point(txtHomePred.Width + lblHomeTeam.Width + txtAwayPred.Width, txtHomePred.Top + 3);
lblAwayTeam.AutoSize = true;
pnlPredCard.Controls.Add(lblHomeTeam);
pnlPredCard.Controls.Add(txtHomePred);
pnlPredCard.Controls.Add(txtAwayPred);
pnlPredCard.Controls.Add(lblAwayTeam);
So what my end goal is, is recieving the input from the user validating them and then storing them in a database.
Well, depending on how the user activates an event that requires the reading of the TextBox you have a few possible solutions.
Here is one where the TextBox (read all TextBox's) waits for enter:
private void Form_Load(object sender, EventArgs e)
{
while(someLoop)
{
TextBox theTextBox = new TextBox();
theTextBox.Name = "SomeUniqeName";//Maybe team name?
theTextBox.KeyUp += TheTextBox_KeyUp;
}
}
private void TheTextBox_KeyUp(object sender, KeyEventArgs e)
{
if ( e.KeyCode == Keys.Enter )
{
TextBox textbox = (TextBox) sender;//Get the textbox
//Just an example
listOfTeams.First( r => r.TeamName == textbox.Name )
.SomeOtherProperty = textbox.Text;
}
}
The textbox's are now identifiable by their name and all have an event. No matter how many you make.
If you will store the data later with 1 click of a button (and another loop) this solution might be better:
string[] Teams = { "teamA", "teamB", "teamC" };
private void Form1_Load(object sender, EventArgs e)
{
for ( int i = 0; i < Teams.Length; i++ )
{
TextBox theTextBox = new TextBox();
//Prefix the name so we know this is a betting textbox
//Add the 'name' (teams[i] in this case) to find it
theTextBox.Name = "ThePrefix" + Teams[i];
}
}
private void someButton_Click(object sender, EventArgs e)
{
//We want all betting textbox's here but also by team name
for ( int i = 0; i < Teams.Length; i++ )
{
//Because we set the name, we can now find it with linq
TextBox textBox = (TextBox) this.Controls.Cast<Control>()
.FirstOrDefault( row => row.Name == "ThePrefix" + Teams[i] );
}
}
This way each textbox is identifiable and won't conflict with other textbox's (because of 'ThePrefix'). This is essentially the other way around from the first method as it looks for the textbox based on data rather than data based on textbox name.

How can i change label text from different button in C#

I descripted my problem in script comments. Function GraphicClassStructure is only for create buttons and label. But mainly function is plusButton_click. I need that if i clicked on first plus button so i need change text for first added label.
Script: (Problem is here: plusButton_click, i descripted problem to case)
class GraphicClassStructure : GraphicPosition
{
// Button
public Button plus;
public PictureBox classBackround = new PictureBox();
// Label
Label points;
public void CreateSpellsButton()
{
for (int j = 0; j < 3; j++)
{
for (int i = 0; i < 10; i++)
{
plus = new Button();
points = new Label();
switch (j)
{
case 0:
points.Location = Location[1][i];
points.Location = new Point(points.Location.X + 8, points.Location.Y + 45);
plus.Location = Location[2][i];
break;
case 1:
plus.Location = Location[2][i];
plus.Location = new Point(plus.Location.X + 205, plus.Location.Y);
points.Location = Location[1][i];
points.Location = new Point(points.Location.X + 213, points.Location.Y + 45);
break;
case 2:
plus.Location = Location[2][i];
plus.Location = new Point(plus.Location.X + 410, plus.Location.Y);
points.Location = Location[1][i];
points.Location = new Point(points.Location.X + 418, points.Location.Y + 45);
break;
}
// Labels for point
points.BackColor = Color.Transparent;
points.ForeColor = Color.FromArgb(((int)(((byte)(193)))), ((int)(((byte)(196)))), ((int)(((byte)(181)))));
points.BackgroundImageLayout = ImageLayout.Stretch;
points.FlatStyle = FlatStyle.Flat;
points.Name = "points";
points.Name = spells.Name + i.ToString() + "_" + j.ToString();
if (i >= 6)
points.Text = "0 / 2";
else
points.Text = "0 / 1";
points.VisibleChanged += new EventHandler(classUniqueButtons_VisibleChanged);
// Plus
plus.BackColor = Color.Transparent;
plus.BackgroundImage = BuildResource.plus;
plus.BackgroundImageLayout = ImageLayout.Stretch;
plus.FlatAppearance.BorderSize = 0;
plus.FlatAppearance.MouseDownBackColor = Color.Transparent;
plus.FlatAppearance.MouseOverBackColor = Color.Transparent;
plus.FlatStyle = FlatStyle.Flat;
plus.Name = "plus";
plus.Size = Size[0][9];
plus.UseVisualStyleBackColor = false;
plus.Name = plus.Name + i.ToString() + "_" + j.ToString();
plus.Click += new EventHandler(plusButton_click);
plus.VisibleChanged += new EventHandler(classUniqueButtons_VisibleChanged);
plus.MouseEnter += new EventHandler(this.classButton_MouseEnter);
plus.MouseLeave += new EventHandler(this.classButton_MouseLeave);
classBackround.Controls.Add(plus);
classBackround.Controls.Add(points);
}
}
}
private void plusButton_click(object sender, EventArgs e)
{
var currentButton = sender as Button;
var name = currentButton.Name;
switch (name)
{
// Ultimate
case "plus0_0":
// If i clicked on this button so i need change text for first added label
// I try this, but it changed only last added labe
points.Text = "test";
break;
case "plus0_1":
// If i clicked on this button so i need change text for second added label
break;
}
}
private void minusButton_click(object sender, EventArgs e)
{
var currentButton = sender as Button;
var name = currentButton.Name;
switch (name)
{
// Ultimate
case "minus0_0":
// If i clicked on this button so i need change text for first added label
// I try this, but it changed only last added labe
points.Text = "test";
break;
case "minus0_1":
// If i clicked on this button so i need change text for second added label
break;
}
}
}
very simple to do here what you need
for (int j = 0; j < 3; j++)
{
for (int i = 0; i < 10; i++)
{
plus = new Button();
//tag is an object and can be used to reference any other object
plus.Tag = new Label();
and to retrieve what you need do this
private void plusButton_click(object sender, EventArgs e)
{
var currentButton = sender as Button;
var name = currentButton.Name;
switch (name)
{
// Ultimate
case "plus0_0":
(currentButton.Tag as Label).Text = "test";
break;
case "plus0_1":
(currentButton.Tag as Label).Text ="test2"
break;
}
}
You are using a global reference to the label which you have overwritten in each iteration of your loop. Therefore it always references the last label. Since you always add the label after the button and have the reference to the button you can probably get the correct label like this:
Label pointsLabel = (Label)classBackround.Controls[classBackround.Controls.IndexOf(currentButton) + 1]

storing multiple controls and update radio button values?

What am doing here is; making a UI to update values visually, will add more support for other types too. Possibly all types.
updateIcons this function is called everytime the controller is loaded, and has new values,names everytime.
countControls to keep track of controllers, so if can update values on clicks.
myP is the object that holds the values taken at runtime, user shuffles values by pressing tab from another screen
created radiobuttons groupboxes to allow radiobutton group to be managed.
properties all belong to one object. each property has few possible values like in my example, the enums.
now am kinda lost, not sure how to best do this, as now my rb_CheckedChanged is returning some kind of mess.
How do i do this the right way ? all together, i feel its somewhat the right approach At least.
I thought of making a dictionary of ? to use it at the checked event. not exactly sure how
private void updateIcons(List<Props> prop) {
countControls++;
locationY = 10;
int gbHeight;
foreach (var p in prop) {
radioButtonY = 10;
IType pType = p.Type;
if (pType is Enum) {
var myP = new MyProp(p, this);
GroupBox gb = new GroupBox();
gb.Location = new Point(nextLocationX,locationY);
nextLocationX += rbWidth+10;
gb.Name = "groupBox" + countControls;
gb.Text = "smthn";
var TypesArray = set here;
gbHeight = TypesArray.Length;
foreach (var type in TypesArray) {
getimagesPath(TypesArray);
RadioButton rb = new RadioButton();
rb.Appearance = Appearance.Button;
rb.Width = rbWidth;
rb.Height = rbHeight;
rb.Name = type.Name + countControls;
rb.Text = type.Name;
string path = imagePaths[type.Name];
Bitmap rbImage = new Bitmap(path);
rb.BackgroundImage = rbImage;
countControls++;
rb.Location = new Point(radioButtonX, radioButtonY);
if (myP.Value != null && type.Name.SafeEquals(myP.Value.ToString())) {
rb.Checked = true;
}
radioButtonY += rbHeight;
gb.Controls.Add(rb);
rb.CheckedChanged += rb_CheckedChanged;
}
gb.Height = rbHeight * gbHeight + 20;
gb.Width = rbWidth + 10;
Controls.Add(gb);
}
}
}
void rb_CheckedChanged(object sender, EventArgs e) {
RadioButton rb = (RadioButton)sender;
Control control = (Control)sender;
if (rb.Checked) {
MessageBox.Show("You have just checked: " + rb.Text);
MessageBox.Show("You have just called Controller: " + control.Name);
var t = PropSeq;
}
else {
MessageBox.Show("you have just unchecked: " + rb.Text);
MessageBox.Show("You have just called Controller: " + control.Name);
}
}
I think your code might be a little messed up and not the easiest to read. It looks plain invalid with not all closing braces present? Try the code below which will create two group boxes, each with five radio buttons. This should help you achieve what you are trying to do (full listing for a basic Form):
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
CreateButton();
}
private void CreateButton()
{
// Add two group boxes
for (int groupCount = 1; groupCount < 3; groupCount++)
{
var groupBox = new GroupBox();
groupBox.Location = new Point(220 * (groupCount - 1), 10);
groupBox.Name = string.Format("groupBox{0}", groupCount);
groupBox.Text = string.Format("Group Box {0}", groupCount);
// Add some radio buttons to each
for (int buttonCount = 1; buttonCount < 6; buttonCount++)
{
var radioButton = new RadioButton();
radioButton.Width = 150;
radioButton.Location = new Point(10, 30 * buttonCount);
radioButton.Appearance = Appearance.Button;
radioButton.Name = string.Format("radioButton{0}", buttonCount);
radioButton.Text = string.Format("Dynamic Radio Button {0} - {1}", groupCount, buttonCount);
radioButton.CheckedChanged += radioButton_CheckedChanged;
// Add radio button to the group box
groupBox.Controls.Add(radioButton);
groupBox.Height += 20;
}
// Add group box to form
Controls.Add(groupBox);
}
}
private void radioButton_CheckedChanged(object sender, EventArgs e)
{
// Get button and only show the selected (not now de-selected item)
var radioButton = (RadioButton)sender;
if (radioButton.Checked)
{
MessageBox.Show("You have just checked: " + radioButton.Text);
}
}
}
}

Categories