I would like to find all of the controls within a WPF control specially in Datagrid, DataGridTemplateColumn. I have had a look at a lot of samples and it seems that they all either require a Name to be passed as parameter or simply do not work.
What have you tried that "require a name to be passed or simply don't work" and what's wrong with the former?
private void FindAllChildren()
{
var depObj = dataGrid;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child is DataGridTemplateColumn)
{
// do a thing
}
}
}
Adapted from here: https://stackoverflow.com/a/978352/1189566
Related
I need to find all TextBox(es) that are on a UWP Page but having no luck. I thought it would be a simple foreach on Page.Controls but this does not exist.
Using DEBUG I am able to see, for example, a Grid. But I have to first cast the Page.Content to Grid before I can see the Children collection. I do not want to do this as it may not be a Grid at the root of the page.
Thank you in advance.
UPDATE: This is not the same as 'Find all controls in WPF Window by type'. That is WPF. This is UWP. They are different.
You're almost there! Cast the Page.Content to UIElementCollection, that way you can get the Children collection and be generic.
You'll have to make your method recurse and look either for Content property if element is a UIElement or Children if element is UIElementCollection.
Here's an example:
void FindTextBoxex(object uiElement, IList<TextBox> foundOnes)
{
if (uiElement is TextBox)
{
foundOnes.Add((TextBox)uiElement);
}
else if (uiElement is Panel)
{
var uiElementAsCollection = (Panel)uiElement;
foreach (var element in uiElementAsCollection.Children)
{
FindTextBoxex(element, foundOnes);
}
}
else if (uiElement is UserControl)
{
var uiElementAsUserControl = (UserControl)uiElement;
FindTextBoxex(uiElementAsUserControl.Content, foundOnes);
}
else if (uiElement is ContentControl)
{
var uiElementAsContentControl = (ContentControl)uiElement;
FindTextBoxex(uiElementAsContentControl.Content, foundOnes);
}
else if (uiElement is Decorator)
{
var uiElementAsBorder = (Decorator)uiElement;
FindTextBoxex(uiElementAsBorder.Child, foundOnes);
}
}
Then you call that method with:
var tb = new List<TextBox>();
FindTextBoxex(this, tb);
// now you got your textboxes in tb!
You can also use the following generic method from the VisualTreeHelper documentation to get all your child controls of a given type:
internal static void FindChildren<T>(List<T> results, DependencyObject startNode)
where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(startNode);
for (int i = 0; i < count; i++)
{
DependencyObject current = VisualTreeHelper.GetChild(startNode, i);
if ((current.GetType()).Equals(typeof(T)) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
{
T asType = (T)current;
results.Add(asType);
}
FindChildren<T>(results, current);
}
}
It basically recursively get the children for the current item and add any item matching the requested type to the provided list.
Then, you just have to do the following somewhere to get your elements:
var allTextBoxes = new List<TextBox>();
FindChildren(allTextBoxes, this);
To my mind, you could do it in the same way as in WPF. Because UWP uses mostly the same XAML that WPF.
So, please check out answer for the same question about WPF
I have a WrapPanel which contains some Images (thumbnails).
When the user press Left or Right arrow key, I want to show the next/previous image.
private void frmMain_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Right)
{
int j = 0;
foreach (Image child in WrapPanelPictures.Children)
{
if (child.Source == MainPic.Source)
{
MainPic.Source = WrapPanelPictures.Children[j + 1].Source;
}
j++;
}
}
}
Also I tried a LINQ approach, but I'm a beginner with LINQ.
var imgfound = from r in WrapPanelPictures.Children.OfType<Image>()
where r.Source == MainPic.Source
select r;
MessageBox.Show(imgfound.Source.ToString());
imgfound is supposed to be a list, right? maybe that's why I can't access Source property. Anyway this return the current Image shown. I want the sibling.
UPDATE:
Well I made a workaround as for now. But still waiting for a proper solution.
I created a ListBox and added all WrapPanel Childrens to it. Then used the SelectedItem and SelectedIndex properties to select the previous and next items.
The WrapPanelPictures.Children[j + 1].Source cannot work since you are trying to access the Source property of UIElement which does not exist. You should cast the UIElement to Image before accessing the Source:
MainPic.Source = (WrapPanelPictures.Children[j + 1] as Image).Source;
I am sure there are more elegant solutions to obtain the same results.
You got your answer why you can't access the Source property by Novitchi. I would nevertheless like to suggest that you rethink your code.
The way I see it, you are in control of what your wrap panel displays. That means you should be able to store things like "all the images" and also the selected index in a field or property. Instead of parsing your wrap panel's children for images every time around and comparing image sources, you should know at any given time what your selected image or index is.
The code might then roughly look something like this:
private List<BitmapImage> _images;
private int _selectedIndex;
private void frmMain_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Right)
{
_selectedIndex = (_selectedIndex + 1) % _images.Count;
MainPic.Source = _images[_selectedIndex];
}
}
If your UI is highly dynamic (drag/dropping images into the wrap panel or something like that), using Bindings to link your data with the UI is the way to go. In any case, I also strongly recommend considering a ViewModel pattern like MVVM.
use method FindVisualChildren. It traverses through Visual Tree and find your desired control.
This should do the trick
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
then you enumerate over the controls like so
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
Do not declare j inside the foreach loop. Otherwise this will always show the image for j=0 which is WrapPanelPictures.Children[1]
private void frmMain_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Right)
{
int j = 0;
foreach (Image child in WrapPanelPictures.Children)
{
// int j = 0;
if (child.Source == MainPic.Source)
{
MainPic.Source = WrapPanelPictures.Children[j + 1].Source;
break;
}
j++;
}
}
}
I'm trying to select all items in ListBox and made this extension method for this purpose:
public static void SetSelectedAllItems(this ListBox ctl)
{
for (int i = 0; i < ctl.Items.Count; i++)
{
ctl.SetSelected(i, true);
}
}
Problem is that if I have lots of items in the ListBox, it takes a long time to accomplish this task and I can watch how the ListBox is automatically scrolling down and selecting items.
Is there a way to temporary pause the update of a control, so that the task would finish faster? I tried using:
ctl.SuspendLayout();
for (int i = 0; i < ctl.Items.Count; i++)
...
ctl.ResumeLayout();
But that doesn't seem to do anything.
Call the BeginUpdate and EndUpdate methods to prevent the drawing/rendering of the control while properties on that control are being set.
Here is the revised code:
public static void SetSelectedAllItems(this ListBox ctl)
{
ctl.BeginUpdate();
for (int i = 0; i < ctl.Items.Count; i++)
{
ctl.SetSelected(i, true);
}
ctl.EndUpdate();
}
You said that you've tried calling SuspendLayout and ResumeLayout, but that only affects the control's layout events. This pair of methods is used when you want to change a control's position relative to other controls, like when you set the Size, Location, Anchor, or Dock properties.
I am trying to get the text value of a "cell" inside of a GridView that is set as the view of a ListView. I do not want to get the SelectedItem of the ListView as that just returns my entire View Model (but not which property the cell refers to).
I am able to get the text value by responding to direct mouse events (up down or whatever) and if the value is a textblock, obviously I can use the text. This works great and as of right now this is my only solution, although its currently limited.
I would like to take it a step further and be able to click anywhere with in the cell area, navigate around to find the appropriate textblock and then use that value. I have tried a half million ways to do this but what seems logical doesn't seem to quite work out like it should.
Setup:
I have a dynamic GridView that creates its own columns and bindings based on data models that I pass to it. I am using a programmatic cell template (shown below) to have individual control over the cells, particularly so I can add a "border" to it making it actually separate out each cell. I have named the objects so I can access them easier when I'm navigating around the VisualTree.
Here is the Template Code. (Note that the content presenter originally was a textblock itself, but this was changed for later flexibility)
private DataTemplate GetCellTemplate(string bindingName)
{
StringBuilder builder = new StringBuilder();
builder.Append("<DataTemplate ");
builder.Append("xmlns='http://schemas.microsoft.com/winfx/");
builder.Append("2006/xaml/presentation' ");
builder.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
builder.Append("xmlns:local = 'clr-namespace:XXXXXXXX");
builder.Append(";assembly=XXXXXXXXX'>");
builder.Append("<Border Name=\"border\" BorderThickness=\"1,0,0,0\" BorderBrush=\"Gray\" Margin=\"-6,-3,-6,-3\">");
builder.Append("<Grid Margin=\"6,3,6,3\">");
builder.Append("<ContentPresenter Name=\"content\" HorizontalAlignment=\"Stretch\" Content=\"{Binding ");
builder.Append(string.Format("{0}", bindingName));
builder.Append("}\"/>");
builder.Append("</Grid>");
builder.Append("</Border>");
builder.Append("</DataTemplate>");
DataTemplate cellTemplate= (DataTemplate)XamlReader.Parse(builder.ToString());
return cellTemplate;
}
What I have Tried:
The logical approach for me was to react to a Mouse event. From the object that had the mouse event I would do either
A. Look at its children to find a textblock, or
B. Get its parent then look for child with a textblock.
My assumption is that if I click in white space I'm clicking in a container that has my textblock. So far the two things that come up are a Border and a Rectangle (if I don't click the text itself). A. Returns absolutely nothing except for the recangle and the border. When I do B i can find textblocks but they are every single text block in the entire row.
So what I try to do from that is get all textblocks, then go backwards till I find which one has a IsMouseOver property as true. It turns out none of these objects EVER have a IsMouseOver except the content presenter for the entire row. So this seems to indicate to me is that the whitespace in the cells does not actually contain the textblock.
What I find is that when I click on the Border and start looking at children, I eventually get to a container that has a rectangle (the rectangle I click) and a grid row view presenter. The presenter shows all of the objects inside the row (hence why i would get all textblocks when i do this recursive scan).
Here is some of the code used to do this to get an idea of what i'm doing. I have written about 10 different versions of this same recursive code generally attempting to find who has the Mouse over it and is related to a textbox.
private void OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
{
object original = e.OriginalSource;
if (original is TextBlock)
{
this.valueTextBlock.Text = ((TextBlock)original).Text;
}
else if (original is FrameworkElement)
{
var result = GetAllNestedChildren<Border>(VisualTreeHelper.GetParent((DependencyObject)original)).Where(x => x.Name == "border").Where(x => HasAChildWithMouse(x)).ToList();
}
else
{
this.valueTextBlock.Text = string.Empty;
}
}
private bool HasAChildWithMouse(UIElement element)
{
if (element.IsMouseOver || element.IsMouseDirectlyOver)
return true;
var childCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(element, i);
if (child is UIElement)
if (HasAChildWithMouse((UIElement)child))
return true;
}
return false;
}
private IEnumerable<T> GetAllNestedChildren<T>(DependencyObject obj) where T : UIElement
{
if (obj is T)
yield return obj as T;
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
foreach (var nested in GetAllNestedChildren<T>(child))
yield return nested;
}
}
private T GetObjectByTypeParentHasMouse<T>(DependencyObject obj) where T : UIElement
{
if (obj is T)
{
if ((VisualTreeHelper.GetParent(obj) as UIElement).IsMouseOver )
{
return obj as T;
}
}
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
var correctType = GetObjectByTypeParentHasMouse<T>(child);
if (correctType != null)
return correctType;
}
return null;
}
private T GetContainedType<T>(DependencyObject obj, bool checkForMouseOver) where T : UIElement
{
if (obj is T && ((T)obj).IsMouseOver)
return obj as T;
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
var correctType = GetContainedType<T>(child, checkForMouseOver);
if (correctType != null)
return correctType;
}
return null;
}
The other approach I took was to start with the TextBlock itself, find its containing parent and find out how i can navigate to the answer. I find the templateparent is the ContentPresenter (named ="content") I find the grid, and then the border. The parent of the border is a content presenter whos content is the data view model for the entire row. The parent of this contentpresenter is the grid column's presenter. This is the same one that i was navigating up to in the other one.
It would appear that the first approach objects while are contain the cell do not actually contain the textblock or the entire cell templated items. It would appear to me there is no way to go from the Border or Rectangle that is clicked, back to the actual text field.
"Long story short" is there ANY way to make this connection?
(Btw I am not willing to give up this ListView/GridView because its payoffs far outweigh this negative and I'd gladly give up on this idea to keep the rest).
I think you sjould be able to either
1) Add some kind of (toggle)button to the root of your data template, and either bind to Command and handle it on your viewmodel or bind to IsChecked/IsPressed and handle changes via data triggers or w/e on the view side.
2) Add EventTrigger to your datatemplate at some point, and handle PreviewNouseUp/Down events there via simple animations.
I have a List<T> with some UserControls.
On the main window there is a Grid and some of the UserControls will be added to Grid.Children.
Now I would like to be able to remove specific UserControls from this Grid e.g. I would like to do something like this
layoutRoot.Children.Remove(controlList[1]);
Is this possible?
I only know FindName() and FindResource() but all the UserControls don't have names so that I can't use these methods :(
Thanks in advance!
just an idea to get you started, if you know the type of your user control, you can use methods like this:
static T FindVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
var visual = (Visual)VisualTreeHelper.GetChild(parent, i);
child = visual as T;
if (child == null)
child = FindVisualChild<T>(visual);
if (child != null)
break;
}
return child;
}