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 is attached (just use the Import canvas app button at the top to install it).
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: -
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: -
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.
ClearCollect(colDevices,Devices)
Looking at the various elements
Main Menu
The top “Computer” is a label lbHeader with the OnSelect
UpdateContext(
{
vViewMan: !vViewMan,
vViewType: false,
vViewDevice: false,
vViewPhoto: false
}
)
the position of this can be anywhere required, but note that all other elements adjust to this.
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(
{
wMan:
Sort(
Distinct(
colDevices,
ManufacturerName
),
Result
)
},
ForAll(
Sequence(
CountRows(wMan)
),
Patch(
Last(
FirstN(
wMan,
Value
)
),
{RowNo: Value}
)
)
)
OnSelect
Controls the visibility of the following elements
UpdateContext(
{
vViewType: true,
vViewDevice: false,
vViewPhoto: false
}
)
Visibility
Toggled on the selection of the head label
vViewMan
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
IMPORTANT STEP - First put a label in the gallery and name it lbSizeMan with this as the Text and hide it
Len(ThisItem.Result)
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(
{
wType:
Sort(
Distinct(
Filter(
colDevices,
ManufacturerName = galManDevice.Selected.Result
),
DeviceType
),
Result
)
},
ForAll(
Sequence(
CountRows(wType)
),
Patch(
Last(
FirstN(
wType,
Value
)
),
{RowNo: Value}
)
)
)
OnSelect
Same logic as previous
UpdateContext(
{
vViewDevice: true,
vSelDevice: false,
vViewPhoto: false
}
)
Visible
vViewType
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(
{
wModel:
Filter(
colDevices,
ManufacturerName = galManDevice.Selected.Result &&
DeviceType = galTypeDevice.Selected.Result
)
},
ForAll(
Sequence(
CountRows(wModel)
),
Patch(
Last(
FirstN(
wModel,
Value
)
),
{RowNo: Value}
)
)
)
OnSelect
This also sets a Variable vItem to the record selected.
UpdateContext(
{
vViewPhoto: true,
vItem:
LookUp(
colDevices,
Title1 = ThisItem.Title1
)
}
)
Visible
vViewDevice
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 may require a bit more flexibility as I found significant differences in smaller and larger entry widths, particularly is a lot of capital letters are used. I ended up with
With(
{
wWide:
Max(
galModelDevice.AllItems,
lbSizeModel
)
},
If(
wWide < 6,
wWide * 11 + 15,
wWide <15,
wWide * 9 + 10,
wWide * 8 + 10
)
)
Picture
The Image control imDevice is standalone with the following Image
vItem.Photo
Visible
vViewPhoto
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 vItem (vItem.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.