--- name: orchardcore-display-management description: Skill for using Orchard Core's display management system. Covers display drivers, display managers, shapes, display types, shape table providers, placement, and editor/display mode patterns. Use this skill when requests mention Orchard Core Display Management, Create Display Drivers and Shapes, Content Part Display Driver Pattern, View Model Pattern, Display Shape View (Views/{{PartName}}.cshtml), Editor Shape View (Views/{{PartName}}_Edit.cshtml), or closely related Orchard Core implementation, setup, extension, or troubleshooting work. Strong matches include work with OrchardCore.DisplayManagement.Views, OrchardCore.ContentManagement, OrchardCore.ContentManagement.Handlers, DisplayDriver. It also helps with display management examples, Display Shape View (Views/{{PartName}}.cshtml), Editor Shape View (Views/{{PartName}}_Edit.cshtml), Registering a Display Driver, plus the code patterns, admin flows, recipe steps, and referenced examples captured in this skill. license: Apache-2.0 metadata: author: CrestApps Team version: "1.0" --- # Orchard Core Display Management - Prompt Templates ## Create Display Drivers and Shapes You are an Orchard Core expert. Generate display drivers, shapes, and display management code for Orchard Core. ### Guidelines - Every content part that needs custom rendering requires a `DisplayDriver`. - Display drivers inherit from `ContentPartDisplayDriver`. - Drivers handle three operations: `Display`, `Edit`, and `Update`. - Each operation returns `IDisplayResult` (shapes to render). - View models are used to pass data between drivers and views. - Shape names follow the convention `{PartName}` for display and `{PartName}_Edit` for editor. - Use `Initialize` to create shapes with a view model. - Register drivers in `Startup.cs` using `services.AddContentPart().UseDisplayDriver()`. - For non-content-item models rendered through `DisplayDriver`, the root shape still needs its own wrapper template for each display type you build (for example, `CampaignAction.Edit.cshtml` for `CampaignAction_Edit` and `CampaignAction.SummaryAdmin.cshtml` for `CampaignAction_SummaryAdmin`). - When driver results are placed into zones with `.Location("Content:1")`, `.Location("Actions:5")`, or similar, the wrapper template must render those zones (`Model.Content`, `Model.Actions`, `Model.Meta`, etc.) or the child shapes will never appear. - For ALL admin editor views (`*.Edit.cshtml` including `*Settings.Edit.cshtml`), use the `ocat-*` (Orchard Core Admin Theme) CSS classes instead of raw `mb-3`, `form-label`, or hard-coded grid classes. Use `ocat-wrapper` for the outer row, `ocat-label` for labels, `ocat-end` for the input column, `ocat-end-offset` for checkbox-only rows/headings/buttons with no left label, `ocat-limited-wrapper` + `ocat-limited` for narrow-width fields. Do NOT apply these classes to frontend views (Login, Register, etc.) or Admin Menu node editing (needs full width). - The former `@Orchard.GetWrapperClasses()`, `@Orchard.GetLabelClasses()`, `@Orchard.GetEndClasses()` helper methods and `TheAdminThemeOptions` class have been removed. Use the static `ocat-*` CSS classes directly. - Always seal classes. ### Content Part Display Driver Pattern ```csharp using OrchardCore.ContentManagement.Display.ContentDisplay; using OrchardCore.ContentManagement.Display.Models; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; public sealed class {{PartName}}DisplayDriver : ContentPartDisplayDriver<{{PartName}}> { public override IDisplayResult Display({{PartName}} part, BuildPartDisplayContext context) { return Initialize<{{PartName}}ViewModel>("{{PartName}}", model => { model.{{PropertyName}} = part.{{PropertyName}}; model.ContentItem = part.ContentItem; }) .Location("Detail", "Content:5") .Location("Summary", "Content:5"); } public override IDisplayResult Edit({{PartName}} part, BuildPartEditorContext context) { return Initialize<{{PartName}}ViewModel>("{{PartName}}_Edit", model => { model.{{PropertyName}} = part.{{PropertyName}}; model.ContentItem = part.ContentItem; }); } public override async Task UpdateAsync({{PartName}} part, UpdatePartEditorContext context) { var model = new {{PartName}}ViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); part.{{PropertyName}} = model.{{PropertyName}}; return Edit(part, context); } } ``` ### View Model Pattern ```csharp using OrchardCore.ContentManagement; public class {{PartName}}ViewModel { public string {{PropertyName}} { get; set; } public ContentItem ContentItem { get; set; } } ``` ### Display Shape View (Views/{{PartName}}.cshtml) ```cshtml @model {{Namespace}}.ViewModels.{{PartName}}ViewModel

@Model.{{PropertyName}}

``` ### Editor Shape View (Views/{{PartName}}_Edit.cshtml) ```cshtml @model {{Namespace}}.ViewModels.{{PartName}}ViewModel
``` ### Registering a Display Driver ```csharp using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Display.ContentDisplay; public sealed class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) { services.AddContentPart<{{PartName}}>() .UseDisplayDriver<{{PartName}}DisplayDriver>(); } } ``` ### Content Part with Handler Pattern ```csharp using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Handlers; public sealed class {{PartName}}Handler : ContentPartHandler<{{PartName}}> { public override Task InitializingAsync(InitializingContentContext context, {{PartName}} part) { part.{{PropertyName}} = "default value"; return Task.CompletedTask; } } ``` ### Display Types Orchard Core uses display types to differentiate how content is rendered: - `Detail` — Full content display (e.g., a blog post page). - `Summary` — Abbreviated display (e.g., in a list). - `SummaryAdmin` — Admin-specific summary view. - `Edit` — Editor form for the content part. ### Wrapper Templates for `DisplayDriver` **CRITICAL**: When you use `DisplayDriver` (not `ContentPartDisplayDriver`), Orchard Core resolves a **root shape** for each display type. If this root shape template does not exist, you get `InvalidOperationException: The shape type '{ModelName}_Edit' is not found`. You **must** create wrapper templates for every display type the driver builds shapes for. **Required wrapper templates per display type:** | Display type built by driver | Required wrapper template file | |-----|------| | Edit (via `Edit()` / `BuildEditorAsync`) | `Views/{ModelName}.Edit.cshtml` | | SummaryAdmin (via `DisplayAsync` + `Location("Content:1")`) | `Views/{ModelName}.SummaryAdmin.cshtml` | | Detail | `Views/{ModelName}.cshtml` | | Summary | `Views/{ModelName}.Summary.cshtml` | **The wrapper template renders the zones that child shapes are placed into.** If the wrapper doesn't render a zone (e.g., `Model.Content`, `Model.Actions`, `Model.Meta`), the child shapes placed in those zones will be silently dropped. Example driver: ```csharp internal sealed class CampaignActionDisplayDriver : DisplayDriver { public override Task DisplayAsync(CampaignAction model, BuildDisplayContext context) { return CombineAsync( View("CampaignAction_Fields_SummaryAdmin", model) .Location(OrchardCoreConstants.DisplayType.SummaryAdmin, "Content:1"), View("CampaignAction_Buttons_SummaryAdmin", model) .Location(OrchardCoreConstants.DisplayType.SummaryAdmin, "Actions:5") ); } public override IDisplayResult Edit(CampaignAction model, BuildEditorContext context) => Initialize("CampaignActionFields_Edit", m => { }) .Location("Content:1"); } ``` Required wrapper templates: - `Views/CampaignAction.Edit.cshtml` for the root `CampaignAction_Edit` shape. - `Views/CampaignAction.SummaryAdmin.cshtml` for the root `CampaignAction_SummaryAdmin` shape. Minimal edit wrapper: ```cshtml @if (Model.Content != null) { @await DisplayAsync(Model.Content) } ``` Summary admin wrapper with zones: ```cshtml
@if (Model.Content != null) { @await DisplayAsync(Model.Content) } @if (Model.Meta != null) { @await DisplayAsync(Model.Meta) }
@if (Model.Actions != null) { @await DisplayAsync(Model.Actions) }
``` Child shape file-name mapping: - `CampaignAction_Fields_SummaryAdmin` → `Views/Items/CampaignAction.Fields.SummaryAdmin.cshtml` - `CampaignAction_Buttons_SummaryAdmin` → `Views/Items/CampaignAction.Buttons.SummaryAdmin.cshtml` - `CampaignActionFields_Edit` → `Views/CampaignActionFields.Edit.cshtml` ### Placing Shapes in Zones Use `.Location()` to place shapes in zones with positions: ```csharp return Initialize("MyShape", model => { ... }) .Location("Detail", "Content:5") // Detail view, Content zone, position 5 .Location("Summary", "Meta:5") // Summary view, Meta zone, position 5 .Location("SummaryAdmin", "Actions:5"); // Admin summary, Actions zone, position 5 ``` ### Content Field Display Driver ```csharp using OrchardCore.ContentManagement.Display.ContentDisplay; using OrchardCore.ContentManagement.Display.Models; public sealed class {{FieldName}}FieldDisplayDriver : ContentFieldDisplayDriver<{{FieldName}}Field> { public override IDisplayResult Display({{FieldName}}Field field, BuildFieldDisplayContext context) { return Initialize<{{FieldName}}FieldViewModel>( GetDisplayShapeType(context), model => { model.Field = field; model.Part = context.ContentPart; model.PartFieldDefinition = context.PartFieldDefinition; }) .Location("Detail", "Content") .Location("Summary", "Content"); } } ``` ### Shape Table Provider Override shape rendering behavior: ```csharp using OrchardCore.DisplayManagement.Descriptors; public sealed class MyShapeTableProvider : IShapeTableProvider { public ValueTask DiscoverAsync(ShapeTableBuilder builder) { builder.Describe("Content") .OnDisplaying(context => { // Add alternates, wrappers, etc. context.Shape.Metadata.Alternates.Add("Content__{{ContentType}}"); }); return ValueTask.CompletedTask; } } ``` ### Shape Debug Information You can instruct Orchard Core to write HTML comments around rendered shapes. This makes it easier to identify which Razor or Liquid template produced a specific fragment in the page output. Enable during startup: ```csharp services .AddOrchardCms() .AddShapeDebugInformation(); ``` Or enable directly through options: ```csharp services.Configure(options => options.WriteShapeDebugInformation = true); ``` When enabled, rendered shapes are wrapped with HTML comments: ```html ... ``` The start comment contains the shape type and the binding that was used. Razor bindings report the `.cshtml` path, while Liquid bindings report a virtual `.liquid` source. This is useful during development to trace which template is responsible for each fragment of the rendered page.