From dynamics-xpp
Use when authoring a WorkspaceOperational-pattern form in D365 F&O — an activity-focused workspace landing page. As of 10.0.25 the pattern was reorganized to scroll VERTICALLY (no more panorama / horizontal scrolling); content sections are now stacked vertically and use restyled FastTabs. Two variants — standard Operational workspace and Operational workspace w/Tabs (10.0.25+) for organizing into multiple top-level sections.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dynamics-xpp:xpp-pattern-workspace-operationalThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A **workspace** is the primary way users navigate to tasks and
WorkspaceOperational formA workspace is the primary way users navigate to tasks and specific pages. Per MS:
"A workspace should be created for every significant business 'activity' that you want to support. An 'activity' is less granular than a task but more granular than a legacy 'area page.' A workspace is intended to provide a one-page overview of the activity and to help users understand the current status, upcoming workload, and performance of the process or user."
Load dynamics-xpp:xpp-form first if you haven't.
| Pattern | Status | Use it? |
|---|---|---|
| Operational workspace | Current default | YES — standard pattern |
| Operational workspace w/Tabs | Current (added 10.0.25) | YES — when you need multiple top-level tabs (e.g. embedded Power BI) |
| Tabbed workspace | Deprecated | NO — replace with Operational workspace w/Tabs |
| Workspace (old) | OBSOLETE — can't be used after 10.0.25 | NO — migrate away (see Migration below) |
The old "Workspace" pattern used panorama controls with horizontal scrolling. The modern Operational workspace replaced that with vertical FastTab-based layout.
Use for the typical case: one workspace = one activity. Single top-level section structure: action pane → optional filter group → FastTabs with summary tiles, tabbed lists, optional charts, optional PowerBI, and related links.
Use when you need multiple top-level sections within one workspace — e.g. an "Operations" tab + an "Analytics" tab + a "Setup" tab, each containing its own Operational-workspace-style layout, links section, or custom content like embedded Power BI reports.
| Use WorkspaceOperational when... | Use ... instead |
|---|---|
| Building a role-specific landing page with tiles, lists, charts | — |
| The form is a navigation hub, not a data-entry surface | — |
| Multiple disparate views need to live on one screen | — |
| Single-purpose data entry / browsing | Any other pattern |
| Generic "dashboard" without role-specific intent | Reconsider — most "dashboards" become better-shaped Task or ListPage forms |
WorkspaceOperational forms usually have no datasource. The form itself is purely navigation + aggregation; the data lives in the sections it embeds (tile counts, list-fact-boxes, charts) and is pulled by those sub-components, not by the workspace shell.
This is a strong signal: if your workspace would have a datasource, you're probably authoring a ListPage or DetailsMaster instead.
Design
├── Action pane (ActionPane) [Optional]
├── Workspace page filter group (Group) [Optional] ← Workspace Page Filter Group sub-pattern
└── FastTabs (Tab)
├── Section summary tiles (TabPage) ← Section Tiles sub-pattern
├── Section tabbed list (TabPage) ← Section Tabbed List sub-pattern
├── Section charts (TabPage) [Optional] ← Section Stacked Chart sub-pattern
├── Section PowerBI (TabPage) [Optional] ← Section PowerBI sub-pattern
└── Section related links (TabPage) ← Section Related Links sub-pattern
Design
├── Action pane (ActionPane) [Optional]
├── Workspace page filter group (Group) [Optional]
└── StandardTab (Tab)
├── Operational workspace content ← can carry the basic shape above
└── Other content (0..N) ← links section, embedded Power BI, etc.
TabPage.Caption not empty (for all content sections).SectionTiles — tile grid section.SectionTabbedList — tabbed-list section.tab_simpleFastTab — extended style for tab pages.workspace_tileLayout — extended style for tile containers.VerticalTabs — tab styling.These are the rendering hints that turn tabs/groups into the workspace-specific visuals.
The optional Workspace Page Filter Group (the filter strip above the FastTabs)
has several authoring realities that aren't obvious and that a clean create
doesn't catch — only xpp_compile does.
A Workspace-style form may have no data sources (compile errors
BPErrorDataSourceOnWorkspaceStyleForm otherwise). So the page-filter field is
not datasource-bound — it's an unbound control whose ExtendedDataType is
set directly (e.g. a CONRMWorkGroupId). The EDT does double duty: its
tableReferences drive the lookup dropdown (set them, or the dropdown renders
silently dead — see dynamics-xpp:xpp-edt), and the EDT is also what the native
propagation matches against (below).
PatternControlNotAllowed trap)The filter group must satisfy two constraints, and the compiler's
PatternControlNotAllowed names neither (it says only "not allowed at its
current location"):
pattern: "WorkspacePageFilterGroup" (v1.0) — set as the
control's pattern property, NOT in otherProperties (where it's silently
ignored); andTab control — i.e. design controls in the
order [ActionPane, WorkspaceFilterGroup, Tabs].Its prescribed group props: Style=CustomFilter, FrameType=None,
ViewEditMode=Edit, and an empty Caption. Note: write-time
patternConformance.ok can return true while these are still wrong — it does
not currently catch positional/sub-pattern placement, so xpp_compile is the
final arbiter for this control. If you hit PatternControlNotAllowed, check
position (before the Tab) and the first-class pattern property, in that
order.
SectionTabbedList → TabbedList (Tab) → tab pages → each tab page needs a
"Form Part Section List" (a Container/FormPartControl targeting a FormPart form
that must ALREADY exist). Empty/stubbed tabs fail compile
(PatternControlMissingChild). So you cannot scaffold the workspace shell
first and fill the lists later — author the FormPart forms BEFORE the workspace
form, or it won't compile.
Native mechanism (use this when it fits — zero code): F&O auto-propagates the page filter by matching the filter control's EDT against each consumer's data — but only if the matching EDT-typed field is on the ROOT table of that consumer's query/datasource (tiles: the topmost table of the tile's query; FormParts: the root of the part's datasource). If your scope value naturally sits on those root tables — or you can shape the queries so it does — the filter just works, no code. Prefer this.
Escape hatch (only when the native mechanism can't fit): when the scoping
value must live behind an exists join (rooting a consumer on it would
over-count/duplicate on the join fan-out), propagate via code injection through a
session value. The working recipe (verified by live tracing; not in MS docs as a
whole):
tableReferences.public static <EDT> field on a STANDALONE helper
class (not the form — a query's code can't reach a form static; it can reach
MyHelper::FIELD).modified() BEFORE super().
Embedded FormParts requery during the host workspace's super(), which runs
before the workspace's parmFilter() — so priming the static pre-super()
is what makes consumers read the right value on the same interaction. Also
call parmFilter() in the workspace init() to prime on load.QueryRun.init() (the tile count
infra DOES instantiate the QueryRun subclass and honor init()) — inject a
range, or qbds.enabled(false) to drop the scope join when the filter is
blank.implements SysIFilterEventHandler and override
onFilterChanged() → <datasource>_ds.executeQuery() (the framework-native
subscription; cleaner than manually calling research()).[Form]
public class MyWorkspace extends FormRun
{
}
Standard FormRun. Workspaces don't typically have much custom code
in the form class itself — the heavy lifting is in the menu items the
tiles launch.
Workspace tiles are buttons that:
ListPage for the related entity).The count is computed by a Tile Display Method referenced from the tile, which queries the underlying data and returns a number. The menu item driving the tile must exist; the display method must return an int.
SectionTabbedList sections host one or more list fact boxes —
small grids that show a focused slice of data (e.g. "My pending
sales orders" inside an order-processor workspace). Each list
references a query (often a named view) and renders a few key
columns.
Workspaces lean heavily on composite extension components
(AxFormControlExtensionComponentComposite). These are reusable
mini-controls assembled from smaller parts — charts, KPI displays,
filter strips. The example shows the verbose structure for
authoring one.
FormPartControl HeightMode=SizeToAvailable on any section
that uses a FormPartControl to display content.All in the workspace family — each section binds to a specific sub-pattern by name:
See dynamics-xpp:xpp-form-subpatterns for each.
As of version 10.0.25, the workspace patterns were reorganized so content sections stack VERTICALLY and are collapsible. The old panorama / horizontal-scroll layout is gone. Out-of-box workspaces were migrated by MS; user-installed workspaces must be migrated manually.
Run the BP fixer tool from the command line:
c:\AOSService\PackagesLocalDirectory\bin\xppbp.exe ^
-m=<metadataPath> -mu=<moduleName> -me=<modelName> ^
-rules=BPUpgradeMetadataFormPatternVersionNotActive ^
-x=<logFilePath> form:* -packagesRoot=<packagePath> ^
-runfixers
After the tool runs, manually set:
FrameOptionButton=None
(suppress the collapse affordance and the visual line under the
section).WidthMode=SizeToAvailable
(allow the subform to span the page width).Finish with a fit-and-finish review.
For workspaces using the deprecated Workspace pattern:
HubTiles → SectionTilesHubPartLinks → SectionRelatedLinksHubPartGrid → FormPartSectionListStyle=Panorama:
Style to FastTabs.ExtendedStyle to tab_simpleFastTab.FastTabExpanded=Yes on each child tab page.If the base workspace was moved to a newer pattern version, controls added by your form extensions don't auto-pick-up the new metadata. If compile errors appear: open the form extension, save it. For extensions adding new lists or link groups, manually adjust per the mass-update step.
Style=ListExtendedStyle=cardListVisibleColumnsMode=FixedVisibleColumns=0Style=Workspace.xmlns="" rules from dynamics-xpp:xpp-form apply.examples/example-domain.json -- start here. Typed CreateFormRequest for a minimal WorkspaceOperational. Substitute the backing table and field set, then pass to xpp_create_form.examples/example.xml -- structural reference for reading existing forms. Don't hand-author from it; use the typed example above.dynamics-xpp:xpp-form — envelope, namespace rules.dynamics-xpp:xpp-pattern-list-page — what tiles typically launch into.dynamics-xpp:xpp-pattern-details-master, dynamics-xpp:xpp-pattern-details-transaction —
what list-fact-boxes ultimately drill into.xpp://schema/AxForm — authoritative XSD.npx claudepluginhub yobryon/dynamics-tools --plugin xppProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.