Click here to Skip to main content
Click here to Skip to main content

Metro In Motion #8 – AutoCompleteBox Reveal Animation

, 19 Jul 2011
Rate this:
Please Sign up or sign in to vote.
When I started the Metro In Motion series, I thought I would probably post three or four articles and be done. However, every time I use my Windows Phone 7 I seem to spot a new ‘native’ fluid UI effect which I would like to use in my own code. Also, these posts have proven [...]

When I started the Metro In Motion series, I thought I would probably post three or four articles and be done. However, every time I use my Windows Phone 7 I seem to spot a new ‘native’ fluid UI effect which I would like to use in my own code. Also, these posts have proven very popular, there appears to be a real developer ‘need’ for this sort of information.

In this instalment of Metro In Motion I will show how to implement the fluid auto-complete effect that can be seen in the Windows Phone 7 email client. You can see a ‘storyboard’ for this effect below; the items within the auto-complete popup slide gracefully into view when the popup initially renders:

This effect uses the AutoCompleteBox which is part of the Silverlight for Windows Phone Toolkit. I have implemented this effect as a Blend Behaviour, allowing it to be easily added to an AutoCompleteBox either via drag and drop within Expression Blend, or by simply adding the XAML below:

<tk:AutoCompleteBox VerticalAlignment="Top" ItemsSource="{Binding}"
                    ValueMemberPath="Surname">
    <!-- add the metro in motion effect -->
    <i:Interaction.Behaviors> 
        <behaviour:AutoCompleteSlideBehaviour/>
    </i:Interaction.Behaviors>
</tk:AutoCompleteBox>

Let’s take a look at how this behaviour is implemented …

In order for this effect to work, we need to handle the Opening event raised by the Popup control that is part of the AutoCompleteBox template. When this event fires, each of the elements within the ListBox that the Popup contains are animated. The first task is to locate the ListBox and the Popup, this is performed when the behaviour is attached: 

protected override void OnAttached()
{
  base.OnAttached();
 
  // locate the listBox, if this fails, the AutoCOmpleteBox is not loaded,
  // so try again after the Loaded event.
  if (!TryFindListBox())
  {
    RoutedEventHandler onLoaded = null;
    onLoaded = (s, e) =>
        {
          TryFindListBox();
          AssociatedObject.Loaded -= onLoaded;
        };
    AssociatedObject.Loaded += onLoaded;
  }
}
 
/// <summary>
/// Tries to locate the Listbox within the auto-completes Popup.
/// </summary>
private bool TryFindListBox()
{
  // locate the auto-complete  popup
  _popup = AssociatedObject.Descendants<popup>().SingleOrDefault() as Popup;
  if (_popup == null)
    return false;
 
  _popup.Opened += Popup_Opened;
 
  // locate the ListBox
  _popUpListbox = _popup.Child.Descendants<listbox>().SingleOrDefault() as ListBox;
  return true;
}

The above code uses some simple Linq-to-VisualTree to locate these elements. Notice that the approach taken first tries to locate these elements, if this fails, the Loaded event is handled, which is fired when elements (and their template components) are added to the visual tree.

The animation is fired each time the Popup is opened. Creating the animation itself is quite simple, the items within the ListBox are iterated over, with a TranslateTransform created for each, with delayed Storyboard animations used to fire the animations sequentially. However, the container’s (i.e. ListBoxItem instances) may not have been created when the Opened event fires. In order to handle this, the code below checks for the container of the item at the first index. If this container has not yet been generated, the code which creates the animations is deferred until after the next LayoutUpdated event. This should ensure that the containers are now present. The code is shown below:

/// <summary>
/// Handle the Opened even on the Popup in order to animate the contents
/// </summary>
private void Popup_Opened(object sender, System.EventArgs e)
{
  if (_popUpListbox.Items.Count == 0)
    return;
 
  // an action which fires the required animation for each element
  Action fireAnimations = () =>
  {
    // locate all the ListBoxItems
    var itemContainers = _popUpListbox.Items
        .Select(i => _popUpListbox.ItemContainerGenerator.ContainerFromItem(i))
        .Where(i => i != null)
        .Cast<listboxitem>();
 
    // animate each item
    double startTime = 0;
    foreach (var listBoxItem in itemContainers)
    {
      // add a render transform that offsets the element
      var trans = new TranslateTransform()
          {
            Y = -50
          };
      listBoxItem.RenderTransform = trans;
      listBoxItem.Opacity = 0.0;
 
      var sb = new Storyboard();
      sb.BeginTime = TimeSpan.FromMilliseconds(startTime);
      sb.Children.Add(TiltBehaviour.CreateAnimation(null, 0, 0.2, "Y", trans));
      sb.Children.Add(TiltBehaviour.CreateAnimation(null, 1, 0.5, "Opacity", listBoxItem));
      sb.Begin();
 
      startTime += 100;
    }
  };
 
  // check if the item container for the first item has been generated
  if (_popUpListbox.ItemContainerGenerator.ContainerFromIndex(0) == null)
  {
    // if not, wait for the next LayoutUpdated event
    EventHandler updateHandler = null;
    updateHandler = (s, e2) =>
    {
      fireAnimations();
      _popUpListbox.LayoutUpdated -= updateHandler;
    };
    _popUpListbox.LayoutUpdated += updateHandler;
  }
  else
  {
    // otherwise, fire the animations now
    fireAnimations();
  }
}

The full sourcecode for this behaviour can be found in the WP7Contrib project, within the WP7Contrib.View.Controls assembly.

You can download a simple working example here: MetroInMotionEight.zip

I have of course updated the Sandwich Flow, Metro In Motion demo application to include this effect. Again, this is available via the WP7Contrib project:

&amp;amp;lt;/p&amp;amp;gt;

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Colin Eberhardt
Architect Scott Logic
United Kingdom United Kingdom
I am CTO at ShinobiControls, a team of iOS developers who are carefully crafting iOS charts, grids and controls for making your applications awesome.
 
I am a Technical Architect for Visiblox which have developed the world's fastest WPF / Silverlight and WP7 charts.
 
I am also a Technical Evangelist at Scott Logic, a provider of bespoke financial software and consultancy for the retail and investment banking, stockbroking, asset management and hedge fund communities.
 
Visit my blog - Colin Eberhardt's Adventures in .NET.
 
Follow me on Twitter - @ColinEberhardt
 
-
Follow on   Twitter   Google+

Comments and Discussions

 
QuestionAutocomplete problem inside scrollviewer Pinmembercrackzz0725-Aug-11 19:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140709.1 | Last Updated 19 Jul 2011
Article Copyright 2011 by Colin Eberhardt
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid