I know this has been asked quite a few times and I know ot will be really simple but I'm really new to C# and I'm pulling my hair out because I've been coding (not very well) through the night. I have a class ProcessOrdersActive that I Deserialiaze to details. It's falling over when I try to add ProcessOrderNbr[I] to the combobox.
//Deserialise data
ProcessOrdersActive details = JsonConvert.DeserializeObject<ProcessOrdersActive>(responseBody);
var ordersList = new List<ProcessOrdersActive>();
ordersList.Add(details);
int numofitems = ordersList.Capacity;
txtActiveOrders.Text = numofitems.ToString();
for (int i = 0; i < numofitems; i++)
{
comboBoxOrders.Items.Add (details.ProcessOrderNbr[i]);
}
Many thanks for the responses. I had a good chat with a proficient C# programmer today and here is the solution that he came to. The names are slightly different from the original post.
//Deserialise data & send to DataGrid
ProcessOrderDetails details = JsonConvert.DeserializeObject<ProcessOrderDetails>(responseBody);
//Get number of operations and display to screen
int numofitems = details.MaterialList.Count<Materiallist>();
txtNumOfMaterials.Text = numofitems.ToString();
//Find the last operation
var lastone = details.MaterialList.Last<Materiallist>();
//Create a new material/operation list
var materialList = new List<Materiallist>();
//Add the last operation to the list
materialList.Add(lastone);
//Parse the list to the data grid
dataGridProcessOrderDetails.DataSource = materialList;
You are trying to access an index that may be out of bounds of the array/list.
The number of items should be the Length/Count of the array/List you are accessing.
//Deserialise data
ProcessOrdersActive details = JsonConvert.DeserializeObject<ProcessOrdersActive>(responseBody);
var ordersList = new List<ProcessOrdersActive>();
ordersList.Add(details);
int numofitems = details.ProcessOrderNbr.Count;//If this is a list use Count. If it is an array use Length
txtActiveOrders.Text = numofitems.ToString();
for (int i = 0; i < numofitems; i++) {
comboBoxOrders.Items.Add (details.ProcessOrderNbr[i]);
}
Related
I am trying to add data to an array but everytime I try to add new data, the old data gets deleted. I have 20 pages and the array does not add all the 20 pages but only 1. The rest just gets deleted.
HtmlNode[] array = null;
for (int i = 0; i <= 10; i++)
{
var doc1 = web.Load(loopingpage);
array = doc1.DocumentNode.SelectNodes("//*[contains(#class, '-row-cp')]").ToArray();
}
The data does not get added to the array. It only displays the current data.
You overwrite the array in each iteration in the for loop in this line.
array = doc1.DocumentNode.SelectNodes("//*[contains(#class, '-row-cp')]").ToArray();
You could of course add nodes to a predefined array, but arrays are fixed in size, so you would need to know beforehand how many nodes there are to allocate an appropriate size. Instead, you can use a List<T>, which is not fixed-size and more suitable for your scenario.
// Create a new list of nodes
var nodes = new List<HtmlNode>();
for (var i = 0; i <= 10; i++)
{
var doc1 = web.Load(loopingpage);
var doc1Nodes = doc1.DocumentNode.SelectNodes("//*[contains(#class, '-row-cp')]");
// Add all selected nodes from doc1 to the list
nodes.AddRange(doc1Nodes);
}
// Convert it to an array later if needed
var nodesArray = nodes.ToArray();
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.
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.
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");
}
Ok, I cannot get this. I've looked at it and I don't see why it's out of bounds. I get the error at paypalItems[paypalItems.Length] = new PaymentDetailsItemType
PaymentDetailsItemType[] paypalItems = new PaymentDetailsItemType[order.OrderItems.Count];
for (int i = 0; i < order.OrderItems.Count; i++)
{
paypalItems[i] = new PaymentDetailsItemType
{
Name = order.OrderItems[i].Name,
Amount = ApiUtility.CreateBasicAmount(order.OrderItems[i].Price),
Description = order.OrderItems[i].Name,
Number = order.OrderItems[i].Sku,
};
}
// paymentItems now has 1 item...now to the if statement:
if (giftCardsTotal != 0)
{
// add Coupons & Discounts line item
paypalItems[paypalItems.Length] = new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
};
}
UPDATED: I changed the size of the array..now it's all good. No need for the -1
PaymentDetailsItemType[] paypalItems = new PaymentDetailsItemType[order.OrderItems.Count + 1];
for (int i = 0; i < order.OrderItems.Count; i++)
{
paypalItems[i] = new PaymentDetailsItemType
{
Name = order.OrderItems[i].Name,
Amount = ApiUtility.CreateBasicAmount(order.OrderItems[i].Price),
Description = order.OrderItems[i].Name,
Number = order.OrderItems[i].Sku,
};
}
// paymentItems now has 1 item...now to the if statement:
if (giftCardsTotal != 0)
{
paypalItems[paypalItems.Length -1] = new PaymentDetailsItemType
{
Name = "Certificates",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Certificates"
};
}
I think you might be better off using a List<>, so you would declare:
List<PaymentDetailsItemType> paypalItems = new List<PaymentDetailsItemType>();
Then when you iterate through order.OrderItems, just do a paypalItems.Add()
Finally on your if(giftCardsTotal != 0) conditional just do a:
paypalItems.Add(new PaymentDetailsItemType...);
This way you don't have to worry about off by one array indexing issues. Here would be the rewritten code (I use var for my convenience):
var paypalItems = new List<PaymentDetailsItemType>();
foreach (var orderitem in order.OrderItems)
{
paypalItems.Add(new PaymentDetailsItemType
{
Name = orderitem.Name,
Amount = ApiUtility.CreateBasicAmount(orderitem.Price),
Description = orderitem.Name,
Number = orderitem.Sku,
});
}
if (giftCardsTotal != 0)
{
// add Coupons & Discounts line item
paypalItems.Add(new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
});
}
Per John Saunders' suggestion (in the comments), here's a Linq alterative to the variable declaration and first loop:
var paypalItems =
(from orderitem in order.OrderItems
select new PaymentDetailsItemType
{
Name = orderitem.Name,
Amount = ApiUtility.CreateBasicAmount(orderitem.Price),
Description = orderitem.Name,
Number = orderitem.Sku
}).ToList();
Addition based on comment: If you need an Array when you're done, call:
paypalItems.ToArray()
Arrays are zero-based in C#. Using array[array.Length] will always fail. You want Length-1.
In addition, I see that you're trying to expand the array. You can't do that! Once an array is instantiated, it's length cannot change.
If you need a collection that can expand, use List<PaymentDetailsItemType>.
Because they are 0-indexed, so first one is the 0 and last one is length-1.
This is true for almost any programming language..
It seems that first you fill up an array by trasforming elements from another one and then you want to replace last one. Maybe you intended to append an element to the end?
In that case you should build it larger:
PaymentDetailsItemType[] paypalItems = new PaymentDetailsItemType[order.OrderItems.Count+1];
then fill it as you did:
for (int i = 0; i < order.OrderItems.Count; i++)
{
...
}
then set the last one:
paypalItems[paypalItems.Length-1] = ..
paypalItems[paypalItems.Length] =
should be
paypalItems[paypalItems.Length - 1] =
This:
paypalItems[paypalItems.Length]
Will always be outside the bounds of the array as the array index starts at 0. If you want to store something in the last element of the array do:
paypalItems[paypalItems.Length-1] = ...
It looks like you're trying to append to the array by simply using the next index. Perhaps you learned this from exposure to a language like javascript or python, where "arrays" are really complex objects. Real arrays don't work like that.
Instead, you should try using the .Add() method of List<PaymentDetailsItemType>.
You need to change the size of array before add a new array item.
if (giftCardsTotal != 0)
{
// Increase the size
Array.Resize(ref paypalItems, paypalItems.Length + 1);
// add Coupons & Discounts line item
paypalItems[paypalItems.Length -1] = new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
};
}
Length starts from 1 and index from 0. So you should try:
paypalItems[paypalItems.Length - 1]
You are initializing the array to the length of order.OrderItems.Count then after filling it up with data, you are trying to add an additional item to the array. This won't work like this. You have allocated a static size to the array here, you can't add an additional item to that without some sort of reallocation.
You need to create a dynamically allocated array or use a List<T>
To add to the other answers, an Array in .NET is a low-level data type that maps directly to an allocated sequence of bytes in memory. Thus, you cannot add items to an array if it's already full. Maybe what you're looking for is to use a List<T>:
var paypalItems = new List<PaymentDetailsItemType>(order.OrderItems.Count);
// specifying that count is optional, but will increase performance because array space will have to be reallocated less often
// ...
paypalItems.Add(new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
});
paypalItems[paypalItems.Length] would access one position above the allocated space. If you want the last position you need paypalItems[paypalItems.Length-1]
If you want to append an item use a List<> instead of an array.