PowerApps Remix: Expand and Collapse UI

One of the fun parts about coding is when you figure out a new way of doing something. As you solve problems in your app, you learn how to make your code more efficient. It’s problem-based learning–you become engaged in teaching yourself what you need to know to solve the problems that are before you.

So when Daniel Christian presented an app that demonstrated a scheme for expanding and collapsing controls, I took him up on his challenge in which he invited others to add to his app. My goal was to do some problem-based learning as I figured out how to use ‘easings’ in animation. I wanted the expand and collapse UI to accelerate and decelerate like this:

powerapps - easing

Previously I had a lot of fun making a multiplayer version of Tic-Tac-Toe the last time Daniel had posted an idea. I had been wanting to create a multiplayer mechanic in PowerApps, but never got around to it. So it was a ripe opportunity to do some problem-based learning while I tried implementing multiplayer for the first time.

This is a series I would like to start called the “PowerApps Remix.” It’s simple: you take someone’s app and add onto it. You can remix for style, efficiency, methodology–whatever you think up! Then share it again so others could remix it further.

If you’d like to remix an app of mine, maybe you could make an Android or Windows 10 version of my iPhone fake text generator. In the mean time, enjoy this guide on creating an expanding and collapsing control within a gallery.

Check out the remixed app here: 1drv.ms/u/s!AvtvG5BA8E3vj6ELcCXg48QsfRYUWw

poweraps remix - expander

The Gallery

A gallery is an efficient way of repeating controls. Below is how I reduced the amount of controls needed to achieve this UI. There could be fewer, but I maintained some controls to indicate to the developer what’s going on.

GalleryPanels contains these controls:

  • ButtonExpand: the user will click this to expand or collapse the panel
  • RectangleItem and TextInputItem: this is what will expand outward
  • ToggleIsExpanding: since I’ll be using the same conditions in multiple places, I decided to place them into toggles instead. This one will determine if a row in the gallery is currently expanding or collapsing
  • ToggleIsExpanded: this toggle detects if a row in the gallery is already expanded
  • SliderExpandWidth: the changing width of RectangleItem and TextInputItem will be stored here. This slider will also store the starting and ending width of the expand UI (minimum and maximum).
  • TimerExpand: This timer provides an independent timer for expanding each row of the gallery. It also picks which items in the gallery have been expanded (Panels)
  • (optional) LabelExpand: this shows the number stored in SliderExpandWidth

Most of the controls remain hidden, and they are revealed when you click a wrench icon.

Global Variables

Using a gallery cuts down on the variables needed. In the original expander UI, a different variable was created for each of the 3 items. It is a good way to visualize how you want objects to function in your app. The next step is to leverage the table/gallery structure to reduce the variables needed.

In this gallery method, I use 5 variables and a collection.

The variables include:

  • debug: this variable shows and hides controls that only the developer will see to check how things are working. (Watch my video on creating a debugging panel)
  • addPanel: this variable communicates that a panel is expanding
  • removePanel: this variable communicates that a panel is collapsing
  • selectedPanel: this variable identifies which of the panels in the gallery has been selected for expanding or collapsing
  • expandPanel: this variable communicates that movement is taking place, whether expanding or collapsing

Note: I use “panel” to describe the rows/items in the gallery. I will use those terms interchangeably in this blog post.

The collection, Panels, is a table that contains ID numbers of which panels have been expanded. In the image below, panels 1, 4, and 5 have been expanded. Membership in the collection or lack thereof is how the app will decide whether to expand or collapse a panel.

The Button

The button will trigger expansion or collapse. Change ButtonExpand.OnSelect to the following formula:

Select(Parent);
Set(selectedPanel,ThisItem);
If(ToggleIsExpanded.Value,
  Set(removePanel,true),
  Set(addPanel,true)
);
Set(expandPanel,true)

This means,

  1. Click the gallery.
  2. Make the selectedPanel equal to the item you just selected.
  3. Then, if the item has already been expanded, tell the app that you want to be collapsing (“removing” it from the list of items that have already been expanded).
  4. Otherwise tell the app that you are expanding (“adding” the item to the list of items that have already been expanded).
  5. Tell the app that it will experience either expanding or collapsing.

The Timer

Upon ending, the timer will add or remove the selected panel from a collection of panels that have been expanded.

Change TimerExpand.OnTimerEnd to:

If(removePanel,
  Set(removePanel,false);
  Remove(Panels,selectedPanel),
  addPanel,
  Set(addPanel,false);
  Collect(Panels,selectedPanel)
);
Set(expandPanel,false)

This means:

  1. If the app received instructions to collapse, first, it will return the removePanel variable to false so you could trigger it again later. Then it will remove the selected panel from the collection of expanded panels.
  2. If the app received instructions to expand, it will return the addPanel variable to false so you could trigger it again later. Then it will add the selected panel to the collection of expanded panels.
  3. At the end, the timer will stop itself by turning the variable that determines all motion to ‘false.’

There are a few more properties to change for TimerExpand:

The Toggles

You’re going to be checking to see if a panel has been expanded in multiple places. So rather than copy pasting the same condition everywhere, you can stick it into the default property of a toggle. Then you can reference that toggle wherever you need the condition.

ToggleIsExpanding.Default: (selectedPanel.id=ThisItem.id) && expandPanel

This means, “A row in the gallery ‘is expanding’ when it is the one that has been selected and the app is expecting movement (expandPanel is true).

ToggleIsExpanded.Default: ThisItem.id exactin Panels.id

This means, “A row in the gallery has been ‘expanded’ when its ID number can be found among the ID numbers of panels that have been expanded.”

Each row in the gallery will have its own result in the toggles. Based on our setup, at any given time, only one panel will be expanding or collapsing. (Remix idea: allow multiple panels to expand or collapse)

The Width

In PowerApps you could change the width of an object by its Width property. You could reference the width of another control, so that you don’t need write the same formula twice. Since the width is dynamic in this scenario, I think it makes more sense to store it in another control, then reference that control.

The slider control is great for storing 3 numeric values: a minimum, maximum, and current value. This happens to align with our needs:

  • the starting width of the panel–minimum
  • the ending width of the panel–maximum
  • the current width of the panel–current value

Configure the minimum and maximum widths:

  • Set SliderExpand.Min to 0
  • Set SliderExpand.Max to Parent.TemplateWidth or 768 in this case

The Formula

And without further ado, this is what you came for–SliderExpand.Default:

There’s a lot of things going on in the formula above–and you do not need a lot of it. You could actually reduce that big formula to just:

My formula is the same as Daniel’s with a few tweaks. Here’s the formula from the original expand/collapse UI:

And here’s the part of my formula that is equivalent:

Instead of using 768, I changed it to SliderExpandWidth.Max since that’s the property where I store the same number. The name of my timer is TimerExpand, and not Timer1. And I had already determined if a panel is expanding with ToggleIsExpanding; the variables addPanel and removePanel determine its direction. My combination of variables is equivalent to the original ExpandVar1, 2, and 3.

In the image below, you’ll see in the red box that I had included an additional calculation for expanding the panel. It used a math function to advance the expand animation in a more natural way than expanding at a constant speed. It also requires a radio control so you could switch between the two calculations. You don’t need this part.

The last part is kind of unnecessary. In the image below, I placed this last part into the formula to avoid any problems with the timer’s reset property. It means, “If the panel is fully expanded, set its width to the maximum width (768), otherwise, set it to the minimum width (0 in our case).” You could probably get by without this section.

For advanced users, if you’re interested in other ways of calculating the variable width, look into the concept of ‘easing.’ Here are two websites that were helpful to me:

I took the sinusoidal in/out formula from the first website and replaced the variables with the names of controls in the app. I had been wanting to try this for a while and this UI was a good place to exercise it.

What I found is that with a 300ms expand time, you likely won’t see too much difference between the linear method and this fancy sinusoidal method. I placed a slider in the debugging panel so that you could fine-tune the incremental width that the UI expands.

That’s all the big changes that I made in my remix. Check out the remixed app here: 1drv.ms/u/s!AvtvG5BA8E3vj6ELcCXg48QsfRYUWw

So are you ready to remix it some more? Will you remix my fake text generator?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s