The Editor control reads standard .NET attributes on your record properties and automatically adjusts how each field is labelled, validated, hidden, or rendered. You annotate the data model once and every form that binds to it picks up the behaviour — no per-field UI code required.
Attributes on the data model are read once by the Editor at render time and map directly to visual field behaviour.
Display Attributes
[Description]
Adds explanatory help text directly below the field, giving users the context they need without cluttering the label.
public record Person
{
[Description("Enter your full legal name as it appears on official documents")]
public string Name { get; init; }
}
The description appears as a subtle hint beneath the input.
[DisplayName]
Overrides the label that would otherwise be derived from the property name. Useful when the code name is abbreviated, technical, or ambiguous.
public record Settings
{
[DisplayName("Enable Email Notifications")]
public bool NotificationsEnabled { get; init; }
}
The field label shows "Enable Email Notifications" instead of the auto-generated "Notifications Enabled".
[Browsable(false)]
Completely hides a property from the rendered form. Internal IDs, computed fields, and implementation details that have no place in a user-facing editor belong here.
public record Entity
{
[Browsable(false)]
public string InternalId { get; init; }
public string Name { get; init; }
}
Only Name appears in the form; InternalId is invisible.
Validation Attributes
[Required]
Marks a field as mandatory. The Editor will show a validation error and prevent submission until the field has a value.
public record User
{
[Required]
public string Email { get; init; }
}
[Range]
Constrains a numeric field to a minimum and maximum. Works for integers, decimals, and doubles.
public record Product
{
[Range(0, 10000)]
public decimal Price { get; init; }
[Range(1, 100)]
public int Quantity { get; init; }
}
Values outside the declared range surface inline validation errors immediately.
[Editable(false)]
Renders a field as read-only — the value is visible but cannot be changed. Perfect for system-assigned fields like order numbers or record IDs that you still want users to see.
public record Order
{
[Editable(false)]
public string OrderNumber { get; init; }
public string Notes { get; init; }
}
OrderNumber is displayed but locked; Notes remains editable.
Control Override Attributes
[UiControl<T>]
Replaces the default control chosen by type inference with a specific control type. Pass options as named parameters when the target control accepts configuration.
public record Preferences
{
[UiControl<TextAreaControl>]
public string Bio { get; init; }
[UiControl<RadioGroupControl>(Options = new[] { "Light", "Dark", "System" })]
public string Theme { get; init; }
}
Bio renders as a multi-line text area; Theme renders as a radio button group.
[Dimension<T>]
Populates a dropdown from a typed data source. Decorate the foreign-key property with the entity type that holds the valid values. The Editor queries all records of that type and presents them as selectable options.
public record Country
{
[Key]
public string Code { get; init; }
public string Name { get; init; }
}
public record Address
{
public string Street { get; init; }
[Dimension<Country>]
public string CountryCode { get; init; }
}
CountryCode renders as a dropdown pre-populated with every Country record in the mesh.
[MeshNode]
Marks a string property as a reference to a mesh node. The Editor renders a searchable MeshNodePickerControl; the selected node's path is stored as the property value. This is the standard way to pick a node — never hand-build a select + search.
public record TaskItem
{
// Multiple queries run in parallel and merge; the user's typed text is appended to each.
[MeshNode("nodeType:User namespace:{node.namespace}")]
public string? AssigneePath { get; init; }
// Compact picker opening upwards, auto-selecting the first result when unset —
// the shape the chat composer uses for its agent/model pickers.
[MeshNode("nodeType:Agent namespace:Agent",
Layout = MeshNodePickerLayout.Thin,
Open = MeshNodePickerOpenDirection.Up,
DefaultToFirst = true)]
public string? AgentPath { get; init; }
}
| Option | Effect |
|---|---|
Queries (ctor args) |
Query strings (see Query Syntax) run in parallel and merged. Template variables {node.namespace} / {node.path} resolve against the editing context at render time; {node.PropertyName} resolves against the bound object. |
Layout |
Default (full card: avatar, name, node-type subtitle) or Thin (small icon + name, minimal padding for tight rows). |
Open |
Down (default) or Up — open the dropdown above the field when it is anchored to the bottom of the viewport. |
DefaultToFirst |
Opt-in: when no value is set, auto-select (and persist) the first available result. |
The read-only rendering is a plain label showing the stored path.
[MeshNodeCollection]
The collection counterpart of [MeshNode]: marks a collection property as holding mesh-node references. The Editor renders a full-width inline collection section — existing entries as chips, with add/remove actions when the property is editable. Queries use the same syntax and template variables as [MeshNode].
public record Team
{
[MeshNodeCollection("nodeType:User namespace:{node.namespace}")]
public ImmutableList<string> MemberPaths { get; init; } = [];
}
[Markdown]
Renders a string property as markdown: MarkdownControl for display, MarkdownEditorControl for editing (own edit button by default — SeparateEditView = true).
public record Article
{
[Markdown(EditorHeight = "400px", ShowPreview = true, TrackChanges = false,
Placeholder = "Write the article body…")]
public string Body { get; init; } = "";
}
| Option | Default | Effect |
|---|---|---|
EditorHeight |
"300px" |
Height of the editor area |
ShowPreview |
true |
Side-by-side preview while editing |
TrackChanges |
false |
Enable tracked-changes annotations |
Placeholder |
"Enter content…" | Hint shown when empty |
[ContentItem]
Marks a string property as a reference to a file in a content collection (image URL, attachment, …). The Editor renders a text field with a Browse button that opens a modal file browser over the node's content collection.
public record Profile
{
[ContentItem] // browses the default "content" collection
public string? AvatarUrl { get; init; }
[ContentItem("uploads")] // browse a specific collection
public string? AttachmentPath { get; init; }
}
Default Type-to-Control Mapping
When no override attribute is present, the Editor picks the most appropriate control for each property type:
| Property Type | Default Control |
|---|---|
string |
TextFieldControl |
int, double, decimal |
NumberFieldControl |
bool |
CheckBoxControl |
DateTime |
DateTimeControl |
These defaults apply only when no override attribute is present — [UiControl<T>], [Dimension<T>], [MeshNode], [MeshNodeCollection], [Markdown], and [ContentItem] all take precedence.
Combining Attributes
Attributes compose freely. Stack display, validation, and control-override attributes on the same property to express exactly the behaviour you need.
public record Employee
{
[Required]
[Description("Full name as it appears on official documents")]
public string FullName { get; init; }
[Required]
[Description("Work email address")]
public string WorkEmail { get; init; }
[Range(18, 100)]
[Description("Must be at least 18")]
public int Age { get; init; }
[Browsable(false)]
public string InternalCode { get; init; }
}
Live Example
The snippet below renders a quick reference card summarising which attribute controls which aspect of a field. It runs directly in the kernel so you can experiment by modifying the markup.
MeshWeaver.Layout.Controls.Stack
.WithView(MeshWeaver.Layout.Controls.Markdown("### Attribute Quick Reference"))
.WithView(MeshWeaver.Layout.Controls.Markdown(
"| Attribute | Effect |\n" +
"|---|---|\n" +
"| `[Description(\"...\")]` | Help text below the field |\n" +
"| `[DisplayName(\"...\")]` | Custom field label |\n" +
"| `[Browsable(false)]` | Hides the field entirely |\n" +
"| `[Required]` | Field must have a value |\n" +
"| `[Range(min, max)]` | Numeric bounds validation |\n" +
"| `[Editable(false)]` | Read-only display |\n" +
"| `[UiControl<T>]` | Override rendered control type |\n" +
"| `[Dimension<T>]` | Dropdown from data source |\n" +
"| `[MeshNode(\"query\")]` | Searchable mesh-node picker (stores the path) |\n" +
"| `[MeshNodeCollection(\"query\")]` | Inline chip collection of node references |\n" +
"| `[Markdown]` | Markdown display + editor |\n" +
"| `[ContentItem]` | Text field + Browse over a content collection |"
))
See Also
- Editor Control — how these attributes are consumed when rendering forms
- DataBinding — how data flows into and out of the Editor