I have a problem. In a scene I have a list and when I click on it shows me with some clones prefab the contents of the database. The point is that once generated, no matter how much I select another item from the list, the prefabs do not update me and show the contents of the first selection. I've tried to destroy them. But the most I've managed to do is destroy the original prefab and don't believe me anymore. How can I fix this?
public Transform contentParent;
public List<TablePlayerTransctData> playersTransct;
public GameObject playerTransactItemPrefab;
private Models.Club _club;
public TextMeshProUGUI memberUuid;
public void Start()
{
GetTablePlayerTransactRequest();
}
private void GetTablePlayerTransactRequest()
{
_club = TablesManager.Instance.club;
new LogEventRequest().SetEventKey("Transactions")
.SetEventAttribute("clubId", _club.id)
.SetEventAttribute("playerId", memberUuid.text)
.Send(GetTablePlayerTransactResponse);
}
private void HandleResults()
{
var cont = 0f;
Vector3 posicion = new Vector3();
foreach (TablePlayerTransctData data in playersTransct)
{
GameObject item = Instantiate(playerTransactItemPrefab, contentParent);
PlayerTransact playerTableTransact = item.GetComponent<PlayerTransact>();
if (cont == 0)
{
posicion = playerTableTransact.transform.position;
}
else
{
posicion.Set(posicion.x, (posicion.y) - 0.5f, posicion.z);
playerTableTransact.transform.position = posicion;
}
playerTableTransact.Setup(data);
cont++;
}
}
private void GetTablePlayerTransactResponse(LogEventResponse response)
{
if (response.HasErrors)
{
return;
}
GSData scriptData = response.ScriptData;
var list = scriptData.GetGSDataList("list");
playersTransct = new List<TablePlayerTransctData>();
foreach (GSData entry in list)
{
TablePlayerTransctData data = new TablePlayerTransctData()
{
chips = GSUtil.GetInt(entry, "chips"),
date = GSUtil.GetDouble(entry, "date"),
};
playersTransct.Add(data);
}
HandleResults();
}
public class TablePlayerTransctData
{
public int chips;
public double date;
}
I think these prefabs should be reset or removed just before they are re-created. But I don't know how to do this or maybe there's another, more correct way to do it.
Edit:
I changed Start to Update and magically works almost as it should. I don't understand what this miracle is like. Since with starting every time you enter you should cool off on your own. Now all that remains is to remove the leftover prefabs which is what I don't finish doing properly.
Before instantiating the new items you could destroy them all like e.g.
// General hint: Make this of the correct type
// - Makes sure that the referenced prefab actually HAS that component
// - Gets rid of some GetComponent calls later since Instantiate already
// returns the same type as the given prefab has
public PlayerTransact playerTransactItemPrefab;
foreach(Transform child in contentParent)
{
Destroy(child.gameObject);
}
foreach (var data in playersTransct)
{
PlayerTransact item = Instantiate(playerTransactItemPrefab, contentParent);
...
}
Alternatively you could of course only create the amount of objects you need and destroy others and instead of destroying and recreating all of them reuse those that already exists like e.g.
// cache the counts
var childCount = contentParent.childCount;
var dataCount = playersTransct.Count;
// we want to iterate the count that is greater
var count = Mathf.Max(childCount, dataCount);
for (var i = 0; i < count; i++)
{
if (i < dataCount)
{
// there is data for this index
var data = playersTransct[i];
// so we need a child for it
PlayerTransact playerTableTransact;
if (i < childCount)
{
// We are still in the range of existing childs -> Re-use a child that already exists
playerTableTransact = contentParent.GetChild(i).GetComponent<PlayerTransact>();
posicion = playerTableTransact.transform.position;
}
else // i < dataCount && i >= childCount
{
// There is still data but no childs left -> create a new one now
playerTableTransact = Instantiate(playerTransactItemPrefab, contentParent);
if (i == 0)
{
posicion = playerTableTransact.transform.position;
}
else
{
posicion -= Vector3.up * 0.5f;
playerTableTransact.transform.position = posicion;
}
}
playerTableTransact.Setup(data);
}
else // i < childCount && i >= dataCount
{
// There are more childs than data -> destroy the unneeded childs
Destroy(contentParent.GetChild(i).gameObject);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(PickupObjects))]
public class PickupObjectsEditor : Editor
{
private static List<GameObject> pickeditems = new List<GameObject>();
private static bool picked = false;
private SerializedProperty _serializedpickeditems;
[MenuItem("GameObject/Generate as Pickup Item", false, 30)]
public static void GeneratePickupItems()
{
if (Selection.gameObjects.Length > 0)
{
for (int i = 0; i < Selection.gameObjects.Length; i++)
{
if (Selection.gameObjects[i].GetComponent<TestScript>() == null)
{
Selection.gameObjects[i].AddComponent<BoxCollider>();
Selection.gameObjects[i].AddComponent<TestScript>();
}
Selection.gameObjects[i].layer = 9;
Selection.gameObjects[i].tag = "Pickup Item";
pickeditems.Add(Selection.gameObjects[i]);
}
picked = true;
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
PickupObjects myTarget = (PickupObjects)target;
DrawDefaultInspector();
if (picked == true)
{
for (int i = 0; i < pickeditems.Count; i++)
{
myTarget.pickUpObjects.Add(pickeditems[i]);
var item = _serializedpickeditems.GetArrayElementAtIndex(i);
var serializedItem = new SerializedObject(item.objectReferenceValue);
serializedItem.Update();
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " " + item.name));
serializedItem.ApplyModifiedProperties();
}
pickeditems.Clear();
picked = false;
serializedObject.ApplyModifiedProperties();
}
}
private void OnEnable()
{
_serializedpickeditems = serializedObject.FindProperty("pickUpObjects");
}
}
And the mono script
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class PickupObjects : MonoBehaviour
{
public List<GameObject> pickUpObjects = new List<GameObject>();
}
I tried to use serialize and PropertyField but still it's showing the List with Element0,Element1,Element2.... And I want it to be :
Picked Item Box
Picked Item Can
Picked Item Cube
Picked Item Dock_Pod
Your
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " " + item.name));
sits within a code block that is only executed once.
What you see currently is actually only the list drawn by
DrawDefaultInspector();
since the rest is disappeared after 1 frame/draw call.
You would rather want to separate the pick "method" from the drawing like e.g.
public override void OnInspectorGUI()
{
serializedObject.Update();
if (picked)
{
for (var i = 0; i < pickeditems.Count; i++)
{
// NOTE: Never mix serializedProperties and direct access/modifications on the target!
// This messes up the marking dirty and saving these changes!
// Rather always go through the SerializedProperties so the editor handles everything automatically
_serializedpickeditems.arraySize++;
_serializedpickeditems.GetArrayElementAtIndex(i).objectReferenceValue = pickeditems[i];
}
picked = false;
pickeditems.Clear();
}
for (var i = 0; i < _serializedpickeditems.arraySize; i++)
{
var item = _serializedpickeditems.GetArrayElementAtIndex(i);
// little bonus from me: Color the field if the value is null ;)
var color = GUI.color;
if(!item.objectReferenceValue) GUI.color = Color.red;
{
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " " + (item.objectReferenceValue ? item.objectReferenceValue.name : "null")));
}
GUI.color = color;
// The only case you would need to go deeper here and use
// your new SerializedObject would be if you actually make changes
// to these objects/components like e.g. directly allow to edit their name
}
serializedObject.ApplyModifiedProperties();
}
Note you should also clear the pickeditems list before adding new items:
[MenuItem("GameObject/Generate as Pickup Item", false, 30)]
public static void GeneratePickupItems()
{
if (Selection.gameObjects.Length > 0)
{
pickeditems.Clear();
for (int i = 0; i < Selection.gameObjects.Length; i++)
{
if (Selection.gameObjects[i].GetComponent<Whilefun.FPEKit.FPEInteractablePickupScript>() == null)
{
Selection.gameObjects[i].AddComponent<BoxCollider>();
Selection.gameObjects[i].AddComponent<Whilefun.FPEKit.FPEInteractablePickupScript>();
}
Selection.gameObjects[i].layer = 9;
Selection.gameObjects[i].tag = "Pickup Item";
pickeditems.Add(Selection.gameObjects[i]);
}
picked = true;
}
}
In general I always recommend to use a ReorderableList!
It's a bit tricky at first to get into it but as soon as you have set it up it is an amazing tool. even if you don't make it actually reorderable it is still a huge advantage to be e.g. able to dynamically remove an item from the middle ;)
I'm working with four prefabs, "Elements_" is the parent, content_image, content_tittle and content_Options are the children. Inside a for I am adding a number at the end of your name to put them inside the father. Any idea how to do it?
If you want Content_image_1, Content_tittle_1 and Content_Options_1 to be children of elements_1, you can do so:
public GameObject[] Elements;
public GameObject aux;
public int numberofchildren=3;
//...
//instances yours objects
//...
public void makeChildren()
{
for (int i = 0; i < numberofchildren; i++)
{
aux = GameObject.FindGameObjectWithTag("Content_image_" + i);
aux.transform.parent = Elements[i].transform;
aux = GameObject.FindGameObjectWithTag("Content_tittle_" + i);
aux.transform.parent = Elements[i].transform;
aux = GameObject.FindGameObjectWithTag("Content_Options_" + i);
aux.transform.parent = Elements[i].transform;
}
}
}
If you want to undo this action you can do this:
public void undoChildren(){
for(int i=0;i<numberofchildren;i++){
Elements[i].transform.DetachChildren();
}
}
Remember to add the tags to each prefab
I hope it helps you.
This code is creating an inventory for an RPG game. I'm calling the item information from Items database that is a json file. For some reasons it is skipping this line. I marked it in the code to make it stand out a little more.
data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();
here is the entire code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
ItemDataBase database;
GameObject inventoryPanel;
GameObject slotPanel;
public GameObject inventorySlot;
public GameObject inventoryItem;
int slotAmount;
public List<Item> items = new List<Item>();
public List<GameObject> slots = new List<GameObject>();
private void Start()
{
database = GetComponent<ItemDataBase>();
slotAmount = 20;
inventoryPanel = GameObject.Find("Inventory Panel");
slotPanel = inventoryPanel.transform.FindChild("Slot Panel").gameObject;
for (int i = 0; i < slotAmount; i++)
{
items.Add(new Item());
// and empty slot
slots.Add(Instantiate(inventorySlot));
//set parent to slot panel
slots[i].transform.SetParent(slotPanel.transform);
}
for (int i = 0; i < items.Count; i++)
{
items[i].ID = -1;
}
AddItem(0);
AddItem(1);
AddItem(1);
Debug.Log(items[1].Title);
}
public void AddItem(int id)
{
Item itemToAdd = database.FetchItemByID(id);
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd))
{
for (int i = 0; i < items.Count; i++)
{
if (items[i].ID == id)
{
ItemData data = slots[i].transform.GetChild(0).GetComponent<ItemData>();
data.amount++;
**data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();**
break;
}
}
}
else
{
for (int i = 0; i < items.Count; i++)
{
// in video had this set to -1
if (items[i].ID == -1)
{
items[i] = itemToAdd;
GameObject itemObj = Instantiate(inventoryItem);
itemObj.transform.SetParent(slots[i].transform);
itemObj.transform.position = Vector2.zero;
itemObj.GetComponent<Image>().sprite = itemToAdd.Sprite;
itemObj.name = itemToAdd.Title;
itemObj.name = itemToAdd.Title;
break;
}
}
}
}
bool checkIfItemIsInInventory(Item item)
{
for (int i = 0; i<items.Count; i++)
{
if (items[i].ID == item.ID)
{
return true;
}
}
return false;
}
}
Here is some debugging information. First here is the whole function it is messing up on
public void AddItem(int id)
{
Item itemToAdd = database.FetchItemByID(id);
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd))
{
for (int i = 0; i < items.Count; i++)
{
if (items[i].ID == id)
{
ItemData data = slots[i].transform.GetChild(0).GetComponent<ItemData>();
data.amount++;
data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();
break;
}
}
}
so when I get to this line while debugging
data.amount++;
the watch says
data ---- null
data.amount ---- Could not find the member 'amount' for 'object'
amount---- the identifier 'amount'is not in the scope
so to break all of this down data is initialized here
ItemData data = slots[i].transform.GetChild(0).GetComponent();
amount is here (this is a different file called ItemData
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemData : MonoBehaviour {
public Item item;
public int amount;
}
thanks everyone for being helpful I really just want to learn how to code better.
Try placing a break point at the block above:
if (items[i].ID == id){
or even higher at:
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd)){
Step through (using F10) and see if it's hitting the line. It's probably jumping out when one of the conditions aren't met. That, or if you need to execute this line through the entirety of the for loop, it's jumping out of the loop when it hits the line the first time because of the break; following.
Either way, step through the lines after the break point, and check the values of everything. Make sure you're not getting null references or values.
How do I loop through the all controls in a window in WPF?
I found this in the MSDN documenation so it helps.
// Enumerate all the descendants of the visual object.
static public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
// Do processing of the child visual object.
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
Looks simpler to me. I used it to find textboxes in a form and clear their data.
This way is superior to the MSDN method, in that it's reusable, and it allows early aborting of the loop (i.e. via, break;, etc.) -- it optimizes the for loop in that it saves a method call for each iteration -- and it also lets you use regular for loops to loop through a Visual's children, or even recurse it's children and it's grand children -- so it's much simpler to consume.
To consume it, you can just write a regular foreach loop (or even use LINQ):
foreach (var ctrl in myWindow.GetChildren())
{
// Process children here!
}
Or if you don't want to recurse:
foreach (var ctrl in myWindow.GetChildren(false))
{
// Process children here!
}
To make it work, you just need put this extension method into any static class, and then you'll be able to write code like the above anytime you like:
public static IEnumerable<Visual> GetChildren(this Visual parent, bool recurse = true)
{
if (parent != null)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
// Retrieve child visual at specified index value.
var child = VisualTreeHelper.GetChild(parent, i) as Visual;
if (child != null)
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetChildren(true))
{
yield return grandChild;
}
}
}
}
}
}
Also, if you don't like recursion being on by default, you can change the extension method's declaration to have recurse = false be the default behavior.
Class to get a list of all the children's components of a control:
class Utility
{
private static StringBuilder sbListControls;
public static StringBuilder GetVisualTreeInfo(Visual element)
{
if (element == null)
{
throw new ArgumentNullException(String.Format("Element {0} is null !", element.ToString()));
}
sbListControls = new StringBuilder();
GetControlsList(element, 0);
return sbListControls;
}
private static void GetControlsList(Visual control, int level)
{
const int indent = 4;
int ChildNumber = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i <= ChildNumber - 1; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(control, i);
sbListControls.Append(new string(' ', level * indent));
sbListControls.Append(v.GetType());
sbListControls.Append(Environment.NewLine);
if (VisualTreeHelper.GetChildrenCount(v) > 0)
{
GetControlsList(v, level + 1);
}
}
}
}
I've used the following to get all controls.
public static IList<Control> GetControls(this DependencyObject parent)
{
var result = new List<Control>();
for (int x = 0; x < VisualTreeHelper.GetChildrenCount(parent); x++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, x);
var instance = child as Control;
if (null != instance)
result.Add(instance);
result.AddRange(child.GetControls());
}
return result;
}
A slight variation on the MSDN answer ... just pass in an empty List of Visual objects into it and your collection will be populated with all the child visuals:
/// <summary>
/// Enumerate all the descendants (children) of a visual object.
/// </summary>
/// <param name="parent">Starting visual (parent).</param>
/// <param name="collection">Collection, into which is placed all of the descendant visuals.</param>
public static void EnumVisual(Visual parent, List<Visual> collection)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
// Get the child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(parent, i);
// Add the child visual object to the collection.
collection.Add(childVisual);
// Recursively enumerate children of the child visual object.
EnumVisual(childVisual, collection);
}
}
The previous answers will all return the children that are identified by VisualTreeHelper.GetChildrenCount and VisualTreeHelper.GetChild. However, I have found that for a TabControl, the TabItems and their content are not identified as children. Thus, these would be omitted, and I think the original question ("all controls in a window") would like to have them included.
To properly loop through tabbed controls as well, you will need something like this (modified from the answer of #BrainSlugs83):
public static IEnumerable<Visual> GetChildren(this Visual parent, bool recurse = true)
{
if (parent != null)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
// Retrieve child visual at specified index value.
var child = VisualTreeHelper.GetChild(parent, i) as Visual;
if (child != null)
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetChildren(true))
{
yield return grandChild;
}
// Tabs and their content are not picked up as visual children
if (child is TabControl childTab)
{
foreach (var childTabItem in childTab.Items)
{
yield return childTabItem;
foreach (var childTabItemChild in childTabItem.GetChildren(true))
{
yield return childTabItemChild;
}
if (childTabItem.Content != null && childTabItem.Content is Visual childTabItemContentAsVisual)
{
yield return childTabItemContentAsVisual;
foreach (var childTabItemGrandChild in childTabItemContentAsVisual.Children(true)
{
yield return childTabItemGrandChild;
}
}
}
}
}
}
}
}
}
Alternatively, you could iterate over the logical tree instead of the visual tree:
public static IEnumerable<DependencyObject> GetLogicalChildren(this DependencyObject parent, bool recurse = true)
{
if (parent == null) yield break;
foreach (var child in LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>())
{
yield return child;
if (recurse)
{
foreach (var grandChild in child.GetLogicalChildren(true))
{
yield return grandChild;
}
}
}
}
I tried it myself and I found an elegant solution that also works in any scenario, not like all the solutions posted here that are overengineered and broken. The majority of the answers here are over-engineered and unstable.
The idea is to loop through a parent control in Windows Presentation Foundation to get the children controls of the desired type or types.
First you need a loop that has an integer, set to zero preferably, with the use of indexing and a VisualTreeHelper object that is counting all the objects within the parent control as the loop's condition.
for(int ControlCounter = 0; ControlCounter <= VisualTreeHelper.GetChildrenCount(MaterialsContentComputerSystemsFoundationYear) - 1; ControlCounter++)
{
if(VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter).GetType() == File1.GetType())
{
Button b = (Button)VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter);
if (ActualButtonControlIndex == App.FileIndex[ActualButtonControlIndex])
{
}
else
{
b.Visibility = Visibility.Hidden;
}
ActualButtonControlIndex++;
System.Diagnostics.Debug.WriteLine(ActualButtonControlIndex + " Button");
}
}
Within the for loop you can make a conditional statement that is verifying if the type of the current control at the current index is equal with the type of control that is desired. In this example I used a control that is named and is part of the desired type of control that is currently searched. You can use a variable that is storing a button instead, for type comparison.
var b = new Button();
for(int ControlCounter = 0; ControlCounter <= VisualTreeHelper.GetChildrenCount(MaterialsContentComputerSystemsFoundationYear) - 1; ControlCounter++)
{
if(VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter).GetType() == b.GetType())
{
Button B = (Button)VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter);
if (ActualButtonControlIndex == App.FileIndex[ActualButtonControlIndex])
{
}
else
{
B.Visibility = Visibility.Hidden;
}
ActualButtonControlIndex++;
System.Diagnostics.Debug.WriteLine(ActualButtonControlIndex + " Button");
}
}
Within the for loop's conditional statement, an object of the type of the desired control is created that has its value set with the value of the VisualTreeHelper object at the current index casted to the type Button.
You can use the previously mentioned button to set proprieties like size, with and content, colour, etc. to the control in the application's window, within the parent control within that window, at the current index.
var b = new Button();
for(int ControlCounter = 0; ControlCounter <= VisualTreeHelper.GetChildrenCount(MaterialsContentComputerSystemsFoundationYear) - 1; ControlCounter++)
{
if(VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter).GetType() == b.GetType())
{
Button B = (Button)VisualTreeHelper.GetChild(MaterialsContentComputerSystemsFoundationYear, ControlCounter);
if (ActualButtonControlIndex == App.FileIndex[ActualButtonControlIndex])
{
B.Content = "Hello";
B.FontSize = 20;
B.BackGround = new SolidColorBrush(Colors.Red);
}
else
{
B.Visibility = Visibility.Hidden;
}
ActualButtonControlIndex++;
System.Diagnostics.Debug.WriteLine(ActualButtonControlIndex + " Button");
}
}
This solution is modular, simple and super-stable and thus useful in any scenario, give a like.