Accessing unknown child - c#

In my code, I need to find a child of a child of a child.
Only the first child will always be the same child but the child of the child is randomized between 4 different possible prefabs.
on that last child I want to disable it's image.
here would be the ideal code if the child wouldn't be randomized between others:
protected void SetUnpickedPrizesVisibility(bool isVisible)
{
foreach (var element in list)
{
if (!element.IsPicked)
{ element.transform.FindChild("FirstChild").FindChild("SecondChild").FindChild("ThirdChild").GetComponent<Image>().enabled = false;
}
}
}
But since, the SecondChild varies, I can't simply do that.
Is my only option to do something like this?
protected void SetUnpickedPrizesVisibility(bool isVisible)
{
foreach (var element in list)
{
if (!element.IsPicked)
{
if(element.transform.FindChild("FirstChild").FindChild("SecondChild1"))
element.transform.FindChild("FirstChild").FindChild("SecondChild1").FindChild("ThirdChild").GetComponent<Image>().enabled = false;
if(element.transform.FindChild("FirstChild").FindChild("SecondChild2"))
element.transform.FindChild("FirstChild").FindChild("SecondChild2").FindChild("ThirdChild").GetComponent<Image>().enabled = false;
if(element.transform.FindChild("FirstChild").FindChild("SecondChild3"))
element.transform.FindChild("FirstChild").FindChild("SecondChild3").FindChild("ThirdChild").GetComponent<Image>().enabled = false;
}
}
}
Or does someone have a different idea?
I know this is a very confusing to read question but I tried my best to simplify it.
Thanks,

So why not add a script to the prefab of the last child that disables the image. That way you only need to keep a reference to the script when you instantiate it and not go looking for the children.

You can just search through, something like this:
foreach (var element in list)
{
if (!element.IsPicked)
{
foreach(var grandChild in element)
{
foreach(var greatGrandChild in grandChild)
{
greatGrandChild.GetComponent<Image>().enabled = false;
}
}
}
}
It's easier to use child, grandChild and greatGrandChild rather than child of the child of child :-)

Related

How to display the part of the hierarchy depending on selection object in editor window ui panel with the option expand the children?

private void OnGUI()
{
EditorGUILayout.BeginHorizontal();
{
scroll = EditorGUILayout.BeginScrollView(scroll, GUILayout.Width(200), GUILayout.Height(500));
{
GameObject[] gameObjects = GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[];
foreach (GameObject go in gameObjects)
{
// Start with game objects without any parents
if (go.transform.parent == null)
{
// Show the object and its children
ShowObject(go, gameObjects);
}
}
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.EndHorizontal();
void ShowObject(GameObject parent, GameObject[] gameObjects)
{
// Show entry for parent object
if (UnityEditor.EditorGUILayout.Foldout(true, parent.name))
{
foreach (GameObject go in gameObjects)
{
// Find children of the parent game object
if (go.transform.parent == parent.transform)
{
ShowObject(go, gameObjects);
}
}
}
}
}
The display the whole hierarchy on the editor window ui panel.
but i have some problems :
In this line i can set true or false but how can i make that i will be able to expand/collapse it when clicking on the arrow like in the hierarchy it self ?
How to make that when expanding it will show it in a tree like in the hierarchy the children like in a tree ? The same as in the hierarchy.
How to make that it will show only from a selection object, when selecting object cliking on object in the hierarhcy display the hierarchy from the selected object and not the whole hierarchy like now ?
In order to use Foldout, you need a boolean value that can track the expansion/contraction.
isOpen = EditorGUILayout.Foldout(isOpen, "Expand");
if (isOpen)
{
GUILayout.Label("Expanded content");
}
This would take a fair amount of architecture to work as a hierarchy view. In your context it could look something like..
if (thisObj.ParentObj.ExpandedInEditor)
{
thisObj.ExpandedInEditor = EditorGUILayout.Foldout(thisObj.ExpandedInEditor, thisObj.name);
if (thisObj.ExpandedInEditor)
{
// Draw object here
}
}

How can I clear the pushpins from dynamically created MapLayers on a Bing Map?

I have this code:
DataLayer.Children.Clear();
...which removes the pushpins from the design-time MapLayer, declared like this:
<bm:Map Credentials="CuellarDobsonMcNallyPalmer420s" x:Name="photraxMap" Margin="0,0,0,0" >
<bm:Map.Children>
<!-- Data Layer-->
<bm:MapLayer Name="DataLayer"/>
. . .
But I also create additional MapLayers programmatically:
MapLayer mapLayerToUse = DataLayer;
if (Cre8DynamicMapLayer)
{
mapLayerToUse = new MapLayer();
mapLayerToUse.Name = String.Empty;
photraxMap.Children.Insert(0, mapLayerToUse);
}
...so how can I also clear any of these dynamically created MapLayers?
I could do something involved such as give the dynamic map layers randomized names, store them in a list, and then iterate over that list, calling Children.Clear on them (after finding a MapLayer with that name), but I imagine there must be a simpler and more elegant way than that. Does anybody know of one?
UPDATE
Nicolas, is something like this:
// App.xaml.cs
sealed partial class App : Application
{
public static List<MapLayer> dynamicMapLayers { get; set; }
. . .
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
. . .
dynamicMapLayers = new List<MapLayer>();
//MainPage.xaml.cs (update of previously shown code):
MapLayer mapLayerToUse = DataLayer;
if (Cre8DynamicMapLayer)
{
mapLayerToUse = new MapLayer();
mapLayerToUse.Name = String.Empty;
dynamicMapLayers.Add(mapLayerToUse); // <= this is the new line
photraxMap.Children.Insert(0, mapLayerToUse);
}
// Finally, insted of doing all that looping as shown in my proposed answer, simply do this instead:
foreach (MapLayer ml in dynamicMapLayers)
{
ml.Children.Clear();
}
...what you had in mind?
If you did map.Children.Clear it would remove all MapLayers on the map, including the dynamically added ones. If you want to clear each MapLayer, without removing it, then you have to recursively loop through all the items in the MapLayer and check to see an item is a MapLayer or not as MapLayers can have child MapLayers. If an item is not a MapLayer then remove it, if it is, then recursively go through the child MapLayer.
I was hoping there would be something very concise and pithy, but it may be that I have to do something like this after all:
// Adapted the following from http://www.codeease.com/only-remove-pushpins-from-a-layer-in-bing-map.html
private void ClearPushpins()
{
List<UIElement> elementsToRemove = new List<UIElement>();
List<UIElement> pushpinToRemove = new List<UIElement>();
foreach (String photoset in App.CurrentlyMappedPhotosets)
{
foreach (UIElement element in photraxMap.Children)
{
if (element.GetType() == typeof(MapLayer))
{
MapLayer Lay = (MapLayer)element;
if (Lay.Name == photoset)
{
foreach (UIElement p in Lay.Children)
{
if (p.GetType() == typeof(Pushpin))
{
pushpinToRemove.Add(p);
}
}
foreach (UIElement pin in pushpinToRemove)
{
Lay.Children.Remove(pin);
}
elementsToRemove.Add(Lay);
}
}
foreach (UIElement e in elementsToRemove)
{
photraxMap.Children.Remove(e);
}
}
}
}
You can delete map layers by using the remove at function
int number_of_layers = map.Layers.Count;
for (var i = 0; i < number_of_layers; i++)
{
map.Layers.RemoveAt(0);
}

Unchecking ListViewItems recursively

I have a ListView with cascading checkboxes inside of it. What I would like to do is be able to uncheck all child boxes if the parent box is unchecked (and similarly I'd like to check all child boxes if the parent is checked). Currently, if I check or uncheck a box, the parent node and child nodes are greyed out, but the check mark is still visible on the child boxes.
I presume I'd have to do this recursively. I've given the following code a try and I think it should work, but it breaks at runtime due to casting issues.
private void listViewChildNodeModifier(ListViewItem item)
{
if (item.SubItems.Equals(null)) return;
else
{
foreach (ListViewItem childItem in item.SubItems)
{
listViewChildNodeModifier(childItem);
if(childItem.Tag is TestingNode)
((TestingNode)childItem.Tag).Enabled = item.Checked;
}
}
}
Could you not do this
foreach (ListViewItem item in this.ListView.Items)
{
for (int i = 0; i < item.SubItems.Count; i++)
{
item.SubItems[i].Checked = false;
//will something like this work for you let me know
}
}
another alternative which should work as well would be the following
foreach (ListViewItem.ListViewSubItem childItem in item.SubItems)
{
if(item.Checked)
{
item.Checked = false;
}
}

Why are my child nodes not detected without the parent node being selected?

I have a treelist in my form with checkboxes enabled. I need to add anything checked to a list so I can write that list out. If I check any parent nodes (or highest level nodes) it includes them. If I select any parent nodes it also selects its children nodes (this is intentional), and displays those. But If I check any child nodes only, it won't add them to my list.
//check to see if there are any nodes checked
bool nodeHasCheck = false;
foreach (TreeNode n in nodes)
{
if (n.Checked)
{
nodeHasCheck = true;
break;
}
GetExtendedFeatures(n.Nodes);
}
//only return stuff if something's checked
if (nodeHasCheck == true)
{
foreach (TreeNode n in nodes)
{
if (n.Checked)
{
//n.BackColor = Color.Black;
nodeList.Add(n.Text);
}
GetExtendedFeatures(n.Nodes);
}
It also appears that if I select 2 parent nodes, the recursion that occurs (Think that's the right term) is causing it to find the first checked node, then starts over, and adds that same checked node a second time before it hits the second set of nodes.
I provided my node test, hopefully it's enough to identify why it's not detecting child nodes selected without the parent node selected.
UPDATED - 3/18/13
My button click code:
private void btnGenerate_Click(object sender, EventArgs e)
{
ScanNodes(treeView1.Nodes[0]);
}
private void ScanNodes(TreeNode parent)
{
foreach (TreeNode node in parent.Nodes)
{
if (node.Checked)
{
nodeList.Add(node.Text.ToString());
}
if (node.Nodes.Count > 0)
{
ScanNodes(node);
}
}
var message = string.Join(Environment.NewLine, nodeList);
message = message.Replace(Environment.NewLine, ", ");
MessageBox.Show(message);
nodeList.Clear();
}
To get a List of all the selected nodes in a TreeView you can use the following:
Supose the list is named nodeList:
//We First declare a recursive method to loop through all nodes,
//we need to pass a root node to start
private void ScanNodes(TreeNode parent)
{
foreach (TreeNode node in parent.Nodes)
{
if (node.Checked)
{
nodeList.Add(node.Text);
}
if (node.Nodes.Count > 0)
{
ScanNodes(node);
}
}
}
With that set up You just need to call the ScanNodes method and pass the root node of your TreeView:
ScanNodes(treeView1.Nodes[0]);
Regards,

Breadth first search of C# Control Collection

I'm trying to write an extension method for System.Web.UI.Control which will search its ControlCollection for an instance of specific Type, and return the first instance found. Depth-first is simple, however I want to search breadth first so that higher collections take priority.
My current method is flawed and will exit early, returning null in certain cases where the entire search wasn't completed. I'm hoping I'm close to the right solution but need some fresh eyes on it. Any suggestions?
public static T FindFirstControlOfType<T>(this Control rootControl, bool searchRecursively) where T : Control
{
// list for container controls
List<Control> controlsWithChildren = new List<Control>();
// iterate the current control collection first
foreach (Control child in rootControl.Controls)
{
if (child.GetType().IsAssignableFrom(typeof(T)))
{
return (T)child;
}
// track those controls containing children
if (child.HasControls())
{
controlsWithChildren.Add(child);
}
}
// if recursion is enabled, search the child nodes
if (searchRecursively)
{
foreach (Control control in controlsWithChildren)
{
return FindFirstControlOfType<T>(control, true);
}
}
// if never found, return null
return null;
}
EDIT - Working Solution based on the marked answer, for anyone interested:
public static T FindFirstControlOfType<T>(this Control rootControl, bool searchNestedControls) where T : Control
{
Queue<Control> queue = new Queue<Control>();
EnqueueChildControls(queue, rootControl);
while (queue.Count > 0)
{
Control current = queue.Dequeue();
if (current.GetType() == typeof(T))
{
return (T)current;
}
if (searchNestedControls)
{
EnqueueChildControls(queue, current);
}
}
return null;
}
private static void EnqueueChildControls(Queue<Control> queue, Control control)
{
foreach (Control current in control.Controls)
{
queue.Enqueue(current);
}
}
Breadth first search (pseudo code)
Queue fringe
fringe.Enqueue(rootControl)
while(fringe.Count > 0)
{
current = fringe.Dequeue()
Visit(current)
fringe.EnqueueAll(current.Children)
}
There's no need for recursion for breadth first search.
Visit is whatever you want to do with the current object. If you're searching for something then Visit would mean: if(current matches criteria) return current
Update:
Working example: https://gist.github.com/1960108
Well the main problem is here:
foreach (Control control in controlsWithChildren)
{
return FindFirstControlOfType<T>(control, true);
}
You are returning the results of the recursive search on the very first child with children, regardless of whether that search succeeded. Breadth-first search is a widely-discussed topic, but it boils down to using a queue:
Queue<Control> controlsWithChildren = new Queue<Control>();
and (usually) a loop rather than recursion.

Categories

Resources