Finding Element in ItemsControl DataTemplate (ListView/GridView/FlipView) - Window Store Apps
This article will cover how to find an element inside DataTemplate for ItemsControl (including but not limited to ListView, GridView and FlipView) at particular index. It provides the boilerplate code in response to MSDN questions of how to find the element inside GridView/ListView/FlipView DataTemplate.
Before we proceed with actual application of api's, it is worthwhile to note the following -
- It is DataTemplate so we have to wait till Visual Tree is completely created. Code is put inside page loaded.
- Run or Paragraph element will not be part of visual tree.
- FindName can be used to get Run/Paragraph elements from parent.
Now let's get hand dirty with code. For this article, I am taking control of type GridView.
Here is XAML will look like
<GridView x:Name="gridView" ItemsSource="{Binding ItemsColl}">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stackPanelToFind" Height="240" Width="320" Background="Green">
<Grid x:Name="gridToFind" Background="Blue" Height="60">
<TextBlock FontSize="18">
<Run Text="Name - "></Run>
<Run Text="{Binding Name}" />
<LineBreak/>
<Run Text="Description - "></Run>
<Run x:Name="runToFind" Text="Find Run Element" />
<LineBreak/>
</TextBlock>
</Grid>
<RichTextBlock Margin="0,12,0,0" FontSize="24">
<Paragraph>Find Paragraph below</Paragraph>
<Paragraph x:Name="paraToFind"></Paragraph>
<Paragraph>
<InlineUIContainer>
<Image x:Name="imageToFind" Height="90" Width="90"></Image>
</InlineUIContainer>
</Paragraph>
</RichTextBlock>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
So basically if we want to find the control by name inside the DataTemplate for some Nth item, we can't just use FindName, as it will not work until unless we reach a certain depth at visual tree (in code we will see, use of FindName for Run/Paragraph item).
Here are standard functions which takes inputs of** Item control, item index** inside ItemsControl and name of control and traverses the whole feature tree to find the control. We will see slight tweak to get Run/Paragraph Element. Basically it uses VisualTreeHelper and FindName to retrieve the child elements recursively.
DependencyObject findElementInItemsControlItemAtIndex( ItemsControl itemsControl,
int itemOfIndexToFind,
string nameOfControlToFind)
{
if (itemOfIndexToFind >= itemsControl.Items.Count) return null;
DependencyObject depObj = null;
object o = itemsControl.Items[itemOfIndexToFind];
if (o != null)
{
var item = itemsControl.ItemContainerGenerator.ContainerFromItem(o);
if (item != null)
{
//GridViewItem it = item as GridViewItem;
//var i = it.FindName(nameOfControlToFind);
depObj = getVisualTreeChild(item, nameOfControlToFind);
return depObj;
}
}
return null;
}
DependencyObject getVisualTreeChild(DependencyObject obj, String name)
{
DependencyObject dependencyObject = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childrenCount; i++)
{
var oChild = VisualTreeHelper.GetChild(obj, i);
var childElement = oChild as FrameworkElement;
if (childElement != null)
{
//Code to take care of Paragraph/Run
if (childElement is RichTextBlock || childElement is TextBlock)
{
dependencyObject = childElement.FindName(name) as DependencyObject;
if (dependencyObject != null)
return dependencyObject;
}
if (childElement.Name == name)
{
return childElement;
}
}
dependencyObject = getVisualTreeChild(oChild, name);
if (dependencyObject != null)
return dependencyObject;
}
return dependencyObject;
}
Here are snippets to find the controls (refer earlier XAML for control names and also note they are called after page is loaded)
// Find StackPanel by name in DataTemplate
StackPanel stackPanel = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "stackPanelToFind") as StackPanel;
if (stackPanel != null)
{
stackPanel.Background = new SolidColorBrush(Colors.Brown);
}
// Find Grid by name in DataTemplate
Grid gridToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "gridToFind") as Grid;
if (gridToFind != null)
{
gridToFind.Background = new SolidColorBrush(Colors.CadetBlue);
}
// Find Run by name in DataTemplate
Run runToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "runToFind") as Run;
if (runToFind != null)
{
runToFind.FontSize = 18;
runToFind.Text = "<Run set through code>";
}
// Find Paragraph by name in DataTemplate
Paragraph paraToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "paraToFind") as Paragraph;
if (paraToFind != null)
{
Run run = new Run();
run.Text = "<ParagraphFound>";
paraToFind.Inlines.Add(run);
}
// Find Image by name inside RichTextBlock
Image imageToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "imageToFind") as Image;
if (imageToFind != null)
{
BitmapImage bitmapImage = new BitmapImage(new Uri("ms-appx:///Assets/Logo.png"));
imageToFind.Source = bitmapImage;
}
Complete page code -
public class ItemCollDataModelItem
{
public string Name { get; set; }
public string Description { get; set; }
public ItemCollDataModelItem()
{
Name = string.Empty;
Description = string.Empty;
}
}
public class ItemCollDataModel
{
public ObservableCollection<ItemCollDataModelItem> ItemsColl { get; set; }
public ItemCollDataModel()
{
ItemsColl = new ObservableCollection<ItemCollDataModelItem>();
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name I" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name II" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name III" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name IV" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name V" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name VI" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name VII" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name VIII" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name IX" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name X" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name XI" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name XII" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name XIII" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name XIV" });
ItemsColl.Add(new ItemCollDataModelItem() { Name = "Name XV" });
}
}
public sealed partial class BasicFindChild : TestApp2.Common.LayoutAwarePage
{
public BasicFindChild()
{
this.InitializeComponent();
this.DataContext = new ItemCollDataModel();
this.Loaded += BasicFindChild_Loaded;
}
void BasicFindChild_Loaded(object sender, RoutedEventArgs e)
{
int indexOfItemToFind = 3;
// Different Controls
// Find Stack Panel by name in DataTemplate
StackPanel stackPanel = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "stackPanelToFind") as StackPanel;
if (stackPanel != null)
{
stackPanel.Background = new SolidColorBrush(Colors.Brown);
}
// Find Grid by name in DataTemplate
Grid gridToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "gridToFind") as Grid;
if (gridToFind != null)
{
gridToFind.Background = new SolidColorBrush(Colors.CadetBlue);
}
// Find Run by name in DataTemplate
Run runToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "runToFind") as Run;
if (runToFind != null)
{
runToFind.FontSize = 18;
runToFind.Text = "<Run set through code>";
}
// Find Paragraph by name in DataTemplate
Paragraph paraToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "paraToFind") as Paragraph;
if (paraToFind != null)
{
Run run = new Run();
run.Text = "<ParagraphFound>";
paraToFind.Inlines.Add(run);
}
// Find Image is inside RichTextBlock
Image imageToFind = findElementInItemsControlItemAtIndex(gridView, indexOfItemToFind, "imageToFind") as Image;
if (imageToFind != null)
{
BitmapImage bitmapImage = new BitmapImage(new Uri("ms-appx:///Assets/Logo.png"));
imageToFind.Source = bitmapImage;
}
}
DependencyObject findElementInItemsControlItemAtIndex( ItemsControl itemsControl,
int itemOfIndexToFind,
string nameOfControlToFind)
{
if (itemOfIndexToFind >= itemsControl.Items.Count) return null;
DependencyObject depObj = null;
object o = itemsControl.Items[itemOfIndexToFind];
if (o != null)
{
var item = itemsControl.ItemContainerGenerator.ContainerFromItem(o);
if (item != null)
{
depObj = getVisualTreeChild(item, nameOfControlToFind);
return depObj;
}
}
return null;
}
DependencyObject getVisualTreeChild(DependencyObject obj, String name)
{
DependencyObject dependencyObject = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childrenCount; i++)
{
var oChild = VisualTreeHelper.GetChild(obj, i);
var childElement = oChild as FrameworkElement;
if (childElement != null)
{
//Code to take care of Paragraph/Run
if (childElement is RichTextBlock || childElement is TextBlock)
{
dependencyObject = childElement.FindName(name) as DependencyObject;
if (dependencyObject != null)
return dependencyObject;
}
if (childElement.Name == name)
{
return childElement;
}
}
dependencyObject = getVisualTreeChild(oChild, name);
if (dependencyObject != null)
return dependencyObject;
}
return dependencyObject;
}
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
}
protected override void SaveState(Dictionary<String, Object> pageState)
{
}
However this article shows how to get the element inside DataTemplate, however in most scenarios we would not need to do this. It is always recommended to use DataBinding (some part of DataTemplate shown here is also using some DataBinding) instead of using this technique.