Introduction
Do you need to iterate a WPF <Grid>
control's row collection? This code shows you how to do that by "dropping" down to a more fundamental layer of WPF's UIElement
layer. On the way "down there", the VisualTreeHelper
class is used to find the children to the <Grid>
rows.
Background
DataGrid
s, DataGridView
s and other data controls, classes, and collections all allow row iteration using built-in methods. The WPF <Grid>
control does not have this function. This article shows how to create a <Grid>
row iterator to access the text content of each row.
Using the Code
A helper class to iterate the "Visual" component of the <Grid>
control is in order. Look at this as the HTMLElement
equivalent in WPF. We want the individual UIElement
s of the Grid
. Once we have them, getting the Value
of each element is simple.
This static Iterate
helper method below uses the VisualTreeHelper
to iterate the "Visual Tree" of WPF. Notice the it accepts a delegate EnumVisualCallBack
named "cb
".
public static void Iterate(Visual visual, EnumVisualCallBack cb)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual childVisual = (Visual)VisualTreeHelper.GetChild(visual, i);
cb(childVisual);
Iterate(childVisual, cb);
}
}
The delegate lives at the namespace level for each cross-class access, and looks like this: delegate void EnumVisualCallBack(Visual visual);
.
The delegate signature is simply looking for a method that returns void
and accepts a Visual. The reason the design is done like this is for the sake of the caller of Iterate
. The caller can implement their own logic in their class and leave the iteration up to someone else.
The callback code is shown below, and is placed in the portion of code that needs to iterate the <Grid>
.
private StringBuilder sb = null;
public void VisualCallBack(Visual visual) {
if (visual.GetType().Name == "ContentPresenter") return;
UIElement uie = (UIElement)visual;
System.Reflection.PropertyInfo[] pi = uie.GetType().GetProperties();
var stuff = from q in pi.OfType<System.Reflection.PropertyInfo>()
where q.Name=="Content"
select q;
foreach (System.Reflection.PropertyInfo pi2 in stuff) {
sb.Append(pi2.GetValue(uie, null).ToString()) ;
}
}
We now have the Iterator
and the callback to process the iteration, so how do we invoke all of this? Assuming you have a button that will copy a <Grid>
content to the clipboard in the Click
event, the code would look like this:
private void b_Copy_Click(object sender, RoutedEventArgs e)
{
Grid temp = SomeGrid;
EnumVisualCallBack cb = new EnumVisualCallBack(VisualCallBack);
sb = new StringBuilder();
EnumVisual.Iterate(temp, cb);
Clipboard.SetText(sb.ToString());
}
Points of Interest
Using callbacks for iteration is good because it allows you to keep the "code once" mantra. It also add immense flexibility in the callback logic because there could be as many variations as needed to process that data in anyway necessary. All of this while maintaining a never changing iterator.
The filter in the callback to reject types of "ContentPresenter
" stops duplicate iterations; as the VisualTreeHelper
will find the ContentPresenter
as well as the content within, we don't want the presenter, just the content.
Using the PropertInfo
class, we call the GetValue
method which is similar to an HTMLElement
GetValue
call. All we want is the value of the property which is named "Content"...
So why didn't WPF have a built in for <Grid>
row iteration? Don't know...
History
- 19/Oct/2012- Fixed Code download link
- 30/Jul/2010 - Version 1.