Visual Tree - Find a label (anywhere on the window) where content equals - c#

I have many labels as children of many different stack panels which are all children of a list box, and I need to reference one of these labels were the Content.toString() == "criteria". In other words, traversing the visual tree in WPF would be a ball ache because there are many parent/child methods to run. Is there a way of finding one of these labels on my window without it having a name and assuming I don't know how far 'down' it is in the tree? Maybe there's an item collection of everything in a window (without heirarchy) that I can run some LINQ against??
If you're wondering why I don't have a name for the labels - it's because they are generated by a data template.
Thanks a lot,
Dan

Seems like what you're looking for: Find DataTemplate-Generated Elements

I made a slight change to the code that #anatoliiG linked in order to return all the child controls of the specified type (instead of the first one):
private IEnumerable<childItem> FindVisualChildren<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
yield return (childItem)child;
foreach (var childOfChild in FindVisualChildren<childItem>(child))
yield return childOfChild;
}
}
With this function you could do something like this:
var criteriaLabels =
from cl in FindVisualChildren<Label>(myListBox)
where cl.Content.ToString() == "criteria"
select cl;
foreach (var criteriaLabel in criteriaLabels)
{
// do stuff...
}

i think this code might be usefull for you:
foreach (Control control in this.Controls)
{
if (control.GetType() == typeof(Label))
if (control.Text == "yourText")
{
// do your stuff
}
}
i used This question as my base

I don't know whether this will help or not:
If you are looking for a specific label in each stack panel in the listBox, then you could just look for that specific label with its specific name and compare the content.

Related

How to remove last children from stack panel in WPF?

I am adding children to my stackpanel dynamically. What I need is, I want to remove the last children for certain scenario. Is there any option to get last children?
Here is my code:
var row = new somecontrol();
stackpanel.Children.Add(row);
Is there any possible way to remove children.lastOrDefault()?
stackpanel.Children.Last();
Any help would be appreciated. Thanks in advance.
How about:
if(stackpanel.Children.Count != 0)
stackpanel.Children.RemoveAt(stackpanel.Children.Count - 1);
...or if you want to use Linq, just use the OfType<> ExtensionMethod. Then you can do whatever with Linq you wish, like LastOrDefault:
var child = stackpanel.Children.OfType<UIElement>().LastOrDefault();
if(child != null)
stackpanel.Children.Remove(child);
But, the first is probably fastest.
Or you can make your own Extension method, if you want:
class PanelExtensions
{
public static void RemoveLast(this Panel panel)
{
if(panel.Children.Count != 0)
panel.Children.RemoveAt(panel.Children.Count - 1);
}
}
Use like this
stackpanel.Children.RemoveLast();
But Like Xeun mentions an MVVM solution with Bindings would be preferable.

Adding a child node in the middle of a list of other children

This question is a follow up to this question. Basically what I want to do is add nodes to my treeView in numerical order. Right now I am working with a list of child nodes. Each child has a DisplayName of a numerical value. In my program I have a loop that checks if the node being added has a DisplayName that is less than the DisplayName of the current child in the loop. If the new child's DisplayName is less than the child being checked I would like to add the new node before the already existing one. I am having trouble figuring out a method to do this.
Here is my code:
var node = Data.GetAllChildren(x => x.Children).Distinct().ToList().First(x => x.identify == 'B');
//Get # of children -- if children exist
if (node.Children.Count() > 0)
{
newChildTitle = int.Parse(nodeName.Value); //node to add
for (int i = 0; i < node.Children.Count(); i++)
{
currentChildTitle = int.Parse(node.Children.ElementAt(i).DisplayName.Value); //current node value
if (newChildTitle == currentChildTitle) //Check if location already exists
{
MessageBox.Show("Sorry, that location name already exists under this category.", "Duplicate Location Name", MessageBoxButton.OK, MessageBoxImage.Warning);
break;
}
else if (newChildTitle < currentChildTitle) //If new location value is less, add before current location in Tree
{
//CODE WOULD GO HERE**
break;
}
}
}
else //if no children exist, just add
node.Children.Add(CreateBlockingLocation(model, blockTreeCollection, nodeName));
XAML of the TreeView (the TreeView is bound to an ObservableCollection):
<!-- Tree view items & Functions -->
<TreeView Name="Tree_One" IsEnabled="{Binding DataContext.DataTreeEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" ItemsSource="{Binding DataTree.Data}" />
Data and Properties of the ObservableCollection<HierarchicalVM> that the TreeView is bound to:
private HierarchicalVM _parent;
private ObservableCollection<HierarchicalVM> _children;
public HierarchicalVM()
{
Commands = new ObservableCollection<Command>();
}
//Relationship Properties
public HierarchicalVM Parent
{
get { return _parent; }
set
{
_parent = value;
NotifyPropertyChange(() => Parent);
}
}
public ObservableCollection<HierarchicalVM> Children
{
get { return _children ?? (_children = new ObservableCollection<HierarchicalVM>()); }
set { _children = value; }
}
Current Solution attempt:
//Gets parent item (Data is the complete Tree)
var node = Data.GetAllChildren(x => x.Children).Distinct().ToList().First(x => x.identify == 'B');
if (!node.Children.Any(n => n.numValue == newNumValue))
{
//Error is on this line: **Operator '.' cannot be applied to operand of type 'void'
node.Children = node.Children.Add(newNode).Orderby(n => n.numValue);
}
Ok, assuming you already have a reference to the Parent object (not the DataTreeItem UI Element), there are two ways I can think of to accomplish this. This is off the top of my head with a little bit of checking against MSDN, so I apologize for any bugs.
Parent parent = //however you are getting the parent
if (!parent.Children.Any(n => n.DisplayName == newNode.DisplayName))
{
parent.Children.Add(newNode)
parent.Children = new ObservableCollection<ViewModel>(parent.Children.OrderBy(n => n.DisplayName));
//OR,
parent.Children.InsertItem(parent.Children.IndexOf(parent.Children.OrderBy(n => n.DisplayName).Last(n => n.DisplayName < newNode.DisplayName)), newNode);
}
The first method just adds the new item and resorts the list, which gets you the same result as an in-place insert. The second actually does a in-place insert, but is more complicated because you have to find the index to insert at.
If the list is already sorted you could remove the OrderBy in the second method. This also assumes that DisplayName is an int and a property of each child node (which from the code you posted it isn't, but it should be).
Clearly I don't understand your architecture, so I will explain how I think it should be, and then perhaps you can discover where your code is broken.
We have a object type called Parent, which contains a collection called children, like so:
class Parent<T>
{
ObservableCollection<T> Children;
}
Now we have an collection of Parent called Data in the view model that the TreeView is bound to. Somehow, we are getting a single instance of this Parent class (which definitely shouldn't be void, the fact that your "node.Children" object is should be a HUGE red flag).
If you agree with what I said above, the insert code I posted should work.
Please let me know if I can clarify or add to this answer, or help in any other way.

How to get all Windows Form Elements in a list

I have a windows form application.
Windows form consist of several elements. I can reach each of them by their name.
(ex: textbox1.Text)
Can I get all of form element in a collection?
You can use the method below to traverse a tree and get all of the child controls (at all depths) below any control.
public static IEnumerable<Control> GetAllControls(Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
You can then pass in the form as the root to get all of the controls in that form in a single sequence. Use ToList if you want them all in a List instead.
If you want to filter out only the controls of a particular type, use OfType:
var textboxes = GetAllControls(someForm).OfTYpe<Textbox>();
Use this.Controls (or more formally: Control.Controls)
To get all TextBox controls, use this:
this.Controls.OfType<Control>().Where(x => x is TextBox);
This will only get top level items. If you need to go deeper (cue inception), you need to do some recursion.
Try this.Controls in the code behind file and should give you a list of all the controls.

Excluding the GroupBoxes that are inside another GroupBox

Let's say I have 7 group boxes but some of them also have group box inside them and some do not.
now if I want to iterate through those 7 group boxes and apply something to them, is there a way that I can exclude those Child group boxes from this loop?
though i question the choice of implementation (can you use polymorphism instead? what exactly are you trying to do?), there is a Parent property, e.g.
void soSomething(Control ctrl)
{
if (ctrl is GroupBox && (ctrl.Parent is null || !(ctrl.Parent is GroupBox)))
{
//do something here
}
foreach(Control child in ctrl.Controls)
{
doSomething(child);
}
}
Mark them with the tag property, or something.

Find all ASP.NET controls matching a predicate?

I need to enumerate over a collection of controls - regardless of their nesting level - that match a given predicate.
Originally the problem occurred, when I needed to set all textbox's in a grids row to ReadOnly, if a column in that row indicated that the record should not be editable.
Later I realized, that I already solved a problem in the past very much like this one, only with a different criteria (find a single control recursively by its ID).
After trying a few alternatives I came up with a general solution that works. But since I will use this method very often, I wanted to gather possible improvements.
This method will return all child controls matching a predicate:
public static IEnumerable<T> FindChildControls<T>(this Control parentControl,
Predicate<Control> predicate) where T : Control
{
foreach (Control item in parentControl.Controls) {
if (predicate(item))
yield return (T)item;
foreach (T child in item.FindChildControls<T>(predicate)) {
yield return child;
}
}
}
Using this method I can do the following:
var allTxt = Page.FindChildControls<TextBox>(c => c is TextBox);
var submit = Page.FindChildControls<Button>(c => c.ID == "btnSubmit").First();
You can use a queue to get rid of recursion if you want.
public static IEnumerable<T> FindChildControls<T>(Control parentControl,
Predicate<Control> predicate) where T : Control
{
Queue<Control> q = new Queue<Control>();
foreach (Control item in parentControl.Controls)
{
q.Enqueue(item);
}
while (q.Count > 0)
{
Control item = q.Dequeue();
if (predicate(item))
yield return (T)item;
foreach (Control child in item.Controls)
{
q.Enqueue(child);
}
}
}

Categories