With this post we are going to do a demo application to explain the use of the TMultiView component with the typical menu of mobile devices. In addition, the demo will have as a requirement that the forms shown in it will be embedded into a TPanel of the main form. To do this, once the form is created, we will change the parent of all its direct components by assigning their parent to the TPanel of the main form.
Another requirement of this demo, will be able to navigate backwards through the different forms that are created. For this we will define a “stack” LIFO (Last In First Out).
The TMultiView component
The TMultiView component allows us to create master-detail interfaces on any platform. In the master panel we can show any set of visual components (buttons, labels, lists, ….). These controls can be linked to a specific view of the detail panel.
Their most important properties are:
- Mode: sets the presentation mode of the master panel. The different modes are the following:
- Drawer: in this mode, the master panel can be hidden or overlap the detail panel.
- Panel: the master panel is always shown anchored to right or left.
- PlatformBehaviour: the application selects the presentation mode depending on the orientation and the type of device.
Mobile Applications Device Type Device Orientation Master Pane Presentation Phone Landscape, Portrait Drawer (push/overlap) Tablet Landscape Docked panel Tablet Portrait Drawer (push/overlap) Desktop Applications OS Version Master Pane Presentation Windows 10 Navigation panel Windows 8 or earlier Docked panel OS X Docked panel - Popover: the master panel is shown in a drop-down menu.
- NavigationPane:the master panel is shown with a minimum width defined in the CollapsedWidth property.
- Custom: custom (for more info, read this link).
- MasterButton: specifies which control will be used to show / hide the master panel. Essential for Drawer, Popover and NavigationPane modes.
The application
In this demo, we will use the TMultiView to create the typical application menu for mobile devices and the application forms will be displayed in a TPanel (named pContent). For this we will put in the main form the following components:
– A TToolBar with Top alignment, the header of the application.
Inside this, we will put 2 buttons, one aligned to the right (bBack button) and one to the left (bMultiview button). The first will do the “back” navigation or it will close the application if there are no screens to show again. To standardize your display according to platform, we will define your StyleLookup property to backtoolbutton. The second will show or hide the application menu. Like the bBack button, we will set the StyleLookup property to drawertoolbuttonbordered to standardize its display according to platform.
– A TMultiView (mvMenu), to which we will set the MasterButton property with the bMultiView button; and the Mode property to Drawer.
Inside the MultiView, we will add two buttons to show a form in each of them.
– A TPanel (pContent) to which we will set the Align property to Client.
To finish the visual work, we will create two forms with some control inside, the one we want, only to be able to check how they are changing. In the example, a button has been placed on each one, on the first form aligned to top and on the second to bottom.
Now that we have the whole design, let’s see the code.
First of all, will be define the “stack” of forms. For this we will use generics and we will declare the private variable FFrmList in the following way:
FFrmList: TObjectList<TCustomForm>;
This list will be created in the constructor of the class and we will destroy it in the destructor.
We will also define two methods for stacking and unstacking forms.
procedure PushForm(AForm: TCustomForm); procedure PopForm;
The first one, PushForm, will receive the form to be stacked by parameter. In addition to stacking it, we will need to clean the area in case to showing a form. This will be done by returning the parent to the original form. Then, it will only be necessary to change the parent of the components of the form passed by parameter.
The second one, PopForm, will erase the current form, and in case there are stacked forms, it will show the last one.
And finally, we will define a method to create the forms.
procedure CreateForm(ClassForm: TFmxObjectClass);
This method will receive by parameter the class of the form to be created. In addition, to avoid duplicated names of objects, it will assign a name based on the system time (something to improve). Once the form is created, it will call to PushForm method.
To finish, we will have to program the OnClick events of the bBack button to unstack the forms, and of the menu buttons to create the forms, which will make a call to the CreateForm method.
Let’s see the code
procedure TForm2.bBackClick(Sender: TObject); begin // if exist any visible form, do a pop if FFrmList.Count > 0 then PopForm else // else, close the application Close; end; procedure TForm2.Button1Click(Sender: TObject); begin // create a child form CreateForm(TForm3); end; procedure TForm2.Button2Click(Sender: TObject); begin // create a child form CreateForm(TForm4); end; constructor TForm2.Create(AOwner: TComponent); begin inherited; FFrmList := TObjectList<TCustomForm>.Create; end; procedure TForm2.CreateForm(ClassForm: TFmxObjectClass); var aForm: TCustomForm; begin inherited; aForm := ClassForm.Create(Self) as TCustomForm; aForm.Name := aForm.Name + FormatDateTime('hhnnssmm', Now); PushForm(aForm); mvMenu.HideMaster; end; destructor TForm2.Destroy; begin if Assigned(FFrmList) then FreeAndNil(FFrmList); inherited; end; procedure TForm2.PopForm; var AForm: TCustomForm; begin // if don't have stack forms, bye bye if FFrmList.Count = 0 then Exit; // we return parent references while pContent.ChildrenCount > 0 do pContent.Children[0].Parent := FFrmList.Items[FFrmList.Count - 1]; // unstack last shown form FFrmList.Delete(FFrmList.Count - 1); // if any form is into the stack if FFrmList.Count > 0 then begin // get last form AForm := FFrmList.Items[FFrmList.Count - 1]; // put new references to the principal container while AForm.ChildrenCount > 0 do AForm.Children[0].Parent := pContent; end; end; procedure TForm2.PushForm(AForm: TCustomForm); begin // if exists an active form, we return the references if FFrmList.Count > 0 then begin while pContent.ChildrenCount > 0 do pContent.Children[0].Parent := FFrmList.Items[FFrmList.Count - 1]; end; // adds new form to the stack FFrmList.Add(AForm); // we assign new references to the principal container while AForm.ChildrenCount > 0 do AForm.Children[0].Parent := pContent; end;
Like usually, here you have the code of the demo.