Does anybody have a good way of finding ALL the controls within an object that is of the same type? Here's my scenario, I have a tab control and within each tab control exists a user control (ALL of which match the same base type e.g. MyBaseClassControl). I want to be able to find that user control WITHOUT having to use the control.FindName("controlName") method, rather I would like to get a handle on the control by type (e.g. base class). The VisualTreeHelper class seems to do nothing for me as it only returns native Silverlight objects.
Given this:
public static IEnumerable<DependencyObject> AllChildren(this DependencyObject root)
{
var children = root.DirectChildren().ToList();
return children.Union(children.SelectMany(o => o.AllChildren()));
}
public static IEnumerable<DependencyObject> DirectChildren(this DependencyObject parent)
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
yield return System.Windows.Media.VisualTreeHelper.GetChild(parent, i);
}
You could do this:
myObj.AllChildren().OfType<MyBaseClassControl>();
Related
I have the following code:
ControlTemplate ct = (ControlTemplate)XamlReader.Load(validXmlString);
Now I need to obtain the control that this template created, in my case, a Button. I have searched far and wide and can't find a simple explanation for how this is done.
Please note that for some unexplained reason, Microsoft provided a FindControl() method for ControlTemplate in WPF, but not in Silverlight. I've read that this can be done with the VisualTreeHelper, but I have yet to see an explanation for how.
Below you will find an example that loops through the Visual Tree recursively and finds all buttons adding them to a collection. You can check the name of the button etc.. and do what you need to do. I just used a collection as an example, as I found a quick sample on it.
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
List<UIElement> buttons = new List<UIElement>();
GetChildren(this, typeof(Button), ref buttons);
}
private void GetChildren(UIElement parent, Type targetType, ref List<UIElement> children)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (child.GetType() == targetType)
{
//DO something with the button in the example added to a collection. You can also verify the name and perform the action you wish.
children.Add(child);
}
GetChildren(child, targetType, ref children);
}
}
}
Hope this helps
I'm working on making an app come back nicely from being tombstoned. The app contains large listboxes, so I'd ideally like to scroll back to wherever the user was while they were scrolling around those listboxes.
It's easy to jump back to a particular SelectedItem - unfortunately for me, my app never needs the user to actually select an item, they're just scrolling through them. What I really want is some sort of MyListbox.ScrollPositionY but it doesn't seem to exist.
Any ideas?
Chris
You need to get hold of the ScrollViewer that is used by the ListBox internally so you can grab the value of the VerticalOffset property and subsequently call the SetVerticalOffset method.
This requires that you reach down from the ListBox through the Visual tree that makes up its internals.
I use this handy extension class which you should add to your project (I've gotta put this up on a blog because I keep repeating it):-
public static class VisualTreeEnumeration
{
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root, int depth)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
if (depth > 0)
{
foreach (var descendent in Descendents(child, --depth))
yield return descendent;
}
}
}
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
{
return Descendents(root, Int32.MaxValue);
}
public static IEnumerable<DependencyObject> Ancestors(this DependencyObject root)
{
DependencyObject current = VisualTreeHelper.GetParent(root);
while (current != null)
{
yield return current;
current = VisualTreeHelper.GetParent(current);
}
}
}
With this available the ListBox (and all other UIElements for that matter) gets a couple of new extension methods Descendents and Ancestors. We can combine those with Linq to search for stuff. In this case you could use:-
ScrollViewer sv = SomeListBox.Descendents().OfType<ScrollViewer>().FirstOrDefault();
I'm making a Silverlight behavior to enable dragging an element by a contained "drag handle" element (rather than the whole element being draggable). Think of it like a window title bar.
In the OnAttached method I am calling: AssociatedObject.FindName(DragHandle)
but this is returning null.
I then tried handling the AssociatedObject's Loaded event and running my code there, but I still get a null returned.
Am I misunderstanding what FindName is able to do? The AssociatedObject is in an ItemsControl (I want a collection of draggable elements). So is there some kind of namescope problem?
Yes, it sounds like a namescope problem. The MSDN documentation on XAML namescopes goes over how namesopes are defined for templates and item controls. Are you using a template for the items in your ItemsControl?
You may just have to walk the visual tree recursively with something like this to find the correct element by name:
private static FrameworkElement FindChildByName(FrameworkElement parent, string name)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
FrameworkElement child = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (child != null && child.Name == name)
{
return child;
}
else
{
FrameworkElement grandChild = FindChildByName(child, name);
if (grandChild != null)
{
return grandChild;
}
}
}
return null;
}
I have a reusable usercontrol that uses a few commands and corresponding keyboard gestures,
(specifically Escape and Ctrl+1...Ctrl+9)
Now as I use this usercontrol in multiple locations I'd like to define the input gestures in the usercontrol, which works fine as long as the focus is within the usercontrol. However, I'd need it to work as long as focus is within the current page/window.
How can I do it, or do I really have to do command/input bindings on every page?
You could handle the Loaded event of the UserControl and walk up the logical tree to find the owning page/window, then you can add the bindings there.
e.g.
public partial class Bogus : UserControl
{
public Bogus()
{
Loaded += (s, e) => { HookIntoWindow(); };
InitializeComponent();
}
private void HookIntoWindow()
{
var current = this.Parent;
while (!(current is Window) && current is FrameworkElement)
{
current = ((FrameworkElement)current).Parent;
}
if (current != null)
{
var window = current as Window;
// Add input bindings
var command = new AlertCommand();
window.InputBindings.Add(new InputBinding(command, new KeyGesture(Key.D1, ModifierKeys.Control)));
}
}
}
I have a text box with verticalscrollbarvisibility set to auto. I would like to do a test to find out if the scrollbar is actually visible during runtime. I have tried the statement:
if (textbox1.VerticalScrollBarVisibility == ScrollBarVisibility.Visible)
but it does not work. Any ideas?
First place the following extension method in static class (either place the class in the same namespace as the rest of your code or in namespace included with a using statement in your code file):-
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
foreach (var descendent in Descendents(child))
yield return descendent;
}
}
With this extension method available you can dig out the ScrollViewer inside the text box which is responsible for the scroll bar and test its ComputedVerticalScrollBarVisibility.
if (textbox1.Descendents().OfType<ScrollViewer>()
.FirstOfDefault().ComputedVerticalScrollBarVisibility == Visibility.Visible)