Social Icons

twitter google plus linkedin rss feed

Pages

21.4.11

Creating a Expander Control for Silverlight for Windows Phone

I was migrating a solution to WP7 when I realized that there was not expander control available… I downloaded then the Silverlight  for Windows Phone Toolkit but realized that there wasn’t an expander there either.
 
I really couldn’t conceive a new version of the tool without expanders so I have created a rudimentary Expander for WP7. It's composed by a Grid, a StackPanel and a TextBlock.

It is very limited and of course it's not supposed to be a substitute of the good old Expander (it's focused to the specific problem I was trying to solve), but maybe it can help someone else out there.

The XAP code of the control is:
<Grid x:Name="LayoutRoot">
  <Border VerticalAlignment="Top" BorderBrush="#FF929EB0" BorderThickness="1" CornerRadius="2" Background="#FFE9E9E9" Margin="5,2">
            <Grid d:LayoutOverrides="Width">
    <Grid.RowDefinitions>
     <RowDefinition Height="Auto" MinHeight="20"/>
     <RowDefinition Height="Auto" MinHeight="5"/>
    </Grid.RowDefinitions>
    <TextBlock x:Name="Header" TextWrapping="Wrap" d:LayoutOverrides="Width" Foreground="#FF656565" Margin="15,2,0,0" VerticalAlignment="Top"/>
    <StackPanel x:Name="ChildrenPanel" VerticalAlignment="Top" d:LayoutOverrides="Width" Margin="0,2,0,0" Grid.Row="1"/>
   </Grid>
  </Border>
 </Grid>
The trick is adding a TextBlock in the header of the "expander" and subscribe to the MouseLeftButtonUp event of the TextBlock to control the folding and unfolding. I have also added a Folded property so I can externally set up the status of the expander.

If the expander is folded I add the items to the stack panel, if the expander is unfolded I remove the items. Sounds really simple, but I couldn't make it work. The StackPanel will remain the same size it was when it has all the items in it, but empty… without any Children… too ugly.

After a while trying to fake the behaviour changing the Height of the StakPanel I found the solution.

I was trying to delete all the items in the StackPanel at once with Children.Clear and that was driving it nuts. The solution was to remove the items inside the children one by one.

The code for the Expander class is here.
public partial class Expander : UserControl
 {
        bool _Folded;
        public bool Folded
        {
            get { return _Folded; }
            set
            {
                _Folded = !value;
                ExpanderSwitch();
            }
        }
        List ChildrenList;


  public Expander()
  {
   // Required to initialize variables
   InitializeComponent();

            ChildrenList = new List();

            Header.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(UserActivity_MouseLeftButtonUp);
  }

        void UserActivity_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            ExpanderSwitch();
        }

        private void ExpanderSwitch()
        {
            if (Folded)
            {
                foreach (var child in ChildrenList)
                    ChildrenPanel.Children.Add(child);
            }
            else
            {
                if (ChildrenList.Count == 0)
                    foreach (var item in ChildrenPanel.Children)
                        ChildrenList.Add(item);

                while (ChildrenPanel.Children.Count > 0)
                    ChildrenPanel.Children.RemoveAt(0);
            }

            _Folded = !_Folded;
        }
 }
And to use it you just have to add the control:
<Grid x:Name="LayoutRoot">
        <this:Expander x:Name="UserNews" />
    </Grid>
In code behind you can do something like this:
UserNews.Header.Text = GetHeader(News[0]);

            foreach (FrameworkNews news in News)
                UserNews.ChildrenPanel.Children.Add(new NewsDetail(news.Link, GetDetail(news), news.Where));

            UserNews.Folded = true;
If you have time you can add images and animations to the expander as well as a better "Folding Control"… Sadly I can't spend more time working on it, even though I'd love to :(