Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

UserControl populating ItemsControl – Manipulation of ItemsControl collection from inner UserControl

0.00/5 (No votes)
23 May 2012 1  
UserControl populating ItemsControl – manipulation of ItemsControl collection from inner UserControl.

Introduction

I had a problem: on a certain Silverlight page, I needed to un-expand all the Expanders except for the Expander that the user selects to expand. Googled around and couldn’t get a solution so I had to dig in on it and get it to work.

This is what I came up with and I think it’s good enough to share. I just apologize for the naming of namespaces, classes, methods, and variables for they use my own language (most of the time!). The content of the UserControl and data are very specific to every project so I’ll show only the needed information for clarity purposes.

Background 

The general solution is a Parent control, a ScrollViewer, holding a StackPanel. In it the ItemsControl with its ItemTemplate being of a UserControl type bound to the ItemsControl.ItemsSource. Whenever a user expands one of the child Expanders, the child UserControl fires an event that is picked up by the Parent and then processes whatever is to be done on the children collection. The problem is that the StackPanel’s children collection is an ItemsControl and I needed to get to the Expanders in it. 

Using the code - passing the event to parent control

The UserControl - myTema 

The UserControl myTema is basically an Expander with some stuff in it that consumes the bound data (in my case, it's a HeaderTemplate and a bunch of nested UserControls not needed for this explanation) and an action to the Expanded event: 

<Grid x:Name="LayoutRoot" >
  <toolkit:Expander x:Name="ExpanderTema" Expanded="ExpanderTema_Expanded" 
            HeaderTemplate="{StaticResource ExpanderTemaDataTemplate}" 
            Header="{Binding myTema_Conteudo, ElementName=myTemaRoot}" >
    <StackPanel x:Name="stackSugest" >
      <!-- Controls that hold the data -->
    </StackPanel>
  </toolkit:Expander>
</Grid>
public event EventHandler ExpanderTemaExpanded;

private void ExpanderTema_Expanded(object sender, System.Windows.RoutedEventArgs e)
{
    ExpanderTemaExpanded(sender, null);
}

The ParentControl

The StackPanel parent control has its ItemsControl.ItemsSource based on a local variable and catches the ExpanderTemaExpanded event of any of its UserControl children. 

List<myTema_Info> ListTema = new List<myTema_Info>();
itemsMyTemas.ItemsSource = ListTema; 
<ScrollViewer x:Name="ScrollViewer1" >
  <Grid>
    <StackPanel x:Name="stackMyTemas" >
      <ItemsControl x:Name="itemsMyTemas" >
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <componente:myTema x:Name="myCategory_Temas" 
                ExpanderTemaExpanded="myCategory_Temas_ExpanderTemaExpanded" 
                myTema_Conteudo="{Binding}" />
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </StackPanel>
  </Grid>
</ScrollViewer>

In the method called by the USerControl, the ExpanderTemaExpanded event is where the magic happens with the GetAllChildFrameworkElement function explained in the next section.

private void myCategory_Temas_ExpanderTemaExpanded(object sender, System.EventArgs e) 
{ 
  var found = Tools.GetAllChildFrameworkElement(stackMyTemas, typeof(Expander));
  foreach (Expander item in found)
  {
    if (item != (Expander)sender) // If item is different from the one that originated the call 
      item.IsExpanded = false; // Do something to it (my case, unexpand) 
  }
} 

Using the code - the generic static class 

In my case I use a generic static class where I put all generic functions like the ones needed to find all the Expanders on the parent StackPanel children's collection.

public static class Tools
{
  public static List<FrameworkElement> GetAllChildFrameworkElement(
         FrameworkElement parentElement, Type typeRequired)
  {
     List<FrameworkElement> childFrameworkElementFound = 
                                  new List<FrameworkElement>();
     SearchAllChildFrameworkElement(parentElement, childFrameworkElementFound, typeRequired);
     return childFrameworkElementFound;
  }

  private static void SearchAllChildFrameworkElement(FrameworkElement parentFrameworkElement, 
          List<FrameworkElement> allChildFrameworkElement, Type typeRequired)
  {
    int childrenCount = VisualTreeHelper.GetChildrenCount(parentFrameworkElement);
    if (childrenCount > 0)
    {
      for (int i = 0; i < childrenCount; i++)
      {
        FrameworkElement childFrameworkElement = 
           (FrameworkElement)VisualTreeHelper.GetChild(parentFrameworkElement, i);
        if (childFrameworkElement.GetType().Equals(typeRequired))
        {
          allChildFrameworkElement.Add(childFrameworkElement);
        }

        SearchAllChildFrameworkElement(childFrameworkElement, 
                  allChildFrameworkElement, typeRequired);
      }
    }
  }
}

This particular piece of code I found in http://forums.silverlight.net/t/160356.aspx.

It uses the VisualTreeHelper class and a recursive method to loop through the children collection looking for the ones with a matching type to the type we are looking for.

It returns a collection of them all, if any, and on that collection, you can do whatever you want or need to do.

Hope it helps someone.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here