orchardcore-display-management
Scannednpx machina-cli add skill CrestApps/CrestApps.AgentSkills/orchardcore-display-management --openclawOrchard 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<TPart>. - Drivers handle three operations:
Display,Edit, andUpdate. - 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}_Editfor editor. - Use
Initialize<TModel>to create shapes with a view model. - Register drivers in
Startup.csusingservices.AddContentPart<TPart>().UseDisplayDriver<TDriver>(). - Always seal classes.
Content Part Display Driver Pattern
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<IDisplayResult> 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
using OrchardCore.ContentManagement;
public class {{PartName}}ViewModel
{
public string {{PropertyName}} { get; set; }
public ContentItem ContentItem { get; set; }
}
Display Shape View (Views/{{PartName}}.cshtml)
@model {{Namespace}}.ViewModels.{{PartName}}ViewModel
<p>@Model.{{PropertyName}}</p>
Editor Shape View (Views/{{PartName}}_Edit.cshtml)
@model {{Namespace}}.ViewModels.{{PartName}}ViewModel
<div class="mb-3">
<label asp-for="{{PropertyName}}" class="form-label">{{DisplayLabel}}</label>
<input asp-for="{{PropertyName}}" class="form-control" />
<span asp-validation-for="{{PropertyName}}" class="text-danger"></span>
</div>
Registering a Display Driver
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
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.
Placing Shapes in Zones
Use .Location() to place shapes in zones with positions:
return Initialize<MyViewModel>("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
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:
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;
}
}
Source
git clone https://github.com/CrestApps/CrestApps.AgentSkills/blob/main/src/CrestApps.AgentSkills/orchardcore/orchardcore-display-management/SKILL.mdView on GitHub Overview
This skill teaches how to implement Orchard Core's display management system, covering display drivers, display managers, shapes, display types, shape table providers, placement, and editor/display mode patterns. It explains how to create a custom DisplayDriver<TPart>, bind data to view models, name shapes, and register drivers so parts render in chosen zones and modes.
How This Skill Works
A DisplayDriver overrides Display, Edit, and Update to return shapes via IDisplayResult. Shapes are created with Initialize<TModel> to bind a view model and are placed using Location calls (e.g., Detail, Summary). Names follow the convention {PartName} for display and {PartName}_Edit for editor. Classes should be sealed, and drivers are registered in Startup.cs with services.AddContentPart<TPart>().UseDisplayDriver<TDriver>().
When to Use It
- When a content part requires bespoke visuals beyond default rendering
- When you need to render a part in multiple display zones (Detail, Summary) with different layouts
- When editing a part you want a tailored editor UI via a dedicated ViewModel
- When you want to bind user input from the editor back to the part (UpdateAsync)
- When wiring up and registering the display driver in Startup for a new Part
Quick Start
- Step 1: Create a sealed DisplayDriver class that inherits ContentPartDisplayDriver<YourPart> and implement Display, Edit, and Update methods
- Step 2: In Display, return Initialize<YourViewModel>("YourPart", model => { ... }).Location("Detail", "Content:5").Location("Summary", "Content:5"); and in Edit return Initialize<YourViewModel>("YourPart_Edit", ...)
- Step 3: Register the driver in Startup.cs with services.AddContentPart<YourPart>().UseDisplayDriver<YourPartDisplayDriver>();
Best Practices
- Seal all DisplayDriver classes to prevent unintended inheritance
- Implement Display, Edit, and Update and return IDisplayResult for each
- Use Initialize<TModel> to create shapes and bind a ViewModel
- Follow shape naming conventions: {PartName} for Display and {PartName}_Edit for Editor
- Register the driver in Startup.cs with services.AddContentPart<TPart>().UseDisplayDriver<TDriver>()
Example Use Cases
- BlogPostPart: Display shows Title and Summary in Detail and Summary views; Editor uses BlogPostPart_Edit shape for editing
- ProductPart: Display renders Price and Availability; Editor allows updating Price via a dedicated view model
- EventPart: Display includes EventDate and Location; Editor provides a tailored form for editing event details
- NewsletterSubscriberPart: Display shows Email; Editor enables editing the subscriber email in a structured editor shape
- AuthorProfilePart: Display combines Avatar and DisplayName in the profile view, with an editor for updating profile fields