Introduction
I had a problem: On a certain Silverlight page, I needed to unexpand 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 binded 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 are 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 binded data (in my case it's a HeaderTemplate
and a bunch of nested UserControls not need 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" >
-->
</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 local variable and catches the ExpanderTemaExpanded
event of any of it's 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 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) item.IsExpanded = false; }
} 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 anyone.