How do I add for() loop index identifier to label name? - c#

I am attempting to dynamically add content to labels in WPF from an object in a list.
I can add the content from the Object fine by hard-coding the index number:
// assign forecast values to 5 user interface labels
Forecast fc1 = day.Weather.getForecastInList(0);
day1Label.Content = fc1.Day;
day1ConditionLabel.Content = fc1.ForecastCondition;
day1TempLabel.Content = formatHiLow(fc1.TemperatureHigh, fc1.TemperatureLow);
Forecast fc2 = day.Weather.getForecastInList(1);
day2Label.Content = fc2.Day;
day2ConditionLabel.Content = fc2.ForecastCondition;
day2TempLabel.Content = formatHiLow(fc2.TemperatureHigh, fc2.TemperatureLow);
etc...
But then I would have to repeat a lot of the assignment code for each of the 5 labels.
I have attempted to use a for() loop which works fine to get each of the objects from the list but won't let me rename the labels e.g.
// assign forecast values to 5 user interface labels
for (int i = 0; i < 5; i++ )
{
Forecast fc+(i+1) = day.Weather.getForecastInList(i);
day+(i+1)+Label.Content = fc+(i+1)+.Day;
}
How can I dynamically identify WPF labels using the index in a for() loop?

I don't recommend this but to answer your question, theres a possibility:
Create a list which contains all your labels:
List<Label> dayLabels = new List<Label>();
dayLabels.Add(day1Label);
dayLabels.Add(day2Label);
//and so on...
Loop through all the labels:
for (int i = 0; i < 5; i++ )
{
Forecast fc = day.Weather.getForecastInList(i);
dayLabels.ElementAt(i).Content = fc.Day;
}

Related

How to add dynamic checkboxes in a Blazor-server app that use a list of booleans?

To clarify my question more I'll first show you add a checkbox using the blazor component library I'm using:
<MudCheckBox #bind-Checked="#Basic_CheckBox1"></MudCheckBox>
#code{
public bool Basic_CheckBox1 { get; set; } = false;
}
the code above creates a checkbox and then toggles the boolean Basic_CheckBox1 to true or false based on if the checkbox is checked or not.
here is my code
#for (int i = 0; i < checkboxNames.Count; i++)
{
<MudCheckBox #bind-Checked="#checkboxBools[i]" Label="#checkboxNames[i]"></MudCheckBox>
}
this code works until I check or uncheck my created checkboxes, then I get the index out of range error, how can I fix this? I need to use a list of booleans because I don't know how many checkboxes I will need until runtime, if there is any other solution please let me know.
Think of it like the bind isn't done there and then in the loop, but later. By the time the bind comes to be done, the value of i is off the end of the array Length, having been incremented repeatedly. Instead, you can save the current value of your loop variable i into another variable, then use that other variable in your bind:
#for (int i = 0; i < checkboxNames.Length; i++)
{
var x = i;
<MudCheckBox #bind-Checked="#checkboxBools[x]" Label="#checkboxNames[i]"></MudCheckBox>
}
checkBoxBools should be an array/list that is the same length (initalized to have the same number of elements as) checkBoxNames.. For example:
string[] checkboxNames = new string[]{"a","b","c"};
bool[] checkboxBools = new bool[3];
Or, if something else is providing a variable number of names:
checkboxNames = context.People.Select(p => p.Name).ToArray();
checkboxBools = new bool[checkBoxNames.Length];
https://try.mudblazor.com/snippet/GamQEFuFIwdRbzEx

How to populate over multiple columns?

I am working on an 'attendance' system for the school I work at. I'm using a listview for this, but noticed that there's simply not enough space to house all employees in one list. So I want to add a second column (or at least, another 'instance' of the three columns currently used). How do I manage to get such job done?
I have been thinking about workarounds such as a second listview instance, but that would make things only more complex in my opinion. So I'd like to stick by one.
See image below for visual description of what I want to get; I want the output to first fill the left part of the listview, then the right one.
And this is the part of my code that it's (probably) about:
public void LoadEmployees()
{
lvEmployees.View = View.Details;
lvEmployees.GridLines = true;
lvEmployees.Items.Clear();
List<Employees> data = database.LoadEmployees();
for (int i = 0; i < data.Count; i++)
{
// Define the list items
ListViewItem emp = new ListViewItem(data[i].Abbreviation);
emp.SubItems.Add(data[i].Name);
emp.SubItems.Add(data[i].Status);
// Add the list items to the ListView
lvEmployees.Items.Add(emp);
}
}
With lvEmployees1 and lvEmployees2 (or left/right), two ListView.
We can simply split the List<Employees> data in half.
lvEmployees1.View = View.Details;
lvEmployees1.GridLines = true;
lvEmployees1.Items.Clear();
lvEmployees2.View = View.Details;
lvEmployees2.GridLines = true;
lvEmployees2.Items.Clear();
List<Employees> data = database.LoadEmployees();
int half = data.Count / 2;
for (int i = 0; i < data.Count; i++)
{
// Define the list items
ListViewItem emp = new ListViewItem(data[i].Abbreviation);
emp.SubItems.Add(data[i].Name);
emp.SubItems.Add(data[i].Status);
if (i <= half) lvEmployees1.Items.Add(emp);
else lvEmployees2.Items.Add(emp);
}
To create the second "grid", you can simply copy past the element in the designer.

How to update text in a listview without clearing it

I am making a hack for a game that reads all of the enemies information (location, health, etc.) and stores it accordingly in a listview. I am using this method currently:
listView1.Items.Clear();
string[] arr = new string[6];
ListViewItem item;
for (int i = 0; i < engine.maxPlayers; i++)
{
engine.enemy[i].readInfo(i);
arr[0] = i.ToString()
arr[1] = engine.enemy[i].name.ToString();
arr[2] = engine.enemy[i].getRank(csgo.enemy[i].compRank);
arr[3] = engine.enemy[i].Wins.ToString();
arr[4] = engine.enemy[i].health.ToString();
arr[5] = engine.enemy[i].armor.ToString();
item = new ListViewItem(arr);
listView2.Items.Add(item);
}
This is being done every 200 ms to assure real time information.
This works to an extent. It does show everything correctly. However, it has 2 big flaws.
1: it flickers constantly when it clears and rewrites the data.
2: If my listview is only 10 columns long and i need to read data for 20 players, i can't scroll down to see the last 10 players because each time it clears, it resets the scroll bar position.
So is it possible to ONLY update the text of a specific text? Say i only want to update enemy[3]'s health and leave the rest. Can this be done? I don't need to redraw some information like wins and rank because they won't be changing during the game.
Yes, you can use the indexer to directly reference the item you wish to update. For example:
listView2.Items[playerIndex] = new ListViewItem(arr);
As for accomplishing this relative to your game engine, you'll want to use the Reconciliation Design Pattern, which involves the following given game engine list (Master), and list view (Target):
Create a temporary of items or keys in Target. Call this ToDelete.
Step through Master, add any entries that aren't in Target, update those that are. Delete from ToDelete for each match to Target.
Delete from Target all items remaining in ToDelete.
Update: as Slai mentions, you can update via the SubItems member if you don't want to even replace an entire row.
I believe you have to first initially fill the list view with the data from the first call, and then every 500ms-1000ms (I think that 200ms offers nothing), refresh the list view with fresh data.
Something like:
private void InitialPopulation()
{
var listOfItems = new List<ListViewItem>();
for (int i = 0; i < engine.maxPlayers; i++)
{
engine.enemy[i].readInfo(i);
var item = new ListViewItem(i.ToString());
item.Name = engine.enemy[i].name;
item.SubItems.Add(engine.enemy[i].name);
item.SubItems.Add(engine.enemy[i].getRank);
item.SubItems.Add(engine.enemy[i].Wins);
item.SubItems.Add(engine.enemy[i].health);
item.SubItems.Add(engine.enemy[i].armor);
listOfItems.Add(item);
}
listView1.BeginUpdate();
listView1.Items.Clear();
listView1.Items.AddRange(listOfItems.ToArray());
listView1.EndUpdate();
}
private void RefreshData()
{
listView1.BeginUpdate();
var listOfItems = new List<ListViewItem>();
var playersNames = new List<string>();
var itemsNames = new List<string>();
for (int i = 0; i < engine.maxPlayers; i++)
{
engine.enemy[i].readInfo(i);
playersNames.Add(engine.enemy[i].name);
var items = listView1.Items.Find(engine.enemy[i].name, false);
switch (items.Length)
{
case 1: // update
items[0].SubItems[0].Text = engine.enemy[i].name;
items[0].SubItems[1].Text = engine.enemy[i].getRank;
items[0].SubItems[2].Text = engine.enemy[i].Wins;
items[0].SubItems[3].Text = engine.enemy[i].health;
items[0].SubItems[4].Text = engine.enemy[i].armor;
break;
case 0: // add
var item = new ListViewItem(i.ToString());
item.Name = engine.enemy[i].name;
item.SubItems.Add(engine.enemy[i].name);
item.SubItems.Add(engine.enemy[i].getRank);
item.SubItems.Add(engine.enemy[i].Wins);
item.SubItems.Add(engine.enemy[i].health);
item.SubItems.Add(engine.enemy[i].armor);
listOfItems.Add(item);
break;
}
}
if (listOfItems.Count > 0)
listView1.Items.AddRange(listOfItems.ToArray());
foreach (ListViewItem item in listView1.Items)
itemsNames.Add(item.Name);
// check if there are more listview items than data.
if (itemsNames.Count > playersNames.Count)
foreach (var name in itemsNames.Except(playersNames))
listView1.Items.RemoveByKey(name);
listView1.EndUpdate();
}
I assumed that engine.enemy[i].name is unique for each palyer.
Maybe there are some errors in the code since I have not written anything for winforms for a long time, but I think you should get the meaning.

how to make loop with a list work, that finds new elements? c# webdriver [duplicate]

I am getting "index out of range" from this loop. But I need to use new elements that loop founds, how do I do that? Please help to fix the problem
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
string[] links = new string[linkCount];
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
links[i] = linksToClick[i].GetAttribute("href");
}
I think that you could refactor your code:
var linkElements = driver.FindElements(By.CssSelector("a[href]")).ToList();
var links = new List<string>();
foreach (var elem in linkElements)
{
links.Add(elem.GetAttribute("href"));
}
If that works, you could simplify the query:
var instantLinks = driver.FindElements(By.CssSelector("a[href]"))
.Select(e => e.GetAttribute("href"))
.ToList();
You can rewrite your code to bypass the for loop:
string[] links = driver.FindElements(By.CssSelector("a[href]")).Select(l => l.GetAttribute("href")).ToArray();
This should also avoid the index out of range problem, and cut down the amount of code you have to write.
First of all i dont see a point in assigning linkstoclick values inside loop... And Reason for error must be that linksToClick list's length is more than that of linkCount.
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
List<string> links = new List<string>();
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
if (linksToClick.Count < i)
links.Add(linksToClick[i].GetAttribute("href"));
}
This might help with the out of range exception.
Doing this allows you to create a list of type: string without having to explicitly define the size of the list
the first one gets all of your elements by tag name ...let's assume 5.
in the loop, your driver get's all the elements by css selector, and you might have a different number here. let's say 4.
then, you might be trying to set the fifth element in a four element array.
boom.
Easiest fix to debug:
int linkCount = driver.FindElements(By.TagName("a")).Count;
string[] links = new string[linkCount];
// WRITE OUT HOM MANY links you have
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
// ASSERT THAT YOU HAVE THE SAME AMOUNT HERE
If (links.Count != linksToClick.Count)
// your logic here
links[i] = linksToClick[i].GetAttribute("href");
}

modifying a group of WinForm controls using a loop?

I have a group of textbox controls that i would like to populate with an array of doubles.The controls names are numerically incremented like so
Tol1.Text = lineTolFront[0].ToString();
Tol2.Text = lineTolFront[1].ToString();
Tol3.Text = lineTolFront[2].ToString();
Tol4.Text = lineTolFront[3].ToString();
Tol5.Text = lineTolFront[4].ToString();
Tol6.Text = lineTolFront[5].ToString();
//and so on
is there a simpler way to do this using a loop without having to manually input the values?
First, get all those TextBoxes using LINQ (note: this is useful particularly when you have many controls you do not want manually put in a collection).
var tboxes = this.Controls.Cast<Control>()
.OfType<TextBox>()
.Where(l => l.Name.Contains("Tol"));
and then loop through them and set the content.
int i = 0;
foreach(var tb in tboxes)
tb.Text = lineTolFront[i++].ToString();
You could add the TextBoxes to a collection first. It's a little less copy/paste work at least.
var textBoxes = new List<TextBox> { Tol1, Tol2, Tol3, Tol4, Tol5, Tol6 };
for (var i = 0; i < lineTolFront.Count; i++)
textBoxes[i].Text = lineTolFront[i].ToString();
Regarding M Patel's comment, make sure you add the TextBoxes to the collection in the same order you want to assign the doubles from the lineTolFront array.
You could add your controls to an array and loop through it:
var controls = new[] { Tol1, Tol2, Tol3, Tol4, Tol5, }; //etc
for(int i = 0; i < controls.Length; i++)
{
controls[i].Text = lineTolFront[i].ToString();
}

Categories