Xamarin.Tip – Expanding ViewCells

Here’s another helpful Xamarin tip to allow you to expand your ViewCells – for example when the user taps a ViewCell, expand it to reveal more information.
Take this gif as an example:

ExpandingCell

There are 3 major parts to making a feature like this work:
1. Bind visibility to the hidden parts of your cell
2. Handle updating that visibility on selection or tap
3. Force the ViewCell to remeasure its size to show the hidden bits.

Let’s first create a new class called ExpandingViewCell:

ExpandingViewCell.cs

/// <summary>
/// Expanding view cell.
/// </summary>
public class ExpandingViewCell : ViewCell
{
    protected override void OnTapped()
    {
        base.OnTapped();
        ForceUpdateSize();
    }
}

Yeah it’s literally that stupid easy. Override OnTapped and call ForceUpdateSize(). However, it’s important to note that this is so easy because of the order – calling the base.OnTapped() first allows for your click / select events to fire off before measuring the size. This means that if we bind an update to show / hide certain controls on tap, those will show first, then we remeasure to make sure they fit.

Here’s the XAML for the ViewCell shown in the gif above:

<components:ExpandingViewCell>
    <StackLayout Orientation="Vertical" Spacing="4" Padding="64,8,16,8" Margin="0">
        <Label Text="{Binding DateLabel}" IsVisible="{Binding ShowDate}" Style="{DynamicResource BodySecondary}" HorizontalOptions="End" HorizontalTextAlignment="End" />
        <Frame CornerRadius="12" Padding="12" Margin="0" BackgroundColor="{Binding MessageColor, Converter={StaticResource ColorConverter}}" VerticalOptions="Center" HorizontalOptions="EndAndExpand" effects:LongPressedEffect.Command="{Binding Path=BindingContext.MessageOptionsCommand,Source={x:Reference Page}}" effects:LongPressedEffect.CommandParameter="{Binding .}">
            <Frame.Effects>
                <effects:LongPressedEffect />
            </Frame.Effects>
            <Label Text="{Binding Content}" Style="{DynamicResource BodySecondary}" />
        </Frame>
        <Label Text="{Binding SentDateLabel}" IsVisible="{Binding IsSentDateVisible}" Style="{DynamicResource BodySecondary}" HorizontalOptions="End" HorizontalTextAlignment="End" />
    </StackLayout>
</components:ExpandingViewCell>

There is a lot going on in this cell (it’s a chat page after all, so it’s a feature rich cell). I even have some long press handlers from my LongPressedEffect that you can read about here: Xamarin.Tip – Xamarin.Forms Long Press Effect

The important pieces to note are the IsVisible="{Binding IsSentDateVisible}" to show the date label or not.

Now in order to update this, we need to bind our SelectedItem in our ListView.

Here’s an example from the gif:

<ListView x:Name="ChatListView" ItemsSource="{Binding Messages}" SelectedItem="{Binding SelectedMessage, Mode=TwoWay}" HasUnevenRows="True" SeparatorVisibility="None" ItemTemplate="{StaticResource BubbleSelector}" />

We use the DataTemplateSelector to choose between the sent bubble and the received bubble, but that isn’t important. If you want to read more about that, check out this doc from Xamarin: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector

Now lastly, let’s look at what’s going on in the ViewModel behind the ListView and how we are binding the SelectedItem.

ChatPageViewModel.cs

...

public ChatMessageViewModel SelectedMessage
{
    get
    {
        return _selectedMessage;
    }
    set
    {
        Set(ref _selectedMessage, value);
        if (value != null)
        {
            ToggleDetails(value);
            SelectedMessage = null;
        }
    }
}

/// <summary>
/// Toggles the details of the given message.
/// </summary>
/// <param name="message">The message view model.</param>
private void ToggleDetails(ChatMessageViewModel message)
{
    message.ShowSenderName = !message.ShowSenderName;
    message.IsSentDateVisible = !message.IsSentDateVisible;
}

...

When the SelectedMessage we bound is changed, and not null, we toggle the ShowSenderName and the IsSentDateVisible to show/hide those views.

So the process looks like this:
– ViewCell is tapped
– ViewCell base call
– SelectedItem toggled
– SelectedMessage toggles the ViewModel properties
– Two way binding triggers to show / hide the inner views
– ViewCell ForceUpdateSize called
– ViewCell expands or collapses

And once again, we get a nice little effect like this!

ExpandingCell


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

6 thoughts on “Xamarin.Tip – Expanding ViewCells”

  1. Hi Alex! Excellent post. Currently there is a bug with ForceUpdateSize happening in XF under iOS, when you tap multiple times the same cell, each time it fades more slowly, until it freezes… Did you have the same behaviour?

    Like

      1. I had encountered the freezing issue on an app which I was working on. Had resolved it by setting CachingStratergy to RecycleElement. Can you please try it out in your sample, Alex.

        Like

  2. Hi Alex,
    thanks a lot for this tip. In my case I was using the same code ( base.OnTapped(); ForceUpdateSize(); ) but I was also using the ItemTapped event of the ListView to call the VM Toggle method. This (less elegant) way to togle the IsVisible property somehow was interfering with the ForceUpdateSize that didn’t work as expected (i.e. the detail was showed over the other listview items, without updating the cell size.

    Your way of binding the SelectedItem to a VM property is more elegant and MVVM coding style but more important solved the issue that was driving me mad 🙂

    My little contribution is that I’ve tried the CachingStrategy=”RecycleElement” setting of the ListView and I’ve found that it indeed solve the multiple tap freezing issue.

    With that, the Accordion list works as a charm!

    Like

Leave a comment