Linq has been shipped with VS2008/.Net 3.5 and is not really new. If more and more people are getting used to its syntax, it’s sometimes hard to imagine all the scenarii where Linq to object can replace the classical way we program. During some Silverlight coding, I changed my code to make it run in a better Linq spirit and I wanted to share with you this little sample.
Silverlight (and WPF) maintains a tree of visuals. A useful class named VisualTreeHelper provides a very simple api to navigate this tree. One very common scenario is to look for the first visual parent of a T type for a given control. For instance, your control is in a template and you want to retrieve the templated control which you know is a ListBoxItem.
The VisualTreeHelper.GetParent() method (see prototype below) simply gives the graphical parent of any visual (as DependencyObject).
public static class VisualTreeHelper { // // Summary: // Returns an object's parent object in the visual tree. // // Parameters: // reference: // The object to get the parent object for. // // Returns: // The parent object of the reference object in the visual tree. // // Exceptions: // System.InvalidOperationException: // reference is null, or is not a valid System.Windows.UIElement. public static DependencyObject GetParent(DependencyObject reference); ... }
Let’s try to implement this simple scenario.
public static class MyDependencyObjectExtensions { public static T GetVisualParent<T>(this DependencyObject source) where T : class { do { source = VisualTreeHelper.GetParent(source); } while (!(source is T) && (source != null)); return source as T; } }
var lbi = button1.GetVisualParent<ListBoxItem>();
In a first step we will just try to retrieve the list of parents :
public static class MyDependencyObjectExtensions { public static IEnumerable<DependencyObject> GetVisualParents(this DependencyObject source) { while (true) { source = VisualTreeHelper.GetParent(source); if (source != null) yield return source; else yield break; } } }
public static class MyDependencyObjectExtensions { public static IEnumerable<DependencyObject> GetVisualParents(this DependencyObject source) { while (true) { source = VisualTreeHelper.GetParent(source); if (source != null) yield return source; else yield break; } } public static T GetVisualParent<T>(this DependencyObject source) where T : class { return source.GetVisualParent<T>(0); } public static T GetVisualParent<T>(this DependencyObject source, int level) where T : class { return source.GetVisualParents().OfType<T>().Skip(level).FirstOrDefault(); } }
Last effort, let’s try to imagine a generic way to create enumerables from functional parts.
In our case we have :
public static class EnumerableHelper { public static IEnumerable<T> Create<T>(T startElement, Func<T, T> nextElementProvider, Predicate<T> exitCondition) { while (true) { startElement = nextElementProvider(startElement); if (!exitCondition(startElement)) yield return startElement; else yield break; } } public static IEnumerable<T> Create<T>(T startElement, Func<T, T> nextElementProvider) { return Create(startElement, nextElementProvider, e => e == null); } }
var parents = EnumerableHelper.Create( button1 as DependencyObject, d => VisualTreeHelper.GetParent(d)); var lbi = parents.OfType<ListBoxItem>().FirstOrDefault();