I seem to have gotten away from posting any coding things on this blog. At first I thought its because I really haven’t done anything exciting; most of the work has been “figure out customer’s need, create something that solves that need”. Then as I dug deeper, I rediscovered some fun things that I had created along the way:
The Problem
There’s a screen with a list of grids (this is a stock trading app) – when tabbing out of one grid, it needed to get to the next grid. I also needed to jump left and right from master to detail easily. (Sorry about all the blurring, but those were real numbers):
The screen, in and of itself, was a solution to “how to balance a portfolio to get it back in line with sector totals easily”. The user could enter a proposed change in a holding, see how the math worked, and then click save positions to generate all the trade(s) necessary to make it happen.
Solution
I needed to write code that would take the grid that was currently focused, figure out what the next grid was, and focus there. when solved, it looks like this:
Oy! What is this? It’s a helper function that I wrote. Basically, Starting with a known visual element, I look for “stuff that I’m interested in” (if the lambda returns true, then I’m interested in it):
This cuts down the (very) complicated visual control tree (see: http://www.codeproject.com/Articles/21495/Understanding-the-Visual-Tree-and-Logical-Tree-in) down into something much more manageable (this is the output of the Dump() function defined below with some additional comments thrown in)
Here’s the source that does it:
public static VisualTreeInterestingNode GetInterestingNodes(this FrameworkElement topDependencyObject, Func<FrameworkElement, bool> isInteresting) { var timer = new Stopwatch(); timer.Start(); var topInterestingNode = new VisualTreeInterestingNode() { FrameworkElement = topDependencyObject, Parent = null }; var nodesToVisit = new LinkedList<Tuple<DependencyObject, VisualTreeInterestingNode>>(); nodesToVisit.AddLast(new Tuple<DependencyObject, VisualTreeInterestingNode>(topDependencyObject, topInterestingNode)); while (nodesToVisit.Any()) { var a = nodesToVisit.First(); nodesToVisit.RemoveFirst(); var currentDependencyObject = a.Item1; var currentInterestingNode = a.Item2; int childCount = VisualTreeHelper.GetChildrenCount(currentDependencyObject); for (int i = 0; i < childCount; i++) { var childDependencyObject = VisualTreeHelper.GetChild(currentDependencyObject, i); if (childDependencyObject == null) continue; var childFrameworkElement = childDependencyObject as FrameworkElement; if (childFrameworkElement != null) { if (isInteresting(childFrameworkElement)) { var b = new VisualTreeInterestingNode() { FrameworkElement = childFrameworkElement, Parent = currentInterestingNode }; currentInterestingNode.InterestingChildNodes.Add(b); nodesToVisit.AddLast(new Tuple<DependencyObject, VisualTreeInterestingNode>(childDependencyObject, b)); } else { nodesToVisit.AddLast(new Tuple<DependencyObject, VisualTreeInterestingNode>(childDependencyObject, currentInterestingNode)); } } else { nodesToVisit.AddLast(new Tuple<DependencyObject, VisualTreeInterestingNode>(childDependencyObject, currentInterestingNode)); } } } timer.Stop(); return topInterestingNode; }
And the VisualTreeNode class that the result is in:
public class VisualTreeInterestingNode { private List<VisualTreeInterestingNode> _all; public VisualTreeInterestingNode() { InterestingChildNodes = new List<VisualTreeInterestingNode>(); } public FrameworkElement FrameworkElement { get; set; } public List<VisualTreeInterestingNode> InterestingChildNodes { get; set; } public VisualTreeInterestingNode Parent { get; set; } public List<VisualTreeInterestingNode> Flatten { get { if (_all != null) return _all; _all = new List<VisualTreeInterestingNode> { this }; foreach (var child in InterestingChildNodes) _all.AddRange(child.Flatten); return _all; } } public StringBuilder Dump(StringBuilder prefix = null, StringBuilder result = null) { if (prefix == null) prefix = new StringBuilder(); if (result == null) result = new StringBuilder(); result.Append(prefix).Append(FrameworkElement.GetType().Name); result.Append(" ").Append(FrameworkElement.Name); result.AppendLine(); var l = prefix.Length; prefix.Append(" "); foreach (var node in InterestingChildNodes) { node.Dump(prefix, result); } prefix.Length = l; return result; } }