Dynamic UI, Loop not working correctly - c#

Here is the code I'm using
It's supposed to create labels for each module name entered by a user and all the assessment names for that module under it. Should work with any number of modules and assessments. The problem is that it only shows assessments for the last module displayed.
dat.Modules and dat.Assessments are arraylists, each of them holds 4 elements with info about a module or an assessments, that is why i divide the count by 4.
private void testingButton_Click(object sender, EventArgs e)
{
int pos1 = 50;
int pos2 = 150;
int modLength = dat.Modules.Count;
modLength = modLength / 4;
int assessLength = 0;
int arrayData = 0;
int displayCount = 0;
for (int i = 0; i < modLength; i++)
{
this.moduleLabels.Add(new Label());
System.Drawing.Point pLabel1 = new System.Drawing.Point(50, pos1);
(moduleLabels[i] as Label).Location = pLabel1;
(moduleLabels[i] as Label).Size = new System.Drawing.Size(100, 13);
(moduleLabels[i] as Label).Text = dat.Modules[arrayData].ToString();
tabPage5.Controls.Add((moduleLabels[i] as Label));
String asd = dat.Modules[arrayData + 2].ToString();
Console.WriteLine(asd);
assessLength = int.Parse(asd);
pos2 = pos1 + 25;
for (int y = 0; y < assessLength; y++)
{
this.assessLabels.Add(new Label());
System.Drawing.Point pLabel2 = new System.Drawing.Point(70, pos2);
(assessLabels[y] as Label).Location = pLabel2;
(assessLabels[y] as Label).Size = new System.Drawing.Size(250, 13);
(assessLabels[y] as Label).Text = dat.Assessments[displayCount + 1].ToString() + " weights " + dat.Assessments[displayCount+2].ToString() +"%, Enter your mark:";
textboxComputer.Add(new TextBox());
System.Drawing.Point pText1 = new System.Drawing.Point(400, pos2);
(textboxComputer[y] as TextBox).Location = pText1;
(textboxComputer[y] as TextBox).Size = new System.Drawing.Size(20, 20);
tabPage5.Controls.Add(assessLabels[y] as Label);
tabPage5.Controls.Add(textboxComputer[y] as TextBox);
pos2 = pos2 + 25;
displayCount = displayCount + 4;
}
pos1 = pos2+25;
arrayData = arrayData + 4;
}
}
this is an example of what it displays
http://dc540.4shared.com/download/YI8IENYI/tsid20120501-211723-cbc785f9/asd.jpg
The first two modules should have their assessments listed. The first one doesn't display any. For Java it only displays the last one, out of 3 total for that module. And for the last Module "Another Module" it displays all assessments.

For each increment of i, y starts at 0. You then add new labels to assessLabels, but attempt to access the one you added by using assessLabels[y] which would usually yield the labels created for the previous value of i. This causes labels created for the first module to be reused by the next, and so forth.
A quick solution is not to use assessLabels[y] but assessLabels[assessLabels.Count - 1].
A better solution is to create a local variable for the labels, set their properties, and then add them to the list:
for (int y = 0; y < assessLength; y++)
{
Label assessLabel = new Label();
assessLabel.Location = ...;
// etc.
tabPage5.Controls.Add(assessLabel);
assessLabels.Add(assessLabel);
}
This would also remove the need to continuously cast the ArrayList members and unneeded access to the list.
PS. If assessLabels only contains objects of type Labels, consider using a List<Label> instead.

Related

How do I update an array of labels

This sounds wierd but i have no idea how to do this, despite the fact i've read lots of articles and answers about it.
this one seems to be the most clear, but unfortunately it doesn't help me.
Like this
so i have an array of labels that should be updated during the for cycles:
public const int NUM_OF_PAGES = 128;
...
int[] PEcnt = new int[NUM_OF_PAGES];
Label[] PElabels = new Label[NUM_OF_PAGES];
for (int i = 0; i < NUM_OF_PAGES; i++) // initialization
{
PEcnt[i] = 0;
PElabels[i] = new Label();
PElabels[i].Content = 0;
PElabels[i].Margin = new Thickness(50 + 80 * (i % 16), 50 + 20 * (i / 16), 0, 0);
Grid1.Children.Add(PElabels[i]);
}
...
for (int i = 0; i < NUM_OF_PAGES; i++)
{
PEcnt[i] += 2;
PElabels[i].Content = PEcnt[i];
BindingOperations.GetBindingExpressionBase(PElabels[i], Label.ContentProperty).UpdateTarget(); //trying to update
}
but this throws me "System.NullReferenceException"
hope u will help me, learning is difficult, but im trying hard
don't .UpdateTarget(); and it should be okay I believe
you don't need to get binding expressions for this, if you are using it else where do not update target here.
so first of all:
The Label.Content Property stores an object! So you can store an Integer in it without casting!
You are trying to update an BindingExpression on an NOTbinded Property!
So the way you should solve your problem, is by just NOT updating it.
Because your Label isn't using any bindings your called function returns null.
So just Store your Integer in the content property and YOLO. Here is the working code:
public const int NUM_OF_PAGES = 128;
Label[] PElabels = new Label[NUM_OF_PAGES];
...
for (int i = 0; i < NUM_OF_PAGES; i++) // initialization
{
PElabels[i] = new Label();
PElabels[i].Content = 0; //Storing your Data as int in object
PElabels[i].Margin = new Thickness(50 + 80 * (i % 16), 50 + 20 * (i / 16), 0, 0);
Grid1.Children.Add(PElabels[i]);
}
...
for (int i = 0; i < NUM_OF_PAGES; i++)
{
PElabels[i].Content = (int)PElabels[i].Content + 2; //Storing your new Data
}
SO!!!!
If you realy need to use this update binding, you have to set your content property to an Binding. To do so, you have to store your data in some property for example in the Label.DataContext. Now you need to set the Binding (pointing at the DataContext) to you Label.ContentProperty. After doing that you can update your BindingExpression and the Label will change.
Working Code here:
public const int NUM_OF_PAGES = 128;
Label[] PElabels = new Label[NUM_OF_PAGES];
...
for (int i = 0; i < NUM_OF_PAGES; i++) // initialization
{
PEcnt[i] = 0;
PElabels[i] = new Label();
PElabels[i].DataContext = 0; //Storing your Data
Binding PEcntBind = new Binding("DataContext");
PEcntBind.Source = PElabels[i];
PElabels[i].SetBinding(Label.ContentProperty, PEcntBind); //Binding to your stored Data
PElabels[i].Margin = new Thickness(50 + 80 * (i % 16), 50 + 20 * (i / 16), 0, 0);
Grid1.Children.Add(PElabels[i]);
}
...
for (int i = 0; i < NUM_OF_PAGES; i++)
{
PEcnt[i] += 2;
PElabels[i].DataContext = PEcnt[i]; //Storing your new Data
BindingExpression LabBindEx = (PElabels[i] as FrameworkElement).GetBindingExpression(Label.ContentProperty);
LabBindEx.UpdateTarget(); //Updating the Bindings
}
(Oh and by the way you have to use GetBindingExpression not GetBindingExpressionBase)

C# Generating Labels Dynamically

I have got the below code to generate labels using for loop, however there is a problem with the for loop, if the productList has 4 items, it generates 1 label instead of 4. I can't figure out what the problem is.
List<models.Car> carList = carController.getCars();
for (int i = 0; i < carList.Count; i++)
{
List<models.Product> productList = productController.getProducts(carList[i].Model);
for (int j = 0; j < productList.Count; j++)
{
productLabels.Add(new Label());
var productLabelsPoint = new System.Drawing.Point(200, 40 + i * 50);
(productLabels[j] as Label).Location = productLabelsPoint;
(productLabels[j] as Label).Size = new System.Drawing.Size(150, 15);
(productLabels[j] as Label).Text = productList[j].Title;
this.Tab.TabPages["tab1"].Controls.Add((productLabels[j] as Label));
}
}
This only relies on i, not on j:
System.Drawing.Point productLabelsPoint = new System.Drawing.Point(200, 40 + i * 50);
So you might be drawing the labels one on top of the other.
In which case, you'd need to add j into the mix, for example like this:
System.Drawing.Point productLabelsPoint = new System.Drawing.Point(200, 40 + i * 50 + j * 50);
I would also change the way you are referencing the label. (I can't tell from the context if what you are doing is okay or not, as it depends how that productLabels variables has been instantiated.)

Issue with Creating List of Link Labels

I am using code as below to create a list of link labels :
LinkLabel[] lnkArray = new LinkLabel[10];
for (int i = 0; i < 10; i++)
{
lnkArray[i] = new LinkLabel();
lnkArray[i].Text = "test" + i;
lnkArray[i].Location = new System.Drawing.Point(20 + (i + 5), 50);
lnkArray[i].Size = new Size(200, 25);
}
panel1.Controls.AddRange(lnkArray);
Here is a image of the result :
It looks good to me but this always makes one linklabel in the panel with text = test0 .So basically it is adding just the first one in the list any solution ?
There is no problem with AddRange.
The problem in your code is that the LinkLabel(s) is overlapping.
The width of the LinkLabel in your code is 200. Therefore, you should leave at least 200px gap between the labels.
Try changing your code to this:-
LinkLabel[] lnkArray = new LinkLabel[10];
for (int i = 0; i < 10; i++)
{
lnkArray[i] = new LinkLabel();
lnkArray[i].Text = "test" + i;
lnkArray[i].Location = new System.Drawing.Point(20 + (i + 200), 50);
lnkArray[i].Size = new Size(200, 25);
}
panel1.Controls.AddRange(lnkArray);
simply use this instead of array
for (int i = 0; i < 10; i++)
{
LinkLabel lnkLbl = new LinkLabel();
// add properties i.e Text , Location , size
panel1.Controls.Add(lnlLbl);
}

c#: Creating a 10x10 field (and array) of CheckBoxes

I have a problem, and i don't know why but i can't figure out what it actually is.
All I want to do is create a 10 x 10 field of checkboxes as a board for a game basically.
The thing I came up with should use an Array of these boxes to easily identify them from box[0,0] to box[9,9] according to theirt coordinates in the field.
This is the code i am struggling with:
private void Form1_Load(object sender, EventArgs e)
{
// == OPTIONS ========================================
int xpos = 60; // Position of the first Checkbox (x)
int ypos = 60; // Position of the first Checkbox (y)
int size = 10; // Number of Rows and Columns
int spc = 30; // Space between boxes (Default:20)
// ====================================================
//other Variables
int x, y;
//Creating the Game Field
CheckBox[,] box = new CheckBox[size,size];
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
box[i, j] = new CheckBox();
//For Debugging Purpuses: Showing i and j next to checkBox
box[i, j].Text = i + "" + j;
//Set Position
y = i * spc + xpos;
x = j * spc + ypos;
box[i, j].Location = new Point(x,y);
this.Controls.Add(box[i, j]);
}
}
}
All I get from this is one single column of checkboxes marked [0,0] to [0,9].
And even if i switch around x and y or i and j, that never changes. So i.E. i will never get a row of checkboxes, just always a single column. Where am I going wrong with this?
i just get those 10 checkboxes, nothing more. they do not seem to be placed over each other either.
Hope you can help me out here :) thanks.
timo.
The check-boxes are too wide by default.
Try it with (for example):
int spc = 50;
And also add this in the loop:
box[i, j].Width = 40;

C# label control not working properly

When I run the following code, it populates the label1 control one time. Then the label1 control does nothing else. How do I get the label1 control to change on the mouse enter events. Please provide code examples.
int currentXposition, currentYposition;
const string positionLabel = "Current Position: ";
private void Test_Load(object sender, EventArgs a)
{
var temp=Color.Transparent; //Used to store the old color name of the panels before mouse events
var colorName = Color.Red; //Color used to highlight panel when mouse over
int numBlocks = 8; //Used to hold the number of blocks per row
int blockSize=70;
//Initialize new array of Panels new
string[,] Position = new string[8, 8];
Panel[,] chessBoardPanels = new Panel[numBlocks, numBlocks];
string Alphabet = "A,B,C,D,E,F,G,H";
string Numbers ="1,2,3,4,5,6,7,8";
string[] alphaStrings = Alphabet.Split(',');
string[] numStrings=Numbers.Split(',');
// b = sub[0];
int FirstValue, SecondValue;
//Store Position Values
for (int firstValue = 0; firstValue < 8; ++firstValue)
{
FirstValue = Alphabet[firstValue];
for (int SecValue = 0; SecValue < 8; ++SecValue)
{
SecondValue = Numbers[SecValue];
Position[firstValue, SecValue] = alphaStrings[firstValue] + numStrings[SecValue];
}
}
//Loop to create panels
for (int iRow = 0; iRow < numBlocks; iRow++)
{
for (int iColumn = 0; iColumn < numBlocks; iColumn++)
{
Panel p = new Panel();
//set size
p.Size = new Size(blockSize, blockSize);
//set back colour
p.BackColor = (iRow + (iColumn % 2)) % 2 == 0 ? Color.Black : Color.White;
//set location
p.Location = new Point(blockSize *iRow+15, blockSize * iColumn+15);
chessBoardPanels[iRow, iColumn] = p;
chessBoardPanels[iRow,iColumn].MouseEnter += (s,e) =>
{
currentXposition = iRow;
currentYposition = iColumn;
var oldColor = (s as Panel).BackColor;
(s as Panel).BackColor = colorName;
temp = oldColor;
label1.Text = Position[iRow, iColumn];
};
chessBoardPanels[iRow, iColumn].MouseLeave += (s, e) => {
(s as Panel).BackColor = temp;
};
groupBox1.Controls.Add(p);
}
}
}
I'm answering this only because I think it's important to show what happens when scope is confused. The reason you're having your issue is the scope of your variables. Your label is changing iRow * iColumn times, but only during initial execution. From then on, iRow and iColumn are fixed at their final values.
To achieve your desired end goal, it'd be easiest to create an extension of Panel:
public class ChessPanel : Panel {
private const Color HighlightColor = Color.Red;
public int iColumn { get; set; }
public int iRow { get; set; }
public Color PrimaryColor { get; set; }
public ChessPanel() : base()
{
this.MouseEnter += (s,e) =>
{
this.PrimaryColor = this.BackColor;
this.BackColor = HighlightColor;
};
this.MouseLeave += (s,e) =>
{
this.BackColor = this.PrimaryColor;
};
}
}
This will then allow you to reduce your code as follows:
int currentXposition, currentYposition;
const string positionLabel = "Current Position: ";
private void Test_Load(object sender, EventArgs a)
{
var temp=Color.Transparent; //Used to store the old color name of the panels before mouse events
var colorName = Color.Red; //Color used to highlight panel when mouse over
int numBlocks = 8; //Used to hold the number of blocks per row
int blockSize=70;
//Initialize new array of Panels new
string[,] Position = new string[8, 8];
ChessPanel[,] chessBoardPanels = new ChessPanel[numBlocks, numBlocks];
string Alphabet = "A,B,C,D,E,F,G,H";
string Numbers ="1,2,3,4,5,6,7,8";
string[] alphaStrings = Alphabet.Split(',');
string[] numStrings=Numbers.Split(',');
int FirstValue, SecondValue;
//Store Position Values --- no idea what this is supposed to do...
for (int firstValue = 0; firstValue < 8; ++firstValue)
{
FirstValue = Alphabet[firstValue];
for (int SecValue = 0; SecValue < 8; ++SecValue)
{
SecondValue = Numbers[SecValue];
Position[firstValue, SecValue] = alphaStrings[firstValue] + numStrings[SecValue];
}
}
//Loop to create panels
for (int iRow = 0; iRow < numBlocks; iRow++)
{
for (int iColumn = 0; iColumn < numBlocks; iColumn++)
{
ChessPanel p = new ChessPanel();
//set size
p.Size = new Size(blockSize, blockSize);
//set back colour
p.BackColor = (iRow + (iColumn % 2)) % 2 == 0 ? Color.Black : Color.White;
//set location
p.Location = new Point(blockSize *iRow+15, blockSize * iColumn+15);
p.MouseEnter += (s,e) =>
{
var cpSelf = s as ChessPanel;
if (cpSelf != null)
{
label1.Text = Position[cpSelf.iRow, cpSelf.iColumn];
}
};
groupBox1.Controls.Add(p);
chessBoardPanels[iRow, iColumn] = p;
}
}
}
I'm assuming you're making use of many of those variables later on int the program, and looking at this kind of makes my head hurt, so I left most of it in place.
Delegates are a very powerful and useful utility, but they can cause confusion with variable scope. Be very careful when using these, and make sure you treat them as functional units that execute later and can therefore only rely on the state of the program at execution of the block rather than at creation of the block. If you notice, I kept the reference to the Position array simply to show that you can still access local variables within the scope of the delegate because it is technically still in scope. Structurally, this could easily be moved into the ChessPanel class and referenced 100% locally. This example should be used as a caution as it can show how a "local" variable that many people assume are garbage-collected at end of function execution can hang around and eat up memory.
This code is untested and may have minor syntax errors. Hopefully the spirit of the structure is understood.

Categories