How to prevent a child destroying in Unity - c#

I am preparing a puzzle game. I can drag and drop from down generated sticks to up places. Let's say that there are two sticks on the first node and we have 2 more sticks in bottom. We can drag it to right place and make them one parent again. I can make them one parent but second stick from buttom is destroying when i drop on the first node.
In this picture it is aqua color:
before drop
after drop
How to resolve this issue?
if(nodes[smallestId].transform.childCount > 0)
{
for (int i = 0; i < rayhit.transform.childCount; i++)
{
nodes[smallestId].transform.GetChild(0).GetComponent<Node>().sticks.Add(rayhit.transform.GetChild(i).GetComponent<Stick>());
rayhit.transform.GetChild(i).transform.SetParent(nodes[smallestId].transform.GetChild(0));
}
Destroy(rayhit.transform.gameObject);
}
else
{
rayhit.transform.SetParent(nodes[smallestId].transform);
Debug.Log("Stick : " + nodes[smallestId].transform);
}

The issue is that you iterate over rayhit.transform.childCount.
When you remove a child due to rayhit.transform.GetChild(i).transform.SetParent the rayhit.transform.childCount is already reduced by one.
Therefore in the next iteration i might already be greater then rayhit.transform.childCount so e.g. the second object is not moved to the new parent!
Instead You could use
// do this only once
var targetParent = nodes[smallestId].transform;
if(targetParent > 0)
{
targetParent = targetParent.GetChild(0);
// do also this only once
var node = targetParent.GetComponent<Node>();
var toMove = new List<Transform>();
foreach (Transform child in rayhit.transform)
{
node.sticks.Add(child.GetComponent<Stick>());
toMove.Add(child);
}
foreach(var child in toMove)
{
child.SetParent(targetParent);
}
Destroy(rayhit.transform.gameObject);
}
else
{
rayhit.transform.SetParent(targetParent);
Debug.Log("Stick : " + targetParent);
}
Which first stores them all in a list so you don't depend on whether they already where moved to a new parent or not.
As alternative I think you could even rather use GetComponentsInChildren here which would completely save you from the trouble and you wouldn't need to use two loops at all:
var targetParent = nodes[smallestId].transform;
if(targetParent > 0)
{
targetParent = targetParent.GetChild(0);
// do also this only once
var node = targetParent.GetComponent<Node>();
// These references now don't depend on their parent object
// so you can safely iterate on them without any issues
foreach (var stick in rayhit.transform.GetComponentsInChildren<Stick>(true))
{
node.sticks.Add(stick);
stick.transform.SetParent(targetParent);
}
Destroy(rayhit.transform.gameObject);
}
else
{
rayhit.transform.SetParent(targetParent);
Debug.Log("Stick : " + targetParent);
}

guys here is solution :)
rayhit.transform.position = nodes[smallestId].transform.position;
if (rayhit.transform.parent != nodes[smallestId].transform)
{
if (nodes[smallestId].transform.childCount > 0 && nodes[smallestId].transform != rayhit.transform.parent)
{
if (currNode != null)
{
for (int i = 0; i < currNode.sticks.Count; i++)
{
nodes[smallestId].transform.GetChild(0).GetComponent<Node>().sticks.Add(currNode.sticks[i]);
currNode.sticks[i].transform.SetParent(nodes[smallestId].transform.GetChild(0));
}
Destroy(rayhit.transform.gameObject);
}
}
else
{
if (currNode != null)
{
currNode.isMoved = true;
}
rayhit.transform.SetParent(nodes[smallestId].transform);
}
}
Thank you for your help.

Related

How can i match two different List?

I have 2 GameObject and each other has a lot of child. This two GameObject same as same except some child's exist or child's position. I found each child's name.
And also I found locations info for each item. Then I compared this Lists and i found relocated, exist, added and removed items. I want to match with relocated items with names. How can I do that?
On Project1
List<string> existItems = GetNames().Intersect(revisedBasicProject4.GetNames4()).ToList();
List<Vector3> comparePosition = GetLocations().Except(revisedBasicProject4.GetLocations()).ToList();
foreach (var item in existItems)
{
isExist = true;
}
if (isExist)
{
foreach (var item in comparePosition)
{
Debug.Log("RELOCATED ITEMS :" +item);
}
}
I do not know if I understood what you meant correctly or not, but you can use the following code
List<string> existItems = GetNames().Intersect(revisedBasicProject4.GetNames4()).ToList();
List<Vector3> comparePosition = GetLocations().Except(revisedBasicProject4.GetLocations()).ToList();
foreach (var item in existItems)
foreach (var li in comparePosition)
if (item.(...) == li.(...)) {
Debug.Log("RELOCATED ITEMS :" + item);
}
Following on from https://stackoverflow.com/users/7111561/derhugo and https://stackoverflow.com/users/13922490/mohammad-asadi - You want to store names and locations together, so use Transform:
Then, you can compare your revisedBasicProject4 Transforms with the current class like so:
foreach(var transform in GetTransforms())
{
foreach(var revisedBasicProject4Transform in revisedBasicProject4.GetTransforms())
{
if ( transform.name == revisedBasicProject4Transform.name
&& transform.position != revisedBasicProject4Transform.position)
{
Debug.Log("RELOCATED ITEM : " + transform.name);
}
}
}
If you find this is taking forever (because you have millions of Transforms, you may be able to optimise this by doing the following:
var transforms = new List<Transform>(GetTransforms());
var otherTransforms= new List<Transform>(revisedBasicProject4.GetTransforms());
for(var i = 0; i < transforms.Count; i++)
{
for(var j = 0; j < otherTransforms.Count; j++)
{
if ( transforms[i].name == otherTransforms[j].name )
{
if ( transforms[i].position != otherTransforms[j].position )
Debug.Log("RELOCATED ITEM : " + transforms[i].name);
otherTransforms.RemoveAt(j); //We found it so remove it from our temporary "otherTransforms" list
break; //We found it so move on to the next "transforms" item
}
}
}

Destroy clone prefab

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 c# remove duplicate html span elements

I have to convert word to html which I'm doing with Aspose and that is working well. The problem is that it is producing some redundant elements which I think is due to the way the text is store in word.
For example in my word document the text below appears:
AUTHORIZATION FOR RELEASE
When converted to html it becomes:
<span style="font-size:9pt">A</span>
<span style="font-size:9pt">UTHORIZATION FOR R</span>
<span style="font-size:9pt">ELEASE</span>
I'm using C# and would like a way to remove the redundant span elements. I'm thinking either AngleSharp or html-agility-pack should be able to do this but I'm not sure this is the best way?
What I wound up doing is iterating over all the elements and when adjacent span elements were detected I concatenated the text together. Here is some code if others run into this issue. Note code could use some cleanup.
static void CombineRedundantSpans(IElement parent)
{
if (parent != null)
{
if (parent.Children.Length > 1)
{
var children = parent.Children.ToArray();
var previousSibling = children[0];
for (int i = 1; i < children.Length; i++)
{
var current = children[i];
if (previousSibling is IHtmlSpanElement && current is IHtmlSpanElement)
{
if (IsSpanMatch((IHtmlSpanElement)previousSibling, (IHtmlSpanElement)current))
{
previousSibling.TextContent = previousSibling.TextContent + current.TextContent;
current.Remove();
}
else
previousSibling = current;
}
else
previousSibling = current;
}
}
foreach(var child in parent.Children)
{
CombineRedundantSpans(child);
}
}
}
static bool IsSpanMatch(IHtmlSpanElement first, IHtmlSpanElement second)
{
if (first.ChildElementCount < 2 && first.Attributes.Length == second.Attributes.Length)
{
foreach (var a in first.Attributes)
{
if (second.Attributes.Count(t => t.Equals(a)) == 0)
{
return false;
}
}
return true;
}
return false;
}

Implementing an AVL tree using a List

So I have implemented an Binary search tree using a parameterized List i.e.
List<Node> tree = new List<>();
The tree works fine. The node itself doesn't know anything about its parent or children. This is because I calculate the locations based on the index e.g.
If i is the index of some None N then:
N's left child is in tree[i*2]
N's right child is in tree[(i*2)+1]
This binary tree works fine. But now I want to put AVL tree features to it. Im am stuck at this point because I do not know how to make the rotations on a List. On rotation, how do i move the children of the new root? Fact is they have to shift indexes don't they? Also doing this on an List gives me the problem that displaying the tree will require looping through the List everytime i add a node. THis wont happen in O(logn) anymore destroying the whole point of an AVL tree.
I am doing this in C#. I just want to know how to make this AVL tree efficiently using a List or any array based data structure thats indexable and not a Linked list. This is important.Some code to illustrate would be greatly appreciated.
Representing a tree in an array/list the way you are doing is common for the heap data structure, but it does not work for virtually any other kind of tree. In particular, you cannot do this (efficiently) for AVL trees because each rotation would require too much copying.
I had a need for this for an embedded application where we did not have malloc available. Having not any done any kind of data structure algorithm implementation before I was trying if it could be done. While writing code I realized I would have to move a lot of things around. I searched for a remedy and got to this post.
Thanks to Chris's reply, I am not going to spend any more time on it. I will find some other way to implement what I need.
I believe I found an answer, the Trick is to move subtrees up and down the list so that you don't overwrite valid nodes while rotating.
void shiftUp(int indx, int towards) {
if (indx >= size || nodes[indx].key == NULL) {
return;
}
nodes[towards] = nodes[indx];
nodes[indx].key = NULL;
shiftUp(lChild(indx), lChild(towards));
shiftUp(rChild(indx), rChild(towards));
}
void shiftDown(int indx, int towards) {
if (indx >= size || nodes[indx].key == NULL) {
return;
}
// increase size so we can finish shifting down
while (towards >= size) { // while in the case we don't make it big enough
enlarge();
}
shiftDown(lChild(indx), lChild(towards));
shiftDown(rChild(indx), rChild(towards));
nodes[towards] = nodes[indx];
nodes[indx].key = NULL;
}
As you can see this is done by exploring recursively each subtree until the NULL (defined in this as -1) nodes then copying each element one by one up or down.
with this we can define the 4 types of rotations named according this Wikipedia Tree_Rebalancing.gif
void rotateRight(int rootIndx) {
int pivotIndx = lChild(rootIndx);
// shift the roots right subtree down to the right
shiftDown(rChild(rootIndx), rChild(rChild(rootIndx)));
nodes[rChild(rootIndx)] = nodes[rootIndx]; // move root too
// move the pivots right child to the roots right child's left child
shiftDown(rChild(pivotIndx), lChild(rChild(rootIndx)));
// move the pivot up to the root
shiftUp(pivotIndx, rootIndx);
// adjust balances of nodes in their new positions
nodes[rootIndx].balance--; // old pivot
nodes[rChild(rootIndx)].balance = (short)(-nodes[rootIndx].balance); // old root
}
void rotateLeft(int rootIndx) {
int pivotIndx = rChild(rootIndx);
// Shift the roots left subtree down to the left
shiftDown(lChild(rootIndx), lChild(lChild(rootIndx)));
nodes[lChild(rootIndx)] = nodes[rootIndx]; // move root too
// move the pivots left child to the roots left child's right child
shiftDown(lChild(pivotIndx), rChild(lChild(rootIndx)));
// move the pivot up to the root
shiftUp(pivotIndx, rootIndx);
// adjust balances of nodes in their new positions
nodes[rootIndx].balance++; // old pivot
nodes[lChild(rootIndx)].balance = (short)(-nodes[rootIndx].balance); // old root
}
// Where rootIndx is the highest point in the rotating tree
// not the root of the first Left rotation
void rotateLeftRight(int rootIndx) {
int newRootIndx = rChild(lChild(rootIndx));
// shift the root's right subtree down to the right
shiftDown(rChild(rootIndx), rChild(rChild(rootIndx)));
nodes[rChild(rootIndx)] = nodes[rootIndx];
// move the new roots right child to the roots right child's left child
shiftUp(rChild(newRootIndx), lChild(rChild(rootIndx)));
// move the new root node into the root node
nodes[rootIndx] = nodes[newRootIndx];
nodes[newRootIndx].key = NULL;
// shift up to where the new root was, it's left child
shiftUp(lChild(newRootIndx), newRootIndx);
// adjust balances of nodes in their new positions
if (nodes[rootIndx].balance == -1) { // new root
nodes[rChild(rootIndx)].balance = 0; // old root
nodes[lChild(rootIndx)].balance = 1; // left from old root
} else if (nodes[rootIndx].balance == 0) {
nodes[rChild(rootIndx)].balance = 0;
nodes[lChild(rootIndx)].balance = 0;
} else {
nodes[rChild(rootIndx)].balance = -1;
nodes[lChild(rootIndx)].balance = 0;
}
nodes[rootIndx].balance = 0;
}
// Where rootIndx is the highest point in the rotating tree
// not the root of the first Left rotation
void rotateRightLeft(int rootIndx) {
int newRootIndx = lChild(rChild(rootIndx));
// shift the root's left subtree down to the left
shiftDown(lChild(rootIndx), lChild(lChild(rootIndx)));
nodes[lChild(rootIndx)] = nodes[rootIndx];
// move the new roots left child to the roots left child's right child
shiftUp(lChild(newRootIndx), rChild(lChild(rootIndx)));
// move the new root node into the root node
nodes[rootIndx] = nodes[newRootIndx];
nodes[newRootIndx].key = NULL;
// shift up to where the new root was it's right child
shiftUp(rChild(newRootIndx), newRootIndx);
// adjust balances of nodes in their new positions
if (nodes[rootIndx].balance == 1) { // new root
nodes[lChild(rootIndx)].balance = 0; // old root
nodes[rChild(rootIndx)].balance = -1; // right from old root
} else if (nodes[rootIndx].balance == 0) {
nodes[lChild(rootIndx)].balance = 0;
nodes[rChild(rootIndx)].balance = 0;
} else {
nodes[lChild(rootIndx)].balance = 1;
nodes[rChild(rootIndx)].balance = 0;
}
nodes[rootIndx].balance = 0;
}
Note that in cases where shifting would overwrite nodes we just copy the single node
As for efficiency storing the balance in each node would be a must as getting the differences of heights at each node would be quite costly
int getHeight(int indx) {
if (indx >= size || nodes[indx].key == NULL) {
return 0;
} else {
return max(getHeight(lChild(indx)) + 1, getHeight(rChild(indx)) + 1);
}
}
Though doing this requires us to update the balance at affected nodes when modifying the list, though this can be somewhat efficiently by only updating strictly necessary cases.
for deletion this adjustment is
// requires non null node index and a balance factor baised off whitch child of it's parent it is or 0
private void deleteNode(int i, short balance) {
int lChildIndx = lChild(i);
int rChildIndx = rChild(i);
count--;
if (nodes[lChildIndx].key == NULL) {
if (nodes[rChildIndx].key == NULL) {
// root or leaf
nodes[i].key = NULL;
if (i != 0) {
deleteBalance(parent(i), balance);
}
} else {
shiftUp(rChildIndx, i);
deleteBalance(i, 0);
}
} else if (nodes[rChildIndx].key == NULL) {
shiftUp(lChildIndx, i);
deleteBalance(i, 0);
} else {
int successorIndx = rChildIndx;
// replace node with smallest child in the right subtree
if (nodes[lChild(successorIndx)].key == NULL) {
nodes[successorIndx].balance = nodes[i].balance;
shiftUp(successorIndx, i);
deleteBalance(successorIndx, 1);
} else {
int tempLeft;
while ((tempLeft = lChild(successorIndx)) != NULL) {
successorIndx = tempLeft;
}
nodes[successorIndx].balance = nodes[i].balance;
nodes[i] = nodes[successorIndx];
shiftUp(rChild(successorIndx), successorIndx);
deleteBalance(parent(successorIndx), -1);
}
}
}
similarly for insertion this is
void insertBalance(int pviotIndx, short balance) {
while (pviotIndx != NULL) {
balance = (nodes[pviotIndx].balance += balance);
if (balance == 0) {
return;
} else if (balance == 2) {
if (nodes[lChild(pviotIndx)].balance == 1) {
rotateRight(pviotIndx);
} else {
rotateLeftRight(pviotIndx);
}
return;
} else if (balance == -2) {
if (nodes[rChild(pviotIndx)].balance == -1) {
rotateLeft(pviotIndx);
} else {
rotateRightLeft(pviotIndx);
}
return;
}
int p = parent(pviotIndx);
if (p != NULL) {
balance = lChild(p) == pviotIndx ? (short)1 : (short)-1;
}
pviotIndx = p;
}
}
As you can see this just uses plain arrays of "node"s as i translated it from c code given gitHub array-avl-tree and optimizations and balancing from (a link i'll post in a comment) but would work quite similar in a List
Finally I have minimal knowledge of AVL trees, or optimal implementations so i don't claim that this is bug free or the most efficient but have passed my preliminary tests at least for my purposes

WPF: How do I loop through the all controls in a window?

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.

Categories

Resources