Every FormCast surface is documented here. Variable functions
(@FORM*) return a value through TCC's variable expansion; commands
(plain FORMEVENTS etc.) write to stdout for do ... in /p
consumption. All names are case-insensitive.
his a form handle from@FORMOPEN(e.g.L:1234:5).ctrlis the control id you supplied to@FORMADD. Use.to address the form itself (form-level properties).- Numeric error codes returned in the function buffer:
20100invalid handle20101bad arguments20102unknown control type20103unknown control id20104unknown form property20105I/O failure20106JSON parse failure20107unknown @FORMSIMULATE action
Allocate a new form descriptor. Returns the handle string on success or empty buffer on failure.
Close the form, fire any bound close event, and tear down the
realized window. Returns 0 on success.
Realize the form and display it. mode is empty/visible for
non-modal, modal for modal-with-no-auto-close, modal:N for modal
with an N-millisecond WinForms.Timer auto-dismiss. Returns 0
non-modal, the integer DialogResult for modal.
Plugin version string (e.g. 1.0.0).
Render the form to a PNG file at path. The form is realized
lazily if needed but never displayed (off-screen rendering via
Control.DrawToBitmap). Returns 0 on success.
Render multiple realized forms into a single composite PNG. Each form is drawn at its current screen position relative to the bounding rectangle of all supplied forms. Gaps between windows are transparent. Useful for capturing multi-window layouts as a single screenshot.
Example (capture the designer's three windows):
set RC=%@formsavecomposite[%TEMP\designer.png,%hTool,%hCanvas,%hProps]
The README screenshots are generated by
examples/screenshots/capture-readme-images.btm
which uses @FORMSAVECOMPOSITE for the designer composite image.
Returns 0 on success.
Move foreground window focus to a realized form, or pass the
literal string TCC to focus the TCC console window. Returns
0 on success.
Send a raw Win32 message to the realized form's HWND. msg
accepts decimal or 0x-prefixed hex (e.g. 0x10 for
WM_CLOSE). Returns the LRESULT as a decimal string.
Return the control id at the given form-relative pixel position, or empty string if nothing is there. Used by the designer to map mouse clicks to controls.
Write a value into the BTM caller's variable scope via the
SetEVariable P/Invoke. Pass an empty value to delete the
variable. Returns 0 on success.
Execute a TCC command on the CallbackWorker thread via
TakeCmd.Command. The entire argument string (including commas)
is passed as-is to TCC. Returns the TCC command's return code.
Add a control to the form. Recognized TYPEs:
Basic: LABEL, EDIT, BUTTON, CHECKBOX, RADIO, LINKLABEL,
MASKEDTEXTBOX, PICTUREBOX, TOGGLE
Numeric/Date: NUMERICUPDOWN, DOMAINUPDOWN, TRACKBAR,
PROGRESSBAR, DATETIMEPICKER, MONTHCALENDAR
List/Tree/Grid: LISTBOX, CHECKEDLISTBOX, COMBOBOX,
LISTVIEW, TREEVIEW, DATAGRID
Text: MEMO, RICHMEMO
Container: PANEL, GROUPBOX, SPLITCONTAINER, FLOWPANEL,
TABLEPANEL, TABCONTROL, TABPAGE
App chrome: MENUSTRIP, TOOLBAR, STATUSBAR, CONTEXTMENU,
SEPARATOR (toolbar child only)
Specialized: WEBBROWSER, PROPERTYGRID, HSCROLLBAR, VSCROLLBAR
Use the slash-id form (panel1/btn) to add a control nested inside
any container type.
Set a property on the form (ctrl=.) or a control. Universal props:
type, id, x, y, width, height, text. Per-type pseudo-props
documented below. Unknown props land in the control's prop bag for
forward compatibility.
Live-update behavior: on a realized (shown) form, the following props update the visible control immediately:
text-- labels, buttons, edits, and other text-bearing controlsvalue,min,max,checked-- ProgressBar, TrackBar, NumericUpDown, ScrollBar, CheckBox, Toggleposition(x:y),size(w:h) -- control boundssplitterdistance-- SplitContainer divider position
Read a property. Empty buffer on miss. selecteditem (LISTVIEW),
text (RICHMEMO), position, size are computed reads.
GROUPBOX is a container like PANEL but with a titled border.
Critical for radio button grouping: WinForms only allows one
selected RadioButton per parent container, so two groups of radios
on the same form need two GROUPBOXes. Uses the same slash-id child
syntax as PANEL.
COMBOBOX is a dropdown selection control. Items are added via the
additem pseudo-prop (same verb as LISTVIEW, stored as _cb.item.N
internally).
| Prop | Effect |
|---|---|
additem |
append an item to the dropdown list |
style |
dropdown (editable, default), list (non-editable), simple (always-open list) |
selectedindex |
0-based index of the pre-selected item |
Read via @FORMGET[h,cmb,selecteditem] to get the currently selected
text.
TABCONTROL holds TABPAGE children. Each tab page has its own
child controls. Use the slash-id syntax to nest:
%@formadd[%h,tabs,TABCONTROL,8,8,400,260,]
%@formadd[%h,tabs/general,TABPAGE,0,0,0,0,General]
%@formadd[%h,tabs/general/lbl,LABEL,12,12,200,20,Option:]
%@formadd[%h,tabs/advanced,TABPAGE,0,0,0,0,Advanced]
TABPAGE position/size are ignored (the TabControl manages layout).
The text argument becomes the tab label.
Numeric spinner control. Props:
| Prop | Effect |
|---|---|
min |
minimum value (default 0) |
max |
maximum value (default 100) |
value |
current value (default 0) |
decimals |
decimal places (default 0 for integer) |
Fires a change event with the new value on every spin.
Date/time selection control. Props:
| Prop | Effect |
|---|---|
format |
long (default), short, time, custom |
customformat |
format string when format=custom (e.g. yyyy-MM-dd) |
Fires a change event with the new value in ISO 8601 format.
A label with a clickable hyperlink. Set the url prop to the target:
%@formadd[%h,link,LINKLABEL,12,12,200,20,Visit our website]
%@formset[%h,link,url,https://example.com]
Fires a click event when the link is clicked. The URL opens in
the default browser.
Display an image from a file path. Set the image prop:
%@formadd[%h,img,PICTUREBOX,12,12,200,150,]
%@formset[%h,img,image,C:\path\to\logo.png]
Supports PNG, JPG, BMP, GIF. Uses Zoom size mode (aspect-preserving).
Horizontal or vertical slider.
| Prop | Effect |
|---|---|
min |
minimum value (default 0) |
max |
maximum value (default 100) |
value |
current value (default 0) |
tickfrequency |
tick mark interval (default 10) |
orientation |
horizontal (default) or vertical |
Fires a change event with the new value on every slide.
Simple scrollable list without columns. Simpler than LISTVIEW when you
just need a plain list of strings. Items added via additem.
Fires a change event with the selected item text on selection change.
ListBox where each item has a checkbox. Items added via additem.
Fires a change event with index:true or index:false on each
check/uncheck.
TextBox with an input mask. Set the mask prop:
%@formadd[%h,phone,MASKEDTEXTBOX,12,12,200,24,]
%@formset[%h,phone,mask,(000) 000-0000]
Standard WinForms mask characters: 0=digit, L=letter, A=alphanumeric.
Visual month-view calendar. Fires a change event with the selected
date in yyyy-MM-dd format.
Hierarchical tree with expand/collapse. Nodes are added via addnode
with path:text format:
%@formset[%h,tree,addnode,root:Root Node]
%@formset[%h,tree,addnode,root/child1:First Child]
%@formset[%h,tree,addnode,root/child2:Second Child]
%@formset[%h,tree,addnode,root/child1/leaf:Leaf Node]
The path uses / as a separator; intermediate nodes are created
automatically. addnode works both before and after @FORMSHOW
(live-apply). To clear all nodes, set text to empty:
%@formset[%h,tree,text,]. Fires a change event with the selected
node name.
Resizable divider between two panels. The first two children go into Panel1 and Panel2 respectively. Props:
| Prop | Effect |
|---|---|
orientation |
vertical (left/right, default) or horizontal (top/bottom) |
splitterdistance |
initial position of the splitter in pixels (also live-settable after @FORMSHOW) |
An on/off slider switch control. Fires a change event with true
or false when toggled. Read state with @FORMGET[h,tgl,checked].
%@formadd[%h,tgl,TOGGLE,12,12,50,24,]
%@formset[%h,tgl,checked,true]
A visual separator for use as a child of TOOLBAR. Equivalent to
using text "-" on a LABEL child, but expressed as its own type:
%@formadd[%h,tb/sep1,SEPARATOR,0,0,0,0,]
These props work on any control (or on the form itself via ctrl=.)
through @FORMSET:
| Prop | Format | Effect |
|---|---|---|
backcolor |
named color or #RRGGBB |
background color (e.g. Blue, DarkBlue, Highlight, #336699) |
forecolor |
named color or #RRGGBB |
foreground/text color (e.g. HighlightText, #FFFFFF) |
font |
family:size[:style] |
font face, size, optional style (e.g. Segoe UI:11:bold, Consolas:10:italic) |
Named colors include all System.Drawing.Color names plus the
system colors Highlight, HighlightText, Window, WindowText,
Control, ControlText, etc.
Font inheritance: ApplyFontRecursive propagates a form-level font
to all child controls that have not been individually styled, matching
the .NET Framework 4.8 font inheritance model.
| Prop | Where | Effect |
|---|---|---|
theme |
form (.) |
system (default), dark, or light. Applies live -- switching theme re-paints the form and all controls immediately. Dark mode sets the DWM title bar attribute for a native dark title bar on Windows 10+. |
darkmode |
form (.) |
Legacy alias: %@formset[%h,.,darkmode,1] is equivalent to %@formset[%h,.,theme,dark]. |
| Prop | Where | Effect |
|---|---|---|
acceptbutton |
form (.) |
Wire the Enter key to a named button: %@formset[%h,.,acceptbutton,btnOK] |
cancelbutton |
form (.) |
Wire the Escape key to a named button: %@formset[%h,.,cancelbutton,btnCancel] |
Designer keyboard shortcuts (active when design_mode=1):
| Key | Action |
|---|---|
| Arrow keys | Nudge selected control(s) by grid size (or 1px if grid off) |
| Delete | Delete selected control(s) |
| Ctrl+Z | Undo |
| Ctrl+Y | Redo |
| Ctrl+C | Copy |
| Ctrl+X | Cut |
| Ctrl+V | Paste |
| Ctrl+S | Save |
| Ctrl+N | New canvas |
| Ctrl+O | Open template |
| Escape | Deselect all |
FormCast includes a library of 216 built-in icons across 16 categories (File, Edit, Navigation, Actions, Formatting, Status, Objects, Controls, Alignment, Layout, Arrows, Media, Development, Communication, Shapes, Misc).
Apply a stock icon to any image-capable control (Button, Label, PictureBox, CheckBox, RadioButton, LinkLabel):
%@formset[%h,btn,stockicon,FileNew]
%@formset[%h,lbl,stockicon,StatusInfo]
List all stock icons with their categories, one per line via stdout.
Optional filter limits the output to icons matching the filter
string. Consume with do ... in /p:
do icon in /p formicons
echo %icon
enddo
do icon in /p formicons File
echo %icon
enddo
| Prop | Where | Effect |
|---|---|---|
anchor |
any control | Resize anchoring: top+bottom+left+right (any combination). Controls anchored to opposite edges stretch with the parent. |
autoscroll |
PANEL | Enable auto-scrolling when child controls exceed the panel bounds: %@formset[%h,panel1,autoscroll,1] |
textfromfile |
any text control | Set the control's text from a file path, bypassing TCC variable expansion: %@formset[%h,memo,textfromfile,C:\data\log.txt] |
textfromvar |
any text control | Set the control's text from a TCC variable name (read at set-time): %@formset[%h,lbl,textfromvar,MYVAR] |
designtarget |
PROPERTYGRID | Bind a PropertyGrid to a control or form descriptor for live editing: %@formset[%h,pg,designtarget,btnOK] |
A menu bar at the top of the form. Children (via slash-id) become
top-level menus; grandchildren become menu items. Text "-" creates
a separator. Each item fires a click event on the per-form queue.
%@formadd[%h,menu,MENUSTRIP,0,0,0,0,]
%@formadd[%h,menu/file,LABEL,0,0,0,0,File]
%@formadd[%h,menu/file/open,LABEL,0,0,0,0,Open...]
%@formadd[%h,menu/file/save,LABEL,0,0,0,0,Save]
%@formadd[%h,menu/file/sep1,LABEL,0,0,0,0,-]
%@formadd[%h,menu/file/exit,LABEL,0,0,0,0,Exit]
%@formadd[%h,menu/edit,LABEL,0,0,0,0,Edit]
%@formadd[%h,menu/edit/copy,LABEL,0,0,0,0,Copy]
%@formadd[%h,menu/edit/paste,LABEL,0,0,0,0,Paste]
%@formbind[%h,open,click,gosub :on_open]
%@formbind[%h,exit,click,gosub :on_exit]
Position/size args on menu children are ignored.
Same item model as MENUSTRIP but attached as a right-click menu.
Set the form-level contextmenu prop to the CONTEXTMENU control's id
to attach it to the form background.
Real toolbar with proper styling. Children with type BUTTON become
ToolStripButtons; type SEPARATOR or text "-" creates a separator;
type LABEL creates a ToolStripLabel. Each button fires a click
event.
Toolbar buttons use click-through activation: clicking a button fires immediately even when the parent form is not the active window. This is essential for multi-window apps (like the visual designer) where the user selects a control on one form then clicks a toolbar button on another without needing to click twice.
%@formadd[%h,tb,TOOLBAR,0,0,0,0,]
%@formadd[%h,tb/btnNew,BUTTON,0,0,0,0,New]
%@formadd[%h,tb/sep1,LABEL,0,0,0,0,-]
%@formadd[%h,tb/btnOpen,BUTTON,0,0,0,0,Open]
%@formbind[%h,btnNew,click,gosub :on_new]
Status bar at the bottom of the form. Children become
ToolStripStatusLabel panels. Set spring=1 on a child to make it
fill remaining space.
%@formadd[%h,sb,STATUSBAR,0,0,0,0,]
%@formadd[%h,sb/msg,LABEL,0,0,0,0,Ready]
%@formset[%h,sb/msg,spring,1]
%@formadd[%h,sb/pos,LABEL,0,0,0,0,Ln 1 Col 1]
Full spreadsheet-like grid. Columns via addcolumn name:width:type
(types: text, checkbox, combobox, button, link, image).
Rows via addrow cell0:cell1:....
| Prop | Effect |
|---|---|
addcolumn |
add a column (name:width:type) |
addrow |
add a row (cell0:cell1:...) |
readonly |
1 makes the grid read-only |
allowaddrows |
1 allows the user to add rows |
allowdeleterows |
1 allows the user to delete rows |
Auto-flowing container. Children are laid out left-to-right (or top-to-bottom) and wrap automatically. Props:
| Prop | Effect |
|---|---|
direction |
lefttoright (default) or toptobottom |
nowrap |
1 disables wrapping |
Grid-based container. Props:
| Prop | Effect |
|---|---|
rows |
number of rows (default 2) |
cols |
number of columns (default 2) |
Children specify their position via row and col props.
Embedded browser control. Set url prop to navigate, or set text
to load HTML content directly:
%@formadd[%h,web,WEBBROWSER,0,0,600,400,]
%@formset[%h,web,url,https://example.com]
String spinner (like NUMERICUPDOWN but for text lists). Items via
additem.
Standalone scroll bars. Props: min, max, value.
Property editor control (like Visual Studio's Properties window).
Not a control type — a prop that works on ANY control:
%@formset[%h,btn,tooltip,Click here to save your work]
Manage a notification area icon. Actions:
%@formnotify[show,My App] :: show tray icon
%@formnotify[show,My App,icon.ico] :: with custom icon
%@formnotify[balloon,Alert,Check this,info] :: balloon notification
%@formnotify[hide] :: remove tray icon
Balloon icon types: none, info, warning, error.
These are standalone dispatch verbs, not control types. Each shows a native Windows dialog and returns the result as a string. All are suppressed in headless mode (return empty immediately).
Native "Open File" dialog. Returns the selected file path, or empty
on cancel. Filter format: Text files:*.txt:All files:*.* (colon-
separated pairs; pipe also works but is unsafe in BTM SET).
Native "Save File" dialog. Same args and return as FORMOPENDIALOG.
Native "Browse for Folder" dialog. Returns the selected directory path, or empty on cancel.
Native color picker. Returns #RRGGBB hex string, or empty on
cancel. Pass an initial color as #RRGGBB or a named color.
Native font picker. Returns family:size[:style]
(e.g. Consolas:12:Bold), or empty on cancel.
Control the TCC console window's visibility:
| Action | Effect |
|---|---|
hide |
Hide the console completely. The BTM keeps running invisibly. |
show |
Restore the console (from hidden or minimized). |
minimize |
Minimize the console to the taskbar. |
The "BTM as a Windows app" pattern:
A BTM can run as a standalone app by accepting a /app argument.
When /app is passed, the BTM loads the plugin, hides the TCC
console via @FORMCONSOLE[hide], and runs in a single process
with no visible console window:
@echo off
setlocal
:: Load the plugin
call formcast-check.btm load
:: /app mode: hide the TCC console, show in taskbar
if "%1" == "/app" set RC=%@formconsole[hide]
set h=%@formopen[form,myapp,100,100,600,400]
set RC=%@formset[%h,.,title,My Application]
iff "%1" == "/app" then
set RC=%@formset[%h,.,showintaskbar,1]
set RC=%@formset[%h,.,icon,myapp.ico]
endiff
:: ... add controls, show, event loop ...
Setup: set the FORMCAST_DLL environment variable so the
plugin can be found by any BTM:
set FORMCAST_DLL=C:\path\to\bin\FormCast.dll
A shared formcast-check.btm helper handles load, unload, and exit
cleanup. It checks (in order): already loaded, FORMCAST_DLL env
var, same-directory fallback. Every example BTM calls it instead of
hardcoding a path. When a BTM is launched from Explorer
(%_parent contains explorer), the script performs
plugin /u + exit on close for clean teardown.
From a TCC prompt (normal development):
plugin /l FormCast.dll
myapp.btm
As a standalone app (no visible console):
myapp.btm /app
Desktop shortcut:
For zero-flash launch, use start /inv in the shortcut target:
| Field | Value |
|---|---|
| Target | "C:\path\to\tcc.exe" /c start /inv /pgm "C:\path\to\tcc.exe" /c "C:\path\to\myapp.btm" /app |
| Start in | Directory containing the BTM |
| Run | Minimized |
| Icon | Browse to your .ico file |
The outer tcc.exe /c runs start /inv which launches the app
in a fully invisible TCC session. The shortcut's Run=Minimized
hides the brief outer TCC window. The shortcut must target
tcc.exe /c -- pointing directly at the .btm file will not
pass arguments correctly.
Alternatively, skip the launcher and target the app directly:
| Target | "C:\path\to\tcc.exe" /c "C:\path\to\myapp.btm" /app |
|---|
This works but may flash the console briefly before
@FORMCONSOLE[hide] runs.
For multi-window apps, use the owner property to group windows
so they activate together from the taskbar:
set RC=%@formset[%hChild,.,owner,%hMain]
| Prop | Effect |
|---|---|
icon |
Load a .ico file as the form's title bar and taskbar icon: %@formset[%h,.,icon,myapp.ico] |
showintaskbar |
1 shows the form in the Windows taskbar. Default is 0 (hidden) since most forms are utility dialogs under TCC. Set to 1 for the "BTM as a standalone app" pattern. |
owner |
Set the WinForms Owner relationship. Value is a form handle. Owned forms always appear above their owner, activate together when the owner is clicked in the taskbar, and close when the owner closes. Example: %@formset[%hChild,.,owner,%hMain] |
runtimecontextmenu |
Attach a CONTEXTMENU to the form or a control at the WinForms level WITHOUT storing it in the descriptor. @FORMSAVE produces a clean template. Value is ctrlid (same form) or handle:ctrlid (cross-form). Used by the designer to give canvas controls a right-click menu that lives on the tool window. |
Event bindings can be stored in a template as inert data using
_bind.* props on each control:
%@formset[%h,btnOK,_bind.click,gosub :on_ok]
%@formset[%h,btnCancel,_bind.click,gosub :on_cancel]
%@formset[%h,.,_bind.close,gosub :on_exit]
These are saved by @FORMSAVE as part of the control's property bag.
They are NOT automatically activated on @FORMLOAD — the loading
BTM must explicitly call @FORMAPPLYBINDINGS:
Walk the form descriptor and register every _bind.* prop as a live
@FORMBIND entry. Returns the count of bindings applied.
set h=%@formload[myform.jsonc]
set count=%@formapplybindings[%h]
echo Applied %count bindings
%@formshow[%h]
The designer saves bindings but does NOT call this function, so it never trips over missing subroutine labels. The end-user BTM calls it after load to activate the wiring.
Pass validate as the second argument to check binding targets
before activating. Returns 20106 if any target fails validation.
Recognized binding props: any _bind.<event> key where <event>
is one of the events listed in the Event Reference section
(e.g. _bind.click, _bind.change, _bind.focus, _bind.blur,
_bind.dblclick, _bind.keypress, _bind.close,
_bind.mousedown, _bind.mouseup, _bind.keydown, _bind.keyup,
_bind.resize, _bind.dragenter, _bind.dragdrop,
_bind.mouseenter, _bind.mouseleave, _bind.scroll,
_bind.columnclick, _bind.cellclick,
_bind.beforeexpand, _bind.afterexpand,
_bind.beforecollapse, _bind.aftercollapse).
| Prop | Meaning |
|---|---|
design_mode |
1/0 toggle the designer mode flag |
selected |
string id of the currently selected control (read) |
selectedall |
space-separated ids of all multi-selected controls (read) |
selectioncount |
number of selected controls (read) |
controllist |
JSON array of {"id":"...","type":"..."} for all controls (read) |
controls |
number of top-level controls (read) |
gridsize |
snap grid size in pixels (read/write, default 8) |
refreshdesign |
force repaint of selection overlay after programmatic changes (write) |
Control-level:
| Prop | Format | Effect |
|---|---|---|
position |
x:y |
absolute set X,Y |
size |
w:h |
absolute set Width,Height |
moveby |
dx:dy |
delta move |
resizeby |
dw:dh |
delta resize |
All four operations apply live to realized controls (the visual
position/size updates immediately). position and size are also
readable via @FORMGET and return the same :-separated pair.
Clipboard operations (form-level, ctrl=.):
| Prop | Value | Effect |
|---|---|---|
clipcopy |
control id | Copy one control (and its children) to clipboard |
clipcopy |
(empty) | Copy all multi-selected controls to clipboard |
clipcut |
control id | Copy one control to clipboard and delete it |
clipcut |
(empty) | Copy all multi-selected controls and delete them |
clippaste |
(empty) | Paste clipboard contents onto the form |
ID handling on paste:
- If the original ID is available (e.g. after cut), it is reused.
- If the original ID already exists (copy+paste), a new unique ID
is generated by incrementing the trailing number (
Label1->Label2). - Children of containers are recursively renamed with unique IDs.
- Multiple pastes produce sequential IDs (
Button2,Button3, etc.).
| Prop | Format | Notes |
|---|---|---|
addcolumn |
name:width:type |
append column. Types: text, number, date, size, icon |
additem |
col0:col1:col2:... |
append a row |
clear |
(empty) | wipe columns and items |
multiselect |
1/0 |
toggle MultiSelect |
sort |
colName:asc or colName:desc |
initial sort |
Read via @FORMGET[h,lst,selecteditem] to get the first non-icon
column of the currently selected row.
MEMO is a Multiline=true TextBox. Prop bag flags:
| Prop | Effect |
|---|---|
readonly |
1 makes it read-only |
nowrap |
1 disables word wrap and adds horizontal scroll |
appendtext |
append a line to the realized control (live-only; does not update the descriptor's text field) |
RICHMEMO is a WPF RichTextBox in a WinForms ElementHost. It
adds three live ops that mutate the realized control:
| Prop | Format | Effect |
|---|---|---|
appendcolor |
`text | colorName` |
appendstyle |
`text | bold+italic+underline` |
loadrules |
`regex | color,regex |
@FORMGET[h,rm,text] returns the live document as plain text.
Bitmask of the form's runtime state:
| Bit | Meaning |
|---|---|
1 |
visible |
2 |
enabled |
4 |
focused |
8 |
modal (ShowDialog in progress) |
32 |
events_pending (queue is non-empty) |
-1 for an invalid handle. Bit 32 is the polling target for
on condition-driven scripts.
Register a TCC command to run when the named event fires on the
named control. Pass an empty ctrl for form-level events such as
close. Pass an empty command to clear a binding.
See the Event Reference section below for the full list of available events and their data formats.
Synthesize an event for tests. Actions: click, type, settext,
check, uncheck, focus, blur, dblclick, keypress.
Drain the per-form event queue and write handle kind ctrl data
lines to stdout. Empty handle drains every form. Consume with
do ev in /p formevents.
Read stdin line by line and append each line to the named MEMO or
RICHMEMO control on the realized form. Exits on EOF (when the
upstream pipe closes). For RICHMEMO, the optional color argument
sets the foreground brush for every appended line (any WPF color
name or #RRGGBB hex). Consume with a TCC pipe:
dir /s /b | FORMPIPE %h memo
type build.log | FORMPIPE %h rm Red
This is the complement of FORMEVENTS: FORMEVENTS streams events
OUT of a form; FORMPIPE streams data INTO a form.
FormCast events are powered by the EventWiringTable, a registry-based system where each event is a named entry mapping a .NET event to a data-extraction lambda. Adding a new event to the system requires a single registration call -- no plumbing changes anywhere else.
When an event fires, FormCast enqueues a FormEvent record with a
data string whose format depends on the event type. The tables
below document every registered event and its data format.
These events are available on every control type:
| Event | Data format | Description |
|---|---|---|
focus |
(empty) | Control received keyboard focus |
blur |
(empty) | Control lost keyboard focus |
dblclick |
(empty) | Double-click on the control |
mousedown |
x:y:button:clicks |
Mouse button pressed. button is Left, Right, or Middle; clicks is the click count (1 for single, 2 for double) |
mouseup |
x:y |
Mouse button released at the given position |
mouseenter |
(empty) | Mouse pointer entered the control bounds |
mouseleave |
(empty) | Mouse pointer left the control bounds |
keydown |
keycode:shift:ctrl:alt |
Key pressed. keycode is the integer Keys value; modifier flags are 1/0 |
keyup |
keycode:shift:ctrl:alt |
Key released. Same format as keydown |
resize |
width:height |
Control was resized |
dragenter |
formats |
Drag operation entered the control. formats is a pipe-separated list of available data formats (e.g. Text|FileDrop) |
dragdrop |
x:y:data |
Data was dropped on the control at the given position |
The click event fires only on controls where a click is a
meaningful user action:
| Control type | Data format |
|---|---|
BUTTON |
(empty) |
LINKLABEL |
(empty) |
PICTUREBOX |
(empty) |
PANEL |
(empty) |
LISTVIEW |
(empty) |
Menu items (MENUSTRIP, CONTEXTMENU children) and TOOLBAR buttons
also fire click.
The change event fires when a control's value is modified by the
user. The data format varies by control type:
| Control type | Data format | Example |
|---|---|---|
CHECKBOX |
true or false |
true |
RADIO |
true or false |
true |
EDIT (TextBox) |
current text | Hello world |
TOGGLE (ToggleSwitch) |
true or false |
false |
LABEL |
current text | (rare -- programmatic change) |
COMBOBOX |
selected text | Option B |
NUMERICUPDOWN |
numeric value | 42 |
DATETIMEPICKER |
ISO 8601 date | 2026-04-10T00:00:00 |
TRACKBAR |
integer value | 75 |
CHECKEDLISTBOX |
index:true or index:false |
2:true |
LISTBOX |
selected text | Item three |
TREEVIEW |
selected node name | child1 |
MONTHCALENDAR |
yyyy-MM-dd |
2026-04-10 |
DATAGRID (DataGridView) |
row:col |
3:1 |
| Event | Data format | Description |
|---|---|---|
keypress |
single character | Fires for each printable character typed. Useful for input filtering or live validation. |
| Event | Data format | Description |
|---|---|---|
scroll |
value:type (ScrollBar) or value (TrackBar) |
Fires during scrolling. type is the ScrollEventType name (e.g. SmallIncrement, LargeDecrement, ThumbTrack). TrackBar omits the type. |
| Event | Data format | Description |
|---|---|---|
columnclick |
column index (0-based) | A column header was clicked. Useful for implementing sort-on-click. |
| Event | Data format | Description |
|---|---|---|
beforeexpand |
node name | Fires before a node expands. |
afterexpand |
node name | Fires after a node expands. |
beforecollapse |
node name | Fires before a node collapses. |
aftercollapse |
node name | Fires after a node collapses. |
| Event | Data format | Description |
|---|---|---|
cellclick |
row:col |
A specific cell was clicked (0-based row and column indices). |
| Event | Data format | Description |
|---|---|---|
close |
(empty) | The form is closing. Bind on ctrl=. or empty ctrl. |
The EventWiringTable is a dictionary keyed by (controlType, eventName).
Each entry specifies the .NET event to subscribe and a lambda that
extracts the data string from the EventArgs. Adding a new event
for any control type is a single Register() call -- no changes
needed in the realizer, dispatcher, or event queue.
Serialize the form descriptor to a JSONC file at path. Returns
0 on success.
Deserialize a JSONC template into a fresh form. Optional vars is
a key=value|key=value|... string consumed by ${var} substitution.
Returns the new handle or empty buffer.
Append all controls from file to the form h. Atomic on id
collision: rolls back and returns 20103.
Re-run the layout manager named in the form's layout property
against the current control list, mutating positions in place.
Plugin version string.
Configure plugin logging. level is one of off, error, warn,
info, debug, trace (case-insensitive). path is the log file
(required when level is not off). The file is opened in append mode
with read-sharing so you can tail -f it from another console while
the plugin runs. Returns 0 on success.
Log levels (lowest to highest verbosity):
| Level | What it captures |
|---|---|
off |
Nothing (default) |
error |
Plugin failures, exceptions |
warn |
Unexpected conditions that don't fail |
info |
High-level actions (formopen, formclose, formshow) |
debug |
Property sets/gets, event dispatch, control add |
trace |
Every FORMEVENTS drain, every poll cycle |
Example:
set RC=%@formlog[debug,%TEMP\formcast.log]
:: ... run your BTM ...
set RC=%@formlog[off]
Stream every stock icon name with its category, one per line. Optional
filter limits the output to matching icons.
The cross-process FormCast.Host.exe daemon (Phase 10) is the
plumbing that will host Global\ scoped forms in v1.x; it builds
in v1 but the global registry decorator that talks to it is a
follow-up.