Dynamic expanding multi-level menus
As users have become accustomed in many applications, top menus with context-sensitive sub-menus are the norm, but are not part of the standard Power Apps “suite”, so creating this type of function is a useful enhancement to user experience using a “look and feel” familiar to them.
Firstly, the App and the Excel file (which is already imported) can be downloaded here.
I will start this a bit backwards, showing the end result of a three-level menu based on a list as below and then discuss what needs to be done to accomplish this.
These are galleries with one field in each: –
- galManDevice shows the PC manufacturer list.
- galTypeDevice is for all device types belonging to the manufacturer.
- galModelDevice shows all models of the type from the manufacturer.
To the right of these, there is a picture of the chosen device and the specifications.
So what is happening here? There are some things that need to be done to the galleries and panels to control their size, position and visibility. This greatly enhances the user experience an provides something similar to regular “expanding” menu dropdown trees. The main elements: –
- “Line up” the top of the second and third galleries and panels with the item chosen.
- Control the visibility of all elements giving the user the ability to hide them in a logical way.
- Size the height of all galleries to fit the number of items.
- Size the width of the galleries to fit the largest item contained in them
The data you may recognize as list of computer manufacturers and relevant models as it is part of a few Power Apps courses. The List name is Devices. So what is needed to get this working?
For performance reasons, I have done a Collection (as you normally would with a remote data source) at App OnStart. The Collection is colDevices. Looking at the various elements
Main Menu
The top “Computer” is a label lbHeader with the OnSelect
UpdateContext(
{
varViewMan: !varViewMan,
varViewType: false,
varViewDevice: false,
varViewPhoto: false
}
)
galManDevice
Items
A sequential number needs to be added to the gallery for positioning the following panels. It also uses Distinct values from the list
With(
{
_Man:
Sort(
Distinct(
colDevices,
ManufacturerName
),
Result
)
},
ForAll(
Sequence(
CountRows(_Man)
),
Patch(
Index(
_Man,
Value
),
{RowNo: Value}
)
)
)
OnSelect
Controls the visibility of the following elements
UpdateContext(
{
varViewType: true,
varViewDevice: false,
varViewPhoto: false
}
)
Visible
Again controlled by what is required
varViewMan
Height
To allow for the number of items in the panel
CountRows(Self.AllItems) * (Self.TemplateHeight + Self.TemplatePadding) + Self.TemplatePadding
X
To align with the header label
lbHeader.X
Y
To sit below the header
lbHeader.Y + lbHeader.Height
Width
First put a label in the gallery (lbSizeMan) with this as the Text and hide it
Len(ThisItem.Value)
Then the Width of the gallery depends a bit on the font size and to some degree how many capital letters are involved. I have used this, but you can adjust to suit.
Max(
galManDevice.AllItems,
lbSizeMan
) * 9 + 10
galTypeDevice
Items
Same logic as previous
With(
{
_Type:
Sort(
Distinct(
Filter(
colDevices,
ManufacturerName = galManDevice.Selected.Result
),
DeviceType
),
Value
)
},
ForAll(
Sequence(
CountRows(_Type)
),
Patch(
Index(
_Type,
Value
),
{RowNo: Value}
)
)
)
OnSelect
Same logic as previous
UpdateContext(
{
varViewDevice: true,
varSelDevice: false,
varViewPhoto: false
}
)
Visible
varViewType
Height
(CountRows(Self.AllItems) * (Self.TemplateHeight + Self.TemplatePadding)) + Self.TemplatePadding
X
To align with the edge of the last gallery
galManDevice.X + galManDevice.Width
Y
To align with the item selected in galManDevice
(CountRows(Self.AllItems) * (Self.TemplateHeight + Self.TemplatePadding)) + Self.TemplatePadding
Width
As per the hidden label
Max(
galTypeDevice.AllItems,
lbSizeType
) * 9 + 10
galModelDevice
Items
All uncommented properties below are as per previous logic
With(
{
_Model:
Filter(
colDevices,
ManufacturerName = galManDevice.Selected.Result &&
DeviceType = galTypeDevice.Selected.Result
)
},
ForAll(
Sequence(
CountRows(_Model)
),
Patch(
Index(
_Model,
Value
),
{RowNo: Value}
)
)
)
OnSelect
This also sets a Variable varItem to the record selected.
UpdateContext(
{
varViewPhoto: true,
varItem:
LookUp(
colDevices,
Title1 = ThisItem.Title1
)
}
)
Visible
varViewDevice
Height
(CountRows(Self.AllItems) * (Self.TemplateHeight + Self.TemplatePadding)) + Self.TemplatePadding
X
galTypeDevice.X + galTypeDevice.Width
Y
(CountRows(Self.AllItems) * (Self.TemplateHeight + Self.TemplatePadding)) + Self.TemplatePadding
Width
I will give you a bit more flexibility as I found significant differences in smaller and larger entry widths. I ended up with
With(
{
_Wide:
Max(
galModelDevice.AllItems,
lbSizeModel
)
},
If(
_Wide < 6,
_Wide * 11 + 15,
_Wide <15,
_Wide * 9 + 10,
_Wide * 8 + 10
)
)
Picture
The Image control imDevice is standalone with the following
Image
varItem.Photo
Visible
varViewPhoto
X
galModelDevice.X + galModelDevice.Width
Y
galModelDevice.Selected.RowNo * (galModelDevice.TemplateHeight + galModelDevice.TemplatePadding) + galModelDevice.Y - galModelDevice.TemplateHeight - galModelDevice.TemplatePadding
Specifications
The Rectangle Icon at the right has the same Visibility and Y, with the X based on the Photo.
It has five labels in it, with their Y based on a distance below the Rectangle Y (example for the first one)
icDetails.Y + 50
and their X based on the distance from the left side of the rectangle
icDetails.X +20
Their Text is based on varItem (varItem.Price/Memory/Storage/Processor/Screen
I hope this is useful for you – bear in mind you will need to adjust everything for your field names and what needs to match.
6 Comments
Kerim
Hi Warren,
Thank you for sharing the app and this technic.
quick question, can we use a similar technic but with a multi-select value/column in SharePoint? I’m no expert and I tried to use a multi-select column but couldn’t find the right formula.
Thank you,
Warren Belz
The column (second item) is multi-select presently – all the formulas are in the blog – it is quite complex, so needs to be understood a bit.
Kerim
Hi Warren,
Yes, it is a bit complex but I got it working with my SharePoint list if I use single-choice columns.
however I’d like to use it with choice multiple selection, I tried to start with the multi for the first gallery but it didn’t work then tried to keep the single choice column and match it to the multi in the second gallery but will not allow me.
should we still use distinct?
Warren Belz
That creates a many-to-many relationship and is a whole new subject and structure
Kerim
Hi Warren,
A quick note to thank you. I managed to use your code with some changes with many to many relationships
Warren Belz
Good to see it worked for you