Introduction
I had a problem: on a certain Silverlight page, I needed to un-expand all the Expander
s 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
Expander
s, 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 UserControl
s 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" >
-->
</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) 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
Expander
s
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.