I am developing a solution in C # and I need to add a few buttons to TabbedPanel. Happens that debugger adds only one and even a single button.
public void AddDrinkstoTabbedpanel(TabPage tp)
{
List<string> drinks = new List<string>();//New list empty
food foo = new food();//Call food to get drinks
string []product = foo.name;//string product [] = food.name (drink)
foreach (string value in product)//(string value in product)//for any "value" in product
{
Button bt = new Button();
bt.Size = new Size(100, 100);
drinks.Add(value);
tp.Controls.Add(bt);
//listofButtons.Add(bt);
}
}
I previously define an array with some items and now i need to add a button for each item founded, how can I modify this code given above to get what I want.
Change the Location for all buttons. They are being created but at the same position and look like one.
Related
I am trying to think of a way to filter a list of buttons that I dynamically create, based on values looped through from the SQlite DB. Each button is tagged with a student name and there could be a large number of buttons, hence the need to filter the buttons.
I create my buttons as so:
public RectTransform GridWithNameElements;
public GameObject StudentNamePrefabButton;
while (reader.Read())
{
//create a new button object and use the prefab button to make sure spacing etc is correct
goButton = (GameObject)Instantiate(StudentNamePrefabButton);
//set the parent of the button
goButton.transform.SetParent(GridWithNameElements, false);
goButton.transform.localScale = new Vector3(1, 1, 1);
//set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
goButton.GetComponentsInChildren<Text>()[0].text = reader["fullName"].ToString()+" | "+ reader["studentNumber"].ToString();
goButton.name = reader["studentID"].ToString();
Button tempButton = goButton.GetComponent<Button>();
int tempInt = i;
tempButton.onClick.AddListener(() => ButtonClicked(tempInt));
i++;
Debug.Log(goButton.name);
}
Then I created an input field and attached a script to the onValueChanged in the input field and attempted to write a script.
//This method is called when the student attempts to search for their own name when taking the individual quiz
public void SearchExistingStudentName()
{
//Get the name entered from the text field
string student_name = searchExistingStudentNameInput.text;
//Add names that exist to the new list
List<string> names = new List<string>();
names.Clear();
//sets an id to the onclick listener of newly created buttons
int x = 0;
//if a new button object exists and the name was entered and then removed, clear it from the list and remove the created button
if (student_name == "")
{
//Clear the filtered names List
names.Clear();
x = 0;
//Destroy the create filter buttons if the user clears the text area
GameObject[] objects = GameObject.FindGameObjectsWithTag("filterButton");
//loop through the buttons that share the tag "filterButton"
for (int count = 0; count < objects.Length; count++)
{
Destroy(objects[count]);
Debug.Log("Number of objects to be deleted " + objects.Length);
}
//Loop through and show all children
foreach (Transform child in GridWithNameElements)
{
child.gameObject.SetActive(true);
}
}
else if (student_name != "")
{
int count = 0;
int filteredNameCount = 0;
int filteredIDCount = 1;
//loop through the list of buttons with student names
for (int i = 0; i < GridWithNameElements.childCount; i++)
{
Debug.Log("Children of grid "+GridWithNameElements.childCount);
//Check if the user has typed their name with capitals or lowercase etc, and check the inputted value against names already in the list
//If true, proceed
if (GridWithNameElements
.GetComponentsInChildren<Text>()[i].text.Contains(student_name) || GridWithNameElements
.GetComponentsInChildren<Text>()[i].text.ToLower().Contains(student_name) || GridWithNameElements
.GetComponentsInChildren<Text>()[i].text.ToUpper().Contains(student_name))
{
//If the name entered contains letters found in the parent GridWithNameElements, then add them to the list array and their name value (unique id)
names.Add(GridWithNameElements.GetComponentsInChildren<Text>()[i].text.Replace(#"[", string.Empty).Replace(#"]", string.Empty));
names.Add(GridWithNameElements.GetChild(i).name); //this is the unique id of the student
Debug.Log("Number of items in filtered names list " + names.Count);
//Loop through and hide all children and hide them
if (count == 0)
{
foreach (Transform child in GridWithNameElements)
{
child.gameObject.SetActive(false);
}
}
count++;
//Then create a button that represents a name added to the names List
newButton = (GameObject)Instantiate(StudentNamePrefabButton);
//set the parent of the button
newButton.transform.SetParent(GridWithNameElements, false);
newButton.transform.localScale = new Vector3(1, 1, 1);
//set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
newButton.GetComponentsInChildren<Text>()[filteredNameCount].text = names[filteredNameCount].ToString();
newButton.name = names[filteredIDCount].ToString().Trim();
newButton.tag = "filterButton";
filteredNameCount++;
filteredIDCount++;
//Then add a click listener to the button
Button tempButton = newButton.GetComponent<Button>();
int tempInt = x;
tempButton.onClick.AddListener(() => ButtonClicked(tempInt));
x++;
// Debug.Log("Student Unique ID " + newButton.name);
}
}
count = 0;
}
}
I was hoping to simply Loop through all list items, and hide those that don't match the search query. However, at the moment, my loop is wrong, as I get an out of bounds exception (which I think is to do with adding a new child element dynamically when making a search). I see this in the console log, however, the program executes as expected (so this is a problem, but currently not the biggest).
I am hiding the original list and showing the new list based on matching criteria.
However, I am only ever returning one possible name, rather than a bunch of possible choices. For example, if I had the name Ben Jones and Bob Dylan, based on my code at the moment, I am only ever able to return Ben Jones and never Bob Dylan.
I feel like I am going the wrong way around doing this because I want something similar to this, and unable to recreate it. I am trying to figure out, however, if I am heading in the right direction or not.
UPDATE
I think I have found the reason for the IndexOutOfRangeException: Array index is out of range. It is because whenever I type a letter, it is calling the SearchExistingStudentName() method. This means that as long as the letter typed is found within one of the names, it is added to list - only the letter. This is why I can only return one name, rather than a list of possible names. Therefore I think the if statement needs amending, which I am trying to look into now.
I have managed to narrow down the array exception in the code to this section:
//Loop through and hide all children and hide them
if (count == 0)
{
foreach (Transform child in GridWithNameElements)
{
child.gameObject.SetActive(false);
}
}
count++;
//Then create a button that represents a name added to the names List
newButton = (GameObject)Instantiate(StudentNamePrefabButton);
//set the parent of the button
newButton.transform.SetParent(GridWithNameElements, false);
newButton.transform.localScale = new Vector3(1, 1, 1);
//set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
newButton.GetComponentsInChildren<Text>()[filteredNameCount].text = names[filteredNameCount].ToString();
newButton.name = names[filteredIDCount].ToString().Trim();
newButton.tag = "filterButton";
filteredNameCount++;
filteredIDCount++;
Since you have tagged this with Unity3D I'm assuming you are looking to use this code in a game. If that is the case I would not recommend using LINQ, if the same result can easily be obtained in a more traditional way. That being said, I'll show both ways to do it.
List<string> names = GridWithNameElements.Where(nameInList => nameInList.Contains(input))
The above finds all the names where nameInList.Contains(input) evalutes to true.
In a loop you'd do the following.
List<string> names = new List<string>();
foreach (string nameInList in GridWithNameElements)
{
if (nameInList.Contains(input)
{
names.Add(nameInList)
}
}
I am not entirely confident on the types of the variables but I think the structure should be clear enough. Feel free to ask some more if it isn't.
Managed to solve this myself in the end, and although it is far from an elegant solution, it works. I am now going to work on improving the code itself, but for those that might want a starting point, I thought I would share what I did.
As mentioned in my question, I loop out a bunch of buttons onto my main, unfiltered scrollable list as so.
while (reader.Read())
{
//create a new button object and use the prefab button to make sure spacing etc is correct
GameObject goButton = (GameObject)Instantiate(StudentNamePrefabButton);
//set the parent of the button
goButton.transform.SetParent(GridWithNameElements, false);
goButton.transform.localScale = new Vector3(1, 1, 1);
//set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
goButton.GetComponentsInChildren<Text>()[0].text = reader["fullName"].ToString() + " | " + reader["studentNumber"].ToString();
goButton.name = reader["studentID"].ToString();
Button tempButton = goButton.GetComponent<Button>();
int tempInt = i;
tempButton.onClick.AddListener(() => ButtonClicked(tempInt));
i++;
Debug.Log(goButton.name);
}
I then created a user inputfield in my UI, so the user could search for their name. Then attached a listener. To do this go to the Unity editor and attach your script to the On Value Changed method.
Every-time a user then types into your inputfield, your attached script is called.
I then ended up doing the following - again, I stress, I have just got this working so not a perfect example, but at least a starting point and there might still be more bugs to sort out. I hope it helps someone as I didn't find any complete examples when I was looking.
//This method is called when the student attempts to search for their own name when taking the individual quiz
public void SearchExistingStudentName()
{
//Get the name entered from the text field
string student_name = searchExistingStudentNameInput.text;
//Create a list to store all the user names
List<string> filteredNamesList = new List<string>();
//Add another list to store the user unique ids
List<string> filteredNameIDList = new List<string>();
//Clear both lists before using them
filteredNamesList.Clear();
filteredNameIDList.Clear();
//sets an id to the onclick listener of newly created buttons
int x = 0;
//if a new button object exists and the name was entered and then removed, clear it from the list and remove the created button
if (student_name == "")
{
//Clear the filtered filteredNamesList List
filteredNamesList.Clear();
filteredNameIDList.Clear();
x = 0;
//Destroy the create filter buttons if the user clears the text area
GameObject[] objects = GameObject.FindGameObjectsWithTag("filterButton");
//loop through the buttons that share the tag "filterButton"
for (int count = 0; count < objects.Length; count++)
{
Destroy(objects[count]);
Debug.Log("Number of objects to be deleted " + objects.Length);
}
//Loop through and show all children
foreach (Transform child in GridWithNameElements)
{
child.gameObject.SetActive(true);
}
GridWithNameElements.gameObject.SetActive(true);
GridWithNameElements2.gameObject.SetActive(false);
}
else if (student_name != "")
{
//loop through the list of buttons in the main/unfiltered list with student filteredNamesList
for (int i = 0; i < GridWithNameElements.childCount; i++)
{
//Check if the user has typed their name with capitals or lowercase etc, and check the inputted value against filteredNamesList already in the list
//If true, proceed
//Get the name value that contains entered character
if (GridWithNameElements
.GetComponentsInChildren<Text>()[i].text.Contains(student_name) || GridWithNameElements
.GetComponentsInChildren<Text>()[i].text.ToLower().Contains(student_name) || GridWithNameElements
.GetComponentsInChildren<Text>()[i].text.ToUpper().Contains(student_name))
{
//Do not allow duplicates to the list
if (filteredNamesList.Distinct().Count() == filteredNamesList.Count())
{
//If the name entered contains letters found in the parent GridWithNameElements, then add them to the list array and their name value (unique id)
filteredNamesList.Add(GridWithNameElements.GetComponentsInChildren<Text>()[i].text.Replace(#"[", string.Empty).Replace(#"]", string.Empty));
filteredNameIDList.Add(GridWithNameElements.GetChild(i).name); //this is the unique id of the student
}
//end of if statement
}
//end of main loop
}
//hide original list
GridWithNameElements.gameObject.SetActive(false);
//show filtered list
GridWithNameElements2.gameObject.SetActive(true);
//Destroy the created filter buttons if the user presses another button, as previously
//added filteredNamesList might need to be ruled out.
GameObject[] objects = GameObject.FindGameObjectsWithTag("filterButton");
//loop through the buttons that share the tag "filterButton"
for (int count = 0; count < objects.Length; count++)
{
Destroy(objects[count]);
Debug.Log("Number of objects to be deleted " + objects.Length);
}
int filteredNameIDCount = 0;
foreach (string nameInList in filteredNamesList)
{
//Then create a button that represents a name added to the filteredNamesList List
newButton = Instantiate(StudentNamePrefabButton);
//set the parent of the button
newButton.transform.SetParent(GridWithNameElements2, false);
newButton.transform.localScale = new Vector3(1, 1, 1);
//set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
newButton.GetComponentsInChildren<Text>()[0].text = nameInList.ToString();
newButton.name = filteredNameIDList[filteredNameIDCount].ToString().Trim();
newButton.tag = "filterButton";
Debug.Log("Filtered Name " + nameInList.ToString() + " Their ID " + filteredNameIDList[filteredNameIDCount].ToString().Trim());
filteredNameIDCount++;
}
//end of loop
//Then add a click listener to the button
Button tempButton = newButton.GetComponent<Button>();
int tempInt = x;
tempButton.onClick.AddListener(() => ButtonClicked(tempInt));
x++;
// Debug.Log("Student Unique ID " + newButton.name);
}
}
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 have a drop down list populated in code and the list displays everything as it should BUT if Pink Water Buffalo is selected from the drop down, the text displayed is Yellow Snake Did I set something up incorrectly? This is full syntax (and I have no selected index changed event for the drop down list that could cause err)
protected void Page_Load(object sender, EventArgs e)
{
IP = HttpContext.Current.Request.Params["HTTP_CLIENT_IP"] ?? HttpContext.Current.Request.UserHostAddress;
if (!IsPostBack)
{
message.Visible = false;
populateDDL();
}
}
protected void populateDDL()
{
var item = new List<ListItem>
{
new ListItem("", ""),
new ListItem("Yellow Snake", "9"),,
new ListItem("This item works", "12"),
new ListItem("Pink Water Buffalo", "9"),
};
this.dropdownlistone.DataTextField = "Text";
this.dropdownlistone.DataValueField = "Value";
this.dropdownlistone.DataSource = item;
this.dropdownlistone.DataBind();
}
EDIT
This is my asp that is used to create the drop down list
<asp:DropDownList ID="dropdownlistone" runat="server" Height="20px" Width="278px"
AutoPostBack="true"> </asp:DropDownList>
EDIT #2
It's ugly and a lot of syntax if you have multiple drop down lists, but for me it wasn't a big deal. What I did was added a .0, .1 etc and incremented up for each item in the drop down list. Then used the .Split method to strip out only the relevant piece like so:
protected void populateDDL()
{
var item = new List<ListItem>
{
new ListItem("", ""),
new ListItem("Yellow Snake", "9.0"),,
new ListItem("This item works", "12.1"),
new ListItem("Pink Water Buffalo", "9.2"),
};
this.dropdownlistone.DataTextField = "Text";
this.dropdownlistone.DataValueField = "Value";
this.dropdownlistone.DataSource = item;
this.dropdownlistone.DataBind();
}
String[] stringsplit = dropdownlistone.SelectedValue.ToString().Split('.');
String itemprice = stringsplit[0].Trim();
In your example, the value for "yellow Snake" and "Pink Water Buffalo" are both set to 9. "Yellow Snake" comes first in the list, so that is the one you are getting.
You'll have to figure out a way to make the value unique for each item. Otherwise you will get unexpected results, possibly across different browsers. A method I've used is combining the text and value with a pipe (|):
var item = new List<ListItem>
{
new ListItem("", ""),
new ListItem("Yellow Snake", "9|Yellow Snake"),
new ListItem("This item works", "12|This item works"),
new ListItem("Pink Water Buffalo", "9|Pink Water Buffalo"),
};
Then you can easily get the value by splitting it:
string value = dropdownlistone.SelectedValue.Split('|')[0];
I prefer the pipe as it's hardly ever used as a display value. Using a dot you might run into some issues if the text contains a dot, such as "Yellow Snakes are great, but green ones are the the best."
New to c# and .net and I'm trying to throw together a listview that has three columns, Quantity, Item description and Price. I know how to fill the rows with data and I can even add the subitem below but what I can't figure out is how I can group them together as a primary item with "children" items tied to it...
Sorry for my most likely incorrect verbiage, but this is what I am shooting for:
Qty Description Price
1 Widget 2.95
Widget add on .25
Widget insurance 1.25
1 Sprocket 4.95
Sprocket add on .50
I am trying to figure out how to group the subitems below the primary item to make it where when I select, for instance, "Widget insurance", it highlights "Widget add on" and "Wiget" as one entity. So for example if I need to go back and remove "Widget insurance" from the purchase of the "Widget" I can click on any item connected to "Widget", ex. "Widget", "Widget add on" or "Widget insurance" and it will pull up another form that allows me to deselect "Widget insurance", hit OK and it would update the list accordingly...
I've (poorly) thrown together code that visually gives me what I am looking for, but I think I am completely confusing the use of subitem and it's purpose:
string[] newItem = new string[3];
ListViewItem itm;
newItem[0] = "1";
newItem[1] = "Widget";
newItem[2] = "2.95";
itm = new ListViewItem(newItem);
listView1.Items.Add(itm);
string[] newSubItem = new string[3];
ListViewItem sub;
newSubItem[0] = "";
newSubItem[1] = "Widget add on";
newSubItem[2] = ".25";
sub = new ListViewItem(newSubItem);
listView1.Items.Add(sub);
Any help would be greatly appreciated.
Here you are. "Qty" will be ListViewItem's text. "Description" and "Price" will be ListViewItem's SubItems.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
listView1.View = View.Details;
listView1.Columns.Add("Qty", 100, HorizontalAlignment.Left);
listView1.Columns.Add("Description", 100, HorizontalAlignment.Left);
listView1.Columns.Add("Prie", 100, HorizontalAlignment.Left);
var details = new[] {"Widget", "2.95"};
var item = new ListViewItem("1");
item.SubItems.AddRange(details);
listView1.Items.Add(item);
details = new[]{ "Widget add on",".25"};
item = new ListViewItem("");
item.SubItems.AddRange(details);
listView1.Items.Add(item);
}
}
Hope this help.
This is my code for adding to my list:
List<string> myList = new List<string>();
private void button1_Click(object sender, EventArgs e)
{
ListViewItem myList = new ListViewItem(txtBox1.Text);
myList.SubItems.Add(txtBox2.Text);
myList.SubItems.Add(txtBox3.Text);
myList.SubItems.Add(txtBox4.Text);
listView1.Items.Add(myList);
txtBox1.Text = "";
txtBox2.Text = "";
txtBox3.Text = "";
txtBox4.Text = "";
}
This adds to my list and clears and lets me add another post to my list. The problem is I want to be able to repopulate the textboxes to allow me to update a post in the list.
To make my point more clear, my list looks like this:
Code | Name | Price | In stock
------------------------------
123 aa 122 2
124 bb 111 5
Say I want the first post, can I somehow set all the data back into the textboxes by that identifier or do I have search by index? I would like to be able to put the code in the first textbox then hit a button called retrieve that populates the other textfield kinda like going by the unique key in an sql table but I cant find any info on wether its possible or not.
Why don't you create a class that has Code, Name, Price, In stock, and use a List
Public MyItem
{
public string Code;
public string Name,
public float Price;
public bool inStock;
}
List<MyItem> myList = new List<MyItem>();
private void button1_Click(object sender, EventArgs e)
{
MyItem temp = new MyItem()
temp.Code=txtBox1.Text;
temp.Name=(txtBox2.Text);
temp.Price=(txtBox3.Text);
temp.inStock=(txtBox4.Text);
mylist.add(temp);
txtBox1.Text = "";
txtBox2.Text = "";
txtBox3.Text = "";
txtBox4.Text = "";
}
If you implement it this way then you can just use the index to go through all the items in the list. With the example above you would probably need to convert some of the values from the text boxes before you can use them (price to double)
Also if you want to add the items to a Listview and display them a certain way then override the toString property in the MyItem class and you can display whatever text you want. Hope this helps.
You shouldn't store data only in controls. I would create a class to hold all the info about a particular item, and populate THAT with the contents of the textboxes, and store it in some collection (Maybe a dictionary if you want to look one up by code)
You can then add a reference to this object in the "Tag" property of ListViewItem, so you can immediately grab it from any selected ListViewItem.
I dont quite understand what you mean by saying "can I somehow set all the data back into the textboxes by (that identifier) " which identifier ?!
but i would get data like this:
var item = listview1.items.where(x=>x.SubItems[0].value == mycodeId).firstordefault();
now i have the item i can retrieve data from it:
textbox1.Text = item[1].value;