Forms, Menus, and UI Patterns
Build user interfaces in D365 F&O β form creation, form patterns (Details Master, Simple List, Workspace), form controls, data sources, menus, menu items (Display/Output/Action), and the label system for localisation.
Forms are how users see your data
Every table, view, and entity youβve built so far is invisible to users until you create a form. Forms are the AOT objects that render the browser-based UI in D365 Finance & Operations.
Think of a restaurant menu and ordering system.
The kitchen (database) has all the ingredients (data). But customers donβt walk into the kitchen β they sit at a table and use a menu (the form) to see whatβs available, make choices, and place orders. The waiter (form data source) carries requests back and forth between the customer and the kitchen.
The restaurant also follows a layout standard β every menu has starters at the top, mains in the middle, desserts at the bottom. This is a form pattern. It means customers always know where to look, no matter which restaurant they visit.
D365 F&O forms work the same way. They follow standard patterns (Detail Master, Simple List, Workspace) so that every screen in the system feels consistent. Users learn one layout, and it works everywhere.
Form patterns
Form patterns are mandatory layout templates. When you create a form, you apply a pattern, and Visual Studio validates that your form structure matches the patternβs requirements.
Major form patterns
| Pattern | Use Case | Structure |
|---|---|---|
| Details Master | Viewing/editing a single record with full detail | Navigation list (left) + detail view (right) with header and tab-based FastTabs |
| Details Transaction | Header-line transactions (sales orders, purchase orders) | Header fields at top + line grid below with line details |
| Simple List | Flat list of records (setup tables, reference data) | Grid with inline editing, no separate detail view |
| Simple List and Details | List with a detail pane | Grid on left + detail fields on right/below |
| List Page | Browse and filter records before opening details | Full-width grid with filter pane, preview pane, FactBoxes |
| Workspace | Role-based dashboards with tiles, lists, and links | Tiles + tabbed lists + links + charts β configurable by user |
| Dialog | Modal input forms (confirmations, parameters) | Simple field layout with OK/Cancel buttons |
| Drop Dialog | Quick input that drops down from a button | Minimal fields, compact layout, no modal overlay |
| Lookup | Lookup popup from a field | Grid showing lookup results with search |
Sub-patterns
Sub-patterns apply to sections within a form, not the whole form:
- Fields and Field Groups β standard field layout within a group
- Toolbar and List β action buttons above a grid
- Tabular Fields β multi-column field layout
- Filters and Toolbar β filter fields + action buttons above content
- Section Tiles β tile layout for workspace sections
Exam tip: pattern compliance
Visual Studio shows a pattern indicator on each form node. Green = compliant, yellow = missing optional elements, red = required elements missing.
The exam may ask: βA developer creates a form but sees red pattern violations. What should they check?β Answer: the formβs control hierarchy doesnβt match the patternβs required structure β missing an ActionPane, wrong group nesting, or controls in the wrong order.
Form data sources
A formβs data sources connect it to tables, views, or queries.
Key data source properties
| Property | Purpose | Common Values |
|---|---|---|
| Table | Which table/view to bind | Any AOT table or view |
| Index | Which index to use for initial sort | Typically the primary index |
| AllowEdit | Whether users can edit records | Yes / No |
| AllowCreate | Whether the New button works | Yes / No |
| AllowDelete | Whether the Delete button works | Yes / No |
| InsertIfEmpty | Auto-create a blank record if the data source is empty | Yes (for parameter forms) / No |
| JoinSource | Parent data source for master-detail | References another data source on the same form |
| LinkType | How child data source relates to parent | Active (filter by selected parent), Delayed, InnerJoin, OuterJoin, ExistJoin |
Linking data sources (master-detail)
// Form: AxionInspectionDetails
// Data Sources:
// AxionInspectionTable (primary β the header)
// JoinSource: (none β root data source)
// AxionInspectionLine (child β the lines)
// JoinSource: AxionInspectionTable
// LinkType: Active
// (relation: InspectionId == InspectionId β auto-resolved from table relation)
When the user selects an inspection header, the lines data source automatically filters to show only that inspectionβs lines. This is Active linking β the child refreshes whenever the parent selection changes.
Scenario: Sophie's form data source confusion
Sophie Chen at Ferris Industries creates a form with two data sources β Products and ProductVariants. She sets the JoinSource but forgets to set the LinkType. The form shows ALL variants regardless of which product is selected.
Mentor Carl explains: βWithout LinkType = Active, the child data source doesnβt know to filter when the parent selection changes. Set LinkType to Active for master-detail relationships where the child should refresh on parent selection.β
Form controls
Controls are the visual elements on a form β buttons, fields, grids, tabs.
Common control types
| Control | Purpose | Example |
|---|---|---|
| StringEdit | Text input field | Customer name, description |
| IntEdit / RealEdit | Number input | Quantity, amount |
| DateEdit | Date picker | Order date, due date |
| ComboBox | Dropdown (bound to enum) | Status, type |
| CheckBox | Boolean toggle | Active, approved |
| Grid | Tabular data display | Order lines, item list |
| ActionPane | Ribbon-style toolbar | New, Delete, Post, Print buttons |
| FastTab (Tab) | Collapsible sections | General, Financial, Delivery tabs |
| FormPart | Embedded FactBox or sub-form | Related records, quick info |
| MenuButton | Dropdown menu on action pane | Functions menu, Inquiries menu |
Binding controls to data
Controls bind to data source fields automatically when you drag a field from the data source onto the design. The control type is determined by the fieldβs EDT:
strβ StringEditintβ IntEditTransDateβ DateEdit- Enum β ComboBox
Menu items: the navigation system
Users donβt open forms directly β they click menu items. A menu item is a pointer that opens a form, runs a report, or triggers an action.
Three types of menu items
| Type | Icon | What It Opens | Examples |
|---|---|---|---|
| Display | π | A form for viewing/editing data | All Customers, Sales Orders, Inspection List |
| Output | π¨οΈ | A report (SSRS) or output | Invoice print, Packing slip report |
| Action | β‘ | Runs a class (batch job, process) | Post invoice, Recalculate prices, Close period |
Menu item properties
| Property | Purpose |
|---|---|
| Object | The form, report, or class this menu item opens |
| ObjectType | Form, Class, or SSRSReport |
| Label | What the user sees (should use a label reference) |
| NormalImage | Icon displayed in menus and tiles |
| SecurityKey | Security privilege required to access this menu item |
| RunOn | Where the code executes β Client, Server, or CalledFrom |
Exam tip: menu item security
Menu items are the entry points for the D365 security model. Privileges grant access to menu items, roles contain privileges.
Exam pattern: βUsers in the Quality Inspector role cannot see the Inspection List form.β The fix is to check that the role includes a privilege that grants access to the AxionInspectionList Display menu item. Security is based on menu items, not forms.
Menus
Menus organise menu items into the navigation structure:
- Main Menu β appears in the module navigation
- Submenus β group related items
- Menu Item References β pointers to Display/Output/Action menu items
// Menu: AxionQualityModule
// Submenu: Inspections
// Display Menu Item: AxionInspectionList
// Display Menu Item: AxionInspectionDetails
// Submenu: Setup
// Display Menu Item: AxionInspectionTypes
// Display Menu Item: AxionInspectionParameters
// Submenu: Reports
// Output Menu Item: AxionInspectionReport
// Submenu: Periodic
// Action Menu Item: AxionCloseInspectionPeriod
Labels: the localisation system
Labels are the way D365 F&O handles multi-language text. Instead of hardcoding strings like βCustomerβ or βSave,β you create a label and reference it everywhere.
Label syntax
// Label file: @AxionLabels
// Label ID: @AxionLabels:InspectionId
// English (en-us): "Inspection ID"
// German (de): "PrΓΌfungs-ID"
// French (fr): "ID d'inspection"
// Usage in AOT:
// EDT Label property: @AxionLabels:InspectionId
// Menu Item Label: @AxionLabels:InspectionList
// Usage in X++ code:
info("@AxionLabels:InspectionId");
// At runtime, displays in the user's language
Label best practices
| Do | Donβt |
|---|---|
| β Create a label file per model | β Reuse Microsoftβs @SYS labels for custom text |
| β Use descriptive label IDs | β Use label IDs like @AxionLabels:Label001 |
| β One label per unique concept | β Create duplicate labels for the same text |
β
Use %1, %2 for dynamic values | β Concatenate strings with labels |
| β Translate all labels before go-live | β Leave labels in developer language only |
The @SYS prefix
Microsoftβs standard labels use the @SYS prefix β for example, @SYS14352 means βDescriptionβ in every language. You can reference @SYS labels in your objects, but never modify them.
Exam tip: labels are exam-relevant
The MB-500 exam explicitly tests label knowledge:
- Every user-visible string must use a label β never hardcode text
- Labels are stored in .label files per language in your model
- @SYS labels are Microsoftβs β reference them but donβt modify
- Label search: In Visual Studio, use the Label Editor to find existing labels before creating new ones
- String parameters: Use
strFmt("@AxionLabels:InspectionCount", numInspections)with%1placeholders β never concatenate translated strings
If a question asks βhow to display a form title in the userβs language,β the answer always involves labels.
Putting it together: form development checklist
When Vik builds a new form at Axion, he follows this flow:
- Choose the pattern β Details Master, Simple List, etc.
- Add data sources β primary table, set JoinSource and LinkType for children
- Apply the pattern β right-click form Design β Apply Pattern
- Build the layout β add ActionPane, groups, tabs per pattern requirements
- Drag fields β from data source, let EDT determine control type
- Create menu items β Display for the form, Action for any process buttons
- Add to a menu β so users can navigate to the form
- Label everything β use existing @SYS or create @Axion labels
- Set security β configure privileges on the menu items
- Test pattern compliance β green indicator = ready
Scenario: Elena's workspace form
Elena Vasquez at PacificForge Manufacturing builds a Quality Manager workspace. CTO Gregor wants: a tile showing overdue inspections, a list of todayβs inspections, and quick links to setup forms.
Elena applies the Workspace pattern:
- Section Tiles: Overdue Inspection Count (tile with a query counting overdue records)
- Section Tabbed List: Todayβs Inspections (form part showing a filtered list)
- Section Related Links: Setup links (menu item references to parameter and setup forms)
The workspace pattern handles responsive layout automatically β tiles wrap on smaller screens, lists stack vertically.
Vik needs to create a form for viewing and editing individual inspection records. The form should show a list of inspections on the left and full details (with multiple FastTabs) on the right. Which form pattern should he apply?
Sophie creates a form with two data sources β InspectionHeader and InspectionLines. She sets InspectionLines.JoinSource to InspectionHeader but when she selects different headers, the lines grid always shows all lines from all inspections. What did she miss?
Nikhil hardcodes the string 'Inspection saved successfully' in an info() call in his X++ code. A German-speaking user reports that the message still appears in English. What should Nikhil have done instead?
A security administrator reports that users in the 'Quality Inspector' role can't access the Inspection List form. The form exists and works for admins. Where should the developer check first?
Next up: Classes, Events, and Extensions β create X++ classes, extend standard behaviour with event handlers, and use attributes for metadata.