Writes S3Dx NoUI (no user interface) commands in C# using BaseFrameworkCommand with CommandUILayout.NoUI. Use when the user asks to create a NoUI command, background command, fire-and-forget command, batch operation command, or any command that runs without a ribbon/dialog UI. Also trigger when they mention CommandUILayout.NoUI, or non-interactive command patterns.
This skill writes NoUI commands — commands that execute without a user interface (no ribbon, no dialog). These range from simple clipboard operations to complex graphic-view interactions and batch processing with progress bars.
Use this skill when the user:
Ask these questions to determine the right pattern: (Must if you dont have proper context)
Does it interact with the graphic view via mouse?
Does it modify the database?
Does it process many objects and need a progress bar?
Does it need to chain to another command afterward?
Does it show a dialog for user input?
Most commands (no graphic interaction):
public MyCommand()
: base(uiLayout: CommandUILayout.NoUI, modal: true, suspendable: false) { }
Graphic view commands (mouse events or selection):
public MyCommand()
: base(uiLayout: CommandUILayout.NoUI,
supportFlags: CommandSupportFlags.Graphic,
suspendable: false)
{
CanSuspendActiveCommand = true;
}
Modal commands cannot have
CommandSupportFlags.Graphic— modal blocks graphic events.
Use the decision flow above.
If the user's input does not clearly describe what the command should do, ask clarifying questions before writing code. Examples:
Do not guess business logic. Get clarity first, then proceed.
Two choices cover almost all cases:
Read noui-command-patterns.md for the full pattern code for each category. Here is the general structure all NoUI commands follow:
public class MyNoUICommand : BaseFrameworkCommand
{
public MyNoUICommand()
: base(uiLayout: CommandUILayout.NoUI, modal: true, suspendable: false) { }
public override void OnStart(int commandID, object argument)
{
base.OnStart(commandID, argument);
// For transactional commands:
CommitUndoMarker = "My Operation";
CommitSuccessMessage = "Success.";
CommitFailureMessage = "Failed.";
using (BOCollection selectedObjects = ClientServiceProvider.SelectSet.SelectedObjects)
{
if (selectedObjects.Count == 0)
{
AbortUnsavedChanges();
return;
}
foreach (BusinessObject bo in selectedObjects)
{
// Modify objects
}
}
CommitUnsavedChanges();
// Framework auto-stops the command when OnStart returns
}
public override void OnStop()
{
// Unsubscribe from events, clean up resources
base.OnStop();
}
}
For non-transactional commands (Category 1), omit the commit markers and
CommitUnsavedChanges().
// Selected objects (always dispose with 'using')
using (BOCollection selected = ClientServiceProvider.SelectSet.SelectedObjects) { }
// Toast notifications
ClientServiceProvider.MessageService.ShowMessage("Message", UxtMessageType.Information);
// Clipboard
ClientServiceProvider.ClipboardService.SetText(text);
Clipboard.SetDataObject(text); // System.Windows.Clipboard — used by CopyPartNumberCommand
// Graphic view
ClientServiceProvider.GraphicViewMgr.ActiveGraphicView;
// Preferences
ClientServiceProvider.Preferences.GetBooleanValue("PrefKey", defaultValue);
ClientServiceProvider.Preferences.SetValue("PrefKey", newValue);
// Site/Plant model
MiddleServiceProvider.SiteMgr.ActiveSite.ActivePlant;
// Standard commit
CommitUnsavedChanges();
// Suppress notification, show custom message
CommitUnsavedChanges(suppressNotification: true);
ClientServiceProvider.MessageService.ShowMessage("Custom msg", UxtMessageType.Information);
// Per-item commit in batch loop (empty undo marker for subsequent commits)
ClientCommandExtensions.CommitChangesWithEmptyUndoMarker(this);
// Abort (rollback) when nothing changed or on error
AbortUnsavedChanges();
All generated code must follow csharp-coding-guidelines.md — covers file header, naming conventions, var usage, braces/formatting, expression style, modifiers, usings, and the single exit point rule.
BOCollection with using statementsCommitUndoMarker before calling CommitUnsavedChanges() — it is requiredStopCommand() in most NoUI commands — the framework auto-stops modal commands when OnStart returns. Only call it in Category 5 (interactive selection) and Category 7 (non-modal dialog) where the command stays alive waiting for user inputCommandSupportFlags.Graphic — graphic events won't fire on modal commandsCanSuspendActiveCommand = true for view manipulation and selection commands