From df0d56f2d7e097534f4356539cc5add10d7597a9 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Thu, 14 May 2026 18:10:38 +0200 Subject: [PATCH 1/3] Update the WIP. --- WIP.md | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 255 insertions(+), 23 deletions(-) diff --git a/WIP.md b/WIP.md index 0c6c59e..6de66b3 100644 --- a/WIP.md +++ b/WIP.md @@ -4,21 +4,22 @@ Jekyll site (`just-the-docs` theme) deploying to `docs.twinbasic.com`. Source un ## Status -Reference documentation is **complete**. All eleven packages have full reference coverage adapted from primary sources (Microsoft VBA-Docs CC-BY-4.0 for the runtime library, `.twin` source for the twinBASIC-specific packages); the CEF and WebView2 packages also carry a tutorial set. - -| Package | Reference | Tutorials | -|--------------------------------------|-----------|-----------| -| VBA package | done | — | -| VBRUN package | done | — | -| VB package | done | — | -| WebView2Package | done | done | -| Assert package | done | — | -| CustomControls / CustomControlsPackage | done | — | -| cefPackage (CEF) | done | done | -| WinEventLogLib | done | — | -| WinNamedPipesLib | done | — | -| WinServicesLib | done | — | -| tbIDE | done | — | +Reference documentation is **mostly complete**. Eleven packages have full reference coverage adapted from primary sources (Microsoft VBA-Docs CC-BY-4.0 for the runtime library, `.twin` source for the twinBASIC-specific packages); a twelfth (WinNativeCommonCtls) is in progress. The CEF and WebView2 packages also carry a tutorial set. + +| Package | Reference | Tutorials | +|--------------------------------------|-------------|-----------| +| VBA package | done | — | +| VBRUN package | done | — | +| VB package | done | — | +| WebView2Package | done | done | +| Assert package | done | — | +| CustomControls / CustomControlsPackage | done | — | +| cefPackage (CEF) | done | done | +| WinEventLogLib | done | — | +| WinNamedPipesLib | done | — | +| WinServicesLib | done | — | +| tbIDE | done | — | +| WinNativeCommonCtls | in progress | — | The rest of this file is the maintenance guide for adding new pages or updating existing ones — primary-source paths, page templates, cross-section linking conventions, the per-symbol workflow, and the integrity check. @@ -37,6 +38,7 @@ When working from a primary source: always read it first — **never paraphrase - `docs/Reference/WinEventLogLib/` — Windows Event Log package: the generic `EventLog(Of T1, T2)` class and the `EventLogHelperPublic` module with its single `RegisterEventLogInternal` helper. Three pages total — `index.md`, `EventLog.md`, `EventLogHelperPublic.md`. - `docs/Reference/WinNamedPipesLib/` — Windows Named Pipes package: the IOCP-based async pipe framework — `NamedPipeServer` + `NamedPipeServerConnection` on the server side, `NamedPipeClientManager` + `NamedPipeClientConnection` on the client side. Five pages total (`index.md` + one per class). - `docs/Reference/WinServicesLib/` — Windows Services package: a thin OS-services wrapper. `Services` (predeclared singleton) coordinates one or more `ServiceManager` configurations; `ServiceCreator(Of T)` is the generic factory the dispatcher uses to instantiate each user-defined `ITbService` class; `ServiceState` is a read-only state snapshot for an installed service. Four public enums (`ServiceTypeConstants`, `ServiceStartConstants`, `ServiceControlCodeConstants`, `ServiceStatusConstants`) live under `Enumerations/`. +- `docs/Reference/WinNativeCommonCtls/` — Windows Native Common Controls compatibility package: a VB6-compatible Microsoft Common Controls 6.0 (`MSCOMCTL.OCX`) replacement, written on top of the Win32 ComCtl32 controls. Eight controls (**DTPicker**, **ImageList**, **ListView**, **MonthView**, **ProgressBar**, **Slider**, **TreeView**, **UpDown**), plus eight sub-object classes (**ListImages** / **ListImage**, **ListItems** / **ListItem**, **ColumnHeaders** / **ColumnHeader**, **Nodes** / **Node**) reached through container properties on the three collection-bearing controls, plus ~16 user-facing enumerations. Each control is a `BaseCtl` (`[COMCreatable(False)]`) plus a thin `` leaf tagged `[WindowsControl(...)]` — the same split VB-package and CEF use. - `docs/Reference/tbIDE/` — IDE Extensibility package (this is the **addin SDK**). The package is type-only — it ships **public interfaces + CoClasses** that an addin DLL binds to; every implementation behind them lives in the twinBASIC IDE itself. The user-facing surface is one entry-point factory (`tbCreateCompilerAddin`) plus ~20 CoClasses grouped by role: the addin contract (`AddIn`), the root API (`Host`), the loaded `Project`, the editors collection (`Editor` / `CodeEditor` / `Editors`), the virtual file system (`FileSystem` / `FileSystemItem` / `Folder` / `File`), the in-IDE UI surface (`Toolbar` / `Toolbars` / `Button` / `ToolWindow` / `ToolWindows`), the HTML DOM inside a tool window (`HtmlElement` / `HtmlElements` / `HtmlElementProperty` / `HtmlElementProperties` / `HtmlEventProperty` / `HtmlEventProperties`), the `DebugConsole`, `KeyboardShortcuts`, `Themes`, and the single concrete user-instantiable helper class `AddinTimer`. Flat layout — one page per CoClass / Class plus the index landing. - `docs/Reference/Statements.md` — alphabetical index of language statements. - `docs/Reference/Procedures and Functions.md` — alphabetical index of procedures/functions. @@ -72,6 +74,7 @@ All of twinbasic's package sources are at: ..\tb-export\NewProject\Packages\WinEventLogLib\Sources\ ..\tb-export\NewProject\Packages\WinNamedPipesLib\Sources\ ..\tb-export\NewProject\Packages\WinServicesLib\Sources\ +..\tb-export\NewProject\Packages\WinNativeCommonCtls\Sources\ etc. ``` @@ -1237,6 +1240,189 @@ All flat — no `Enumerations/` sub-folder; the four nested enums (`RevealArea`, **License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2022) — same situation as WebView2Package, Assert, CustomControls, CEF, and the three winlibs. Pages are fully original content; **omit** the `vba_attribution: true` flag. +### WinNativeCommonCtls + +The package's `Settings` describes it as the *"twinBASIC - Common Controls Compatibility Package"* — a VB6-compatible replacement for **Microsoft Common Controls 6.0** (`MSCOMCTL.OCX`), written on top of the Win32 ComCtl32 controls (`COMCTL32.DLL` / `MSFTEDIT.DLL`). It ships eight controls that mirror the MSCOMCTL surface name-for-name where possible. The package was first released v0.0.1.0 on 18-FEB-2023 and is independent of (but co-versioned with) the VB compatibility package. + +Layout of `..\tb-export\NewProject\Packages\WinNativeCommonCtls\Sources\` — two sub-folders: + +- `CONTROLS\` — one `.twin` per control. Each file declares two classes: the heavy `BaseCtl` (where every event / method / property is implemented, tagged `[COMCreatable(False)]` + `[EventsUseDispInterface]`) and the thin `` leaf (`Inherits BaseCtl`, tagged `[WindowsControl("/miscellaneous/ICONS??/??.png")]`). The leaf adds only a `Class_BeforeFirstMethodAccess` that calls `[_HiddenModule].EnsureContainerIsLoaded(Me)` — same `BaseCtl` / `` leaf split that CEF and the VB controls use. +- `SUPPORT\` — the sub-object classes (`ListItem` / `ListItems`, `ColumnHeader` / `ColumnHeaders`, `Node` / `Nodes`, `ListImage` / `ListImages`), the per-control `Consts.twin` modules holding Win32 SDK plumbing (with a small minority of user-facing enums mixed in), the `Interfaces.twin` private interfaces, the design-time `ImageListPropertyPage` form, and a couple of private helper modules. + +Eight controls (one `.twin` per pair): + +| File | `BaseCtl` inherits | Role | +|-------------------|---------------------------------|-----------------------------------------------------------------------------------------------| +| `DTPicker.twin` | `VB.BaseControlFocusable` | Date / time picker — calendar drop-down, single-date `Value`, custom format strings | +| `ImageList.twin` | `VB.BaseControlNotFocusable` | Off-screen image collection — feeds `ListView` / `TreeView` icons via `Icons` / `ImageList` properties | +| `ListView.twin` | `VB.BaseControlFocusable` | Multi-column list — four `View` modes (Icon / SmallIcon / List / Report), label-edit, checkboxes | +| `MonthView.twin` | `VB.BaseControlFocusable` | Full-month calendar grid — multi-select, bold-day callbacks, week-number / today display | +| `ProgressBar.twin`| `VB.BaseControlNotFocusable2` | Standard / Smooth / Marquee progress indicator with three visual states (Normal / Error / Paused) | +| `Slider.twin` | `VB.BaseControlFocusableNoFont` | Trackbar / slider — tick marks, range selection, vertical or horizontal orientation | +| `TreeView.twin` | `VB.BaseControlFocusable` | Hierarchical tree of `Node` objects — sorting, label-edit, checkboxes, image lists | +| `UpDown.twin` | `VB.BaseControlFocusableNoFont` | Spin control (up / down arrows) — pure Increment / Min / Max / Value; no auto-buddy binding | + +Every `BaseCtl` carries `[WithDispatchForwarding] Implements Control` (where `Control` is `Private Interface` in `Interfaces.twin`, marked `[COMExtensible]` — essentially an `Object` alias that makes the dispatch forwarding behave). They also implement a chorus of `VB.IWindowsControl`, `VB.IWindowElementEventsCommon`, `VB.IWindowElementEventsCommonControls`, `VB.IWindowElementEventsUC`, `VB.IWindowElementEventsAX` — these are the VB-package event-dispatch interfaces; do **not** surface them on the docs. Each control also implements one private `TbPrivate` interface (declared `[ComImport(True)]` inside the same `.twin`) that the package's collection sub-objects use to refcount and reach internal state without taking a strong reference; **no doc page** for those. + +#### Public user-facing surface + +The eight leaf classes `DTPicker`, `ImageList`, `ListView`, `MonthView`, `ProgressBar`, `Slider`, `TreeView`, `UpDown` are what user code references at design time (via `[WindowsControl(...)]`) and at run time (`Dim lv As ListView`). The `BaseCtl` base classes are the implementation half — `[COMCreatable(False)]` and not user-instantiable, but **the entire user-visible surface is declared on them**. Document on the leaf's name (`ListView.md`), describe the full surface, and don't surface the `BaseCtl` split. + +The package also surfaces eight sub-object classes — collection plus item: + +| Class | Reached via | Notes | +|-----------------|----------------------------------------------------------|------------------------------------------------------------------------------------| +| `ListImages` | `ImageList.ListImages` (Get-only) | Enumerable; `Item(Index or Key)` default member; `Add`, `Remove`, `Clear`, `Exists` | +| `ListImage` | element of `ListImages` (returned from `Add`, indexed) | `Index` (read-only), `Key`, `Picture`, `Tag`, plus `Draw(hDC, x, y, Style)` and `ExtractIcon` | +| `ListItems` | `ListView.ListItems` (Get-only) | Enumerable; `Item(Index or Key)` default member; `Add`, `Remove`, `Clear` | +| `ListItem` | element of `ListItems` | `Text` (default), `SubItems(Index)`, `Icon`, `SmallIcon`, `Checked`, `Selected`, `Ghosted`, `Bold`, `BackColor`, `ForeColor`, `Tag`, `ToolTipText`, `EnsureVisible`, `Left` / `Top` / `Width` / `Height`, `Index` (RO), `Key`, `CreateDragImage` (`[Unimplemented]`) | +| `ColumnHeaders` | `ListView.ColumnHeaders` (Get-only) | Same shape as `ListItems`; `Add(Index, Key, Text, Width, Alignment, Icon)` returns `ColumnHeader` | +| `ColumnHeader` | element of `ColumnHeaders` | `Text` (default), `Width`, `Left` (RO), `Alignment` (typed `ListColumnAlignmentConstants`), `Position`, `SubItemIndex`, `Icon`, `Index` (RO), `Key`, `Tag` | +| `Nodes` | `TreeView.Nodes` (Get-only) | Enumerable; `Item(Index or Key)` default; `Add(Relative, Relationship, Key, Text, Image, SelectedImage)` returns `Node` | +| `Node` | element of `Nodes` | `Text` (default), `Parent`, `Child`, `Next`, `Previous`, `Root`, `FirstSibling`, `LastSibling`, `Children` (count), `Expanded`, `Selected`, `Checked`, `Bold`, `BackColor`, `ForeColor`, `Image`, `SelectedImage`, `Tag`, `FullPath`, `Visible` (RO), `Sorted`, `SortOrder`, `SortType`, `EnsureVisible`, `Index` (RO), `Key` | + +Every sub-object is `[COMCreatable(False)]` — its constructor takes a `BaseCtl` reference, so user code never instantiates these directly. They are returned from container methods (`Add`, `Item`) and reached through container properties. + +Container cross-references (typed as the `BaseCtl` parent, since the controls accept either the base or the leaf — but document the parameter as the **leaf**): + +- `TreeView.ImageList` / `Let` / `Set` — typed `As ImageListBaseCtl`; the user assigns an `ImageList`. +- `ListView.Icons` / `Let` / `Set`, `ListView.SmallIcons` / `Let` / `Set`, `ListView.ColumnHeaderIcons` / `Let` / `Set` — all three typed `As ImageListBaseCtl`; the user assigns an `ImageList`. + +`ListView.BorderStyle` is unusually typed `As TreeBorderStyleConstants` (declared in `TreeViewPublic`, not in a `ListView*` module) — the enum is shared across both controls. Surface this on the `BorderStyle` entry without trying to rationalise it. + +#### Per-control highlights + +These are the points worth surfacing on each control's page that are *not* obvious from a flat property list: + +- **DTPicker** — the only control where most behaviour is in the calendar drop-down, not the inline display. The `Calendar*` colour properties (`CalendarBackColor`, `CalendarForeColor`, `CalendarTitleBackColor`, `CalendarTitleForeColor`, `CalendarTrailingForeColor`) act on the dropped-down month grid via `DTM_SETMCCOLOR`. `Format` (`DTPickerFormatConstants`) chooses between long-date / short-date / time / custom; when set to `dtpCustom`, the picker pulls `CustomFormat` (a `GetDateFormat`-style picture string). The control exposes `Year` / `Month` / `Week` / `Day` / `Hour` / `Minute` / `Second` accessors that decompose the current `Value`. `Value` is `Variant` — it can be `Null` (no date selected) when `CheckBox = True` and the user unchecks the box. Events `Format`, `FormatSize`, `CallbackKeyDown` fire when `Format = dtpCustom` and the format string contains a callback token. +- **ImageList** — purely off-screen; `Visible` does nothing user-meaningful (it's a "store of pictures" control). The `ImageWidth` / `ImageHeight` properties are read/write **only while empty** — once any image is added, the setter raises run-time error 35611 (*"Property is read-only if image list contains images"*). `ColorDepth` is fixed at construction time. `MaskColor` + `UseMaskColor = True` makes the masked pixels transparent when rendered into a control that consumes the image list. `Overlay(Key1, Key2)` composes two list-images into a single `StdPicture`. Bound-count tracking: an `ImageList` cannot be modified (clear / remove) while any control has it bound as `Icons` / `SmallIcons` / `ColumnHeaderIcons` / `ImageList`, throwing error 35617. +- **ListView** — the largest of the eight. `View` switches the visual mode (`lvwIcon` / `lvwSmallIcon` / `lvwList` / `lvwReport`); `Arrange` (`lvwNone` / `lvwAutoLeft` / `lvwAutoTop`) auto-flows the icon mode; `Report` mode is the only one that shows the `ColumnHeaders`. `LabelEdit` defaults to `lvwAutomatic` — F2 / click-and-wait edits a label in place; `lvwManual` requires `StartLabelEdit()` and `lvwDisabled` blocks editing. `TextBackground` (`lvwTransparent` / `lvwOpaque`) acts on the *item* text rendering, not the control's `BackColor`. `MultiSelect = True` enables Ctrl+click / Shift+click range selection. `CheckBoxes = True` adds a leading checkbox per row and fires `ItemCheck`. `AllowColumnReorder` only matters in Report view. `BorderStyle` is `TreeBorderStyleConstants` (`ccNone` / `ccFixedSingle`). The control surfaces `hWnd` and `hWndHeader` (the embedded `SysHeader32` window) separately. `Scroll` is `[Unimplemented]` per the source. `GetFirstVisible() As ListItem`, `SelectedItem` / `SelectedItemIndex` — the latter is read-only (assign through `ListItem.Selected = True` instead). +- **MonthView** — `MonthColumns` / `MonthRows` lay out a grid of side-by-side month panels (`ResizeToFit` auto-sizes the control to fit them). `Day` / `Month` / `Week` / `Year` are the same decomposition pattern as DTPicker. `MaxSelCount` is the upper bound of a multi-day selection (default 7, max ≈ 366 per the Win32 control); `SelStart` and `SelEnd` are the inclusive range. `MinDate` / `MaxDate` bound the navigable range. `Value` is the *current selection's start date* (same as `SelStart` when `MultiSelect = False`). The control fires both `Click` (any click) and `DateClick` (only when a date cell is hit, with the date passed as a parameter); same split for `DblClick` / `DateDblClick`. `GetDayBold` is an event-driven callback — the control fires it for each visible month asking for a `State()` array of which days to render bold; this is the mechanism for highlighting holidays, schedule entries, etc. `DayBold` is an alternative per-date setter. `GetMonthRange(IncludeTrailing, StartDate, EndDate)` returns the visible date span. +- **ProgressBar** — three orthogonal axes. `Min` / `Max` / `Value` are the standard range. `Step` + `StepIt()` advances the bar by `Step` units (typical loop pattern: `Min = 0`, `Max = N`, then `StepIt()` per iteration). `Scrolling = PrbScrollingStandard` (default) animates the bar in segments; `PrbScrollingSmooth` is the continuous block; `PrbScrollingMarquee` is the indeterminate animation (drive with `MarqueeAnimation = True` + `MarqueeSpeed`). `State` (`PrbStateNormal` / `Error` / `Paused`) tints the bar red / yellow per the OS theme. `Orientation` is `PrbOrientation` (`Horizontal` / `Vertical`). The control has `Click` / `DblClick` / `Mouse*` events but **no** `Change` despite the declaration — verify with the source if surfaced (`Change` is declared in the events region but not fired by any Win32 progress-bar notification). +- **Slider** — `Min` / `Max` / `Value` like a scrollbar; `SmallChange` is the arrow-key step, `LargeChange` is the PgUp / PgDn step. `SelStart` + `SelLength` create a highlighted selection range (visible when `SelectRange = True`). `TickFrequency` controls how often tick marks appear; `TickStyle` (`sldBottomRight` / `sldTopLeft` / `sldBoth` / `sldNoTicks`) chooses which side(s) of the channel they render on. `TextPosition` (`sldAboveLeft` / `sldBelowRight`) is for the optional tip text. `HideThumb = True` removes the draggable indicator. `ShowTip = True` enables the floating tooltip showing the current value during drag. `Orientation` is `OrientationConstants` (the shared horizontal / vertical enum used also by `UpDown`). +- **TreeView** — the second-largest control. `Style` (`TreeStyleConstants`, 8 values) is a composite of *show / hide* flags for tree-lines / plus-minus boxes / icons / text — the values name what's shown. `LineStyle` chooses `tvwRootLines` (lines from root nodes) or `tvwTreeLines` (lines only from children). `Sorted` / `SortOrder` / `SortType` apply at the root level; each `Node` has its own per-subtree `Sorted` / `SortOrder` / `SortType`. `LabelEdit` is the same gating as `ListView.LabelEdit` (`tvwAutomatic` / `Manual` / `Disabled`). `CheckBoxes = True` adds per-node checkboxes; `FullRowSelect` extends the selection highlight across the full row width. `Indentation` is in twips. `HitTest(x, y)` returns the `Node` at a point (for hover effects, drag-drop). `SelectedItem` (`Get` / `Let` / `Set`) and `DropHighlight` (`Get` / `Let` / `Set`) are both `Node`-typed. `StartLabelEdit()` for `Manual` mode. `GetVisibleCount()` returns how many full nodes the visible area shows. `Scroll` event new to tB. +- **UpDown** — pure spin control: `Min` / `Max` / `Value` / `Increment`. `Orientation` is `OrientationConstants` (horizontal pair of arrows or vertical, the more common). Events are `Change` (any time `Value` changes), `UpClick`, `DownClick`. There is *no* auto-buddy / partner-control facility in this version (the Win32 `UDS_AUTOBUDDY` flag is in the source enums but not exposed) — user code wires `UpClick` / `DownClick` to update the partner control manually. + +Common surface across every control: `Public Opacity As Double = 100` (with the *"REQUIRES TARGET OS 6.2+ FOR CHILD CONTROLS."* description), `Public TransparencyKey As OLE_COLOR = -1` (same OS requirement), and (where `FEATURE_OLEDRAGDROP` is enabled at compile time) `Public OLEDropMode As VBRUN.OLEDropConstants` plus the `OLECompleteDrag` / `OLEDragDrop` / `OLEDragOver` / `OLEGiveFeedback` / `OLESetData` / `OLEStartDrag` events. `Public OLEDrag()` method on every control. `Public Property Get Parent() As Object` and `Public Property Get Object() As Object` on every control. The inherited surface from `VB.BaseControl*` includes `Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, `Visible`, `Enabled`, `BackColor` / `ForeColor` / `Font` (where focusable), `Appearance`, `MousePointer` / `MouseIcon`, `ToolTipText`, `DragMode` / `DragIcon`, `Drag()`, `Refresh()`, `SetFocus()` (focusable variants), `ZOrder()`, `CausesValidation`, `TabIndex` / `TabStop` (focusable variants), `VisualStyles`, `hWnd`, `HelpContextID` / `WhatsThisHelpID`. Walk the relevant `Inherits VB.BaseControl*` chain in `..\tb-export\NewProject\Packages\VB\Sources\BASE\` to enumerate exactly what each control's variant adds. + +#### Per-control nested enums (fold onto the declaring control's page) + +These enums are declared *inside* each `BaseCtl` (`Enum ` without `Public`, which still surfaces because the enclosing class is public). Following the CustomControls convention for `WaynesSlider.SliderDirection`, document each on its declaring control's page rather than under `Enumerations/`: + +| Enum | Declared on | Members | +|---------------------------------|----------------------------|-------------------------------------------------------------------------------| +| `ImageListColorDepth` | `ImageListBaseCtl` | `ColorDepth4Bit = 4`, `ColorDepth8Bit = 8`, `ColorDepth16Bit = 16`, `ColorDepth24Bit = 24`, `ColorDepth32Bit = 32` | +| `ListViewConstants` | `ListViewBaseCtl` | `lvwIcon = 0`, `lvwSmallIcon = 1`, `lvwList = 2`, `lvwReport = 3` | +| `ListArrangeConstants` | `ListViewBaseCtl` | `lvwNone = 0`, `lvwAutoLeft = 1`, `lvwAutoTop = 2` | +| `ListTextBackgroundConstants` | `ListViewBaseCtl` | `lvwTransparent = 0`, `lvwOpaque = 1` | +| `ListLabelEditConstants` | `ListViewBaseCtl` | `lvwAutomatic = 0`, `lvwManual = 1`, `lvwDisabled = 2` | +| `ListColumnAlignmentConstants` | `ColumnHeader` | `lvwColumnLeft = 0`, `lvwColumnRight = 1`, `lvwColumnCenter = 2` | +| `PrbOrientation` | `ProgressBarBaseCtl` | `PrbOrientationHorizontal = 0`, `PrbOrientationVertical = 1` | +| `PrbScrolling` | `ProgressBarBaseCtl` | `PrbScrollingStandard = 0`, `PrbScrollingSmooth = 1`, `PrbScrollingMarquee = 2` | +| `PrbState` | `ProgressBarBaseCtl` | `PrbStateNormal = 1`, `PrbStateError = 2`, `PrbStatePaused = 3` | +| `TickStyleConstants` | `SliderBaseCtl` | `sldBottomRight = 0`, `sldTopLeft = 1`, `sldBoth = 2`, `sldNoTicks = 3` | +| `TextPositionConstants` | `SliderBaseCtl` | `sldAboveLeft = 0`, `sldBelowRight = 1` | + +Source-side spelling note: every enum is named `` (no `Public` modifier) but the *member* names use the historical VB6 prefix conventions — `lvw` for ListView, `tvw` for TreeView, `sld` for Slider, `dtp` for DTPicker, `Prb` for ProgressBar, `cc` for cross-control. Mixed casing in member names (`SldAboveLeft` literal in the source defaults vs `sldAboveLeft` declaration) is a source-side issue; surface members with the declared casing. + +#### Module-level enums (under `Enumerations/`) + +Five `Consts.twin` modules in `SUPPORT/` carry Win32 SDK plumbing (message IDs, notification IDs, style flags, Win32 types like `NMHDR` / `SYSTEMTIME` / `LVCOLUMNW`) **plus** a small fraction of user-facing enums. The plumbing is unreachable by user code (mostly inside `Private Module …Consts`); the user-facing enums are split into a separate `Public Module` (TreeView's clean case) or coexist with the plumbing in an effectively-public bare `Module` (the rest). Either way, surface only the user-facing enums: + +| Enum | Declared in / module | Members | +|-------------------------------|------------------------------------------------------------|----------------------------------------------------------------------| +| `DTPickerFormatConstants` | `DTPickerConsts.twin` (module `DTPickerConsts`) | `dtpLongDate = 0`, `dtpShortDate = 1`, `dtpTime = 2`, `dtpCustom = 3` | +| `TreeBorderStyleConstants` | `TreeViewConsts.twin` (`Public Module TreeViewPublic`) | `ccNone = 0`, `ccFixedSingle = 1` | +| `TreeLabelEditConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwAutomatic = 0`, `tvwManual = 1`, `tvwDisabled = 2` | +| `TreeLineStyleConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwTreeLines = 0`, `tvwRootLines = 1` | +| `TreeStyleConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | 8 members: `tvwTextOnly`, `tvwPictureText`, `tvwPlusMinusText`, `tvwPlusMinusPictureText`, `tvwTreelinesText`, `tvwTreelinesPictureText`, `tvwTreelinesPlusMinusText`, `tvwTreelinesPlusMinusPictureText` | +| `TreeRelationshipConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwFirst = 0`, `tvwLast = 1`, `tvwNext = 2`, `tvwPrevious = 3`, `tvwChild = 4` | +| `TreeSortOrderConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwAscending = 0`, `tvwDescending = 1` | +| `TreeSortTypeConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwBinary = 0`, `tvwText = 1` | +| `OrientationConstants` | `Misc.twin` (`Private Module Miscellaneous`) | `ccOrientationHorizontal = 0`, `ccOrientationVertical = 1` — used by both **Slider** and **UpDown** | +| `ImlDrawConstants` | `ImageListConsts.twin` (`Private Module ImageListConsts`) | `ImlDrawNormal = 1`, `ImlDrawTransparent = 2`, `ImlDrawSelected = 4`, `ImlDrawFocus = 8`, `ImlDrawNoMask = 16` — flag combination; used as `[TypeHint(ImlDrawConstants)]` on `ListImage.Draw`'s `Style` parameter | + +For `OrientationConstants` and `ImlDrawConstants` (declared `Public Enum` inside a `Private Module`): the enclosing module is unreachable by name from user code, but the enum members are reachable because they're tagged through `[TypeHint]` on the consuming method's parameter and are also surfaced by the IDE's "implicit member visibility" — i.e. user code writes `Slider1.Orientation = ccOrientationVertical` and `ListImage.Draw(hdc, 0, 0, ImlDrawTransparent Or ImlDrawSelected)`. Document the enum and don't worry about qualification — the user's call site never needs `Module.Enum.Member` form. + +The remaining `Consts.twin` modules (`ImageListConsts`, `ListViewConsts`, `ProgressBarConsts`, `TreeViewConsts.TreeViewConsts` (the private half), `UpDownConsts`, `SliderConsts`, `MonthViewConsts`, `DTPickerConsts` non-`DTPickerFormatConstants` content) are package-internal — Win32 message IDs, style flags, notification structures (`NMHDR`, `NMLISTVIEW`, `NMDATETIMECHANGE`, …) that the controls use to talk to ComCtl32 but that the user never references. **No doc pages** for those; do not document `LVMessages`, `TVMessages`, `MonthViewMessages`, `SliderMessages`, `UpDownMessages`, `DTPickerMessages` and the associated `*Notifications` / `*Styles` enums. + +#### Private classes (no doc page) + +- `Private Class ListViewHeaderSubclasser` (in `ListView.twin`) — subclasses the embedded `SysHeader32` window to intercept `HDM_LAYOUT` notifications for the column-resize handler. Implementation detail. +- `Private Class TreeViewNodeCheckState` / `ListViewNodeCheckState` / `TreeViewNodeClick` / `TreeViewNodeDblClick` (in `TreeViewNodeCheckState.twin`) — four `IScheduledCallback`-implementing dispatch helpers that the controls schedule onto the message loop to fire `NodeCheck` / `ItemCheck` / `NodeClick` / `DblClick` events at the right point in the click-handling sequence. Same role as the `…Internal` classes in WinNamedPipesLib; no doc page. +- `Class ImageListPropertyPage` (in `ImageListPropertyPage.twin`) — `[FormDesignerId]` `[PredeclaredId]` `[COMCreatable(False)]` Form class that's the IDE's design-time property editor for `ImageList` (the "Custom Properties..." button). Invoked from `ImageListBaseCtl.HandleInvokePropertyExtension`. Pure design-time tooling, never appears at run-time; no doc page. +- `Private Interface Control` / `IScheduledCallback` / `ITwinBasicDesignerExtensions` (in `Interfaces.twin`) — internal interfaces. `Control` is the empty marker interface that `[WithDispatchForwarding]` resolves names through. No doc pages. +- `Private Module Miscellaneous` (in `Misc.twin`) — `StrPtrSafe`, `CommonTreeViewGetNodeFromHandle`, `SyncBorderStyle` — internal helpers. `OrientationConstants` does surface from this module (see above) but the module itself doesn't get a doc page. +- `Private Module ImagesHelper` (in `ImagesHelper.twin`) — `GetBitsPerPixelFromPic`. Internal helper. No doc page. +- `Private Module ImageListConsts`, `ListViewConsts`, `ProgressBarConsts`, `TreeViewConsts` (the private half), and the bare `Module DTPickerConsts` / `MonthViewConsts` / `SliderConsts` / `UpDownConsts` (effectively public but Win32-plumbing-only) — covered above; no per-module doc page. + +#### `[Unimplemented]` and `[Hidden]` members to flag + +- **DTPicker.RightToLeft** — tagged `[Unimplemented]`; flag with `> [!NOTE]`. +- **MonthView.RightToLeft** — same. +- **ListView.Scroll** event — tagged `[Unimplemented]`; flag on the event entry. +- **ListItem.CreateDragImage** — tagged `[Unimplemented]`; flag with `> [!NOTE]`. +- **ListImages.ControlDefault** — tagged `[Unimplemented]` and `[Hidden]`; **do not document** (`[Hidden]` means the IDE intentionally suppresses it). +- **ListView.AllowColumnReorder** — implemented but only takes effect in Report view; surface as a note on the property. + +#### Doc-side layout (folders / files) + +Twenty-eight pages total, mixing single-file controls with folder-style controls (used when the control has sub-object companion pages): + +``` +docs/Reference/WinNativeCommonCtls/ + index.md ← landing; intro + control table + sub-object map + cross-references (VBRUN/ControlTypeConstants) + DTPicker.md ← single-file + ImageList/index.md + ImageList/ListImage.md + ImageList/ListImages.md + ListView/index.md + ListView/ColumnHeader.md + ListView/ColumnHeaders.md + ListView/ListItem.md + ListView/ListItems.md + MonthView.md ← single-file + ProgressBar.md ← single-file + Slider.md ← single-file + TreeView/index.md + TreeView/Node.md + TreeView/Nodes.md + UpDown.md ← single-file + Enumerations/index.md + Enumerations/DTPickerFormatConstants.md + Enumerations/ImlDrawConstants.md + Enumerations/OrientationConstants.md + Enumerations/TreeBorderStyleConstants.md + Enumerations/TreeLabelEditConstants.md + Enumerations/TreeLineStyleConstants.md + Enumerations/TreeRelationshipConstants.md + Enumerations/TreeSortOrderConstants.md + Enumerations/TreeSortTypeConstants.md + Enumerations/TreeStyleConstants.md +``` + +Decisions explained: + +- **Folder-style for `ImageList/`, `ListView/`, `TreeView/`** — each has 2–4 sub-object companions (`ListImage` + `ListImages`; `ListItem` + `ListItems` + `ColumnHeader` + `ColumnHeaders`; `Node` + `Nodes`) that are 1:1 with the container. Same pattern as `CustomControls/WaynesButton/WaynesButtonState.md`. Each container's index page covers the control's surface; the sub-pages cover the collection / item details. +- **Single-file for the remaining five controls** (`DTPicker.md`, `MonthView.md`, `ProgressBar.md`, `Slider.md`, `UpDown.md`) — no sub-objects to host. Surface large but flat. Same shape as `WebView2/WebView2Request.md` or `CheckBox.md`. +- **`Enumerations/` folder** for the 10 module-level / shared enums. Per-control nested enums (the 11 in the table above) fold onto their declaring control's page. +- **No `Types/` folder** — every `Public Type` in the package (the Win32-flavoured `NMHDR`-derived notification structures, `SYSTEMTIME`, `LVITEMW`, etc.) is internal-only; nothing user-facing matches the WebView2 `COREWEBVIEW2_PHYSICAL_KEY_STATUS` precedent. + +#### Naming + +- Folder / URL segment: `WinNativeCommonCtls/` (matches the source-side package name; no `Package` suffix to drop, same as the three winlibs and tbIDE). +- Index title: `WinNativeCommonCtls Package` — the ` Package` convention. +- Permalinks: `/tB/Packages/WinNativeCommonCtls/` for the landing; `/tB/Packages/WinNativeCommonCtls/` for single-file control pages (`DTPicker`, `MonthView`, `ProgressBar`, `Slider`, `UpDown`); `/tB/Packages/WinNativeCommonCtls//` and `/tB/Packages/WinNativeCommonCtls//` for the folder-style controls (`ImageList/`, `ListView/`, `TreeView/`); `/tB/Packages/WinNativeCommonCtls/Enumerations/` for each enum page. +- `parent: WinNativeCommonCtls Package` on every top-level child page. The enum pages set `parent: Enumerations` and `grand_parent: WinNativeCommonCtls Package` (the grouped-page pattern; same shape as the WebView2 / CEF / CustomControls / WinServicesLib `Enumerations/` directories). The sub-object pages set `parent: ` and `grand_parent: WinNativeCommonCtls Package` (the same shape CustomControls uses for `WaynesButton/WaynesButtonState.md`). + +#### Pre-existing cross-references on the site + +- [`docs/Reference/VBRUN/Constants/ControlTypeConstants.md`](docs/Reference/VBRUN/Constants/ControlTypeConstants.md) already lists every control's `vb` constant: `vbProgressBar = 21`, `vbTreeView = 22`, `vbSlider = 26`, `vbUpDown = 27`, `vbDTPicker = 28`, `vbMonthView = 29`, `vbListView = 30`, `vbImageList = 31`. Each control's reference page should link back to its constant. +- [`docs/Reference/VBRUN/Constants/OLEDropConstants.md`](docs/Reference/VBRUN/Constants/OLEDropConstants.md) and the `OLEDragDrop` events are inherited surface — link the `OLEDropMode` entries to the constant. +- [`docs/Reference/Packages.md`](docs/Reference/Packages.md) — add a new bullet between `tbIDE Package` and the closing of the "Built-In Packages" list. + +**License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2023; first release v0.0.1.0 on 18-FEB-2023) — same situation as every other Wayne Phillips package. Pages are fully original content; **omit** the `vba_attribution: true` flag. + ## Page template Match the existing style. Worked examples to imitate: @@ -1250,6 +1436,7 @@ Match the existing style. Worked examples to imitate: - Assert module page (single-file, all members inline): `docs/Reference/Assert/Exact.md`. - CEF control class (folder-style with a sub-page): `docs/Reference/CEF/CefBrowser/index.md` + `docs/Reference/CEF/CefBrowser/EnvironmentOptions.md`. - Generic class (single-file, `(Of T1, T2)`): `docs/Reference/WinEventLogLib/EventLog.md`. +- Folder-style control with collection sub-objects: pattern to follow for WinNativeCommonCtls's `ImageList/`, `ListView/`, `TreeView/` — `/index.md` for the control's own surface plus sibling `/.md` per collection / item. Mirror CustomControls's `WaynesButton/` + `WaynesButton/WaynesButtonState.md` shape. Skeleton: @@ -1258,10 +1445,12 @@ Skeleton: title: parent: Module | Package> # Pick the permalink that matches the section: -# Core → /tB/Core/ -# VBA module → /tB/Modules// (legacy URL scheme retained) -# VBRUN module → /tB/Packages/VBRUN// -# VB class → /tB/Packages/VB/ (or /tB/Packages/VB// for folder-style) +# Core → /tB/Core/ +# VBA module → /tB/Modules// (legacy URL scheme retained) +# VBRUN module → /tB/Packages/VBRUN// +# VB class → /tB/Packages/VB/ (or /tB/Packages/VB// for folder-style) +# WinNativeCommonCtls control → /tB/Packages/WinNativeCommonCtls/ (single-file) +# or /tB/Packages/WinNativeCommonCtls// (folder-style) permalink: /tB/Core/ redirect_from: # only if relocated; e.g. moved from Core/ to a Module/ - /tB/Core/ @@ -1325,6 +1514,9 @@ The URL prefixes are *not* uniform across packages — VBA pages live one segmen - WinServicesLib class / interface → `/tB/Packages/WinServicesLib/` (single-file; same depth as a single-file VB class) - WinServicesLib enumeration → `/tB/Packages/WinServicesLib/Enumerations/` (one segment deeper, parallel to WebView2 / CEF / CustomControls) - tbIDE class / CoClass → `/tB/Packages/tbIDE/` (single-file; same depth as a single-file VB class — no `Enumerations/` sub-folder, the nested enums live on their declaring class's page) +- WinNativeCommonCtls control → `/tB/Packages/WinNativeCommonCtls/` (single-file — used by `DTPicker`, `MonthView`, `ProgressBar`, `Slider`, `UpDown`) or `/tB/Packages/WinNativeCommonCtls//` (folder-style — used by `ImageList/`, `ListView/`, `TreeView/`, each carrying their sub-object companion pages) +- WinNativeCommonCtls sub-object → `/tB/Packages/WinNativeCommonCtls//` (e.g. `ImageList/ListImage`, `ListView/ListItem`, `TreeView/Node`) +- WinNativeCommonCtls enumeration → `/tB/Packages/WinNativeCommonCtls/Enumerations/` (one segment deeper, parallel to WebView2 / CEF / CustomControls / WinServicesLib) Common patterns: @@ -1414,6 +1606,24 @@ Common patterns: | tbIDE `Packages/tbIDE/X` | VBRUN `Packages/VBRUN//Y` | `[Y](../VBRUN//Y)` | | tbIDE `Packages/tbIDE/X` | VB `Packages/VB/Y` | `[Y](../VB/Y)` | | tbIDE `Packages/tbIDE/X` | `Core/Y` | `[Y](../../Core/Y)` | +| WNCC `Packages/WinNativeCommonCtls/X` (single-file) | sibling `Packages/WinNativeCommonCtls/Y` | `[Y](Y)` | +| WNCC `Packages/WinNativeCommonCtls/X` (single-file) | `Packages/WinNativeCommonCtls//` (folder-style) | `[Y](/)` | +| WNCC `Packages/WinNativeCommonCtls/X` (single-file) | `Packages/WinNativeCommonCtls/Enumerations/Y` | `[Y](Enumerations/Y)` | +| WNCC `Packages/WinNativeCommonCtls/X` (single-file) | VBRUN `Packages/VBRUN//Y` | `[Y](../VBRUN//Y)` | +| WNCC `Packages/WinNativeCommonCtls/X` (single-file) | VB `Packages/VB/Y` | `[Y](../VB/Y)` | +| WNCC `Packages/WinNativeCommonCtls/X` (single-file) | `Core/Y` | `[Y](../../Core/Y)` | +| WNCC `Packages/WinNativeCommonCtls//index` | sibling `Packages/WinNativeCommonCtls/Y` (single-file) | `[Y](../Y)` | +| WNCC `Packages/WinNativeCommonCtls//index` | `Packages/WinNativeCommonCtls//` | `[Y](..//)` | +| WNCC `Packages/WinNativeCommonCtls//index` | `Packages/WinNativeCommonCtls/Enumerations/Y` | `[Y](../Enumerations/Y)` | +| WNCC `Packages/WinNativeCommonCtls//index` | VBRUN `Packages/VBRUN//Y` | `[Y](../../VBRUN//Y)` | +| WNCC `Packages/WinNativeCommonCtls//index` | `Core/Y` | `[Y](../../../Core/Y)` | +| WNCC `Packages/WinNativeCommonCtls//` | sibling `/` | `[Y]()` | +| WNCC `Packages/WinNativeCommonCtls//` | parent `/` (index) | `[Y](.)` | +| WNCC `Packages/WinNativeCommonCtls//` | sibling control (single-file) | `[Y](../)` | +| WNCC `Packages/WinNativeCommonCtls//` | `Packages/WinNativeCommonCtls/Enumerations/Y` | `[Y](../Enumerations/Y)` | +| WNCC `Packages/WinNativeCommonCtls/Enumerations/X` | sibling `Enumerations/Y` | `[Y](Y)` | +| WNCC `Packages/WinNativeCommonCtls/Enumerations/X` | `Packages/WinNativeCommonCtls/` (single-file) | `[Y](../)` | +| WNCC `Packages/WinNativeCommonCtls/Enumerations/X` | `Packages/WinNativeCommonCtls//` (folder-style) | `[Y](..//)` | | WinEventLogLib `Packages/WinEventLogLib/X` | WinServicesLib `Packages/WinServicesLib/Y` | `[Y](../WinServicesLib/Y)` | | WinEventLogLib `Packages/WinEventLogLib/X` | WinNamedPipesLib `Packages/WinNamedPipesLib/Y` | `[Y](../WinNamedPipesLib/Y)` | | `Core/X` | VBA `Modules//Y` | `[Y](../Modules//Y)` | @@ -1427,6 +1637,7 @@ Common patterns: | `Core/X` | WinNamedPipesLib `Packages/WinNamedPipesLib/Y` | `[Y](../Packages/WinNamedPipesLib/Y)` | | `Core/X` | WinServicesLib `Packages/WinServicesLib/Y` | `[Y](../Packages/WinServicesLib/Y)` | | `Core/X` | tbIDE `Packages/tbIDE/Y` | `[Y](../Packages/tbIDE/Y)` | +| `Core/X` | WNCC `Packages/WinNativeCommonCtls/Y` | `[Y](../Packages/WinNativeCommonCtls/Y)` | | `Core/X` | `Core/Y` (sibling) | `[Y](Y)` | Always link to the **canonical** location (the page's `permalink:`), not to a `redirect_from` alias. Pages that have moved out of `Core/` retain a `redirect_from: /tB/Core/` so legacy links still work, but forward-style links should point at the new home. @@ -1444,6 +1655,7 @@ Always link to the **canonical** location (the page's `permalink:`), not to a `r - WinNamedPipesLib package → `..\tb-export\NewProject\Packages\WinNamedPipesLib\Sources\` — one `.twin` per public class: `NamedPipeServer.twin`, `NamedPipeServerConnection.twin`, `NamedPipeClientManager.twin`, `NamedPipeClientConnection.twin`. Each `.twin` also declares a `Private Interface INamedPipe*Internal` (refcount / dispatch helper used by the IOCP threads); skip those. Skip `APIs.twin`, `Constants.twin`, and `Helper.twin` (all `Private Module`). - WinServicesLib package → `..\tb-export\NewProject\Packages\WinServicesLib\Sources\` — one `.twin` per public class: `Services.twin` (the `[PredeclaredId]` coordinator), `ServiceManager.twin`, `ServiceCreator.twin`, `ServiceState.twin`. Plus `Interfaces.twin` for the `Public Interface ITbService` (the file also declares `Private Interface IServiceCreator` and `Private Interface IServiceManagerInternal` — skip both). Enumerations live in `Constants.twin` under `Public Module ServicesConstantsPublic` (four enums: `ServiceTypeConstants`, `ServiceStartConstants`, `ServiceControlCodeConstants`, `ServiceStatusConstants`). Skip `APIs.twin`, `Helper.twin`, and the `Private Module ServicesConstants` half of `Constants.twin` (all package-internal). The worked integration example — services + event log + named pipes wired together — is at `..\tbrepro\winlibs\tbServiceTest2\Sources\` (read `Startup.twin` and `SERVICES\TBSERVICE001.twin` first; the latter is the canonical `ITbService` implementation). - tbIDE package → `..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Sources\` — twenty-four flat `.twin` files, one per CoClass / Class. Read every file (none is `Private` plumbing; even `AddinTimer.twin` — the only `Class` without an explicit `Public` modifier — is user-instantiated). The six consumer-side example addins are at `..\tbrepro\sample10\…\Sources\MainModule.twin` through `..\tbrepro\sample15\…\Sources\MainModule.twin` — read sample10 first (the kitchen-sink "Hello, World" addin), then samples 11–14 (each one focuses on a single advanced custom-element widget — `chartjs` / `monaco` / `listview` / `virtuallistview`), then sample15 (the complete Global Search addin — exercises the file-system traversal + editor-navigation surface). Ignore the `CHANGELOG.md` inside the package source (it's a copy-paste from `WinNativeForms` and unrelated). + - WinNativeCommonCtls package → `..\tb-export\NewProject\Packages\WinNativeCommonCtls\Sources\` — two sub-folders: `CONTROLS\` (eight `.twin` files, one per control; each declares the heavy `BaseCtl` + the thin `` leaf — read both halves), and `SUPPORT\` for the sub-object classes (`ListImage.twin`, `ListImages.twin`, `ListItem.twin`, `ListItems.twin`, `ColumnHeader.twin`, `ColumnHeaders.twin`, `Node.twin`, `Nodes.twin`). For inherited surface, walk the relevant `VB.BaseControl*` chain in `..\tb-export\NewProject\Packages\VB\Sources\BASE\` — each control inherits one of `BaseControlFocusable`, `BaseControlFocusableNoFont`, `BaseControlNotFocusable`, or `BaseControlNotFocusable2`. Skip `SUPPORT\Anchors.twin` (entirely commented out), `SUPPORT\Misc.twin` (private — but `OrientationConstants` surfaces from it), `SUPPORT\ImagesHelper.twin` (private), `SUPPORT\Interfaces.twin` (three private interfaces — no doc page), `SUPPORT\TreeViewNodeCheckState.twin` (four private scheduled-callback helpers — no doc page), `SUPPORT\ImageListPropertyPage.twin` + `.tbform` (design-time `[FormDesignerId]` form — no doc page), and the Win32-plumbing halves of the `Consts.twin` modules. The user-facing enums in `SUPPORT\TreeViewConsts.twin` live in the `Public Module TreeViewPublic` block (line 327+); the user-facing `DTPickerFormatConstants` is at the bottom of `SUPPORT\DTPickerConsts.twin` (line 94+); `OrientationConstants` is in `SUPPORT\Misc.twin`; `ImlDrawConstants` is in `SUPPORT\ImageListConsts.twin`. 2. **Decide placement**: - Pure language keyword (parsed by the compiler, no runtime call) → `docs/Reference/Core/`. - Runtime function/property → `docs/Reference///`. Add `redirect_from: /tB/Core/` so legacy `tB/Core/` links still work. @@ -1461,6 +1673,7 @@ Always link to the **canonical** location (the page's `permalink:`), not to a `r - WinNamedPipesLib class → `docs/Reference/WinNamedPipesLib/.md` (single-file; one page per public class — `NamedPipeServer.md`, `NamedPipeServerConnection.md`, `NamedPipeClientManager.md`, `NamedPipeClientConnection.md`). No folder-style — none of the four classes have sub-pages. - WinServicesLib class → `docs/Reference/WinServicesLib/.md` (single-file; one page each for `Services.md`, `ServiceManager.md`, `ServiceCreator.md`, `ServiceState.md`, `ITbService.md`). WinServicesLib enumeration → `docs/Reference/WinServicesLib/Enumerations/.md` — one page each for `ServiceTypeConstants`, `ServiceStartConstants`, `ServiceControlCodeConstants`, `ServiceStatusConstants` (mirrors `WebView2/Enumerations/`, `CEF/Enumerations/`, `CustomControls/Enumerations/`, `VBRUN/Constants/`). - tbIDE class / CoClass → `docs/Reference/tbIDE/.md` — one single-file page per CoClass (or per concrete `Class` for `AddinTimer`). No folder-style, no `Enumerations/` sub-folder — the package's six nested enums (`RevealArea`, `EditorOpenOptions`, `ReadTextFlags`, `FileSystemItemType`, `VbBuildType`, `DebuggerEvaluateOptions`) each live on the declaring class's page (e.g. `RevealArea` on `CodeEditor.md`). + - WinNativeCommonCtls control without sub-objects → `docs/Reference/WinNativeCommonCtls/.md` (single-file; used by `DTPicker.md`, `MonthView.md`, `ProgressBar.md`, `Slider.md`, `UpDown.md`). WinNativeCommonCtls control *with* sub-objects → `docs/Reference/WinNativeCommonCtls//index.md` (folder-style; used by `ImageList/`, `ListView/`, `TreeView/`). Sub-object → `docs/Reference/WinNativeCommonCtls//.md` (e.g. `ImageList/ListImage.md`, `ListView/ListItem.md`, `ListView/ColumnHeaders.md`, `TreeView/Node.md`). WinNativeCommonCtls module-level enumeration → `docs/Reference/WinNativeCommonCtls/Enumerations/.md`. **Per-control nested enums** (`ImageListColorDepth`, `ListViewConstants`, `ListArrangeConstants`, `ListTextBackgroundConstants`, `ListLabelEditConstants`, `ListColumnAlignmentConstants`, `PrbOrientation`, `PrbScrolling`, `PrbState`, `TickStyleConstants`, `TextPositionConstants`) fold onto the declaring control's page rather than getting their own `Enumerations/` file — same pattern as CustomControls's `WaynesSlider.SliderDirection`. - Pick `` from VBA's grouping (Information, Interaction, Strings, FileSystem, DateTime, Math, Financial, Conversion, ...) and the existing folders under `Reference//`. 3. **Adapt content** (VBA-Docs sources): - Strip MS frontmatter (`ms.assetid`, `f1_keywords`, `keywords`, `ms.date`, `ms.localizationpriority`). @@ -1566,10 +1779,29 @@ Always link to the **canonical** location (the page's `permalink:`), not to a `r - **Folder iteration is thread-sensitive** — surface the `[Description]` warnings on `Folder.Count` / `Folder.Item` verbatim with a `> [!IMPORTANT]` callout, and recommend for-each as the supported traversal pattern. Sample 15's `PopulateFolderResultsRecursive` is the canonical example to inline on `Folder.md`. - The package's `CHANGELOG.md` is a copy-paste error (header says "twinBASIC WinNativeForms"); **do not** propagate it to the docs. There is no usable version history in-package. - Omit the `vba_attribution: true` frontmatter flag — these pages are fully original (the package is MIT-licensed, same as every other Wayne Phillips package). -13. **Flag tB deviations** with a `> [!NOTE]` callout (see next section). -14. **Update the parent index** (`//index.md`, `docs/Reference/VB/index.md`, `docs/Reference/WebView2/index.md`, `docs/Reference/Assert/index.md`, `docs/Reference/CustomControls/index.md` (and its `Styles/`, `Framework/`, `Enumerations/` sub-indices), `docs/Reference/CEF/index.md` (and its `Enumerations/` sub-index), `docs/Reference/WinEventLogLib/index.md`, `docs/Reference/WinNamedPipesLib/index.md`, `docs/Reference/WinServicesLib/index.md` (and its `Enumerations/` sub-index), `docs/Reference/tbIDE/index.md`, `Reference/Statements.md`, or `Reference/Procedures and Functions.md`) — turn an unlinked bullet into a link with a short blurb. Match the existing style of the page. If a new package is being added, also extend `docs/Reference/Packages.md` to list it. -15. **Add the page** to `Reference/Statements.md` or `Reference/Procedures and Functions.md` if it's a statement or callable and not already listed there. -16. **Run the [site integrity check](#site-integrity-check)** after the batch and before committing. +13. **Adapt content** (WinNativeCommonCtls `.twin` sources): + - Each control is a `BaseCtl` (where every member is declared) plus a thin `` leaf (`Inherits BaseCtl`, tagged `[WindowsControl(...)]`). Document on the leaf's name and treat the split as invisible — same approach as CEF's `CefBrowser` / `CefBrowserBaseCtl`. + - Walk `Inherits VB.BaseControl*` (one of `BaseControlFocusable` / `BaseControlFocusableNoFont` / `BaseControlNotFocusable` / `BaseControlNotFocusable2`) and its ancestors (`BaseControlRectDockable` → `BaseControlRect` → `BaseControl`, plus `BaseFont` for non-`NoFont` variants) to fold the inherited surface (`Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, `Visible`, `Enabled`, `BackColor` / `ForeColor` / `Font`, `Appearance`, `MousePointer` / `MouseIcon`, `ToolTipText`, `DragMode` / `DragIcon`, `Drag()`, `Refresh()`, `SetFocus()` for focusable, `ZOrder()`, `CausesValidation`, `TabIndex` / `TabStop` for focusable, `VisualStyles`, `hWnd`, `HelpContextID` / `WhatsThisHelpID`, …) into the control's Properties listing, same way VB-package and CEF / CustomControls control pages do. + - List own + inherited members alphabetically within Properties / Methods / Events sections (mirror `CheckBox.md`). + - The `[Description("…")]` attribute on each `Public` field / property / event gives the user-visible IDE one-liner — use it as the basis for the page entry, then expand. Some `[Description("")]` blocks are empty placeholders; write fully original prose for those. + - Do not list `[ClassId]`, `[InterfaceId]`, `[EventInterfaceId]`, `[COMCreatable(False)]`, `[EventsUseDispInterface]`, `[ComImport(True)]`, `[CustomDesigner(...)]`, `[Serialize(...)]`, `[NonBrowsable(...)]`, `[WithDispatchForwarding]` on any page — they are all COM / designer plumbing. + - **Do** mention `[WindowsControl(...)]` on the leaf class's intro paragraph as the marker that places the control on the form designer toolbox. **Do** mention `[Unimplemented]` (via `> [!NOTE]`) and `[Hidden]` (suppress the member entirely). + - Sub-object classes (`ListImage`, `ListImages`, `ListItem`, `ListItems`, `ColumnHeader`, `ColumnHeaders`, `Node`, `Nodes`) are all `[COMCreatable(False)]` and constructed by their container — write the intro paragraph as *"reached via `.`"* and don't surface the constructor signature. + - The collection classes (`ListImages`, `ListItems`, `ColumnHeaders`, `Nodes`) all have the same shape: `Count` property, `Item(Index|Key)` default member, `Add(…)`, `Clear`, `Remove`, plus `[Enumerator] Public Function _NewEnum() As stdole.IUnknown` for For-Each support. Document the shape consistently across all four pages. + - The container's typed property is `As BaseCtl` for cross-control parameters (e.g. `TreeView.ImageList As ImageListBaseCtl`, `ListView.Icons As ImageListBaseCtl`), but the user assigns the leaf type (`ImageList`). On the doc page, type the parameter as the **leaf** (`ImageList`) — the `BaseCtl` split is invisible to user code. + - `TbImageListPrivate` / `TbListViewPrivate` / `TbTreeViewPrivate` interfaces (declared `[ComImport(True)] Interface` at the top of each control's `.twin`) are package-internal refcount / dispatch helpers — **no doc page**, and do not surface their `Protected ... Implements .` half on the control's surface. + - **Common surface to surface once per control**: `Opacity` (with the OS 6.2+ caveat), `TransparencyKey` (same caveat), `OLEDropMode` + the six OLE drag-drop events + `OLEDrag()` method (only when `FEATURE_OLEDRAGDROP` is enabled — currently always is in the shipping build, so include them), `Parent` (`As Object`), `Object` (`As Object`). + - **Per-control nested enums**: list each on its declaring control's page under a `## ` section near the bottom, with a value table and `{: #_ }` anchors per row for deep-linking from properties / parameters using the enum. Do not split into separate `Enumerations/` pages — there are too many small enums and the navigation reads better with them in-place. + - For `ListView.BorderStyle` and `TreeView.BorderStyle`: both use `TreeBorderStyleConstants` (declared in `TreeViewPublic`). Surface the enum on `Enumerations/TreeBorderStyleConstants.md`; from each control's `BorderStyle` entry, link to the shared enum. + - The `Class_BeforeFirstMethodAccess` calling `[_HiddenModule].EnsureContainerIsLoaded(Me)` on every leaf is internal twinBASIC infrastructure; **do not** surface. + - The leaf class has its own `[ClassId]` / `[InterfaceId]` distinct from the base class's `[ClassId]` / `[InterfaceId]`. Both are internal — don't document either. + - Per-control idioms worth surfacing (see the WinNativeCommonCtls section above for the long list): `ImageList`'s bound-count guard against modification, `ListView`'s `View` / `Arrange` / `LabelEdit` interaction, `MonthView`'s `GetDayBold` callback event for highlighting holidays, `ProgressBar`'s three-axis configuration, `Slider`'s `SelStart` / `SelLength`, `TreeView`'s per-node `Sorted` overriding the control-level setting, `UpDown`'s lack of auto-buddy (manual partner-control wiring). + - The `CHANGELOG.md` is a placeholder (single bullet for v0.0.1.0); use the LICENCE.md copyright year (2023) as the package version reference, not the WIP table. + - Omit the `vba_attribution: true` frontmatter flag — these pages are fully original (the package is MIT-licensed, same as every other Wayne Phillips package). +14. **Flag tB deviations** with a `> [!NOTE]` callout (see next section). +15. **Update the parent index** (`//index.md`, `docs/Reference/VB/index.md`, `docs/Reference/WebView2/index.md`, `docs/Reference/Assert/index.md`, `docs/Reference/CustomControls/index.md` (and its `Styles/`, `Framework/`, `Enumerations/` sub-indices), `docs/Reference/CEF/index.md` (and its `Enumerations/` sub-index), `docs/Reference/WinEventLogLib/index.md`, `docs/Reference/WinNamedPipesLib/index.md`, `docs/Reference/WinServicesLib/index.md` (and its `Enumerations/` sub-index), `docs/Reference/tbIDE/index.md`, `docs/Reference/WinNativeCommonCtls/index.md` (and its `Enumerations/` sub-index, plus the per-container index pages for `ImageList/`, `ListView/`, `TreeView/`), `Reference/Statements.md`, or `Reference/Procedures and Functions.md`) — turn an unlinked bullet into a link with a short blurb. Match the existing style of the page. If a new package is being added, also extend `docs/Reference/Packages.md` to list it. +16. **Add the page** to `Reference/Statements.md` or `Reference/Procedures and Functions.md` if it's a statement or callable and not already listed there. +17. **Run the [site integrity check](#site-integrity-check)** after the batch and before committing. ## twinBASIC deviations from VBA to flag From 67966926118dc228a26ef6aaad00581948ccb48f Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Thu, 14 May 2026 18:36:41 +0200 Subject: [PATCH 2/3] Document WinNativeCommonCtls. --- WIP.md | 4 +- docs/Reference/Packages.md | 1 + .../Reference/WinNativeCommonCtls/DTPicker.md | 344 ++++++++++++++++ .../Enumerations/DTPickerFormatConstants.md | 24 ++ .../Enumerations/ImlDrawConstants.md | 31 ++ .../Enumerations/OrientationConstants.md | 21 + .../Enumerations/TreeBorderStyleConstants.md | 23 ++ .../Enumerations/TreeLabelEditConstants.md | 21 + .../Enumerations/TreeLineStyleConstants.md | 21 + .../Enumerations/TreeRelationshipConstants.md | 24 ++ .../Enumerations/TreeSortOrderConstants.md | 22 ++ .../Enumerations/TreeSortTypeConstants.md | 22 ++ .../Enumerations/TreeStyleConstants.md | 44 +++ .../WinNativeCommonCtls/Enumerations/index.md | 40 ++ .../ImageList/ListImage.md | 90 +++++ .../ImageList/ListImages.md | 123 ++++++ .../WinNativeCommonCtls/ImageList/index.md | 136 +++++++ .../ListView/ColumnHeader.md | 100 +++++ .../ListView/ColumnHeaders.md | 103 +++++ .../WinNativeCommonCtls/ListView/ListItem.md | 147 +++++++ .../WinNativeCommonCtls/ListView/ListItems.md | 105 +++++ .../WinNativeCommonCtls/ListView/index.md | 368 ++++++++++++++++++ .../WinNativeCommonCtls/MonthView.md | 358 +++++++++++++++++ .../WinNativeCommonCtls/ProgressBar.md | 220 +++++++++++ docs/Reference/WinNativeCommonCtls/Slider.md | 217 +++++++++++ .../WinNativeCommonCtls/TreeView/Node.md | 174 +++++++++ .../WinNativeCommonCtls/TreeView/Nodes.md | 111 ++++++ .../WinNativeCommonCtls/TreeView/index.md | 340 ++++++++++++++++ docs/Reference/WinNativeCommonCtls/UpDown.md | 143 +++++++ docs/Reference/WinNativeCommonCtls/index.md | 83 ++++ 30 files changed, 3458 insertions(+), 2 deletions(-) create mode 100644 docs/Reference/WinNativeCommonCtls/DTPicker.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/DTPickerFormatConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/ImlDrawConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/OrientationConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeBorderStyleConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeLabelEditConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeLineStyleConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeRelationshipConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortOrderConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortTypeConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/TreeStyleConstants.md create mode 100644 docs/Reference/WinNativeCommonCtls/Enumerations/index.md create mode 100644 docs/Reference/WinNativeCommonCtls/ImageList/ListImage.md create mode 100644 docs/Reference/WinNativeCommonCtls/ImageList/ListImages.md create mode 100644 docs/Reference/WinNativeCommonCtls/ImageList/index.md create mode 100644 docs/Reference/WinNativeCommonCtls/ListView/ColumnHeader.md create mode 100644 docs/Reference/WinNativeCommonCtls/ListView/ColumnHeaders.md create mode 100644 docs/Reference/WinNativeCommonCtls/ListView/ListItem.md create mode 100644 docs/Reference/WinNativeCommonCtls/ListView/ListItems.md create mode 100644 docs/Reference/WinNativeCommonCtls/ListView/index.md create mode 100644 docs/Reference/WinNativeCommonCtls/MonthView.md create mode 100644 docs/Reference/WinNativeCommonCtls/ProgressBar.md create mode 100644 docs/Reference/WinNativeCommonCtls/Slider.md create mode 100644 docs/Reference/WinNativeCommonCtls/TreeView/Node.md create mode 100644 docs/Reference/WinNativeCommonCtls/TreeView/Nodes.md create mode 100644 docs/Reference/WinNativeCommonCtls/TreeView/index.md create mode 100644 docs/Reference/WinNativeCommonCtls/UpDown.md create mode 100644 docs/Reference/WinNativeCommonCtls/index.md diff --git a/WIP.md b/WIP.md index 6de66b3..82ac075 100644 --- a/WIP.md +++ b/WIP.md @@ -4,7 +4,7 @@ Jekyll site (`just-the-docs` theme) deploying to `docs.twinbasic.com`. Source un ## Status -Reference documentation is **mostly complete**. Eleven packages have full reference coverage adapted from primary sources (Microsoft VBA-Docs CC-BY-4.0 for the runtime library, `.twin` source for the twinBASIC-specific packages); a twelfth (WinNativeCommonCtls) is in progress. The CEF and WebView2 packages also carry a tutorial set. +Reference documentation is **complete** for all twelve packages, adapted from primary sources (Microsoft VBA-Docs CC-BY-4.0 for the runtime library, `.twin` source for the twinBASIC-specific packages). The CEF and WebView2 packages also carry a tutorial set. | Package | Reference | Tutorials | |--------------------------------------|-------------|-----------| @@ -19,7 +19,7 @@ Reference documentation is **mostly complete**. Eleven packages have full refere | WinNamedPipesLib | done | — | | WinServicesLib | done | — | | tbIDE | done | — | -| WinNativeCommonCtls | in progress | — | +| WinNativeCommonCtls | done | — | The rest of this file is the maintenance guide for adding new pages or updating existing ones — primary-source paths, page templates, cross-section linking conventions, the per-symbol workflow, and the integrity check. diff --git a/docs/Reference/Packages.md b/docs/Reference/Packages.md index 51516b2..4493f3b 100644 --- a/docs/Reference/Packages.md +++ b/docs/Reference/Packages.md @@ -30,3 +30,4 @@ These packages are built into twinBASIC and are always available, even offline. - [WinNamedPipesLib Package](WinNamedPipesLib/) -- Windows named pipes as twinBASIC objects with an asynchronous IOCP-driven I/O model; **NamedPipeServer** + **NamedPipeServerConnection** on the host side, **NamedPipeClientManager** + **NamedPipeClientConnection** on the client side, with message-boundary semantics and a cookie-based correlation pattern across `AsyncRead` / `AsyncWrite` and their matching events - [WinServicesLib Package](WinServicesLib/) -- runs a twinBASIC EXE as one or more Windows services; the **Services** singleton coordinates configuration, install / uninstall, and the SCM dispatcher loop, while user-implemented [**ITbService**](WinServicesLib/ITbService) classes are surfaced through [**ServiceCreator**](WinServicesLib/ServiceCreator)`(Of T)` - [tbIDE Package](tbIDE/) -- the **addin SDK** for the twinBASIC IDE: every addin is a Standard DLL that exports `tbCreateCompilerAddin`, returns an object implementing the [**AddIn**](tbIDE/AddIn) contract, and from there reaches the IDE's toolbar, tool-window DOM, virtual file system, debug console, current project (and its `Evaluate` debug-console hook), keyboard shortcuts, and themes — all through the [**Host**](tbIDE/Host) object the IDE passes in +- [WinNativeCommonCtls Package](WinNativeCommonCtls/) -- VB6-compatible replacement for **Microsoft Common Controls 6.0** (`MSCOMCTL.OCX`) built on top of the Win32 ComCtl32 controls: eight controls ([**DTPicker**](WinNativeCommonCtls/DTPicker), [**ImageList**](WinNativeCommonCtls/ImageList/), [**ListView**](WinNativeCommonCtls/ListView/), [**MonthView**](WinNativeCommonCtls/MonthView), [**ProgressBar**](WinNativeCommonCtls/ProgressBar), [**Slider**](WinNativeCommonCtls/Slider), [**TreeView**](WinNativeCommonCtls/TreeView/), [**UpDown**](WinNativeCommonCtls/UpDown)) with the original member names preserved, plus the collection sub-objects ([**ListItems**](WinNativeCommonCtls/ListView/ListItems), [**ColumnHeaders**](WinNativeCommonCtls/ListView/ColumnHeaders), [**Nodes**](WinNativeCommonCtls/TreeView/Nodes), [**ListImages**](WinNativeCommonCtls/ImageList/ListImages)) and the user-facing enumerations diff --git a/docs/Reference/WinNativeCommonCtls/DTPicker.md b/docs/Reference/WinNativeCommonCtls/DTPicker.md new file mode 100644 index 0000000..513fee3 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/DTPicker.md @@ -0,0 +1,344 @@ +--- +title: DTPicker +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/DTPicker +has_toc: false +--- + +# DTPicker class +{: .no_toc } + +A **DTPicker** is a date / time picker control. The inline field shows the current date or time formatted per [**Format**](#format); clicking the dropdown arrow opens a [**MonthView**](MonthView)-style calendar for picking a new date, and dismissing the calendar updates [**Value**](#value). + +```tb +Private Sub Form_Load() + DTPicker1.Format = dtpShortDate + DTPicker1.MinDate = DateSerial(2020, 1, 1) + DTPicker1.MaxDate = DateSerial(2030, 12, 31) + DTPicker1.Value = Date +End Sub + +Private Sub DTPicker1_Change() + Debug.Print "User picked: " & DTPicker1.Value +End Sub +``` + +The control inherits the focusable rect-dockable surface from `BaseControlFocusable` — size, position, **Anchors**, **Dock**, **Font**, **BackColor** / **ForeColor**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **Refresh**, **SetFocus**, **TabIndex** / **TabStop**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. + +* TOC +{:toc} + +## Format and value + +[**Format**](#format) selects one of four display styles — long date, short date, time, or a custom format string supplied through [**CustomFormat**](#customformat). The inline value is always a **Date**, but [**Value**](#value) is typed **Variant** because a [**CheckBox**](#checkbox)-equipped picker may have no date assigned (the user can clear the checkbox), in which case [**Value**](#value) reads as **Null**. + +The convenience accessors [**Year**](#year), [**Month**](#month), [**Week**](#week), [**Day**](#day), [**Hour**](#hour), [**Minute**](#minute), and [**Second**](#second) decompose the current value into individual components; assigning to any of them rewrites [**Value**](#value) with the requested component changed. The [**StartOfWeek**](#startofweek) property selects the first-day-of-week used by the calendar dropdown and by [**Week**](#week). + +## Custom format and callback events + +When [**Format**](#format) is set to **dtpCustom**, the [**CustomFormat**](#customformat) string drives the display. The format syntax follows the Win32 `GetDateFormat` / `GetTimeFormat` picture string (e.g. `"dddd, MMMM dd, yyyy"`). Tokens enclosed in callback markers (`X` literals in the format) raise the [**Format**](#format-event), [**FormatSize**](#formatsize), and [**CallbackKeyDown**](#callbackkeydown) events so the application can render its own field content and respond to keyboard navigation across it. + +## Calendar appearance + +When the dropdown calendar is shown, the [**CalendarBackColor**](#calendarbackcolor), [**CalendarForeColor**](#calendarforecolor), [**CalendarTitleBackColor**](#calendartitlebackcolor), [**CalendarTitleForeColor**](#calendartitleforecolor), and [**CalendarTrailingForeColor**](#calendartrailingforecolor) properties control the calendar's colors via `DTM_SETMCCOLOR`. The [**CalendarShowToday**](#calendarshowtoday), [**CalendarShowTodayCircle**](#calendarshowtodaycircle), [**CalendarShowWeekNumbers**](#calendarshowweeknumbers), and [**CalendarShowTrailingDates**](#calendarshowtrailingdates) booleans toggle the corresponding `MCS_…` style flags on the embedded calendar. + +[**hWndCalendar**](#hwndcalendar) returns the Win32 handle of the dropped-down calendar window — useful for advanced customization. It is only valid between the [**DropDown**](#dropdown) and [**CloseUp**](#closeup) events. + +Properties +---------- + +### CalendarBackColor +{: .no_toc } + +The calendar dropdown's background color. **OLE_COLOR**. Default: **vbWindowBackground**. Applied to the embedded month calendar via `DTM_SETMCCOLOR` / `MCSC_MONTHBK`. + +### CalendarForeColor +{: .no_toc } + +The calendar dropdown's text color. **OLE_COLOR**. Default: **vbButtonText**. + +### CalendarShowToday +{: .no_toc } + +Whether the calendar dropdown shows the "Today: …" string at the bottom. **Boolean**. Default: **True**. + +### CalendarShowTodayCircle +{: .no_toc } + +Whether the calendar dropdown highlights today's date with a circle. **Boolean**. Default: **True**. + +### CalendarShowTrailingDates +{: .no_toc } + +Whether the calendar dropdown shows the leading and trailing days of the previous and next month. **Boolean**. Default: **True**. + +### CalendarShowWeekNumbers +{: .no_toc } + +Whether the calendar dropdown shows a week-number column on the left. **Boolean**. Default: **False**. + +### CalendarTitleBackColor +{: .no_toc } + +The calendar dropdown's title bar background color. **OLE_COLOR**. Default: **vb3DFace**. + +### CalendarTitleForeColor +{: .no_toc } + +The calendar dropdown's title bar text color. **OLE_COLOR**. Default: **vbButtonText**. + +### CalendarTrailingForeColor +{: .no_toc } + +The text color used for trailing days from adjacent months when [**CalendarShowTrailingDates**](#calendarshowtrailingdates) is **True**. **OLE_COLOR**. Default: **vbGrayText**. + +### CheckBox +{: .no_toc } + +Whether the picker includes a checkbox next to the date value. **Boolean**. Default: **False**. When **True**, the user can clear the checkbox to leave the picker without a value, in which case [**Value**](#value) returns **Null**. Assigning **Null** to [**Value**](#value) when **CheckBox** is **False** raises run-time error 35787 (*"Can't set Value to NULL when CheckBox property = FALSE"*). + +Changing this property at run time recreates the underlying Win32 window — the property cannot be flipped in the GWL_STYLE alone. + +### CustomFormat +{: .no_toc } + +The picture string used when [**Format**](#format) is **dtpCustom**. **String**. Default: empty. Follows the Win32 `GetDateFormat` syntax (e.g. `"dddd, MMMM dd, yyyy"`, `"hh:mm:ss tt"`). + +### Day +{: .no_toc } + +The day-of-month component of [**Value**](#value). **Integer** (1–31). Reading returns the current day; assigning rewrites the date with the new day, raising run-time error 380 if the assigned value is out of range for the current month. See also [**DayCount**](#daycount). + +### DayCount +{: .no_toc } + +The number of days in the current value's month. **Long**, read-only. Computed from [**Year**](#year) and [**Month**](#month). Useful for bounds-checking before assigning [**Day**](#day). + +### DayOfWeek +{: .no_toc } + +The day-of-week the current [**Value**](#value) falls on, as a [**VbDayOfWeek**](../../Modules/Constants/VbDayOfWeek) member (`vbSunday` through `vbSaturday`). Read-only. + +### Format +{: .no_toc } + +The display format. A member of [**DTPickerFormatConstants**](Enumerations/DTPickerFormatConstants): **dtpLongDate**, **dtpShortDate**, **dtpTime**, **dtpCustom**. Default: **dtpShortDate**. + +### Hour +{: .no_toc } + +The hour component of [**Value**](#value), in 24-hour form. **Integer** (1–23 — note that `0` is rejected with run-time error 380 by the setter; read returns the live value). Reading is unrestricted. + +### hWndCalendar +{: .no_toc } + +The Win32 handle of the dropped-down calendar window. **HWND**, read-only. Valid only between the [**DropDown**](#dropdown) and [**CloseUp**](#closeup) events; reads as 0 when the calendar is closed. + +### MaxDate +{: .no_toc } + +The upper bound of the navigable date range. **Date**. Default: `9999-12-31`. Assigning a value lower than [**MinDate**](#mindate) raises run-time error 35775. If the current [**Value**](#value) exceeds the new **MaxDate**, [**Value**](#value) is clamped down to **MaxDate**. + +### MinDate +{: .no_toc } + +The lower bound of the navigable date range. **Date**. Default: `1601-01-01`. Assigning a value higher than [**MaxDate**](#maxdate) raises run-time error 35775. If the current [**Value**](#value) is below the new **MinDate**, [**Value**](#value) is clamped up to **MinDate**. + +### Minute +{: .no_toc } + +The minute component of [**Value**](#value). **Integer** (1–59 on assignment; 0–59 on read). + +### Month +{: .no_toc } + +The month-of-year component of [**Value**](#value). **Integer** (1–12). Assigning an out-of-range value raises run-time error 380. + +### RightToLeft +{: .no_toc } + +> [!NOTE] +> **RightToLeft** is tagged `[Unimplemented]` and has no effect; reading and writing the property compiles but the underlying Win32 control's RTL mode is not switched. + +A **Boolean**. + +### Second +{: .no_toc } + +The seconds component of [**Value**](#value). **Integer** (1–59 on assignment; 0–59 on read). + +### StartOfWeek +{: .no_toc } + +Which day of the week is rendered as the leftmost column in the calendar dropdown. A [**VbDayOfWeek**](../../Modules/Constants/VbDayOfWeek) member. Defaults to the system's first-day-of-week setting (resolved through `vbUseSystemDayOfWeek`). + +### UpDown +{: .no_toc } + +Whether the picker uses a spin-button widget instead of a dropdown calendar. **Boolean**. Default: **False**. When **True**, the user adjusts the date by clicking up / down arrows next to each field; the calendar dropdown is suppressed. + +Changing this property at run time recreates the underlying Win32 window. + +### Value +{: .no_toc } + +The selected date / time. **Variant**. The default member. + +Reads as a **Date** when the checkbox is checked (or [**CheckBox**](#checkbox) is **False**) or **Null** when the checkbox is cleared. Assigning **Null** when [**CheckBox**](#checkbox) is **False** raises run-time error 35787. Assigning a date outside [[**MinDate**](#mindate), [**MaxDate**](#maxdate)] raises run-time error 35773. + +Assigning a numeric (non-**Date**) value implicitly converts via **CDate**. Assigning **Empty** is treated the same as assigning **Null**. Changing [**Value**](#value) fires [**Change**](#change) once the control is past its initialization phase. + +### Week +{: .no_toc } + +The ISO-style week-of-year for the current [**Value**](#value). **Integer** (1–53). The setter applies a delta of `DateAdd("ww", …)` so changing **Week** preserves the day-of-week within the week. Assigning out-of-range raises run-time error 380. Honors [**StartOfWeek**](#startofweek) when computing the week boundary. + +### Year +{: .no_toc } + +The year component of [**Value**](#value). **Integer**. + +Events +------ + +### CallbackKeyDown +{: .no_toc } + +Raised when the user presses a key while a custom callback field is focused. Lets the application interpret the key (e.g. arrow-up / arrow-down to cycle through enum values) and rewrite the date. + +Syntax: *object*\_**CallbackKeyDown**( **ByVal** *KeyCode* **As Integer**, **ByVal** *Shift* **As Integer**, **ByVal** *CallbackField* **As String**, *CallbackDate* **As Date** ) + +*KeyCode* +: A [**KeyCodeConstants**](../VBRUN/Constants/KeyCodeConstants) value identifying the pressed key. + +*Shift* +: A bitmask of [**ShiftConstants**](../VBRUN/Constants/ShiftConstants) values. + +*CallbackField* +: The picture-string token identifying which callback field is focused. + +*CallbackDate* +: **In / out** — the current value the application can mutate before the event returns. + +### Change +{: .no_toc } + +Raised when [**Value**](#value) has changed, either by user interaction or by code. Does not fire during the initial property-deserialization pass at form load. + +Syntax: *object*\_**Change**( ) + +### Click +{: .no_toc } + +Raised on a mouse click inside the control's rectangle. + +Syntax: *object*\_**Click**( ) + +### CloseUp +{: .no_toc } + +Raised when the dropdown calendar closes — either by the user picking a date, by clicking outside the calendar, or by pressing **Esc**. + +Syntax: *object*\_**CloseUp**( ) + +### DblClick +{: .no_toc } + +Raised on a double-click inside the control's rectangle. + +Syntax: *object*\_**DblClick**( ) + +### DragDrop +{: .no_toc } + +Inherited drag-drop event. See [**DragMode**](../VB/CheckBox#dragmode). + +### DragOver +{: .no_toc } + +Inherited drag-drop event. + +### DropDown +{: .no_toc } + +Raised when the dropdown calendar opens. The handler can use [**hWndCalendar**](#hwndcalendar) to customize the dropped-down calendar window. + +Syntax: *object*\_**DropDown**( ) + +### Format +{: #format-event .no_toc } + +Raised for each custom callback field that needs rendering, when [**Format**](#format) is **dtpCustom** and the [**CustomFormat**](#customformat) string contains callback tokens. + +Syntax: *object*\_**Format**( **ByVal** *CallbackField* **As String**, *FormattedString* **As String** ) + +*CallbackField* +: The picture-string token identifying which callback field is being rendered. + +*FormattedString* +: **Out** — the application sets this to the text the picker should display in the field. + +### FormatSize +{: .no_toc } + +Raised before [**Format**](#format-event) to ask how many character cells to reserve for the callback field. The picker uses the current [**Font**](../VB/CheckBox#font) to measure the rendered width. + +Syntax: *object*\_**FormatSize**( **ByVal** *CallbackField* **As String**, *Size* **As Integer** ) + +*CallbackField* +: The picture-string token identifying the callback field. + +*Size* +: **Out** — the application sets this to the expected character count. + +### GotFocus +{: .no_toc } + +Inherited focus event. + +### Initialize +{: .no_toc } + +Raised after the control's window has been created and its properties initialised from persisted state. Fires once per form-load. + +Syntax: *object*\_**Initialize**( ) + +### LostFocus +{: .no_toc } + +Inherited focus event. + +### MouseDown +{: .no_toc } + +Inherited mouse event. + +Syntax: *object*\_**MouseDown**( *Button* **As Integer**, *Shift* **As Integer**, *X* **As Single**, *Y* **As Single** ) + +### MouseMove +{: .no_toc } + +Inherited mouse event. + +### MouseUp +{: .no_toc } + +Inherited mouse event. + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. See [**OLEDropConstants**](../VBRUN/Constants/OLEDropConstants) for the **OLEDropMode** values. + +### Validate +{: .no_toc } + +Inherited validation event. Set *Cancel* to **True** to keep focus on the control. + +Syntax: *object*\_**Validate**( *Cancel* **As Boolean** ) + +## See Also + +- [MonthView](MonthView) -- the full-month calendar control; **DTPicker**'s dropdown uses the same underlying Win32 control +- [DTPickerFormatConstants](Enumerations/DTPickerFormatConstants) -- the **Format** values +- [ControlTypeConstants](../VBRUN/Constants/ControlTypeConstants) -- where **vbDTPicker** lives diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/DTPickerFormatConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/DTPickerFormatConstants.md new file mode 100644 index 0000000..e877b55 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/DTPickerFormatConstants.md @@ -0,0 +1,24 @@ +--- +title: DTPickerFormatConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/DTPickerFormatConstants +--- + +# DTPickerFormatConstants +{: .no_toc } + +Selects the display format used by a [**DTPicker**](../DTPicker) control. Carried by the [**DTPicker.Format**](../DTPicker#format) property. + +When set to **dtpCustom**, the picker also reads [**DTPicker.CustomFormat**](../DTPicker#customformat) to drive the actual display. + +| Member | Value | Description | +|-------------------|-------|------------------------------------------------------------------------| +| **dtpLongDate**{: #dtpLongDate } | 0 | Long date format, e.g. *"Tuesday, January 14, 2025"*. | +| **dtpShortDate**{: #dtpShortDate } | 1 | Short date format, e.g. *"1/14/2025"*. | +| **dtpTime**{: #dtpTime } | 2 | Time format, e.g. *"3:45:00 PM"*. | +| **dtpCustom**{: #dtpCustom } | 3 | Custom picture string from [**CustomFormat**](../DTPicker#customformat). | + +## See Also + +- [DTPicker](../DTPicker) -- the consuming control diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/ImlDrawConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/ImlDrawConstants.md new file mode 100644 index 0000000..96236e1 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/ImlDrawConstants.md @@ -0,0 +1,31 @@ +--- +title: ImlDrawConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/ImlDrawConstants +--- + +# ImlDrawConstants +{: .no_toc } + +Flag combinations passed to the *Style* parameter of [**ListImage.Draw**](../ImageList/ListImage#draw). Multiple flags can be **Or**-combined to compose render styles. + +```tb +' Draw a small icon with the focus rectangle overlaid: +ImageList1.ListImages("doc").Draw _ + PictureBox1.hDC, 0, 0, _ + ImlDrawTransparent Or ImlDrawFocus +``` + +| Member | Value | Description | +|---------------------------|-------|--------------------------------------------------------------------------| +| **ImlDrawNormal**{: #ImlDrawNormal } | 1 | Render in the normal state (no overlays). | +| **ImlDrawTransparent**{: #ImlDrawTransparent } | 2 | Honour the image's mask / alpha — transparent pixels stay transparent. | +| **ImlDrawSelected**{: #ImlDrawSelected } | 4 | Render with the selection-color overlay (typically a blue tint). | +| **ImlDrawFocus**{: #ImlDrawFocus } | 8 | Render with the focus-rectangle overlay (dotted border). | +| **ImlDrawNoMask**{: #ImlDrawNoMask } | 16 | Bypass the mask — draw the entire bitmap including pixels that would normally be transparent. | + +## See Also + +- [ImageList](../ImageList/) -- the parent control +- [ListImage.Draw](../ImageList/ListImage#draw) -- the consuming method diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/OrientationConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/OrientationConstants.md new file mode 100644 index 0000000..33ae028 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/OrientationConstants.md @@ -0,0 +1,21 @@ +--- +title: OrientationConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/OrientationConstants +--- + +# OrientationConstants +{: .no_toc } + +The horizontal / vertical enumeration shared by [**Slider.Orientation**](../Slider#orientation) and [**UpDown.Orientation**](../UpDown#orientation). + +| Member | Value | Description | +|---------------------------------|-------|-----------------------| +| **ccOrientationHorizontal**{: #ccOrientationHorizontal } | 0 | Horizontal orientation. | +| **ccOrientationVertical**{: #ccOrientationVertical } | 1 | Vertical orientation. | + +## See Also + +- [Slider](../Slider) -- consumer +- [UpDown](../UpDown) -- consumer diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeBorderStyleConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeBorderStyleConstants.md new file mode 100644 index 0000000..cd22a51 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeBorderStyleConstants.md @@ -0,0 +1,23 @@ +--- +title: TreeBorderStyleConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeBorderStyleConstants +--- + +# TreeBorderStyleConstants +{: .no_toc } + +The border style enumeration used by both [**TreeView.BorderStyle**](../TreeView/#borderstyle) and [**ListView.BorderStyle**](../ListView/#borderstyle). The `cc…` (common-controls) prefix reflects that the enum is shared across multiple controls in this package. + +The effective rendering interacts with [**Appearance**](../../VBRUN/Constants/AppearanceConstants): when **Appearance** is **vbAppear3d** and **BorderStyle** is **ccFixedSingle**, the control gets an OS-themed 3D edge (`WS_EX_CLIENTEDGE`); when **Appearance** is **vbAppearFlat** and **BorderStyle** is **ccFixedSingle**, the control gets a single-pixel flat border (`WS_BORDER`). + +| Member | Value | Description | +|-----------------------|-------|------------------------------| +| **ccNone**{: #ccNone } | 0 | No border around the control. | +| **ccFixedSingle**{: #ccFixedSingle } | 1 | Single-pixel border. | + +## See Also + +- [TreeView](../TreeView/) -- consumer +- [ListView](../ListView/) -- consumer diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeLabelEditConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeLabelEditConstants.md new file mode 100644 index 0000000..6291734 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeLabelEditConstants.md @@ -0,0 +1,21 @@ +--- +title: TreeLabelEditConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeLabelEditConstants +--- + +# TreeLabelEditConstants +{: .no_toc } + +Controls when inline label editing is triggered on a [**TreeView**](../TreeView/). Carried by [**TreeView.LabelEdit**](../TreeView/#labeledit). + +| Member | Value | Description | +|----------------------|-------|-----------------------------------------------------------------------------------| +| **tvwAutomatic**{: #tvwAutomatic } | 0 | Clicking an already-selected node opens the inline editor (after a short pause). | +| **tvwManual**{: #tvwManual } | 1 | Only programmatic [**StartLabelEdit**](../TreeView/#startlabeledit) calls open the editor. | +| **tvwDisabled**{: #tvwDisabled } | 2 | Label editing is disabled. | + +## See Also + +- [TreeView](../TreeView/) -- consumer diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeLineStyleConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeLineStyleConstants.md new file mode 100644 index 0000000..59831d4 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeLineStyleConstants.md @@ -0,0 +1,21 @@ +--- +title: TreeLineStyleConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeLineStyleConstants +--- + +# TreeLineStyleConstants +{: .no_toc } + +Controls whether the [**TreeView**](../TreeView/) draws tree lines from root nodes or only from child nodes. Carried by [**TreeView.LineStyle**](../TreeView/#linestyle). Only has visible effect when [**Style**](../TreeView/#style) is one of the **tvwTreelines…** variants. + +| Member | Value | Description | +|-----------------------|-------|----------------------------------------------------------------------------| +| **tvwTreeLines**{: #tvwTreeLines } | 0 | Tree lines connect children to their parents but not root-level peers. | +| **tvwRootLines**{: #tvwRootLines } | 1 | Tree lines connect root-level nodes to each other as well as child connections. | + +## See Also + +- [TreeView](../TreeView/) -- consumer +- [TreeStyleConstants](TreeStyleConstants) -- governs whether tree lines appear at all diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeRelationshipConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeRelationshipConstants.md new file mode 100644 index 0000000..0453636 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeRelationshipConstants.md @@ -0,0 +1,24 @@ +--- +title: TreeRelationshipConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeRelationshipConstants +--- + +# TreeRelationshipConstants +{: .no_toc } + +Describes where a new node is inserted relative to an existing node. Passed as the *Relationship* parameter of [**Nodes.Add**](../TreeView/Nodes#add). + +| Member | Value | Description | +|----------------------|-------|------------------------------------------------------------------------------------------| +| **tvwFirst**{: #tvwFirst } | 0 | The new node becomes the first sibling of *Relative*'s parent — i.e. the leftmost peer. | +| **tvwLast**{: #tvwLast } | 1 | The new node becomes the last sibling of *Relative*'s parent — i.e. the rightmost peer. | +| **tvwNext**{: #tvwNext } | 2 | The new node is inserted immediately after *Relative*, as its next sibling. | +| **tvwPrevious**{: #tvwPrevious } | 3 | The new node is inserted immediately before *Relative*, as its previous sibling. | +| **tvwChild**{: #tvwChild } | 4 | The new node becomes a child of *Relative*. | + +## See Also + +- [TreeView](../TreeView/) -- consumer +- [Nodes.Add](../TreeView/Nodes#add) -- the consuming method diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortOrderConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortOrderConstants.md new file mode 100644 index 0000000..77e1bd5 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortOrderConstants.md @@ -0,0 +1,22 @@ +--- +title: TreeSortOrderConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeSortOrderConstants +--- + +# TreeSortOrderConstants +{: .no_toc } + +The sort direction enumeration used by [**TreeView.SortOrder**](../TreeView/#sortorder) and [**Node.SortOrder**](../TreeView/Node#sortorder). + +| Member | Value | Description | +|-----------------------|-------|-----------------------------------| +| **tvwAscending**{: #tvwAscending } | 0 | Sort A-to-Z. | +| **tvwDescending**{: #tvwDescending } | 1 | Sort Z-to-A. | + +## See Also + +- [TreeView](../TreeView/) -- consumer +- [Node](../TreeView/Node) -- consumer (per-subtree sorting) +- [TreeSortTypeConstants](TreeSortTypeConstants) -- the companion enum selecting the comparison method diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortTypeConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortTypeConstants.md new file mode 100644 index 0000000..a1b6c32 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeSortTypeConstants.md @@ -0,0 +1,22 @@ +--- +title: TreeSortTypeConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeSortTypeConstants +--- + +# TreeSortTypeConstants +{: .no_toc } + +The string comparison enumeration used by [**TreeView.SortType**](../TreeView/#sorttype) and [**Node.SortType**](../TreeView/Node#sorttype). Selects between case-sensitive and case-insensitive sorting. + +| Member | Value | Description | +|--------------------|-------|-------------------------------------------------------------------------------------| +| **tvwBinary**{: #tvwBinary } | 0 | Case-sensitive comparison; uses `lstrcmpW` (binary Unicode order). | +| **tvwText**{: #tvwText } | 1 | Case-insensitive comparison; uses `lstrcmpiW`. | + +## See Also + +- [TreeView](../TreeView/) -- consumer +- [Node](../TreeView/Node) -- consumer (per-subtree sorting) +- [TreeSortOrderConstants](TreeSortOrderConstants) -- the companion enum selecting ascending / descending diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/TreeStyleConstants.md b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeStyleConstants.md new file mode 100644 index 0000000..be3679c --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/TreeStyleConstants.md @@ -0,0 +1,44 @@ +--- +title: TreeStyleConstants +parent: Enumerations +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/TreeStyleConstants +--- + +# TreeStyleConstants +{: .no_toc } + +Composite visual style of a [**TreeView**](../TreeView/), encoding a 3-bit combination of which elements appear: **plus-minus** buttons, **treelines**, and **picture** icons. The labels are always shown. + +Carried by [**TreeView.Style**](../TreeView/#style). Default: **tvwTreelinesPlusMinusPictureText**. + +The composite decoding: + +| [**Style**](../TreeView/#style) | Buttons | Lines | Icons | Labels | +|--------------------------------------------------|---------|-------|-------|--------| +| **tvwTextOnly** | — | — | — | yes | +| **tvwPictureText** | — | — | yes | yes | +| **tvwPlusMinusText** | yes | — | — | yes | +| **tvwPlusMinusPictureText** | yes | — | yes | yes | +| **tvwTreelinesText** | — | yes | — | yes | +| **tvwTreelinesPictureText** | — | yes | yes | yes | +| **tvwTreelinesPlusMinusText** | yes | yes | — | yes | +| **tvwTreelinesPlusMinusPictureText** | yes | yes | yes | yes | + +The enum's underlying values are 0–7, matching the order in the table. + +| Member | Value | +|----------------------------------------------|-------| +| **tvwTextOnly**{: #tvwTextOnly } | 0 | +| **tvwPictureText**{: #tvwPictureText } | 1 | +| **tvwPlusMinusText**{: #tvwPlusMinusText } | 2 | +| **tvwPlusMinusPictureText**{: #tvwPlusMinusPictureText } | 3 | +| **tvwTreelinesText**{: #tvwTreelinesText } | 4 | +| **tvwTreelinesPictureText**{: #tvwTreelinesPictureText } | 5 | +| **tvwTreelinesPlusMinusText**{: #tvwTreelinesPlusMinusText } | 6 | +| **tvwTreelinesPlusMinusPictureText**{: #tvwTreelinesPlusMinusPictureText } | 7 | + +## See Also + +- [TreeView](../TreeView/) -- consumer +- [TreeLineStyleConstants](TreeLineStyleConstants) -- selects whether root-level lines are drawn when the **tvwTreelines…** variants are in effect diff --git a/docs/Reference/WinNativeCommonCtls/Enumerations/index.md b/docs/Reference/WinNativeCommonCtls/Enumerations/index.md new file mode 100644 index 0000000..cec8ed3 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Enumerations/index.md @@ -0,0 +1,40 @@ +--- +title: Enumerations +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Enumerations/ +has_children: true +has_toc: false +nav_order: 99 +--- + +# WinNativeCommonCtls Enumerations +{: .no_toc } + +The ten module-level enumerations declared in the package's shared modules and exposed to user code. Each is reachable from any project that references the package. + +Per-control nested enumerations (those declared *inside* a `BaseCtl` class — `ListViewConstants`, `ListArrangeConstants`, `ListLabelEditConstants`, `ListTextBackgroundConstants`, `ListColumnAlignmentConstants`, `PrbOrientation`, `PrbScrolling`, `PrbState`, `TickStyleConstants`, `TextPositionConstants`, `ImageListColorDepth`) are documented on the page of the control that declares them, not under this folder. + +* TOC +{:toc} + +## DTPicker + +- [DTPickerFormatConstants](DTPickerFormatConstants) -- the [**DTPicker.Format**](../DTPicker#format) values + +## ImageList + +- [ImlDrawConstants](ImlDrawConstants) -- the *Style* flags for [**ListImage.Draw**](../ImageList/ListImage#draw) + +## Shared (Slider, UpDown) + +- [OrientationConstants](OrientationConstants) -- the horizontal / vertical enum used by [**Slider.Orientation**](../Slider#orientation) and [**UpDown.Orientation**](../UpDown#orientation) + +## TreeView (and ListView via TreeBorderStyleConstants) + +- [TreeBorderStyleConstants](TreeBorderStyleConstants) -- the [**TreeView.BorderStyle**](../TreeView/#borderstyle) and [**ListView.BorderStyle**](../ListView/#borderstyle) values +- [TreeLabelEditConstants](TreeLabelEditConstants) -- the [**TreeView.LabelEdit**](../TreeView/#labeledit) values +- [TreeLineStyleConstants](TreeLineStyleConstants) -- the [**TreeView.LineStyle**](../TreeView/#linestyle) values +- [TreeRelationshipConstants](TreeRelationshipConstants) -- the *Relationship* values for [**Nodes.Add**](../TreeView/Nodes#add) +- [TreeSortOrderConstants](TreeSortOrderConstants) -- the [**TreeView.SortOrder**](../TreeView/#sortorder) and [**Node.SortOrder**](../TreeView/Node#sortorder) values +- [TreeSortTypeConstants](TreeSortTypeConstants) -- the [**TreeView.SortType**](../TreeView/#sorttype) and [**Node.SortType**](../TreeView/Node#sorttype) values +- [TreeStyleConstants](TreeStyleConstants) -- the [**TreeView.Style**](../TreeView/#style) values diff --git a/docs/Reference/WinNativeCommonCtls/ImageList/ListImage.md b/docs/Reference/WinNativeCommonCtls/ImageList/ListImage.md new file mode 100644 index 0000000..b77e81e --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ImageList/ListImage.md @@ -0,0 +1,90 @@ +--- +title: ListImage +parent: ImageList +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ImageList/ListImage +has_toc: false +--- + +# ListImage class +{: .no_toc } + +A **ListImage** is one picture inside an [**ImageList**](.)'s [**ListImages**](ListImages) collection. Returned from [**ListImages.Add**](ListImages#add) and from [**ListImages.Item**](ListImages#item). + +The class is tagged `[COMCreatable(False)]` — user code never instantiates a **ListImage** directly; the [**ListImages**](ListImages) collection creates them as part of its **Add** method. + +```tb +Dim img As ListImage = ImageList1.ListImages.Add(, "open", LoadPicture("open.ico")) +img.Tag = "Open document command" +``` + +* TOC +{:toc} + +Properties +---------- + +### Index +{: .no_toc } + +The 1-based position of the image within the parent collection. **Long**, read-only. Equals [**ListImages.Item**](ListImages#item)(*key*).Index for the same image. + +### Key +{: .no_toc } + +The string key the image was added under. **String**. Default: empty string. Lookups through [**ListImages.Item**](ListImages#item) accept either the numeric **Index** or the string **Key**. + +### Picture +{: .no_toc } + +The original `StdPicture` that was passed to [**ListImages.Add**](ListImages#add). **StdPicture**. The image list also keeps an internal scaled bitmap copy; modifying the returned **Picture** does not change what the list renders. + +### Tag +{: .no_toc } + +Arbitrary data the application can attach to the image. **Variant**. + +Methods +------- + +### Draw +{: .no_toc } + +Renders the image into a Win32 device context at a given position. Useful for owner-draw scenarios where the application needs to paint the icon itself rather than going through a consumer control. + +Syntax: *object*.**Draw** *hDC* [, *x* [, *y* [, *Style* ] ] ] + +*hDC* +: An **OLE_HANDLE** to the destination device context. + +*x* +: *optional* A **Variant** containing the horizontal pixel offset. Default: 0. + +*y* +: *optional* A **Variant** containing the vertical pixel offset. Default: 0. + +*Style* +: *optional* A combination of [**ImlDrawConstants**](../Enumerations/ImlDrawConstants) flags controlling the draw mode (normal, transparent, masked, selected, focused). Multiple flags can be **Or**-combined. + +```tb +Private Sub PictureBox1_Paint() + ImageList1.ListImages("doc").Draw _ + PictureBox1.hDC, 0, 0, _ + ImlDrawTransparent Or ImlDrawSelected +End Sub +``` + +### ExtractIcon +{: .no_toc } + +Returns a fresh `StdPicture` containing the image rendered as an `HICON`. Distinct from the [**Picture**](#picture) property, which returns the original bitmap; **ExtractIcon** is a new icon built from the cached bitmap with transparency applied. + +Syntax: *object*.**ExtractIcon** **As IPictureDisp** + +The returned **IPictureDisp** owns its icon handle and destroys it when it goes out of scope. + +## See Also + +- [ImageList](.) -- the parent control +- [ListImages](ListImages) -- the collection holding **ListImage** instances +- [ImlDrawConstants](../Enumerations/ImlDrawConstants) -- the *Style* flag combinations for [**Draw**](#draw) diff --git a/docs/Reference/WinNativeCommonCtls/ImageList/ListImages.md b/docs/Reference/WinNativeCommonCtls/ImageList/ListImages.md new file mode 100644 index 0000000..ef5b229 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ImageList/ListImages.md @@ -0,0 +1,123 @@ +--- +title: ListImages +parent: ImageList +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ImageList/ListImages +has_toc: false +--- + +# ListImages class +{: .no_toc } + +The **ListImages** collection is the entry point for managing the pictures inside an [**ImageList**](.). Reached as `.ListImages`; supports adding, removing, indexed access, and `For Each` iteration. + +The class is tagged `[COMCreatable(False)]` — user code reaches **ListImages** through the parent [**ImageList**](.) control's [**ListImages**](.#listimages) property, never by direct instantiation. + +```tb +With ImageList1.ListImages + .Add , "doc", LoadPicture(App.Path & "\doc.ico") + .Add , "folder", LoadPicture(App.Path & "\folder.ico") + Debug.Print .Count ' 2 +End With + +Dim img As ListImage +For Each img In ImageList1.ListImages + Debug.Print img.Index, img.Key +Next +``` + +* TOC +{:toc} + +## Modification while bound + +If the parent [**ImageList**](.) is bound to a consuming control (a [**ListView**](../ListView/) or [**TreeView**](../TreeView/) has it set as their image-list property), [**Clear**](#clear) and [**Remove**](#remove) raise run-time error 35617 (*"ImageList cannot be modified while another control is bound to it"*). [**Add**](#add) is unaffected — new pictures can always be added. + +To rebuild a bound image list, first unbind by setting the consuming control's image-list property to **Nothing**. + +Properties +---------- + +### Count +{: .no_toc } + +The number of images in the collection. **Long**, read-only. + +### Item +{: .no_toc } + +Returns the [**ListImage**](ListImage) at the given index or with the given key. The default member, so `ImageList1.ListImages("doc")` works without writing `.Item("doc")`. + +Syntax: *object*.**Item** ( *Index* ) **As ListImage** + +*Index* +: A **Variant** that is either a 1-based **Long** position in the collection or a **String** key (case-sensitive — the collection uses `vbBinaryCompare`). + +Methods +------- + +### Add +{: .no_toc } + +Adds a picture to the collection. + +Syntax: *object*.**Add** ( [ *Index* ] [, *Key* ] [, *Picture* ] [, *Tag* ] ) **As ListImage** + +*Index* +: *optional* A **Long** giving the 1-based position at which to insert the new image. When omitted, the image is appended at the end. Out-of-range values raise run-time error 35600. + +*Key* +: *optional* A **String** name under which the image can be looked up. When omitted, the image has no key. Numeric strings are rejected with run-time error 35603. Keys must be unique within the collection. + +*Picture* +: *required* A **StdPicture** to add. Bitmaps (`vbPicTypeBitmap`) are scaled and masked according to [**MaskColor**](.#maskcolor) / [**UseMaskColor**](.#usemaskcolor). Icons (`vbPicTypeIcon`) are added directly with their own alpha mask. Omitting *Picture* raises run-time error 35607. + +*Tag* +: *optional* Arbitrary data attached to the new image; surfaced as [**ListImage.Tag**](ListImage#tag). + +Returns the newly-created [**ListImage**](ListImage). + +The first call to **Add** fixes [**ImageWidth**](.#imagewidth) and [**ImageHeight**](.#imageheight) (unless those were pre-set in the property sheet); all subsequent pictures are scaled to match. + +### Clear +{: .no_toc } + +Removes every picture from the collection. Resets [**ImageWidth**](.#imagewidth) and [**ImageHeight**](.#imageheight) to `0`, unlocking them for reassignment. + +Syntax: *object*.**Clear** + +Raises run-time error 35617 if the parent [**ImageList**](.) is currently bound to a consuming control. + +### Exists +{: .no_toc } + +Returns whether a picture with the given key exists in the collection. + +Syntax: *object*.**Exists** ( *Index* ) **As Boolean** + +*Index* +: A **Variant**, coerced to a **String** for the lookup. Case-sensitive. + +### Remove +{: .no_toc } + +Removes a picture from the collection. The remaining pictures' [**Index**](ListImage#index) values are recomputed so subsequent lookups by index still work. + +Syntax: *object*.**Remove** ( *Index* ) + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. Out-of-range / non-existent values raise run-time error 35601. Non-string non-numeric values raise run-time error 35603. + +Raises run-time error 35617 if the parent [**ImageList**](.) is currently bound to a consuming control. + +### _NewEnum +{: .no_toc } + +Returns the enumerator used by `For Each img In imageList.ListImages`. Iterates the pictures in **Index** order. + +Syntax: *object*.**_NewEnum** **As Object** + +## See Also + +- [ImageList](.) -- the parent control +- [ListImage](ListImage) -- one picture in the collection diff --git a/docs/Reference/WinNativeCommonCtls/ImageList/index.md b/docs/Reference/WinNativeCommonCtls/ImageList/index.md new file mode 100644 index 0000000..555ef00 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ImageList/index.md @@ -0,0 +1,136 @@ +--- +title: ImageList +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ImageList/ +has_toc: false +--- + +# ImageList class +{: .no_toc } + +An **ImageList** is an off-screen container for pictures, all of which are scaled to the same [**ImageWidth**](#imagewidth) × [**ImageHeight**](#imageheight) bitmap size. The control has no visible representation at run time — its purpose is to feed icons to other controls that consume them through their [**Icons**](../ListView/#icons), [**SmallIcons**](../ListView/#smallicons), [**ColumnHeaderIcons**](../ListView/#columnheadericons), or [**ImageList**](../TreeView/#imagelist) properties. + +```tb +Private Sub Form_Load() + ' Load some pictures via the Add method + ImageList1.ListImages.Add , "doc", LoadPicture("doc.ico") + ImageList1.ListImages.Add , "folder", LoadPicture("folder.ico") + ImageList1.ListImages.Add , "image", LoadPicture("image.ico") + + ' Bind to a TreeView + Set TreeView1.ImageList = ImageList1 + + ' Add nodes that reference images by Key + TreeView1.Nodes.Add , , , "My Folder", "folder" + TreeView1.Nodes.Add , , , "Report", "doc" +End Sub +``` + +The control inherits the rectangular non-focusable base surface from `BaseControlNotFocusable` — size and position (size is irrelevant at run time since the control isn't drawn), **Name**, **Tag**, **hWnd**. It does not surface **Visible**, **Anchors**, or **Dock** in any meaningful way, and never accepts focus. + +* TOC +{:toc} + +## Image size lock-in + +The first picture added to the list fixes its [**ImageWidth**](#imagewidth) and [**ImageHeight**](#imageheight) — every subsequent picture is scaled to match those dimensions. The sizes can also be set explicitly *before* any image is added (typically through the design-time properties), in which case the first **Add** call honors the pre-set values rather than measuring the incoming picture. + +Once any image is in the list, attempting to assign [**ImageWidth**](#imagewidth) or [**ImageHeight**](#imageheight) raises run-time error 35611 with the message *"Property is read-only if image list contains images"*. To resize an image list, call [**ListImages.Clear**](ListImages#clear) first. + +## Mask color and transparency + +When [**UseMaskColor**](#usemaskcolor) is **True** (the default), every bitmap added through [**ListImages.Add**](ListImages#add) is masked: pixels matching [**MaskColor**](#maskcolor) become transparent when the image is rendered into a consuming control. The default [**MaskColor**](#maskcolor) is `&H00C0C0C0` (silver), which matches the classic VB6 transparency convention. Icons (passed as `StdPicture` of type `vbPicTypeIcon`) carry their own alpha mask and are unaffected by **MaskColor** / **UseMaskColor**. + +Setting [**ColorDepth**](#colordepth) to **ColorDepth32Bit** disables masking entirely — the alpha channel is preserved directly. + +## Binding to consumers + +When an **ImageList** is bound to a [**ListView**](../ListView/) (through [**Icons**](../ListView/#icons), [**SmallIcons**](../ListView/#smallicons), or [**ColumnHeaderIcons**](../ListView/#columnheadericons)) or a [**TreeView**](../TreeView/) (through [**ImageList**](../TreeView/#imagelist)), the consuming control increments the **ImageList**'s internal bound-count. While the bound-count is non-zero, attempts to call [**ListImages.Clear**](ListImages#clear) or [**ListImages.Remove**](ListImages#remove) raise run-time error 35617 (*"ImageList cannot be modified while another control is bound to it"*). To modify the contents, first unbind by setting the consuming control's image-list property to **Nothing**. + +Properties +---------- + +### BackColor +{: .no_toc } + +The background color used to render the image list into a target DC via [**ListImage.Draw**](ListImage#draw) when [**UseBackColor**](#usebackcolor) is **True**. **OLE_COLOR**. Default: **vbWindowBackground**. + +### ColorDepth +{: .no_toc } + +The pixel depth of the underlying bitmap storage. A member of [**ImageListColorDepth**](#imagelistcolordepth). Default: **ColorDepth32Bit**. Once images have been added, this value is fixed for the life of the list — to change it, call [**ListImages.Clear**](ListImages#clear) first. + +### hImageList +{: .no_toc } + +The Win32 `HIMAGELIST` handle of the underlying ComCtl32 image list. **LongPtr**, read-only. Useful for advanced interoperability with raw Win32 APIs (e.g. `ImageList_Draw`, custom-draw callbacks); the handle is owned by the **ImageList** control and must not be destroyed by user code. + +### ImageHeight +{: .no_toc } + +The pixel height every image in the list is scaled to. **Long**. Default: `0` (uses the first added image's height). Assigning is allowed only while the list is empty; otherwise run-time error 35611. + +### ImageWidth +{: .no_toc } + +The pixel width every image in the list is scaled to. **Long**. Default: `0`. Same locking semantics as [**ImageHeight**](#imageheight). + +### ListImages +{: .no_toc } + +The [**ListImages**](ListImages) collection holding the pictures. Read-only. + +### MaskColor +{: .no_toc } + +The color treated as transparent when adding bitmap pictures. **OLE_COLOR**. Default: `&H00C0C0C0` (silver). Only honoured when [**UseMaskColor**](#usemaskcolor) is **True** and [**ColorDepth**](#colordepth) is not **ColorDepth32Bit**. + +### UseBackColor +{: .no_toc } + +Whether the image list reports a fixed background color (via `ImageList_SetBkColor`) to consuming controls. **Boolean**. Default: **False**. When **False**, the consuming control treats masked pixels as transparent. + +### UseMaskColor +{: .no_toc } + +Whether [**MaskColor**](#maskcolor) is treated as transparent when bitmaps are added. **Boolean**. Default: **True**. + +Methods +------- + +### Overlay +{: .no_toc } + +Composes two list-images into a single overlay picture. The first image is drawn, then the second is drawn over the top with its mask applied. + +Syntax: *object*.**Overlay** ( *Key1*, *Key2* ) **As StdPicture** + +*Key1* +: The **Index** or **Key** of the bottom image. + +*Key2* +: The **Index** or **Key** of the top image. + +Returns an **StdPicture** of type **vbPicTypeIcon** suitable for direct rendering or for use elsewhere — note this is a one-off snapshot, not a reference into the image list. The returned icon is destroyed when the **StdPicture** goes out of scope. + +## ImageListColorDepth +{: #imagelistcolordepth } + +Determines the pixel depth of the bitmap storage in an [**ImageList**](.). Declared on the **ImageList** class. + +| Member | Value | Description | +|-------------------------|-------|--------------------------------------------------------------| +| **ColorDepth4Bit**{: #ImageListColorDepth_ColorDepth4Bit } | 4 | 16-color palette. | +| **ColorDepth8Bit**{: #ImageListColorDepth_ColorDepth8Bit } | 8 | 256-color palette. | +| **ColorDepth16Bit**{: #ImageListColorDepth_ColorDepth16Bit } | 16 | High color (RGB565). | +| **ColorDepth24Bit**{: #ImageListColorDepth_ColorDepth24Bit } | 24 | True color (RGB888), no alpha. | +| **ColorDepth32Bit**{: #ImageListColorDepth_ColorDepth32Bit } | 32 | True color with full alpha channel; masking is disabled at this depth. | + +## See Also + +- [ListImage](ListImage) -- one picture in the list +- [ListImages](ListImages) -- the collection holding the pictures +- [ImlDrawConstants](../Enumerations/ImlDrawConstants) -- the *Style* parameter for [**ListImage.Draw**](ListImage#draw) +- [ListView](../ListView/) -- a typical consumer through [**Icons**](../ListView/#icons), [**SmallIcons**](../ListView/#smallicons), [**ColumnHeaderIcons**](../ListView/#columnheadericons) +- [TreeView](../TreeView/) -- a typical consumer through [**ImageList**](../TreeView/#imagelist) +- [ControlTypeConstants](../../VBRUN/Constants/ControlTypeConstants) -- where **vbImageList** lives diff --git a/docs/Reference/WinNativeCommonCtls/ListView/ColumnHeader.md b/docs/Reference/WinNativeCommonCtls/ListView/ColumnHeader.md new file mode 100644 index 0000000..38ed8a8 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ListView/ColumnHeader.md @@ -0,0 +1,100 @@ +--- +title: ColumnHeader +parent: ListView +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ListView/ColumnHeader +has_toc: false +--- + +# ColumnHeader class +{: .no_toc } + +A **ColumnHeader** represents a single column in a [**ListView**](.) running in **lvwReport** view. Returned from [**ColumnHeaders.Add**](ColumnHeaders#add) and from [**ColumnHeaders.Item**](ColumnHeaders#item). + +The class is tagged `[COMCreatable(False)]` — user code reaches **ColumnHeader** instances through the parent [**ListView**](.)'s [**ColumnHeaders**](ColumnHeaders) collection. + +```tb +With ListView1.ColumnHeaders + .Add , "name", "Name", 150 + .Add , "size", "Size", 80, lvwColumnRight + .Add , "date", "Date", 100, lvwColumnCenter +End With +``` + +* TOC +{:toc} + +Properties +---------- + +### Alignment +{: .no_toc } + +The horizontal alignment of the column's text. A member of [**ListColumnAlignmentConstants**](#listcolumnalignmentconstants). Default: **lvwColumnLeft**. + +> [!NOTE] +> The first column in a ListView must be left-aligned. Attempting to add a non-left-aligned column at position 1 raises run-time error 5. + +### Icon +{: .no_toc } + +The icon rendered in the header. **Variant** — either a 1-based **Long** index into [**ListView.ColumnHeaderIcons**](.#columnheadericons), or a **String** key. Assignment validates against the bound image list. + +### Index +{: .no_toc } + +The 1-based position of the column in the parent collection. **Long**, read-only. Attempting to assign raises run-time error 383. + +### Key +{: .no_toc } + +The string key the column was added under. **String**, read/write. + +### Left +{: .no_toc } + +The column's horizontal pixel position in the listview, computed as the sum of preceding columns' widths. **Single**, read-only. + +### Position +{: .no_toc } + +The column's visual position. **Long**, read/write. Distinct from [**Index**](#index) — when [**ListView.AllowColumnReorder**](.#allowcolumnreorder) is **True**, the user can drag columns to reorder them, in which case **Index** stays fixed but **Position** changes. + +Assigning a value outside `1..Count` raises run-time error 380. + +### SubItemIndex +{: .no_toc } + +The 0-based sub-item index this column displays. **Long**, read-only. Maps the column to a [**ListItem.SubItems**](ListItem#subitemsindex)(*index*) value. Returns `0` for the first column (which shows [**ListItem.Text**](ListItem#text)). + +### Tag +{: .no_toc } + +Arbitrary data the application can attach to the column. **Variant**. + +### Text +{: .no_toc } + +The column header text. **String**, read/write. The default member. + +### Width +{: .no_toc } + +The column's pixel width. **Single**, read/write. + +## ListColumnAlignmentConstants +{: #listcolumnalignmentconstants } + +Determines the horizontal alignment of a column's text. Declared on the **ColumnHeader** class. + +| Member | Value | Description | +|---------------------------|-------|-------------------| +| **lvwColumnLeft**{: #ListColumnAlignmentConstants_lvwColumnLeft } | 0 | Left-aligned text. | +| **lvwColumnRight**{: #ListColumnAlignmentConstants_lvwColumnRight } | 1 | Right-aligned text. | +| **lvwColumnCenter**{: #ListColumnAlignmentConstants_lvwColumnCenter } | 2 | Centered text. | + +## See Also + +- [ListView](.) -- the parent control +- [ColumnHeaders](ColumnHeaders) -- the collection holding **ColumnHeader** instances +- [ListItem](ListItem) -- a row, whose [**SubItems**](ListItem#subitemsindex) align with columns diff --git a/docs/Reference/WinNativeCommonCtls/ListView/ColumnHeaders.md b/docs/Reference/WinNativeCommonCtls/ListView/ColumnHeaders.md new file mode 100644 index 0000000..dc5011d --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ListView/ColumnHeaders.md @@ -0,0 +1,103 @@ +--- +title: ColumnHeaders +parent: ListView +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ListView/ColumnHeaders +has_toc: false +--- + +# ColumnHeaders class +{: .no_toc } + +The **ColumnHeaders** collection is the entry point for managing the columns of a [**ListView**](.) in **lvwReport** view. Reached as `.ColumnHeaders`; supports adding, removing, indexed access, and `For Each` iteration. + +The class is tagged `[COMCreatable(False)]` — user code reaches **ColumnHeaders** through the parent [**ListView**](.) control's [**ColumnHeaders**](.#columnheaders) property. + +```tb +With ListView1.ColumnHeaders + .Add , "name", "Name", 150 + .Add , "size", "Size", 80, lvwColumnRight + .Add , "date", "Date", 100, lvwColumnCenter +End With +``` + +* TOC +{:toc} + +Properties +---------- + +### Count +{: .no_toc } + +The number of columns in the collection. **Long**, read-only. + +### Item +{: .no_toc } + +Returns the [**ColumnHeader**](ColumnHeader) at the given index or with the given key. The default member, so `ListView1.ColumnHeaders("name")` works without writing `.Item("name")`. + +Syntax: *object*.**Item** ( *Index* ) **As ColumnHeader** + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. + +Methods +------- + +### Add +{: .no_toc } + +Adds a column to the listview. + +Syntax: *object*.**Add** ( [ *Index* ] [, *Key* ] [, *Text* ] [, *Width* ] [, *Alignment* ] [, *Icon* ] ) **As ColumnHeader** + +*Index* +: *optional* A **Long** giving the 1-based position at which to insert the new column. When omitted, the column is appended. + +*Key* +: *optional* A **String** name under which the column can be looked up. Keys must be unique within the collection (otherwise run-time error 35602). + +*Text* +: *optional* A **String** giving the column header label. + +*Width* +: *optional* A **Variant** giving the column's pixel width. When omitted, defaults to 96 pixels (scaled). + +*Alignment* +: *optional* A member of [**ListColumnAlignmentConstants**](ColumnHeader#listcolumnalignmentconstants). Default: **lvwColumnLeft**. Attempting to add a non-left-aligned column at position `1` raises run-time error 5. + +*Icon* +: *optional* A **Variant** identifying the header icon — either a 1-based **Long** index into [**ListView.ColumnHeaderIcons**](.#columnheadericons), or a **String** key. + +Returns the newly-created [**ColumnHeader**](ColumnHeader). + +### Clear +{: .no_toc } + +Removes every column from the listview. + +Syntax: *object*.**Clear** + +### Remove +{: .no_toc } + +Removes a column from the listview. + +Syntax: *object*.**Remove** ( *Index* ) + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. + +### _NewEnum +{: .no_toc } + +Returns the enumerator used by `For Each col In listView.ColumnHeaders`. Iterates columns in **Index** order. + +Syntax: *object*.**_NewEnum** **As stdole.IUnknown** + +## See Also + +- [ListView](.) -- the parent control +- [ColumnHeader](ColumnHeader) -- a single column header +- [ListColumnAlignmentConstants](ColumnHeader#listcolumnalignmentconstants) -- the **Alignment** values diff --git a/docs/Reference/WinNativeCommonCtls/ListView/ListItem.md b/docs/Reference/WinNativeCommonCtls/ListView/ListItem.md new file mode 100644 index 0000000..4a1373b --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ListView/ListItem.md @@ -0,0 +1,147 @@ +--- +title: ListItem +parent: ListView +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ListView/ListItem +has_toc: false +--- + +# ListItem class +{: .no_toc } + +A **ListItem** is a single row in a [**ListView**](.). Returned from [**ListItems.Add**](ListItems#add) and from [**ListItems.Item**](ListItems#item). In **lvwReport** view, the first column is the main label ([**Text**](#text)); subsequent columns are exposed through [**SubItems**](#subitemsindex)(*index*). + +The class is tagged `[COMCreatable(False)]` — user code reaches **ListItem** instances through the parent [**ListView**](.)'s [**ListItems**](ListItems) collection, never by direct instantiation. + +```tb +Dim item As ListItem = ListView1.ListItems.Add(, "doc1", "Report.docx", "doc") +item.SubItems(1) = "Word document" +item.SubItems(2) = "24 KB" +item.Bold = True +item.ForeColor = vbBlue +``` + +* TOC +{:toc} + +Properties +---------- + +### BackColor +{: .no_toc } + +The background color used to render this row. **OLE_COLOR**. Default: `-1` (transparent — defer to [**ListView.BackColor**](#backcolor)). + +### Bold +{: .no_toc } + +Whether the row is rendered in a bold font. **Boolean**. Default: **False**. + +### Checked +{: .no_toc } + +Whether the row's checkbox is checked. **Boolean**. Only meaningful when [**ListView.CheckBoxes**](.#checkboxes) is **True**. + +### EnsureVisible +{: .no_toc } + +Scrolls the listview so this row is visible. Available as a method (not a property — listed in the methods section below). + +### ForeColor +{: .no_toc } + +The text color used to render this row. **OLE_COLOR**. Default: **vbWindowText**. + +### Ghosted +{: .no_toc } + +Whether the row is rendered as ghosted / cut (typically half-transparent). **Boolean**. The visual mirrors the Win32 `LVIS_CUT` state. + +### Height +{: .no_toc } + +The pixel height of the row's selection rectangle. **Single**, read-only. + +### Icon +{: .no_toc } + +The large icon for the row in [**lvwIcon**](.#listviewconstants) view. **Variant** — either a 1-based **Long** index into [**ListView.Icons**](.#icons), or a **String** key. Assignment validates against the bound image list and raises run-time error 35601 (*"Element not found"*) for an unknown key, 35600 (*"Index out of bounds"*) for an out-of-range index, or 35613 (*"ImageList must be initialized before it can be used"*) if no image list is bound. + +### Index +{: .no_toc } + +The 1-based position of the row in the parent collection. **Long**, read-only. Attempting to assign raises run-time error 383. + +### Key +{: .no_toc } + +The string key the row was added under. **String**, read/write. Re-assignment moves the row inside the collection's internal index, preserving its position. + +### Left +{: .no_toc } + +The row's horizontal pixel position inside the listview. **Single**, read/write. Useful in [**lvwIcon**](.#listviewconstants) view for repositioning items. + +### Selected +{: .no_toc } + +Whether the row is selected. **Boolean**, read/write. Setting **Selected = True** also sets the focused state. + +### SmallIcon +{: .no_toc } + +The small icon for the row in non-icon views. **Variant** — either an index or a key into [**ListView.SmallIcons**](.#smallicons). Same validation as [**Icon**](#icon). + +### SubItems(Index) +{: #subitemsindex .no_toc } + +The sub-item text at the given 1-based column index in [**lvwReport**](.#listviewconstants) view. **String**, read/write. The main text is accessed through [**Text**](#text); **SubItems**(1) is the second column, **SubItems**(2) is the third, and so on. Index `0` is rejected with run-time error 380. + +Syntax: *object*.**SubItems**( *Index* ) [ **=** *value* ] + +### Tag +{: .no_toc } + +Arbitrary data the application can attach to the row. **Variant**. + +### Text +{: .no_toc } + +The row's main label text. **String**, read/write. The default member. Maps to the listview item's column-0 text. + +### ToolTipText +{: .no_toc } + +A tooltip string shown when the user hovers over this row. **String**. Surfaced through the listview's `LVS_EX_INFOTIP` extended style. + +### Top +{: .no_toc } + +The row's vertical pixel position inside the listview. **Single**, read/write. + +### Width +{: .no_toc } + +The pixel width of the row's selection rectangle. **Single**, read-only. + +Methods +------- + +### CreateDragImage +{: .no_toc } + +> [!NOTE] +> **CreateDragImage** is tagged `[Unimplemented]` in the current source. Calling it has no useful effect — the body is empty. + +### EnsureVisible +{: .no_toc } + +Scrolls the listview so this row is visible. + +Syntax: *object*.**EnsureVisible** + +## See Also + +- [ListView](.) -- the parent control +- [ListItems](ListItems) -- the collection holding **ListItem** instances +- [ColumnHeader](ColumnHeader) -- a column header (defines what [**SubItems**](#subitemsindex) align to) diff --git a/docs/Reference/WinNativeCommonCtls/ListView/ListItems.md b/docs/Reference/WinNativeCommonCtls/ListView/ListItems.md new file mode 100644 index 0000000..d2cbc69 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ListView/ListItems.md @@ -0,0 +1,105 @@ +--- +title: ListItems +parent: ListView +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ListView/ListItems +has_toc: false +--- + +# ListItems class +{: .no_toc } + +The **ListItems** collection is the entry point for managing the rows of a [**ListView**](.). Reached as `.ListItems`; supports adding, removing, indexed access, and `For Each` iteration. + +The class is tagged `[COMCreatable(False)]` — user code reaches **ListItems** through the parent [**ListView**](.) control's [**ListItems**](.#listitems) property. + +```tb +With ListView1.ListItems + .Add , "doc1", "Report.docx" + .Add , "doc2", "Budget.xlsx" + .Add , "doc3", "Photos.zip" + Debug.Print .Count ' 3 +End With + +Dim item As ListItem +For Each item In ListView1.ListItems + Debug.Print item.Index, item.Key, item.Text +Next +``` + +* TOC +{:toc} + +Properties +---------- + +### Count +{: .no_toc } + +The number of rows in the collection. **Long**, read-only. + +### Item +{: .no_toc } + +Returns the [**ListItem**](ListItem) at the given index or with the given key. The default member, so `ListView1.ListItems("doc1")` works without writing `.Item("doc1")`. + +Syntax: *object*.**Item** ( *Index* ) **As ListItem** + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. + +Methods +------- + +### Add +{: .no_toc } + +Adds a row to the listview. + +Syntax: *object*.**Add** ( [ *Index* ] [, *Key* ] [, *Text* ] [, *Icon* ] [, *SmallIcon* ] ) **As ListItem** + +*Index* +: *optional* A **Long** giving the 1-based position at which to insert the new row. When omitted, the row is appended at the end. Out-of-range values raise run-time error 35600. + +*Key* +: *optional* A **String** name under which the row can be looked up. When omitted, the row has no key. Keys must be unique within the collection (otherwise run-time error 35602). + +*Text* +: *optional* A **String** giving the row's main label. + +*Icon* +: *optional* A **Variant** identifying the row's large icon — either a 1-based **Long** index into [**ListView.Icons**](.#icons), or a **String** key. Validated against the bound image list. + +*SmallIcon* +: *optional* A **Variant** identifying the row's small icon, against [**ListView.SmallIcons**](.#smallicons). + +Returns the newly-created [**ListItem**](ListItem). + +### Clear +{: .no_toc } + +Removes every row from the listview. + +Syntax: *object*.**Clear** + +### Remove +{: .no_toc } + +Removes a row from the listview. The remaining rows' [**Index**](ListItem#index) values are renumbered. + +Syntax: *object*.**Remove** ( *Index* ) + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. + +### _NewEnum +{: .no_toc } + +Returns the enumerator used by `For Each item In listView.ListItems`. Iterates rows in **Index** order. + +Syntax: *object*.**_NewEnum** **As stdole.IUnknown** + +## See Also + +- [ListView](.) -- the parent control +- [ListItem](ListItem) -- one row in the collection diff --git a/docs/Reference/WinNativeCommonCtls/ListView/index.md b/docs/Reference/WinNativeCommonCtls/ListView/index.md new file mode 100644 index 0000000..5574bba --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ListView/index.md @@ -0,0 +1,368 @@ +--- +title: ListView +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ListView/ +has_toc: false +--- + +# ListView class +{: .no_toc } + +A **ListView** is a flexible multi-column / icon list with four distinct visual modes selected through the [**View**](#view) property: + +| [**View**](#view) | Description | +|--------------------------------|------------------------------------------------------------------------------------| +| **lvwIcon** | Large icons in a wrapping grid; each item shows an icon plus its label. | +| **lvwSmallIcon** | Small icons in a wrapping grid. | +| **lvwList** | A single column of small-icon-plus-label entries, wrapped into multiple columns to fit. | +| **lvwReport** | Multi-column table view with header row; columns are defined through [**ColumnHeaders**](ColumnHeaders). | + +The two main collections are reached through properties: [**ListItems**](#listitems) for the rows, and [**ColumnHeaders**](#columnheaders) for the **Report**-view column headers. + +```tb +Private Sub Form_Load() + ' Bind an image list and configure the view + Set ListView1.SmallIcons = ImageList1 + ListView1.View = lvwReport + + ' Define columns + ListView1.ColumnHeaders.Add , "name", "Name", 150 + ListView1.ColumnHeaders.Add , "type", "Type", 80 + ListView1.ColumnHeaders.Add , "size", "Size", 80, lvwColumnRight + + ' Add rows + Dim item As ListItem + Set item = ListView1.ListItems.Add(, "doc1", "Report.docx", "doc") + item.SubItems(1) = "Word document" + item.SubItems(2) = "24 KB" +End Sub + +Private Sub ListView1_ItemClick(Item As ListItem) + Debug.Print "Clicked: " & Item.Text +End Sub +``` + +The control inherits the focusable rect-dockable surface from `BaseControlFocusable` — size, position, **Anchors**, **Dock**, **Font**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **Refresh**, **SetFocus**, **TabIndex** / **TabStop**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. + +* TOC +{:toc} + +## Image lists + +A **ListView** can bind to three independent [**ImageList**](../ImageList/) instances, one per role: + +- **[Icons](#icons)** — large icons rendered in **lvwIcon** view. +- **[SmallIcons](#smallicons)** — small icons rendered in **lvwSmallIcon**, **lvwList**, and **lvwReport** views. +- **[ColumnHeaderIcons](#columnheadericons)** — small icons rendered inside the report-view column headers, addressed per-column through [**ColumnHeader.Icon**](ColumnHeader#icon). + +A [**ListItem**](ListItem) selects its icons through its **Icon** and **SmallIcon** properties, which can be either a 1-based **Long** index or a **String** key into the respective image list. + +## Selection and label editing + +Selection is single-row by default; setting [**MultiSelect**](#multiselect) to **True** lets the user **Ctrl**-click and **Shift**-click multiple items. The currently focused item is exposed as [**SelectedItem**](#selecteditem) (a [**ListItem**](ListItem)) and [**SelectedItemIndex**](#selecteditemindex) (a **Long**). [**ListItem.Selected**](ListItem#selected) reads / writes selection on an individual row. + +[**LabelEdit**](#labeledit) controls inline label editing: + +- **lvwAutomatic** — clicking an already-selected item starts an edit (after a short delay; this is the F2 / single-click-and-pause pattern). +- **lvwManual** — only programmatic [**StartLabelEdit**](#startlabeledit) calls open an editor. +- **lvwDisabled** — labels cannot be edited. + +Edit start fires [**BeforeLabelEdit**](#beforelabeledit) (cancellable), and edit end fires [**AfterLabelEdit**](#afterlabeledit) (cancellable, with the proposed new text). + +## Sorting, column reordering, and the header + +In **lvwReport** view, clicking a column header fires [**ColumnClick**](#columnclick), letting the application implement sorting (the package does not auto-sort). When [**AllowColumnReorder**](#allowcolumnreorder) is **True** in **lvwReport** view, the user can drag column headers to reorder them; the resulting order is reflected through [**ColumnHeader.Position**](ColumnHeader#position). + +[**hWndHeader**](#hwndheader) is the Win32 handle of the embedded `SysHeader32` window, exposed for raw Win32 customization. + +Properties +---------- + +### AllowColumnReorder +{: .no_toc } + +Whether the user can drag column headers to reorder them. **Boolean**. Default: **False**. Only effective in **lvwReport** view. + +### Appearance +{: .no_toc } + +How the control's border is drawn. A [**AppearanceConstants**](../../VBRUN/Constants/AppearanceConstants) member. Default: **vbAppear3d**. Inherited. + +### Arrange +{: .no_toc } + +How items are arranged in icon / small-icon view. A member of [**ListArrangeConstants**](#listarrangeconstants). Default: **lvwNone**. + +### BackColor +{: .no_toc } + +The background color of the list area. **OLE_COLOR**. Default: **vbWindowBackground**. + +### BorderStyle +{: .no_toc } + +The control's border style. A [**TreeBorderStyleConstants**](../Enumerations/TreeBorderStyleConstants) member: **ccNone** or **ccFixedSingle**. Default: **ccFixedSingle**. The enum is shared with [**TreeView**](../TreeView/). + +### CheckBoxes +{: .no_toc } + +Whether each row has a leading checkbox. **Boolean**. Default: **False**. When **True**, fires [**ItemCheck**](#itemcheck) on click. + +### ColumnHeaderIcons +{: .no_toc } + +The [**ImageList**](../ImageList/) used for column-header icons in **lvwReport** view. Individual columns reference an icon by setting [**ColumnHeader.Icon**](ColumnHeader#icon). + +### ColumnHeaders +{: .no_toc } + +The [**ColumnHeaders**](ColumnHeaders) collection. Read-only. + +### FlatScrollBar +{: .no_toc } + +Whether the control uses flat (rather than 3D) scrollbars. **Boolean**. Default: **False**. + +### FullRowSelect +{: .no_toc } + +Whether clicking on any cell in a row selects the entire row (as opposed to clicking only on the first column's text). **Boolean**. Default: **False**. Only meaningful in **lvwReport** view. + +### GridLines +{: .no_toc } + +Whether gridlines are drawn between rows and columns. **Boolean**. Default: **False**. Only meaningful in **lvwReport** view. + +### HideColumnHeaders +{: .no_toc } + +Whether the column header row is hidden in **lvwReport** view. **Boolean**. Default: **False**. + +### HideSelection +{: .no_toc } + +Whether selection highlight is hidden when the control does not have focus. **Boolean**. Default: **True**. + +### HotTracking +{: .no_toc } + +Whether items are highlighted as the mouse hovers over them (and tracked-click selection is enabled). **Boolean**. Default: **False**. + +### hWnd +{: .no_toc } + +The Win32 handle of the listview window. **LongPtr**, read-only. + +### hWndHeader +{: .no_toc } + +The Win32 handle of the embedded column-header window (`SysHeader32`). **LongPtr**, read-only. Tagged `[Hidden]` `[NonBrowsable]` — exposed only for advanced Win32 customization (e.g. subclassing the header). + +### Icons +{: .no_toc } + +The [**ImageList**](../ImageList/) used for large icons in **lvwIcon** view. Assignment increments the bound-count on the **ImageList** (and decrements the previous one's); see the [bound-count caveat](../ImageList/#binding-to-consumers). + +### LabelEdit +{: .no_toc } + +How inline label editing is triggered. A member of [**ListLabelEditConstants**](#listlabeleditconstants). Default: **lvwAutomatic**. + +### LabelWrap +{: .no_toc } + +Whether item labels wrap to multiple lines in **lvwIcon** view. **Boolean**. Default: **True**. + +### ListItems +{: .no_toc } + +The [**ListItems**](ListItems) collection — the rows of the list. Read-only. + +### MultiSelect +{: .no_toc } + +Whether the user can select multiple items. **Boolean**. Default: **False**. + +### SelectedItem +{: .no_toc } + +The currently focused [**ListItem**](ListItem), or **Nothing** if no row is focused. Read-only — to change selection, assign to [**ListItem.Selected**](ListItem#selected). + +### SelectedItemIndex +{: .no_toc } + +The 1-based index of the currently focused row, or `-1` if no row is focused. **Long**, read-only. + +### SmallIcons +{: .no_toc } + +The [**ImageList**](../ImageList/) used for small icons in **lvwSmallIcon**, **lvwList**, and **lvwReport** views. + +### TextBackground +{: .no_toc } + +Whether item-label text has an opaque background. A member of [**ListTextBackgroundConstants**](#listtextbackgroundconstants). Default: **lvwTransparent**. + +### View +{: .no_toc } + +The visual mode. A member of [**ListViewConstants**](#listviewconstants). Default: **lvwIcon**. + +Methods +------- + +### GetFirstVisible +{: .no_toc } + +Returns the first [**ListItem**](ListItem) currently visible in the viewport. Useful for virtualized scenarios where the application updates row content based on what the user is looking at. + +Syntax: *object*.**GetFirstVisible** **As ListItem** + +### StartLabelEdit +{: .no_toc } + +Opens the inline editor on the currently selected row. Used when [**LabelEdit**](#labeledit) is **lvwManual**. + +Syntax: *object*.**StartLabelEdit** + +Events +------ + +### AfterLabelEdit +{: .no_toc } + +Raised when an inline label edit completes. Set *Cancel* to **True** to revert; *NewString* carries the user's proposed new text. + +Syntax: *object*\_**AfterLabelEdit**( *Cancel* **As Boolean**, *NewString* **As String** ) + +### BeforeLabelEdit +{: .no_toc } + +Raised when an inline label edit is about to start. Set *Cancel* to **True** to block the edit. + +Syntax: *object*\_**BeforeLabelEdit**( *Cancel* **As Boolean** ) + +### Click +{: .no_toc } + +Raised on a mouse click inside the control. Distinct from [**ItemClick**](#itemclick), which fires only when the click lands on a row. + +Syntax: *object*\_**Click**( ) + +### ColumnClick +{: .no_toc } + +Raised when the user clicks a column header in **lvwReport** view. + +Syntax: *object*\_**ColumnClick**( *ColumnHeader* **As ColumnHeader** ) + +### DblClick +{: .no_toc } + +Raised on a double-click inside the control. + +Syntax: *object*\_**DblClick**( ) + +### DragDrop, DragOver +{: .no_toc } + +Inherited drag-drop events. + +### Initialize +{: .no_toc } + +Raised after the control's window has been created. + +### ItemCheck +{: .no_toc } + +Raised when the user toggles the checkbox on a row (only when [**CheckBoxes**](#checkboxes) is **True**). + +Syntax: *object*\_**ItemCheck**( *Item* **As ListItem** ) + +### ItemClick +{: .no_toc } + +Raised when a row becomes selected (via mouse click or keyboard navigation). + +Syntax: *object*\_**ItemClick**( *Item* **As ListItem** ) + +### KeyDown, KeyPress, KeyUp +{: .no_toc } + +Inherited keyboard events. + +### MouseDown, MouseMove, MouseUp +{: .no_toc } + +Inherited mouse events. + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. + +### Scroll +{: .no_toc } + +> [!NOTE] +> The **Scroll** event is declared on the control but tagged `[Unimplemented]` in the current source. It is reserved for a future release; do not rely on it. + +### Validate +{: .no_toc } + +Inherited validation event. + +## ListViewConstants +{: #listviewconstants } + +Determines the visual mode of a **ListView**. Declared on the **ListView** class. + +| Member | Value | Description | +|-----------------------|-------|----------------------------------------------------------------------| +| **lvwIcon**{: #ListViewConstants_lvwIcon } | 0 | Large icons in a wrapping grid. | +| **lvwSmallIcon**{: #ListViewConstants_lvwSmallIcon } | 1 | Small icons in a wrapping grid. | +| **lvwList**{: #ListViewConstants_lvwList } | 2 | Single-column list (wrapping into multiple columns). | +| **lvwReport**{: #ListViewConstants_lvwReport } | 3 | Multi-column report view with header row. | + +## ListArrangeConstants +{: #listarrangeconstants } + +Determines how items are auto-arranged in icon / small-icon view. Declared on the **ListView** class. + +| Member | Value | Description | +|------------------------|-------|--------------------------------------------------------| +| **lvwNone**{: #ListArrangeConstants_lvwNone } | 0 | No auto-arrangement; items stay where they were placed. | +| **lvwAutoLeft**{: #ListArrangeConstants_lvwAutoLeft } | 1 | Items auto-flow left-to-right. | +| **lvwAutoTop**{: #ListArrangeConstants_lvwAutoTop } | 2 | Items auto-flow top-to-bottom. | + +## ListTextBackgroundConstants +{: #listtextbackgroundconstants } + +Determines whether item-label text has an opaque or transparent background. Declared on the **ListView** class. + +| Member | Value | Description | +|-----------------------|-------|-------------------------------------------------------------------| +| **lvwTransparent**{: #ListTextBackgroundConstants_lvwTransparent } | 0 | Item text overlays the list background unchanged. | +| **lvwOpaque**{: #ListTextBackgroundConstants_lvwOpaque } | 1 | Item text is drawn with an opaque background matching [**BackColor**](#backcolor). | + +## ListLabelEditConstants +{: #listlabeleditconstants } + +Determines when inline label editing is triggered. Declared on the **ListView** class. + +| Member | Value | Description | +|--------------------|-------|----------------------------------------------------------------------------| +| **lvwAutomatic**{: #ListLabelEditConstants_lvwAutomatic } | 0 | F2 or click-and-pause on a selected row starts an edit. | +| **lvwManual**{: #ListLabelEditConstants_lvwManual } | 1 | Only [**StartLabelEdit**](#startlabeledit) opens an editor. | +| **lvwDisabled**{: #ListLabelEditConstants_lvwDisabled } | 2 | Label editing is disabled entirely. | + +## See Also + +- [ListItem](ListItem) -- a single row +- [ListItems](ListItems) -- the collection of rows +- [ColumnHeader](ColumnHeader) -- a single column header (Report view) +- [ColumnHeaders](ColumnHeaders) -- the column header collection +- [ImageList](../ImageList/) -- the picture source for [**Icons**](#icons), [**SmallIcons**](#smallicons), and [**ColumnHeaderIcons**](#columnheadericons) +- [TreeBorderStyleConstants](../Enumerations/TreeBorderStyleConstants) -- the [**BorderStyle**](#borderstyle) enum shared with [**TreeView**](../TreeView/) +- [ControlTypeConstants](../../VBRUN/Constants/ControlTypeConstants) -- where **vbListView** lives diff --git a/docs/Reference/WinNativeCommonCtls/MonthView.md b/docs/Reference/WinNativeCommonCtls/MonthView.md new file mode 100644 index 0000000..2d90033 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/MonthView.md @@ -0,0 +1,358 @@ +--- +title: MonthView +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/MonthView +has_toc: false +--- + +# MonthView class +{: .no_toc } + +A **MonthView** is a full-month calendar grid: a visible matrix of [**MonthColumns**](#monthcolumns) × [**MonthRows**](#monthrows) month panels, navigable forwards and backwards through the month headers, with optional today indicator, week numbers, and bold-day highlighting through the [**GetDayBold**](#getdaybold) callback event. Unlike [**DTPicker**](DTPicker) — which shows only its inline value field and pops the calendar on demand — a **MonthView** is always visible on the form. + +```tb +Private Sub Form_Load() + MonthView1.MonthColumns = 2 + MonthView1.MonthRows = 1 + MonthView1.MultiSelect = True + MonthView1.MaxSelCount = 7 + MonthView1.ShowWeekNumbers = True +End Sub + +Private Sub MonthView1_SelChange( _ + ByVal StartDate As Date, ByVal EndDate As Date, Cancel As Boolean) + Debug.Print "Selection: " & StartDate & " to " & EndDate +End Sub + +Private Sub MonthView1_GetDayBold( _ + ByVal StartDate As Date, ByVal Count As Integer, State() As Boolean) + Dim i As Integer + For i = 1 To Count + State(i) = IsHoliday(DateAdd("d", i - 1, StartDate)) + Next +End Sub +``` + +The control inherits the focusable rect-dockable surface from `BaseControlFocusable` — size, position, **Anchors**, **Dock**, **Font**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **Drag**, **Refresh**, **SetFocus**, **TabIndex** / **TabStop**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. + +* TOC +{:toc} + +## Multi-month layout + +A **MonthView** can display more than one calendar panel at once. [**MonthColumns**](#monthcolumns) and [**MonthRows**](#monthrows) set the panel grid (default 1 × 1); when [**ResizeToFit**](#resizetofit) is **True** (the default), the control auto-sizes its **Width** and **Height** to fit the requested grid using the current [**Font**](../VB/CheckBox#font) and the [**ShowToday**](#showtoday) / [**ShowWeekNumbers**](#showweeknumbers) options. Setting **ResizeToFit** to **False** allows the application to size the control freely, with the calendar panels arranged to fit whatever space is available. + +[**GetMonthRange**](#getmonthrange) returns the span of dates currently visible across all panels — useful inside [**GetDayBold**](#getdaybold) to know which days to populate. + +## Single-day and multi-day selection + +When [**MultiSelect**](#multiselect) is **False** (the default), the user can select one date at a time and [**Value**](#value), [**SelStart**](#selstart), and [**SelEnd**](#selend) all report the same value. When [**MultiSelect**](#multiselect) is **True**, the user can drag-select a contiguous range up to [**MaxSelCount**](#maxselcount) days wide; [**SelStart**](#selstart) and [**SelEnd**](#selend) bracket the range, and [**Value**](#value) returns [**SelStart**](#selstart). + +Changing [**MultiSelect**](#multiselect) at run time recreates the underlying Win32 window — the property cannot be flipped through GWL_STYLE alone. + +## Bold days for highlighting + +The [**GetDayBold**](#getdaybold) event fires after every visible-range change, asking the application which days to render in bold. The application populates the *State* array with **True** / **False** for each day in the visible range; the control then caches the result until the user navigates to a different range. To force an individual day on or off without reissuing the whole event, use [**DayBold**](#dayboldday) ( *date* ) = *boolean*. + +Properties +---------- + +### Appearance +{: .no_toc } + +How the control's border is drawn. A [**AppearanceConstants**](../VBRUN/Constants/AppearanceConstants) member: **vbAppearFlat** or **vbAppear3d**. Default: **vbAppear3d**. Inherited. + +### BackColor +{: .no_toc } + +The main background color of the calendar panels. **OLE_COLOR**. Default: **vbWindowBackground**. + +### BorderStyle +{: .no_toc } + +The control's border style. A [**ControlBorderStyleConstants**](../VBRUN/Constants/ControlBorderStyleConstants) member: **vbNoBorder** or **vbFixedSingleBorder**. Default: **vbFixedSingleBorder**. + +### CalendarCount +{: .no_toc } + +The number of calendar panels the underlying control is rendering. **Byte**, read-only. Usually equals [**MonthColumns**](#monthcolumns) × [**MonthRows**](#monthrows). + +### Day +{: .no_toc } + +The day-of-month component of [**Value**](#value). **Integer** (1–31). See [**DayCount**](#daycount). + +### DayBold(date) +{: #dayboldday .no_toc } + +Whether a specific date in the visible range is rendered in bold. **Boolean**, read/write. The setter updates the underlying day-state bitmask and re-applies it; reading returns the live cached value. + +Syntax: *object*.**DayBold**( *date* ) [ **=** *boolean* ] + +*date* +: A **Date** within the currently visible range. Out-of-range dates raise run-time error 380. + +### DayCount +{: .no_toc } + +The number of days in the current value's month. **Long**, read-only. + +### DayOfWeek +{: .no_toc } + +The day-of-week the current [**Value**](#value) falls on, as a [**VbDayOfWeek**](../../Modules/Constants/VbDayOfWeek) member. Read-only. + +### ForeColor +{: .no_toc } + +The text color used for normal days. **OLE_COLOR**. Default: **vbButtonText**. + +### MaxDate +{: .no_toc } + +The upper bound of the navigable date range. **Date**. Default: `9999-12-31`. Assigning a value lower than [**MinDate**](#mindate) raises run-time error 35775. + +### MaxSelCount +{: .no_toc } + +The maximum number of consecutive days the user can select when [**MultiSelect**](#multiselect) is **True**. **Long**. Default: `7`. + +### MinDate +{: .no_toc } + +The lower bound of the navigable date range. **Date**. Default: `1753-01-01`. + +### Month +{: .no_toc } + +The month-of-year component of [**Value**](#value). **Integer** (1–12). + +### MonthBackColor +{: .no_toc } + +The background color used for the day cells. **OLE_COLOR**. Default: **vbWindowBackground**. Distinct from [**BackColor**](#backcolor), which covers the surrounding area when multiple panels are arranged. + +### MonthColumns +{: .no_toc } + +The number of calendar panels arranged horizontally. **Long**. Default: `1`. Changing this value triggers a resize when [**ResizeToFit**](#resizetofit) is **True**. + +### MonthRows +{: .no_toc } + +The number of calendar panels arranged vertically. **Long**. Default: `1`. + +### MultiSelect +{: .no_toc } + +Whether the user can select a contiguous range of days. **Boolean**. Default: **False**. Changing this property at run time recreates the underlying Win32 window. + +### ResizeToFit +{: .no_toc } + +Whether the control auto-resizes to fit the requested [**MonthColumns**](#monthcolumns) × [**MonthRows**](#monthrows) grid using the current [**Font**](../VB/CheckBox#font). **Boolean**. Default: **True**. + +### RightToLeft +{: .no_toc } + +> [!NOTE] +> **RightToLeft** is tagged `[Unimplemented]` and has no effect on the underlying control's rendering direction. + +A **Boolean**. + +### ScrollRate +{: .no_toc } + +The number of months the navigation arrows scroll the visible range by. **Long**. Default: `0` — meaning "use the calendar's natural width" (typically `MonthColumns`). Pass any positive integer to override. + +### SelEnd +{: .no_toc } + +The end of the current selection range. **Date**, read/write. When [**MultiSelect**](#multiselect) is **False**, **SelEnd** equals [**SelStart**](#selstart) and equals [**Value**](#value). + +### SelStart +{: .no_toc } + +The start of the current selection range. **Date**, read/write. Equals [**Value**](#value). + +### ShowToday +{: .no_toc } + +Whether the calendar shows the "Today: …" line at the bottom. **Boolean**. Default: **True**. + +### ShowTodayCircle +{: .no_toc } + +Whether the calendar highlights today's date with a circle. **Boolean**. Default: **True**. + +### ShowTrailingDates +{: .no_toc } + +Whether the calendar shows the leading and trailing days of the previous and next month. **Boolean**. Default: **True**. + +### ShowWeekNumbers +{: .no_toc } + +Whether the calendar shows a week-number column on the left of each panel. **Boolean**. Default: **False**. + +### StartOfWeek +{: .no_toc } + +Which day of the week is rendered as the leftmost column. A [**VbDayOfWeek**](../../Modules/Constants/VbDayOfWeek) member. Defaults to the system's first-day-of-week setting. + +### TitleBackColor +{: .no_toc } + +The title bar (month name + year header) background color. **OLE_COLOR**. Default: **vbActiveTitleBar**. + +### TitleForeColor +{: .no_toc } + +The title bar text color. **OLE_COLOR**. Default: **vbActiveTitleBarText**. + +### TrailingForeColor +{: .no_toc } + +The text color used for trailing days from adjacent months when [**ShowTrailingDates**](#showtrailingdates) is **True**. **OLE_COLOR**. Default: **vbGrayText**. + +### Value +{: .no_toc } + +The currently selected start date. **Date**. The default member. + +Reading returns [**SelStart**](#selstart). Assigning fires [**SelChange**](#selchange) (the handler can cancel the change). Assigning a date outside [[**MinDate**](#mindate), [**MaxDate**](#maxdate)] raises run-time error 35773. + +### VisibleDays(sIndex) +{: .no_toc } + +The date at the *sIndex*'th cell across all visible panels. **Date**, read-only. + +Syntax: *object*.**VisibleDays**( *sIndex* ) + +*sIndex* +: A 1-based index between 1 and the total cell count returned by [**GetMonthRange**](#getmonthrange). + +### Week +{: .no_toc } + +The week-of-year for the current [**Value**](#value). **Integer** (1–53). + +### Year +{: .no_toc } + +The year component of [**Value**](#value). **Integer**. + +Methods +------- + +### GetMonthRange +{: .no_toc } + +Returns the first and last visible date across all panels. + +Syntax: *object*.**GetMonthRange** ( *IncludeTrailing*, [ *StartDate* ] [ , *EndDate* ] ) **As Long** + +*IncludeTrailing* +: A **Boolean**. When **True**, the range includes the trailing days of the previous month and leading days of the next month that are rendered in the first / last panels (useful for [**GetDayBold**](#getdaybold) population). When **False**, the range covers only the days that belong to the visible month columns. + +*StartDate* +: *output* A **Date** that receives the first visible date. + +*EndDate* +: *output* A **Date** that receives the last visible date. + +Returns the count of months in the visible range. + +Events +------ + +### Click +{: .no_toc } + +Raised on any mouse click that doesn't land on a date cell. + +Syntax: *object*\_**Click**( ) + +### DateClick +{: .no_toc } + +Raised when the user clicks a date cell. The clicked date is passed as a parameter. + +Syntax: *object*\_**DateClick**( **ByVal** *DateClicked* **As Date** ) + +### DateDblClick +{: .no_toc } + +Raised when the user double-clicks a date cell. + +Syntax: *object*\_**DateDblClick**( **ByVal** *DateDblClicked* **As Date** ) + +### DblClick +{: .no_toc } + +Raised on any double-click that doesn't land on a date cell. + +Syntax: *object*\_**DblClick**( ) + +### DragDrop, DragOver +{: .no_toc } + +Inherited drag-drop events. + +### GetDayBold +{: .no_toc } + +Raised for every visible range change, asking the application to populate the *State* array with the days that should be rendered in bold. The first array index is `1`; the array runs from *StartDate* through *StartDate* + *Count* − 1. + +Syntax: *object*\_**GetDayBold**( **ByVal** *StartDate* **As Date**, **ByVal** *Count* **As Integer**, *State*( ) **As Boolean** ) + +*StartDate* +: The first day in the visible range (including trailing days of the previous month). + +*Count* +: The total number of days in the visible range. + +*State* +: An array of **Boolean**, 1-indexed, that the handler sets to **True** for each day that should be bold. + +### GotFocus, LostFocus +{: .no_toc } + +Inherited focus events. + +### Initialize +{: .no_toc } + +Raised after the control's window has been created and properties initialised from persisted state. Fires once per form-load. + +### KeyDown, KeyPress, KeyUp +{: .no_toc } + +Inherited keyboard events. + +### MouseDown, MouseMove, MouseUp +{: .no_toc } + +Inherited mouse events. + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. + +### SelChange +{: .no_toc } + +Raised when the selection has changed. Set *Cancel* to **True** to roll the selection back to the previous range (the control restores the old [**SelStart**](#selstart) / [**SelEnd**](#selend)). + +Syntax: *object*\_**SelChange**( **ByVal** *StartDate* **As Date**, **ByVal** *EndDate* **As Date**, *Cancel* **As Boolean** ) + +### Validate +{: .no_toc } + +Inherited validation event. + +## See Also + +- [DTPicker](DTPicker) -- the inline date picker whose dropdown uses the same Win32 calendar control +- [ControlTypeConstants](../VBRUN/Constants/ControlTypeConstants) -- where **vbMonthView** lives diff --git a/docs/Reference/WinNativeCommonCtls/ProgressBar.md b/docs/Reference/WinNativeCommonCtls/ProgressBar.md new file mode 100644 index 0000000..1f569ed --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/ProgressBar.md @@ -0,0 +1,220 @@ +--- +title: ProgressBar +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/ProgressBar +has_toc: false +--- + +# ProgressBar class +{: .no_toc } + +A **ProgressBar** is a horizontal or vertical bar that visually represents progress through a range. Three configurable axes shape the visual: [**Scrolling**](#scrolling) (segmented / smooth / marquee), [**State**](#state) (Normal / Error / Paused, which tints the bar to match the OS theme), and [**Orientation**](#orientation) (horizontal or vertical). + +```tb +Private Sub StartTask() + ProgressBar1.Min = 0 + ProgressBar1.Max = ItemCount + ProgressBar1.Step = 1 + ProgressBar1.Value = 0 +End Sub + +Private Sub OnItemDone() + ProgressBar1.StepIt ' advances by Step (1 here) and fires Change +End Sub + +Private Sub OnTaskFailed() + ProgressBar1.State = PrbStateError +End Sub +``` + +For indeterminate progress (e.g. waiting on a server response with no length information), use the marquee variant: + +```tb +ProgressBar1.Scrolling = PrbScrollingMarquee +ProgressBar1.MarqueeSpeed = 30 ' milliseconds per animation step +ProgressBar1.MarqueeAnimation = True ' start animating +``` + +The control inherits the non-focusable rect-dockable surface from `BaseControlNotFocusable2` — size, position, **Anchors**, **Dock**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **Refresh**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. + +* TOC +{:toc} + +## Three axes of configuration + +The visual is the cartesian product of three properties: + +- **[Scrolling](#scrolling)** — *Standard* (the default, animated segmented bar), *Smooth* (a continuous block — combine with [**SmoothReverse**](#smoothreverse) to allow the bar to decrease), or *Marquee* (an indeterminate animated stripe; drive with [**MarqueeAnimation**](#marqueeanimation) + [**MarqueeSpeed**](#marqueespeed)). +- **[State](#state)** — *Normal* (theme-default color, typically green), *Error* (typically red), or *Paused* (typically yellow). The OS chooses the actual colors based on its current theme. +- **[Orientation](#orientation)** — *Horizontal* or *Vertical*. + +All three can be changed at run time; the underlying Win32 styles are re-applied without recreating the window. + +## Range, value, and stepping + +[**Min**](#min) and [**Max**](#max) bracket the range. [**Value**](#value) is the current position. [**Step**](#step) is the amount [**StepIt**](#stepit) advances the bar by, used to drive the common loop pattern: + +```tb +ProgressBar1.Min = 0 +ProgressBar1.Max = Items.Count +ProgressBar1.Step = 1 +For Each item In Items + DoWork item + ProgressBar1.StepIt +Next +``` + +Each [**StepIt**](#stepit) call increments [**Value**](#value) by [**Step**](#step) and fires the [**Change**](#change) event. + +Properties +---------- + +### BackColor +{: .no_toc } + +The background color drawn behind the progress bar segments. **OLE_COLOR**. Default: **vbButtonFace**. Applied via `PBM_SETBKCOLOR`. + +### BorderStyle +{: .no_toc } + +The control's border style. A [**ControlBorderStyleConstants**](../VBRUN/Constants/ControlBorderStyleConstants) member. Default: **vbNoBorder**. + +### ForeColor +{: .no_toc } + +The color of the progress bar segments themselves. **OLE_COLOR**. Default: **vbHighlight**. Applied via `PBM_SETBARCOLOR`. + +> [!NOTE] +> The OS visual styles theme typically overrides this value in standard rendering. To see the assigned **ForeColor** at run time, disable visual styles by setting [**VisualStyles**](../VB/CheckBox#visualstyles) to **False**. + +### MarqueeAnimation +{: .no_toc } + +Whether the marquee animation is currently running. **Boolean**. Default: **False**. Only meaningful when [**Scrolling**](#scrolling) is **PrbScrollingMarquee**. Set to **True** to start, **False** to stop. + +### MarqueeSpeed +{: .no_toc } + +The marquee animation update interval, in milliseconds. **Long**. Default: `80`. + +### Max +{: .no_toc } + +The upper bound of the range. **Long**. Default: `100`. The combination of [**Min**](#min) and [**Max**](#max) is applied to the underlying control via `PBM_SETRANGE32`. + +### Min +{: .no_toc } + +The lower bound of the range. **Long**. Default: `0`. + +### Orientation +{: .no_toc } + +The progress bar's orientation. A member of [**PrbOrientation**](#prborientation). Default: **PrbOrientationHorizontal**. + +### Scrolling +{: .no_toc } + +The visual style of progress. A member of [**PrbScrolling**](#prbscrolling). Default: **PrbScrollingStandard**. + +### SmoothReverse +{: .no_toc } + +Whether a smooth progress bar can decrease (when [**Value**](#value) is set to a smaller number). **Boolean**. Default: **False**. Without this flag a smooth bar that has reached, say, 80% will not visibly decrease when [**Value**](#value) is reduced — it simply snaps back. Only meaningful when [**Scrolling**](#scrolling) is **PrbScrollingSmooth**. + +### State +{: .no_toc } + +The visual state of the bar. A member of [**PrbState**](#prbstate). Default: **PrbStateNormal**. The OS uses the value to tint the bar — **PrbStateError** typically renders red, **PrbStatePaused** typically renders yellow, **PrbStateNormal** uses the theme-default progress color (typically green). + +### Step +{: .no_toc } + +The amount [**StepIt**](#stepit) advances [**Value**](#value) by. **Long**. Default: `10`. + +### Value +{: .no_toc } + +The current position in the range. **Long**. The default member. Reads and writes via `PBM_GETPOS` / `PBM_SETPOS`. Fires [**Change**](#change) when the value is changed past the control's initialization phase. + +Methods +------- + +### StepIt +{: .no_toc } + +Advances [**Value**](#value) by [**Step**](#step), wrapping to [**Min**](#min) once [**Max**](#max) is reached. Fires the [**Change**](#change) event. + +Syntax: *object*.**StepIt** + +Events +------ + +### Change +{: .no_toc } + +Raised when [**Value**](#value) has changed — either through direct assignment, through [**StepIt**](#stepit), or by code adjusting [**Step**](#step) and re-applying. + +Syntax: *object*\_**Change**( ) + +### Click, DblClick +{: .no_toc } + +Inherited mouse events. + +### DragDrop, DragOver +{: .no_toc } + +Inherited drag-drop events. + +### Initialize +{: .no_toc } + +Raised after the control's window has been created. + +### MouseDown, MouseMove, MouseUp +{: .no_toc } + +Inherited mouse events. + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. + +## PrbOrientation +{: #prborientation } + +Determines the orientation of the progress bar. Declared on the **ProgressBar** class. + +| Member | Value | Description | +|---------------------------------|-------|--------------------------------------------| +| **PrbOrientationHorizontal**{: #PrbOrientation_PrbOrientationHorizontal } | 0 | Horizontal bar; progress flows left-to-right. | +| **PrbOrientationVertical**{: #PrbOrientation_PrbOrientationVertical } | 1 | Vertical bar; progress flows bottom-to-top. | + +## PrbScrolling +{: #prbscrolling } + +Determines how the progress bar animates as the value changes. Declared on the **ProgressBar** class. + +| Member | Value | Description | +|------------------------------|-------|------------------------------------------------------------------------| +| **PrbScrollingStandard**{: #PrbScrolling_PrbScrollingStandard } | 0 | Segmented bar with the classic discrete blocks animation. | +| **PrbScrollingSmooth**{: #PrbScrolling_PrbScrollingSmooth } | 1 | Continuous block with no inter-segment gaps. Pair with [**SmoothReverse**](#smoothreverse) to allow the value to decrease visibly. | +| **PrbScrollingMarquee**{: #PrbScrolling_PrbScrollingMarquee } | 2 | Indeterminate animated stripe. Drive with [**MarqueeAnimation**](#marqueeanimation) and [**MarqueeSpeed**](#marqueespeed); the actual [**Value**](#value) is irrelevant. | + +## PrbState +{: #prbstate } + +Determines the color theme of the progress bar. Declared on the **ProgressBar** class. + +| Member | Value | Description | +|-----------------------------|-------|-----------------------------------------------------------------------------------| +| **PrbStateNormal**{: #PrbState_PrbStateNormal } | 1 | Theme-default progress color (typically green). | +| **PrbStateError**{: #PrbState_PrbStateError } | 2 | Error state (typically red). | +| **PrbStatePaused**{: #PrbState_PrbStatePaused } | 3 | Paused state (typically yellow). | + +## See Also + +- [Slider](Slider) -- when the user needs to set a value within a range, not just see progress +- [ControlTypeConstants](../VBRUN/Constants/ControlTypeConstants) -- where **vbProgressBar** lives diff --git a/docs/Reference/WinNativeCommonCtls/Slider.md b/docs/Reference/WinNativeCommonCtls/Slider.md new file mode 100644 index 0000000..2b32f5c --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/Slider.md @@ -0,0 +1,217 @@ +--- +title: Slider +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/Slider +has_toc: false +--- + +# Slider class +{: .no_toc } + +A **Slider** is a trackbar control — a horizontal or vertical channel with a draggable thumb that lets the user pick a value between [**Min**](#min) and [**Max**](#max). Optional tick marks, a floating value tip, and a highlighted selection range round out the surface. + +```tb +Private Sub Form_Load() + Slider1.Min = 0 + Slider1.Max = 100 + Slider1.Value = 50 + Slider1.SmallChange = 1 ' arrow-key step + Slider1.LargeChange = 10 ' PgUp/PgDn step + Slider1.TickFrequency = 10 +End Sub + +Private Sub Slider1_Change() + Label1.Caption = Slider1.Value & " %" +End Sub +``` + +The control inherits the focusable rect-dockable surface from `BaseControlFocusableNoFont` — size, position, **Anchors**, **Dock**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **Refresh**, **SetFocus**, **TabIndex** / **TabStop**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. Slider does not carry a [**Font**](../VB/CheckBox#font) property (its thumb and tick marks are drawn by the OS theme). + +* TOC +{:toc} + +## Change vs Scroll + +The slider raises two distinct events as the user interacts with the thumb: + +- **[Scroll](#scroll)** fires *during* drag and during keyboard navigation — every time the thumb position is updated, regardless of whether the user has settled. +- **[Change](#change)** fires *only when the drag completes* (mouse release) or when the user reaches an extremity with the keyboard. + +Use **Scroll** for live previews ("show the value as the user is dragging") and **Change** for commit-style handlers ("apply the value once the user lets go"). + +## Selection range + +Set [**SelectRange**](#selectrange) to **True** to enable a highlighted selection band overlay on the channel. [**SelStart**](#selstart) and [**SelLength**](#sellength) then become writable and define the selection's bounds. This is useful for "between X and Y" UI patterns where the user picks a value but the application wants to highlight a recommended sub-range. + +When [**SelectRange**](#selectrange) is **False** (the default), [**SelLength**](#sellength) always reads as `0` and [**SelStart**](#selstart) reads back as the [**Min**](#min) value. + +Properties +---------- + +### BackColor +{: .no_toc } + +The background color of the channel. **OLE_COLOR**. Default: **vbButtonFace**. + +### BorderStyle +{: .no_toc } + +The control's border style. A [**ControlBorderStyleConstants**](../VBRUN/Constants/ControlBorderStyleConstants) member. Default: **vbNoBorder**. + +### HideThumb +{: .no_toc } + +Whether the draggable thumb is hidden. **Boolean**. Default: **False**. When **True** the channel still functions but the user can only navigate it with the keyboard. + +### LargeChange +{: .no_toc } + +The amount **PgUp** / **PgDn** moves the thumb by. **Long**. Default: `2`. + +### Max +{: .no_toc } + +The upper bound of the range. **Long**. Default: `10`. + +### Min +{: .no_toc } + +The lower bound of the range. **Long**. Default: `0`. + +### Orientation +{: .no_toc } + +The slider's orientation. A [**OrientationConstants**](Enumerations/OrientationConstants) member (**ccOrientationHorizontal** or **ccOrientationVertical**). Default: **ccOrientationHorizontal**. + +### SelectRange +{: .no_toc } + +Whether a highlighted selection band overlay is enabled. **Boolean**. Default: **False**. When **False**, [**SelStart**](#selstart) and [**SelLength**](#sellength) are inert; assignments are silently dropped. + +### SelLength +{: .no_toc } + +The length of the highlighted selection band. **Long**. Read/write only when [**SelectRange**](#selectrange) is **True**. Assigning a value that pushes the end past [**Max**](#max) raises run-time error 380. + +### SelStart +{: .no_toc } + +The starting position of the highlighted selection band. **Long**. Assigning a value outside [[**Min**](#min), [**Max**](#max)] raises run-time error 380. + +### ShowTip +{: .no_toc } + +Whether the slider shows a floating tip with the current value while the thumb is being dragged. **Boolean**. Default: **True**. The tip side is controlled by [**TextPosition**](#textposition). + +### SmallChange +{: .no_toc } + +The amount the arrow keys move the thumb by. **Long**. Default: `1`. + +### TextPosition +{: .no_toc } + +Which side of the channel the floating tip is rendered on. A member of [**TextPositionConstants**](#textpositionconstants). Default: **sldBelowRight**. The naming reflects both axes: in horizontal orientation, **sldAboveLeft** renders above the channel and **sldBelowRight** renders below; in vertical orientation, **sldAboveLeft** renders to the left and **sldBelowRight** renders to the right. + +### TickFrequency +{: .no_toc } + +How often tick marks appear along the channel. **Long**. Default: `1` (one tick per unit). Assigning `0` is silently coerced to the same as `1`. + +### TickStyle +{: .no_toc } + +Which side(s) of the channel tick marks appear on. A member of [**TickStyleConstants**](#tickstyleconstants). Default: **sldBottomRight**. + +### Value +{: .no_toc } + +The current thumb position. **Long**. The default member. Fires [**Change**](#change) when set programmatically. + +Events +------ + +### Change +{: .no_toc } + +Raised when the user lets go of the thumb at a new position, or when the keyboard navigates to a track extremity. Distinct from [**Scroll**](#scroll), which fires continuously during drag. + +Syntax: *object*\_**Change**( ) + +### Click +{: .no_toc } + +Raised on a mouse click inside the control's rectangle. + +Syntax: *object*\_**Click**( ) + +### DragDrop, DragOver +{: .no_toc } + +Inherited drag-drop events. + +### GotFocus, LostFocus +{: .no_toc } + +Inherited focus events. + +### Initialize +{: .no_toc } + +Raised after the control's window has been created. + +### KeyDown, KeyPress, KeyUp +{: .no_toc } + +Inherited keyboard events. The trackbar consumes arrow keys, **Home**, **End**, **PgUp**, and **PgDn** to navigate; the events still fire for the application to observe. + +### MouseDown, MouseMove, MouseUp +{: .no_toc } + +Inherited mouse events. + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. + +### Scroll +{: .no_toc } + +Raised continuously while the user is interacting with the thumb. Use this for live previews; use [**Change**](#change) for commit-style handlers. + +Syntax: *object*\_**Scroll**( ) + +### Validate +{: .no_toc } + +Inherited validation event. + +## TickStyleConstants +{: #tickstyleconstants } + +Determines which side(s) of the slider channel tick marks are rendered on. Declared on the **Slider** class. + +| Member | Value | Description | +|-----------------------|-------|----------------------------------------------------------------------| +| **sldBottomRight**{: #TickStyleConstants_sldBottomRight } | 0 | Ticks render below the channel (horizontal) or to the right (vertical). | +| **sldTopLeft**{: #TickStyleConstants_sldTopLeft } | 1 | Ticks render above the channel (horizontal) or to the left (vertical). | +| **sldBoth**{: #TickStyleConstants_sldBoth } | 2 | Ticks render on both sides of the channel. | +| **sldNoTicks**{: #TickStyleConstants_sldNoTicks } | 3 | Tick marks are hidden. | + +## TextPositionConstants +{: #textpositionconstants } + +Determines which side of the slider channel the floating tip is rendered on when [**ShowTip**](#showtip) is **True**. Declared on the **Slider** class. + +| Member | Value | Description | +|---------------------------------|-------|------------------------------------------------------------------------------| +| **sldAboveLeft**{: #TextPositionConstants_sldAboveLeft } | 0 | Tip renders above the channel (horizontal) or to the left (vertical). | +| **sldBelowRight**{: #TextPositionConstants_sldBelowRight } | 1 | Tip renders below the channel (horizontal) or to the right (vertical). | + +## See Also + +- [ProgressBar](ProgressBar) -- when the user only needs to see progress, not adjust a value +- [UpDown](UpDown) -- a spin control for numeric value entry +- [OrientationConstants](Enumerations/OrientationConstants) -- the shared horizontal / vertical enum used by **Slider** and **UpDown** +- [ControlTypeConstants](../VBRUN/Constants/ControlTypeConstants) -- where **vbSlider** lives diff --git a/docs/Reference/WinNativeCommonCtls/TreeView/Node.md b/docs/Reference/WinNativeCommonCtls/TreeView/Node.md new file mode 100644 index 0000000..726e992 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/TreeView/Node.md @@ -0,0 +1,174 @@ +--- +title: Node +parent: TreeView +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/TreeView/Node +has_toc: false +--- + +# Node class +{: .no_toc } + +A **Node** is a single entry in a [**TreeView**](.)'s [**Nodes**](Nodes) collection. Returned from [**Nodes.Add**](Nodes#add) and from [**Nodes.Item**](Nodes#item). Each node carries its own text, icons, sort settings, check state, and sibling / parent / child relationships. + +The class is tagged `[COMCreatable(False)]` — user code reaches **Node** instances through the parent [**TreeView**](.)'s [**Nodes**](Nodes) collection or through navigation properties on other nodes. + +```tb +Dim root As Node = TreeView1.Nodes.Add(, , "root", "My Computer") +Dim drive As Node = TreeView1.Nodes.Add(root, tvwChild, "c", "C: drive") +drive.Bold = True +drive.Image = "disk" + +Debug.Print drive.FullPath ' "My Computer\C: drive" +Debug.Print drive.Parent.Text ' "My Computer" +Debug.Print drive.Root.Text ' "My Computer" +``` + +* TOC +{:toc} + +Properties +---------- + +### BackColor +{: .no_toc } + +The background color used to render this node. **OLE_COLOR**. Default: **vbWindowBackground**. + +### Bold +{: .no_toc } + +Whether the node text is rendered in a bold font. **Boolean**. Default: **False**. + +### Checked +{: .no_toc } + +Whether the node's checkbox is checked. **Boolean**. Only meaningful when [**TreeView.CheckBoxes**](.#checkboxes) is **True**. + +### Child +{: .no_toc } + +The first child node of this node, or **Nothing** if it has no children. **Node**, read-only. + +### Children +{: .no_toc } + +The number of immediate child nodes of this node. **Long**, read-only. + +### Expanded +{: .no_toc } + +Whether the node is currently expanded (showing its children). **Boolean**, read/write. Assigning fires [**TreeView.BeforeExpand**](.#beforeexpand) / [**TreeView.BeforeCollapse**](.#beforecollapse) (cancellable) followed by [**TreeView.Expand**](.#expand) / [**TreeView.Collapse**](.#collapse). + +### FirstSibling +{: .no_toc } + +The first sibling of this node (the leftmost peer under the same parent). **Node**, read-only. If the node is itself the first sibling, returns the node. + +### ForeColor +{: .no_toc } + +The text color used to render this node. **OLE_COLOR**. Default: **vbWindowText**. + +### FullPath +{: .no_toc } + +The hierarchical path from the root to this node, with [**TreeView.PathSeparator**](.#pathseparator) inserted between node texts. **String**, read-only. + +Example: a node "C: drive" whose parent is "My Computer" returns `"My Computer\C: drive"`. + +### Image +{: .no_toc } + +The icon rendered when the node is not selected. **Variant** — either a 1-based **Long** index into [**TreeView.ImageList**](.#imagelist), or a **String** key. Assignment validates against the bound image list. + +### Index +{: .no_toc } + +The 1-based position of this node in the parent collection. **Long**, read-only. + +### Key +{: .no_toc } + +The string key the node was added under. **String**, read/write. + +### LastSibling +{: .no_toc } + +The last sibling of this node (the rightmost peer under the same parent). **Node**, read-only. + +### Next +{: .no_toc } + +The next sibling of this node, or **Nothing** if this is the last sibling. **Node**, read-only. + +### Parent +{: .no_toc } + +The parent **Node**, or **Nothing** if this node is at the root level. **Node**, read/write. Note: assigning **Parent** does not move the node — it merely changes the recorded parent reference. + +### Previous +{: .no_toc } + +The previous sibling of this node, or **Nothing** if this is the first sibling. **Node**, read-only. + +### Root +{: .no_toc } + +The root node of the subtree this node belongs to. **Node**, read-only. + +### Selected +{: .no_toc } + +Whether this node is the [**TreeView.SelectedItem**](.#selecteditem) of the treeview. **Boolean**, read/write. + +### SelectedImage +{: .no_toc } + +The icon rendered when the node is selected. **Variant** — either an index or a key into [**TreeView.ImageList**](.#imagelist). When unset, defaults to the same as [**Image**](#image). + +### Sorted +{: .no_toc } + +Whether this node's children are sorted. **Boolean**. Default: **False**. Independent of [**TreeView.Sorted**](.#sorted), which controls root-level sorting. + +### SortOrder +{: .no_toc } + +The sort direction for this node's children. A member of [**TreeSortOrderConstants**](../Enumerations/TreeSortOrderConstants). Default: **tvwAscending**. + +### SortType +{: .no_toc } + +The string comparison used for sorting this node's children. A member of [**TreeSortTypeConstants**](../Enumerations/TreeSortTypeConstants): **tvwBinary** or **tvwText**. Default: **tvwText**. + +### Tag +{: .no_toc } + +Arbitrary data the application can attach to the node. **Variant**. + +### Text +{: .no_toc } + +The node's label text. **String**, read/write. + +### Visible +{: .no_toc } + +Whether the node is currently visible — i.e. not hidden because an ancestor is collapsed and not scrolled out of view. **Boolean**, read-only. + +Methods +------- + +### EnsureVisible +{: .no_toc } + +Scrolls and expands ancestor nodes as necessary to make this node visible in the treeview. + +Syntax: *object*.**EnsureVisible** + +## See Also + +- [TreeView](.) -- the parent control +- [Nodes](Nodes) -- the collection holding **Node** instances +- [TreeSortOrderConstants](../Enumerations/TreeSortOrderConstants), [TreeSortTypeConstants](../Enumerations/TreeSortTypeConstants) -- the **SortOrder** / **SortType** enums diff --git a/docs/Reference/WinNativeCommonCtls/TreeView/Nodes.md b/docs/Reference/WinNativeCommonCtls/TreeView/Nodes.md new file mode 100644 index 0000000..e730b88 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/TreeView/Nodes.md @@ -0,0 +1,111 @@ +--- +title: Nodes +parent: TreeView +grand_parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/TreeView/Nodes +has_toc: false +--- + +# Nodes class +{: .no_toc } + +The **Nodes** collection is the entry point for managing the [**Node**](Node) tree of a [**TreeView**](.). Reached as `.Nodes`; supports adding, removing, indexed access, and `For Each` iteration. + +The class is tagged `[COMCreatable(False)]` — user code reaches **Nodes** through the parent [**TreeView**](.) control's [**Nodes**](.#nodes) property. + +```tb +With TreeView1.Nodes + Dim root As Node + Set root = .Add(, , "root", "My Computer") + .Add root, tvwChild, "c", "C: drive" + .Add root, tvwChild, "d", "D: drive" +End With + +Dim node As Node +For Each node In TreeView1.Nodes + Debug.Print node.Index, node.Key, node.FullPath +Next +``` + +The `For Each` iteration walks **only the nodes in the order they were added** — not in tree order. For a depth-first or breadth-first traversal that follows the visual hierarchy, walk the parent-child links manually starting from a root [**Node**](Node) and using [**Node.Child**](Node#child) / [**Node.Next**](Node#next). + +* TOC +{:toc} + +Properties +---------- + +### Count +{: .no_toc } + +The total number of nodes in the treeview (root nodes plus all descendants). **Long**, read-only. + +### Item +{: .no_toc } + +Returns the [**Node**](Node) at the given index or with the given key. The default member, so `TreeView1.Nodes("root")` works without writing `.Item("root")`. + +Syntax: *object*.**Item** ( *Index* ) **As Node** + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. + +Methods +------- + +### Add +{: .no_toc } + +Adds a node to the treeview, optionally positioned relative to another node. + +Syntax: *object*.**Add** ( [ *Relative* ] [, *Relationship* ] [, *Key* ] [, *Text* ] [, *Image* ] [, *SelectedImage* ] ) **As Node** + +*Relative* +: *optional* A **Variant** identifying the existing node the new node will be positioned against — either a [**Node**](Node) reference, a 1-based **Long** index, or a **String** key. When omitted, the new node is inserted at the root level using *Relationship* = **tvwNext** semantics. + +*Relationship* +: *optional* A member of [**TreeRelationshipConstants**](../Enumerations/TreeRelationshipConstants) describing where the new node is placed relative to *Relative*. Default: **tvwNext**. + +*Key* +: *optional* A **String** name under which the node can be looked up. Keys must be unique within the **Nodes** collection (otherwise run-time error 35602). + +*Text* +: *optional* A **String** giving the node's label. + +*Image* +: *optional* A **Variant** identifying the unselected-state icon — either a 1-based **Long** index into [**TreeView.ImageList**](.#imagelist), or a **String** key. + +*SelectedImage* +: *optional* A **Variant** identifying the selected-state icon. When unset, defaults to the same as *Image*. + +Returns the newly-created [**Node**](Node). + +### Clear +{: .no_toc } + +Removes every node from the treeview, including all descendants. + +Syntax: *object*.**Clear** + +### Remove +{: .no_toc } + +Removes a node from the treeview, along with all its descendants. The remaining nodes' [**Index**](Node#index) values are recomputed. + +Syntax: *object*.**Remove** ( *Index* ) + +*Index* +: A **Variant** — either a 1-based **Long** position or a **String** key. + +### _NewEnum +{: .no_toc } + +Returns the enumerator used by `For Each node In treeView.Nodes`. Iterates nodes in **Index** order (the order they were added), not in tree-traversal order. + +Syntax: *object*.**_NewEnum** **As stdole.IUnknown** + +## See Also + +- [TreeView](.) -- the parent control +- [Node](Node) -- one node in the collection +- [TreeRelationshipConstants](../Enumerations/TreeRelationshipConstants) -- the *Relationship* values for [**Add**](#add) diff --git a/docs/Reference/WinNativeCommonCtls/TreeView/index.md b/docs/Reference/WinNativeCommonCtls/TreeView/index.md new file mode 100644 index 0000000..d7caff1 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/TreeView/index.md @@ -0,0 +1,340 @@ +--- +title: TreeView +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/TreeView/ +has_toc: false +--- + +# TreeView class +{: .no_toc } + +A **TreeView** is a hierarchical display of [**Node**](Node) objects organized into a tree. Each node can be expanded or collapsed, optionally has a checkbox, and references an icon from an associated [**ImageList**](../ImageList/). The collection of nodes is reached through [**Nodes**](#nodes); each [**Node**](Node) has its own siblings, parent, and child navigation properties. + +```tb +Private Sub Form_Load() + Set TreeView1.ImageList = ImageList1 + TreeView1.Style = tvwTreelinesPlusMinusPictureText + + Dim root As Node + Set root = TreeView1.Nodes.Add(, , "root", "My Computer", "computer") + TreeView1.Nodes.Add root, tvwChild, "c", "C: drive", "disk" + TreeView1.Nodes.Add root, tvwChild, "d", "D: drive", "disk" + root.Expanded = True +End Sub + +Private Sub TreeView1_NodeClick(ByVal Node As Node) + Debug.Print "Clicked: " & Node.FullPath +End Sub +``` + +The control inherits the focusable rect-dockable surface from `BaseControlFocusable` — size, position, **Anchors**, **Dock**, **Font**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **Refresh**, **SetFocus**, **TabIndex** / **TabStop**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. + +* TOC +{:toc} + +## Style: a composite of buttons / lines / icons / text + +[**Style**](#style) is the most-clicked property on this control. It is a single enum value but the eight choices encode a 3-bit combination of which visual elements appear: + +| [**Style**](#style) | Buttons | Lines | Icons | +|----------------------------------------------------|---------|-------|-------| +| **tvwTextOnly** | — | — | — | +| **tvwPictureText** | — | — | yes | +| **tvwPlusMinusText** | yes | — | — | +| **tvwPlusMinusPictureText** | yes | — | yes | +| **tvwTreelinesText** | — | yes | — | +| **tvwTreelinesPictureText** | — | yes | yes | +| **tvwTreelinesPlusMinusText** | yes | yes | — | +| **tvwTreelinesPlusMinusPictureText** (default) | yes | yes | yes | + +The values are decoded internally into the Win32 `TVS_HASBUTTONS` / `TVS_HASLINES` style bits. + +## Sorting + +Sorting is configured at two levels: + +- The **TreeView** as a whole sorts its root-level nodes when [**Sorted**](#sorted) is **True**, using [**SortOrder**](#sortorder) and [**SortType**](#sorttype) to control direction and comparison. +- Each individual [**Node**](Node) has its own [**Sorted**](Node#sorted) / [**SortOrder**](Node#sortorder) / [**SortType**](Node#sorttype) properties, which control how *its* children are sorted, independently of the tree-level setting. + +Toggling either flag triggers an immediate sort. New nodes added after a node has been sorted are inserted into the correct sorted position. + +## Image lists and image references + +Bind an [**ImageList**](../ImageList/) through [**ImageList**](#imagelist). Each [**Node**](Node) references icons through [**Image**](Node#image) (rendered when the node is not selected) and [**SelectedImage**](Node#selectedimage) (rendered when the node is selected); either accepts a 1-based **Long** index or a **String** key into the bound image list. Omitting [**SelectedImage**](Node#selectedimage) defaults the selected icon to the same as [**Image**](Node#image). + +## Checkboxes + +Setting [**CheckBoxes**](#checkboxes) to **True** adds a leading checkbox to every node. The user can click the checkbox or press **Space** while a node is focused to toggle; the [**NodeCheck**](#nodecheck) event then fires. [**Node.Checked**](Node#checked) reads and writes the check state programmatically. + +Properties +---------- + +### Appearance +{: .no_toc } + +How the control's border is drawn. A [**AppearanceConstants**](../../VBRUN/Constants/AppearanceConstants) member. Default: **vbAppear3d**. Inherited. + +### BorderStyle +{: .no_toc } + +The control's border style. A member of [**TreeBorderStyleConstants**](../Enumerations/TreeBorderStyleConstants): **ccNone** or **ccFixedSingle**. Default: **ccFixedSingle**. + +### CheckBoxes +{: .no_toc } + +Whether each node has a leading checkbox. **Boolean**. Default: **False**. + +### DropHighlight +{: .no_toc } + +The [**Node**](Node) currently highlighted as a drag-drop target, or **Nothing**. **Node**, read/write. + +### FullRowSelect +{: .no_toc } + +Whether clicking on the indentation area of a row selects the node (instead of only clicking on its icon or label). **Boolean**. Default: **False**. + +### HideSelection +{: .no_toc } + +Whether the selection highlight is hidden when the control does not have focus. **Boolean**. Default: **True**. + +### HotTracking +{: .no_toc } + +Whether nodes are highlighted as the mouse hovers over them. **Boolean**. Default: **False**. + +### hWnd +{: .no_toc } + +The Win32 handle of the treeview window. **LongPtr**, read-only. + +### hWndLabelEdit +{: .no_toc } + +The Win32 handle of the currently editing label's textbox window, or `0`. **LongPtr**, read-only. + +### ImageList +{: .no_toc } + +The [**ImageList**](../ImageList/) used for node icons. Assignment increments the **ImageList**'s bound-count. + +### Indentation +{: .no_toc } + +The horizontal pixel indent per level of node depth. **Double**, read/write. Default: `20`. + +### LabelEdit +{: .no_toc } + +How inline label editing is triggered. A member of [**TreeLabelEditConstants**](../Enumerations/TreeLabelEditConstants): **tvwAutomatic**, **tvwManual**, or **tvwDisabled**. Default: **tvwAutomatic**. + +### LineStyle +{: .no_toc } + +Whether tree lines are drawn from root nodes or only from child nodes. A member of [**TreeLineStyleConstants**](../Enumerations/TreeLineStyleConstants): **tvwTreeLines** or **tvwRootLines**. Default: **tvwRootLines**. + +### Nodes +{: .no_toc } + +The [**Nodes**](Nodes) collection. Read-only. + +### PathSeparator +{: .no_toc } + +The string inserted between node texts in [**Node.FullPath**](Node#fullpath). **String**. Default: `"\"`. + +### Scroll +{: .no_toc } + +Whether the treeview has scrollbars (when its content extends beyond the visible area). **Boolean**. Default: **True**. + +### SelectedItem +{: .no_toc } + +The currently selected [**Node**](Node), or **Nothing**. Read/write. + +### SingleSel +{: .no_toc } + +Whether only a single node can be expanded at any time (any other expansion automatically collapses sibling subtrees). **Boolean**. Default: **False**. + +### Sorted +{: .no_toc } + +Whether root-level nodes are sorted. **Boolean**. Default: **False**. Per-subtree sorting is controlled through [**Node.Sorted**](Node#sorted) on individual nodes. + +### SortOrder +{: .no_toc } + +The sort direction at the root level. A member of [**TreeSortOrderConstants**](../Enumerations/TreeSortOrderConstants). Default: **tvwAscending**. + +### SortType +{: .no_toc } + +The string comparison used for sorting at the root level. A member of [**TreeSortTypeConstants**](../Enumerations/TreeSortTypeConstants): **tvwBinary** (case-sensitive) or **tvwText** (case-insensitive). Default: **tvwText**. + +### Style +{: .no_toc } + +The composite visual style — see [the **Style** table above](#style-a-composite-of-buttons--lines--icons--text). A member of [**TreeStyleConstants**](../Enumerations/TreeStyleConstants). Default: **tvwTreelinesPlusMinusPictureText**. + +### WheelScrollEvent +{: .no_toc } + +Whether mouse-wheel events trigger [**Scroll**](#scroll-event). **Boolean**. Default: **True**. + +Methods +------- + +### GetVisibleCount +{: .no_toc } + +Returns the maximum number of fully-visible nodes the current viewport can show. **Long**. + +Syntax: *object*.**GetVisibleCount** **As Long** + +### HitTest +{: .no_toc } + +Returns the [**Node**](Node) at the given point, or **Nothing** if no node lies under it. Useful for drag-drop hover effects, custom context menus, and right-click handling. + +Syntax: *object*.**HitTest** ( *x*, *y* ) **As Node** + +*x* +: A **Single** horizontal coordinate in the control's coordinate system (twips by default). + +*y* +: A **Single** vertical coordinate. + +### StartLabelEdit +{: .no_toc } + +Opens the inline editor on the currently selected node. Used when [**LabelEdit**](#labeledit) is **tvwManual**. + +Syntax: *object*.**StartLabelEdit** + +Events +------ + +### AfterLabelEdit +{: .no_toc } + +Raised when an inline label edit completes. Set *Cancel* to **True** to revert the change. + +Syntax: *object*\_**AfterLabelEdit**( *Cancel* **As Boolean**, *NewString* **As String** ) + +### BeforeCollapse +{: .no_toc } + +Raised before a node is collapsed. Set *Cancel* to **True** to prevent the collapse. + +Syntax: *object*\_**BeforeCollapse**( **ByVal** *Node* **As Node**, **ByRef** *Cancel* **As Boolean** ) + +### BeforeExpand +{: .no_toc } + +Raised before a node is expanded. Set *Cancel* to **True** to prevent the expansion. + +Syntax: *object*\_**BeforeExpand**( **ByVal** *Node* **As Node**, **ByRef** *Cancel* **As Boolean** ) + +### BeforeLabelEdit +{: .no_toc } + +Raised when an inline label edit is about to start. Set *Cancel* to **True** to block the edit. + +Syntax: *object*\_**BeforeLabelEdit**( *Cancel* **As Boolean** ) + +### Click +{: .no_toc } + +Raised on a mouse click inside the control. Distinct from [**NodeClick**](#nodeclick), which fires when the click lands on a node. + +Syntax: *object*\_**Click**( ) + +### Collapse +{: .no_toc } + +Raised after a node has been collapsed. + +Syntax: *object*\_**Collapse**( **ByVal** *Node* **As Node** ) + +### DblClick +{: .no_toc } + +Raised on a double-click inside the control. + +Syntax: *object*\_**DblClick**( ) + +### DragDrop, DragOver +{: .no_toc } + +Inherited drag-drop events. + +### Expand +{: .no_toc } + +Raised after a node has been expanded. + +Syntax: *object*\_**Expand**( **ByVal** *Node* **As Node** ) + +### Initialize +{: .no_toc } + +Raised after the control's window has been created. + +### KeyDown, KeyPress, KeyUp +{: .no_toc } + +Inherited keyboard events. Pressing **Space** while [**CheckBoxes**](#checkboxes) is **True** toggles the focused node's check state and fires [**NodeCheck**](#nodecheck). + +### MouseDown, MouseMove, MouseUp +{: .no_toc } + +Inherited mouse events. + +### NodeCheck +{: .no_toc } + +Raised when a node's checkbox is toggled — either by the user clicking it, by **Space** keypress, or by code assigning [**Node.Checked**](Node#checked). + +Syntax: *object*\_**NodeCheck**( **ByVal** *Node* **As Node** ) + +### NodeClick +{: .no_toc } + +Raised when a node is clicked. Distinct from [**Click**](#click), which fires on any mouse click in the control regardless of where it lands. + +Syntax: *object*\_**NodeClick**( **ByVal** *Node* **As Node** ) + +### NodeSelect +{: .no_toc } + +Raised when a node becomes the selected node — either by user click, by keyboard arrow navigation, or by code assigning [**SelectedItem**](#selecteditem). + +Syntax: *object*\_**NodeSelect**( **ByVal** *Node* **As Node** ) + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. + +### Scroll +{: #scroll-event .no_toc } + +Raised when the treeview scrolls. New to this twinBASIC implementation; the original VB6 control did not expose this event. Set [**WheelScrollEvent**](#wheelscrollevent) to **False** to suppress firing on mouse-wheel input. + +Syntax: *object*\_**Scroll**( ) + +### Validate +{: .no_toc } + +Inherited validation event. + +## See Also + +- [Node](Node) -- a single node in the tree +- [Nodes](Nodes) -- the collection of nodes +- [ImageList](../ImageList/) -- the picture source for node icons +- [TreeBorderStyleConstants](../Enumerations/TreeBorderStyleConstants), [TreeLabelEditConstants](../Enumerations/TreeLabelEditConstants), [TreeLineStyleConstants](../Enumerations/TreeLineStyleConstants), [TreeStyleConstants](../Enumerations/TreeStyleConstants), [TreeRelationshipConstants](../Enumerations/TreeRelationshipConstants), [TreeSortOrderConstants](../Enumerations/TreeSortOrderConstants), [TreeSortTypeConstants](../Enumerations/TreeSortTypeConstants) -- the seven user-facing TreeView enums +- [ControlTypeConstants](../../VBRUN/Constants/ControlTypeConstants) -- where **vbTreeView** lives diff --git a/docs/Reference/WinNativeCommonCtls/UpDown.md b/docs/Reference/WinNativeCommonCtls/UpDown.md new file mode 100644 index 0000000..00d2f44 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/UpDown.md @@ -0,0 +1,143 @@ +--- +title: UpDown +parent: WinNativeCommonCtls Package +permalink: /tB/Packages/WinNativeCommonCtls/UpDown +has_toc: false +--- + +# UpDown class +{: .no_toc } + +An **UpDown** is a small spin control — a pair of up / down arrow buttons that adjust an internal [**Value**](#value) by [**Increment**](#increment) on each click. Often paired with an external textbox or label to display the current value. + +```tb +Private Sub Form_Load() + UpDown1.Min = 0 + UpDown1.Max = 100 + UpDown1.Increment = 5 + UpDown1.Value = 50 +End Sub + +Private Sub UpDown1_Change() + Text1.Text = UpDown1.Value +End Sub + +Private Sub Text1_Change() + If IsNumeric(Text1.Text) Then UpDown1.Value = CLng(Text1.Text) +End Sub +``` + +The control inherits the focusable rect-dockable surface from `BaseControlFocusableNoFont` — size, position, **Anchors**, **Dock**, **Appearance**, **MousePointer** / **MouseIcon**, **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **Refresh**, **SetFocus**, **TabIndex** / **TabStop**, **ZOrder**, **CausesValidation**, **VisualStyles**, **hWnd**, **HelpContextID** / **WhatsThisHelpID**. **UpDown** does not carry a [**Font**](../VB/CheckBox#font) property (the arrows are drawn by the OS theme). + +* TOC +{:toc} + +## No auto-buddy + +Unlike the VB6-era Win32 `msctls_updown32` control, this **UpDown** does not auto-attach to a "buddy" textbox — there is no `UDS_AUTOBUDDY` style exposed. Pair the spin control with another control manually by handling [**Change**](#change), [**UpClick**](#upclick), and [**DownClick**](#downclick). + +## Three event flavors + +Three events let the application observe spin interaction at different granularity: + +- **[Change](#change)** fires whenever [**Value**](#value) actually changes — including programmatic assignments. +- **[UpClick](#upclick)** fires when the user clicks the up arrow and [**Value**](#value) increases. +- **[DownClick](#downclick)** fires when the user clicks the down arrow and [**Value**](#value) decreases. + +Properties +---------- + +### Increment +{: .no_toc } + +The amount each click of the arrow buttons changes [**Value**](#value) by. **Long**. Default: `1`. Stored as the `nInc` field of a `UDACCEL` record applied via `UDM_SETACCEL`. + +### Max +{: .no_toc } + +The upper bound of the range. **Long**. Default: `10`. Applied via `UDM_SETRANGE32`. + +### Min +{: .no_toc } + +The lower bound of the range. **Long**. Default: `0`. + +### Orientation +{: .no_toc } + +The control's orientation. A [**OrientationConstants**](Enumerations/OrientationConstants) member (**ccOrientationHorizontal** or **ccOrientationVertical**). Default: **ccOrientationHorizontal**. Changing this property at run time recreates the underlying Win32 window. + +### ToolTipText +{: .no_toc } + +A tooltip string shown when the user hovers over the control. **String**. Inherited but re-exposed. + +### Value +{: .no_toc } + +The current spinner value. **Long**. The default member. Reads via `UDM_GETPOS32`, writes via `UDM_SETPOS32`. Fires [**Change**](#change) when set programmatically. Clamped to [[**Min**](#min), [**Max**](#max)]. + +### VisualStyles +{: .no_toc } + +Whether the OS visual styles theme is applied. **Boolean**. Default: **True**. Inherited but re-exposed. + +Events +------ + +### Change +{: .no_toc } + +Raised when [**Value**](#value) has changed by user interaction, by an arrow click, or by code. + +Syntax: *object*\_**Change**( ) + +### DownClick +{: .no_toc } + +Raised when the user clicks the down arrow and [**Value**](#value) is successfully decreased. + +Syntax: *object*\_**DownClick**( ) + +### DragDrop, DragOver +{: .no_toc } + +Inherited drag-drop events. + +### GotFocus, LostFocus +{: .no_toc } + +Inherited focus events. + +### Initialize +{: .no_toc } + +Raised after the control's window has been created. + +### MouseDown, MouseMove, MouseUp +{: .no_toc } + +Inherited mouse events. + +### OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag +{: .no_toc } + +Inherited OLE drag-and-drop events. + +### UpClick +{: .no_toc } + +Raised when the user clicks the up arrow and [**Value**](#value) is successfully increased. + +Syntax: *object*\_**UpClick**( ) + +### Validate +{: .no_toc } + +Inherited validation event. + +## See Also + +- [Slider](Slider) -- a draggable thumb on a track, when the range is visualised +- [OrientationConstants](Enumerations/OrientationConstants) -- the shared horizontal / vertical enum used by **UpDown** and **Slider** +- [ControlTypeConstants](../VBRUN/Constants/ControlTypeConstants) -- where **vbUpDown** lives diff --git a/docs/Reference/WinNativeCommonCtls/index.md b/docs/Reference/WinNativeCommonCtls/index.md new file mode 100644 index 0000000..1814b61 --- /dev/null +++ b/docs/Reference/WinNativeCommonCtls/index.md @@ -0,0 +1,83 @@ +--- +title: WinNativeCommonCtls Package +parent: Packages +grand_parent: Reference Section +nav_order: 12 +permalink: /tB/Packages/WinNativeCommonCtls/ +has_toc: false +--- + +# WinNativeCommonCtls Package +{: .no_toc } + +The **WinNativeCommonCtls** built-in package is a VB6-compatible replacement for **Microsoft Common Controls 6.0** (the legacy `MSCOMCTL.OCX`), reimplemented on top of the Win32 ComCtl32 controls in `COMCTL32.DLL` and `MSFTEDIT.DLL`. It ships eight controls that mirror the MSCOMCTL surface name-for-name where possible, with the same property / method / event spellings VB6 developers will recognise. + +The package is a built-in package shipped with twinBASIC. Add it through Project → References (**Ctrl-T**) → Available Packages. + +* TOC +{:toc} + +## Why this package exists + +VB6 projects that depend on `MSCOMCTL.OCX` cannot run unmodified in modern environments — the OCX is unsigned, requires per-machine registration with admin rights, ships with known security advisories, and is not available on non-Windows hosts at all. **WinNativeCommonCtls** removes the dependency: drop a [**ListView**](ListView/), [**TreeView**](TreeView/), [**ProgressBar**](ProgressBar), [**Slider**](Slider), [**ImageList**](ImageList/), [**DTPicker**](DTPicker), [**MonthView**](MonthView), or [**UpDown**](UpDown) onto a [**Form**](../VB/Form/) and twinBASIC creates the underlying Win32 ComCtl32 control directly, with no OCX involved. + +The control names, property names, event signatures, and the `vb…` / `tvw…` / `lvw…` / `sld…` / `Prb…` member-name conventions all match the original `MSCOMCTL.OCX` surface, so VB6 code that uses the original controls usually compiles and runs unchanged once the package reference is in place. + +## Controls + +Eight controls, each a leaf class that inherits from a `BaseCtl` where the entire surface is declared: + +- [DTPicker](DTPicker) -- date / time picker: dropdown calendar, single-date [**Value**](DTPicker#value), custom format strings, optional spin-button and checkbox variants +- [ImageList](ImageList/) -- off-screen image collection that feeds icons to [**ListView**](ListView/) and [**TreeView**](TreeView/) through the [**Icons**](ListView/#icons) / [**SmallIcons**](ListView/#smallicons) / [**ImageList**](TreeView/#imagelist) properties; not visible at run time +- [ListView](ListView/) -- multi-column list with four [**View**](ListView/#view) modes (Icon / SmallIcon / List / Report), label-edit, checkboxes, column-header icons, and per-item state +- [MonthView](MonthView) -- full-month calendar grid with multi-month layout ([**MonthColumns**](MonthView#monthcolumns) × [**MonthRows**](MonthView#monthrows)), multi-day selection, bold-day callbacks for highlighting, week numbers, and a today indicator +- [ProgressBar](ProgressBar) -- standard / smooth / marquee progress indicator with three visual states (Normal / Error / Paused), horizontal or vertical orientation +- [Slider](Slider) -- trackbar / slider with tick marks, optional selection range, vertical or horizontal orientation, and a draggable thumb with floating tip +- [TreeView](TreeView/) -- hierarchical tree of [**Node**](TreeView/Node) objects with sorting, label-edit, checkboxes, image lists, and per-node bold / color overrides +- [UpDown](UpDown) -- spin control with up / down arrows: pure [**Min**](UpDown#min) / [**Max**](UpDown#max) / [**Value**](UpDown#value) / [**Increment**](UpDown#increment) (no auto-buddy) + +[**ListView**](ListView/), [**TreeView**](TreeView/), and [**ImageList**](ImageList/) carry collection sub-objects — see the folder index pages for the full hierarchy. + +## Enumerations + +Module-level enumerations shared across multiple controls live under [**Enumerations**](Enumerations/); per-control nested enumerations (`ListViewConstants`, `PrbState`, `TickStyleConstants`, …) are documented on the page of the control that declares them. + +- [Enumerations](Enumerations/) -- the ten user-facing enumerations declared in shared modules + +## Cross-control surface + +Every control inherits the standard VB-package surface from `BaseControl` / `BaseControlRect` / `BaseControlRectDockable` / `BaseControlFocusable` (or `BaseControlNotFocusable`, where the control cannot take focus). Inherited members include: + +- **Positioning and layout** — **Name**, **Left**, **Top**, **Width**, **Height**, **Anchors**, **Dock**, **Visible**, **Enabled**, **Move**, **Refresh**, **ZOrder**, **Container**, **Parent**, **Index**, **Tag**, **hWnd** +- **Appearance** — **BackColor** / **ForeColor** / **Font** (where focusable), **Appearance**, **VisualStyles**, **MousePointer** / **MouseIcon** +- **Tooltips, drag-and-drop, validation** — **ToolTipText**, **DragMode** / **DragIcon**, **Drag**, **CausesValidation**, **Validate** event +- **Focus** (focusable controls only) — **SetFocus**, **TabIndex**, **TabStop**, **GotFocus** / **LostFocus** events +- **Help integration** — **HelpContextID**, **WhatsThisHelpID** +- **OLE drag-and-drop** — **OLEDropMode**, the **OLEDrag** method, and the **OLEStartDrag** / **OLEGiveFeedback** / **OLESetData** / **OLEDragOver** / **OLEDragDrop** / **OLECompleteDrag** events. See [**OLEDropConstants**](../VBRUN/Constants/OLEDropConstants) +- **Transparency** — **Opacity** (`Double`, 0–100, percentage) and **TransparencyKey** (`OLE_COLOR`, `-1` to disable). Both require Windows 8 (target OS 6.2+) for child controls; on older OS versions they are silently no-ops + +Each control's reference page lists the control-specific surface — own properties, methods, events, and nested enums — without re-enumerating the entire inherited base every time. + +## Control type constants + +Every control answers to one of the `vb…` values in [**ControlTypeConstants**](../VBRUN/Constants/ControlTypeConstants): + +| Constant | Value | Control | +|---------------------|-------|------------------------------------| +| **vbProgressBar** | 21 | [ProgressBar](ProgressBar) | +| **vbTreeView** | 22 | [TreeView](TreeView/) | +| **vbSlider** | 26 | [Slider](Slider) | +| **vbUpDown** | 27 | [UpDown](UpDown) | +| **vbDTPicker** | 28 | [DTPicker](DTPicker) | +| **vbMonthView** | 29 | [MonthView](MonthView) | +| **vbListView** | 30 | [ListView](ListView/) | +| **vbImageList** | 31 | [ImageList](ImageList/) | + +Each control's **ControlType** property returns its constant at run time, allowing generic `For Each` loops over a form's **Controls** collection to discriminate between controls. + +## See also + +- [VB Package](../VB/) -- the standard control set: **CheckBox**, **CommandButton**, **TextBox**, **Frame**, **Form**, … +- [CustomControls Package](../CustomControls/) -- owner-drawn `Waynes…` custom controls when the Win32 surface is not enough +- [ControlTypeConstants](../VBRUN/Constants/ControlTypeConstants) -- the `vb…` discriminator constants used by the **ControlType** property on every control +- [OLEDropConstants](../VBRUN/Constants/OLEDropConstants) -- the **OLEDropMode** values shared by every control in this package From d82790c12393a2b14591a5a24b154cf4bfc7ee6b Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Thu, 14 May 2026 19:15:53 +0200 Subject: [PATCH 3/3] Factor out per-package WIP information. --- WIP.Assert.md | 37 + WIP.CEF.md | 48 ++ WIP.CustomControls.md | 81 ++ WIP.WebView2.md | 26 + WIP.WinEventLogLib.md | 102 +++ WIP.WinNamedPipesLib.md | 199 +++++ WIP.WinNativeCommonCtls.md | 130 +++ WIP.WinServicesLib.md | 132 +++ WIP.md | 1561 +----------------------------------- WIP.tbIDE.md | 227 ++++++ 10 files changed, 1009 insertions(+), 1534 deletions(-) create mode 100644 WIP.Assert.md create mode 100644 WIP.CEF.md create mode 100644 WIP.CustomControls.md create mode 100644 WIP.WebView2.md create mode 100644 WIP.WinEventLogLib.md create mode 100644 WIP.WinNamedPipesLib.md create mode 100644 WIP.WinNativeCommonCtls.md create mode 100644 WIP.WinServicesLib.md create mode 100644 WIP.tbIDE.md diff --git a/WIP.Assert.md b/WIP.Assert.md new file mode 100644 index 0000000..46cb799 --- /dev/null +++ b/WIP.Assert.md @@ -0,0 +1,37 @@ +# Assert Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. + +Three sibling modules — `Exact`, `Strict`, `Permissive` — that expose the **same 15-member API**; only the comparison semantics differ: + +| Module | String comparisons | Other comparisons | +|---------------|--------------------|--------------------------------------------------------------------------------| +| `Exact` | case-sensitive | no implicit conversions; datatypes must match exactly (`5 <> 5.0`); `vbNullString <> ""`; `Empty` distinct from `0` / `False` / `""`; object default members **not** evaluated | +| `Strict` | case-sensitive | evaluated as if written directly in twinBASIC; object default members **not** evaluated | +| `Permissive` | case-insensitive | evaluated as if written directly in twinBASIC | + +`Null` is never equal to anything (not even itself) under any of the three flavours — use `IsNull` / `IsNotNull` to test for it. + +The 15 members per module (identical signatures across all three): + +| Member | Purpose | +|-------------------------------------------------|------------------------------------------------------------------| +| `Succeed()` | unconditionally records a pass | +| `Fail([Message])` | unconditionally records a failure | +| `Inconclusive([Message])` | records an inconclusive / skipped result | +| `AreEqual(Expected, Actual, [Message])` | value-equality assertion | +| `AreNotEqual(Expected, Actual, [Message])` | inverse of `AreEqual` | +| `AreSame(Expected, Actual, [Message])` | reference-identity (`Is`) assertion for objects | +| `AreNotSame(Expected, Actual, [Message])` | inverse of `AreSame` | +| `IsTrue(Condition, [Message])` | asserts the condition is `True` | +| `IsFalse(Condition, [Message])` | asserts the condition is `False` | +| `IsNothing(Value, [Message])` | asserts the object reference is `Nothing` | +| `IsNotNothing(Value, [Message])` | inverse of `IsNothing` | +| `IsNull(Value, [Message])` | asserts the value is `Null` | +| `IsNotNull(Value, [Message])` | inverse of `IsNull` | +| `SequenceEquals(Expected, Actual, [FailMessage])` | element-by-element comparison of two sequences / arrays | +| `NotSequenceEquals(Expected, Actual, [FailMessage])` | inverse of `SequenceEquals` | + +Surface each member as if it were an ordinary `Sub` (e.g. `Sub AreEqual(Expected, Actual, [Message])`); the source-side `Lib ""` / `Alias "#N"` / `PreserveSig` / `DeclareWide` decoration is internal pseudo-DLL plumbing and is **not** surfaced. Call out `[DebugOnly(True)]` (assertions compile out of release builds) and `[MustBeQualified(True)]` (callers must write the module name, e.g. `Strict.IsTrue(x)`). + +Layout: one page per module, listing all 15 members inline under `## ` headings (deep-linkable as `…/Strict#areequal`). Replicating 15 × 3 = 45 near-duplicate pages would add noise without value. diff --git a/WIP.CEF.md b/WIP.CEF.md new file mode 100644 index 0000000..62f68e4 --- /dev/null +++ b/WIP.CEF.md @@ -0,0 +1,48 @@ +# CEF Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. + +The entire user-facing surface lives in one source file (`CefControl.twin`): the private `CefBrowserBaseCtl` (where every event / method / property is declared), the private `CefEnvironmentOptions`, and the public `CefBrowser` control which inherits from `CefBrowserBaseCtl`. Everything else under the source tree (`CefControlGlobalWnd`, `APIs`, `MainModule`, `Registry`, `SpecialFolders`, the `CEF\Aliases` / `ApiEntryPoints` / `Globals` / `Initialize` / `Misc` modules, the 30 `_cef_*_t` internal enums and structs, the `CrossProcessIPC` IPC brokers, and every `Implementations/*` callback handler) is `Private Class` / `Private Module` plumbing and gets no doc page. Two user-facing enums live inside otherwise-private files: `CefLogSeverity` in the `_cef_log_severity_t` enum file, and `cefPrintOrientation` inline in `BrowserOM.twin`. + +Public user-facing surface (one control + one options class + two enums): + +| Symbol | Kind | Role | +|------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------| +| `CefBrowser` | Class (inherits `CefBrowserBaseCtl`) | the control itself, tagged `[WindowsControl("/Miscellaneous/cef64.png")]` | +| `CefEnvironmentOptions` | `Private Class`, exposed | reached via `Public EnvironmentOptions As CefEnvironmentOptions = New CefEnvironmentOptions` on the control | +| `CefLogSeverity` | `Enum` (in `_cef_log_severity_t.twin`)| used by `CefEnvironmentOptions.LogSeverity` | +| `cefPrintOrientation` | `Enum` (in `BrowserOM.twin`) | used by the `Orientation` parameter of `CefBrowser.PrintToPdf` | + +`CefBrowserBaseCtl` is `Private Class` but is where every public member is *declared* — `CefBrowser` itself adds nothing beyond inheriting from it. The page (`CefBrowser/index.md`, folder-style, parallel to `WebView2/`) describes the union; the base class is invisible to user code. + +`CefBrowser` inherits from `VB.BaseControlRectDockable`, so its Properties listing folds in the dockable-rect surface (`Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, ...) the same way VB-package and CustomControls control pages do. + +`CefBrowserRequestHeaders` is declared as `Alias CefBrowserRequestHeaders As Object` and appears in the `NavigationStarting` event signature. The underlying `Class CefRequestHeaders` is an empty placeholder for a future header collection. **No doc page;** mention on the `NavigationStarting` event entry that the parameter is currently typed `Object` and reserved for future use. + +The `CefBrowser` public surface: + +- **Events (12):** `Create`, `Ready`, `Error`, `NavigationStarting`, `NavigationComplete`, `SourceChanged`, `DocumentTitleChanged`, `DOMContentLoaded`, `PrintToPdfCompleted`, `PrintToPdfFailed`, `JsAsyncResult`, `JsMessage`. +- **Methods (14):** `Initialize`, `Navigate`, `NavigateToString`, `Reload`, `GoBack`, `GoForward`, `ExecuteScript`, `JsRun`, `JsRunAsync`, `PostWebMessage`, `SetVirtualHostNameToFolderMapping`, `ClearVirtualHostNameToFolderMapping`, `OpenDevToolsWindow`, `PrintToPdf`. +- **Properties (12):** `DocumentURL` (Get/Let), `DocumentTitle` (Get), `ZoomFactor` (Get/Let), `UserAgent` (Get/Let), `CanGoBack` (Get), `CanGoForward` (Get), `CefMajorVersion` (Get), `Visible` (Get/Let), `hWnd` (Get), `Parent` (Get), `EnvironmentOptions` (field), `CreateInitialized` (field, `Boolean`, defaults `True`). Plus a `Hidden` `Align` (Get/Let) inherited boilerplate. Plus the inherited rect-dockable surface (`Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, …). + +`CefEnvironmentOptions` is four bare `Public` fields: + +| Field | Type | +|----------------------------|------------------| +| `BrowserExecutableFolder` | `String` | +| `UserDataFolder` | `String` | +| `LogFilePath` | `String` | +| `LogSeverity` | `CefLogSeverity` | + +`CefLogSeverity` members: `CefLogDisable = 0`, `CefLogVerbose = 1`, `CefLogInfo = 2`, `CefLogWarning = 3`, `CefLogError = 4`, `CefLogFatal = 5`. + +`cefPrintOrientation` members: `cefPrintPortrait = 0`, `cefPrintLandscape = 1`. + +**WebView2-parity gap list** (called out on `CEF/index.md`, drawn from the sample-project examples where these are commented out as *"Sorry, this feature is not yet available in the CEF package"*): + +- Methods: `OpenTaskManagerWindow`, `AddObject` (host-object publication), the request-filter machinery (`AddWebResourceRequestedFilter`). +- Events: `AcceleratorKeyPressed`, `PermissionRequested`, `WebResourceRequested`, `ProcessFailed`, `ScriptDialogOpening`, `UserContextMenu`, `SuspendCompleted`, `SuspendFailed`, `DownloadStarting`, `NewWindowRequested`. + +`CefBrowser.NavigationComplete` carries `IsSuccess` and `WebErrorStatus` parameters, but `OnNavigationComplete_UI` currently hard-codes `IsSuccess = True` and `WebErrorStatus = 0` with `FIXME` comments — noted on the event entry. + +**Multi-version source.** The same `.twin` sources compile against three CEF runtimes (v49 / v109 / v145) selected via the `CEF_VERSION` conditional-compilation argument on the project. At runtime, `CefBrowser.CefMajorVersion` returns the value picked at compile time. The user picks a runtime at deploy time by downloading the matching ZIP from `github.com/twinbasic/cef-runtimes` and extracting to `%LocalAppData%\twinBASIC_CEF_Runtime\`, or by overriding `CefBrowser.EnvironmentOptions.BrowserExecutableFolder` before / during the `Create` event. The runtime download + version-picking section lives on `CEF/index.md`. diff --git a/WIP.CustomControls.md b/WIP.CustomControls.md new file mode 100644 index 0000000..0f06e03 --- /dev/null +++ b/WIP.CustomControls.md @@ -0,0 +1,81 @@ +# CustomControls Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. + +Two source-side packages, **one** doc-side package (`docs/Reference/CustomControls/`). The two source halves split by *role*: a **DESIGNER** framework (the abstract surface a custom control hooks into — `ICustomControl`, `ICustomForm`, the `CustomControlContext` / `CustomFormContext` / `CustomControlTimer` / `CustomControlsCollection` CoClasses, plus the `SerializeInfo` / `Canvas` UDTs and the enums) and a **runtime** half (the eight concrete `Waynes…` controls + shared appearance helpers + mixin base classes). + +Public user-facing surface, grouped by role. + +## Concrete controls + +Each is `Class ` (no `Public` modifier — implicitly public), tagged `[CustomControl("/miscellaneous/frm.png")]` (designer icon) and `[COMCreatable(False)]` (cannot be `New`'d through COM; instantiated by the designer). + +| Control | Implements | Co-located public types | +|------------------|-----------------------------------------------------------|--------------------------------------------------| +| `WaynesButton` | `ICustomControl` + `BaseControlFocusable` (mixin) | `WaynesButtonState` (private, but exposed) | +| `WaynesForm` | `ICustomControl` + `BaseForm` (mixin) | — (uses `WindowsFormOptions` from support file) | +| `WaynesFrame` | `ICustomControl` + `BaseControl` (mixin) | — | +| `WaynesGrid` | `ICustomControl` + `BaseControlFocusable` (mixin) | `Column`, `CellRenderingOptions` | +| `WaynesLabel` | `ICustomControl` + `BaseControl` (mixin) | — | +| `WaynesSlider` | `ICustomControl` + `BaseControlFocusable` (mixin) | `WaynesSliderState`, `SliderDirection` & `SliderDisplayValueFormat` (nested enums) | +| `WaynesTextBox` | `ICustomControl` + `BaseControlFocusable` (mixin) | `WaynesTextBoxState` | +| `WaynesTimer` | `ICustomControl` + `BaseControl` (mixin) | — | + +The "mixin" base classes (`BaseControl`, `BaseControlFocusable`, `BaseForm`) are pulled into each control via the twinBASIC `Implements Via _BaseControl = New ` syntax. The base classes themselves get **no doc page** (they're private and never named by user code), but the inherited members **must be folded into each control's Properties listing** the same way VB-package controls list their inherited surface. The visible inherited surface, by mixin: + +- `BaseControl` → `Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, `Visible`. +- `BaseControlFocusable` → all of `BaseControl` + `TabIndex`, `TabStop`. +- `BaseForm` → `FormDesignerId`, `Name`, `Left`, `Top`, `Width`, `Height`, `Controls`. + +The state-holder classes (`WaynesButtonState`, `WaynesSliderState`, `WaynesTextBoxState`) and `WindowsFormOptions` are declared `Private Class` but are exposed on the parent control via `Public WithEvents NormalState As WaynesButtonState` (etc.). Same situation as `WebView2EnvironmentOptions` — document them as **sub-pages** of the parent control using the folder-style layout. + +## Shared appearance helpers + +These helpers are reachable through `Public WithEvents …` properties on one or more of the eight controls: + +| Class | Reached as | +|-----------------|-------------------------------------------------------------------------------------| +| `Anchors` | `.Anchors` (via the mixin base) | +| `Corners` | `.Corners`, `CellRenderingOptions.Corners`, `.BackgroundCorners`, `BlockCorners` | +| `Corner` | `Corners.TopLeft` / `.TopRight` / `.BottomLeft` / `.BottomRight` | +| `Borders` | `.Borders`, `CellRenderingOptions.Borders`, `.BackgroundBorders`, `BlockBorders` | +| `Border` | element of `Borders.Elements()`; also `TextRendering.Outlines()` | +| `Fill` | `.BackgroundFill`, `.BlockFill`, `CellRenderingOptions.Fill`, `Border.Fill`, `Line.Fill`, `TextRendering.Fill` | +| `FillColorPoint` | element of `FillColorPoints.Values()` | +| `FillColorPoints` | `Fill.ColorPoints` | +| `Line` | `WaynesGrid.VerticalLineOptions` / `.HorizontalLineOptions` / `.ResizerBar` | +| `Padding` | `TextRendering.Padding` | +| `TextRendering` | `.TextRendering`, `WaynesLabel.TextRendering`, `CellRenderingOptions.TextRendering` | +| `FontStyle` | `TextRendering.Font` | +| `WindowsFormOptions` | `WaynesForm.WindowsOptions` (only one consumer) | + +These pair their small helpers on a single page (`Corner` inlines under `Corners.md`, `Border` under `Borders.md`, `FillColorPoint` and `FillColorPoints` under `Fill.md`, `FontStyle` under `TextRendering.md`). `WindowsFormOptions` is the exception: it has exactly one consumer (`WaynesForm`), so it sits as a folder-style sub-page of `WaynesForm/`, parallel to how WebView2 carries `EnvironmentOptions`. The `TextDecorator(s)` / `UDTs` / `MathSupport` / `ColorSupport` / mixin-bases content is package-internal and gets **no doc page**. + +## DESIGNER framework surface + +The framework half — what a *control author* writes against. Documented under `docs/Reference/CustomControls/Framework/`: + +| Symbol | Kind | Role | +|------------------------------|---------------------|---------------------------------------------------------------------------------------| +| `ICustomControl` | `Interface` | what every concrete control implements: `Initialize(Context)`, `Destroy()`, `Paint(Canvas)` | +| `ICustomForm` | `Interface` | analogous surface for form-class custom controls | +| `CustomControlContext` | `CoClass` | passed to `ICustomControl.Initialize`; offers `GetSerializer()`, `Repaint()`, `CreateTimer()`, `ChangeFocusedElement()` | +| `CustomFormContext` | `CoClass` | extends `CustomControlContext` with `Show()` / `Close()` | +| `CustomControlTimer` | `CoClass` | returned by `CustomControlContext.CreateTimer()`; `Interval`, `Enabled`, `OnTimer` event | +| `CustomControlsCollection` | `CoClass` | the `Controls` collection on a form — `Count`, `Item`, `Add`, `Remove`, `_NewEnum` | +| `SerializeInfo` | UDT | obtained from `Context.GetSerializer()`; exposes `RuntimeUISrz*` operations (deserialize, mode flags, …) | +| `Canvas` | UDT | parameter to `ICustomControl.Paint`; exposes `RuntimeUICCCanvasAddElement` + DPI / size getters | + +Both UDTs follow a pattern unique to twinBASIC: a `Pointer As LongPtr` field plus `Public DeclareWide PtrSafe Function/Sub … Lib "" Alias "#N"` pseudo-DLL declarations bound directly into the type. From a *caller* perspective these read as instance methods on the UDT (`Canvas.RuntimeUICCCanvasAddElement(descriptor)`); document them as methods, and **do not** surface the `Lib "<…>"` / `Alias "#N"` / `PreserveSig` / `DLLStackCheck` decoration (same treatment as Assert's pseudo-DLL plumbing). The verbose `RuntimeUISrz*` / `RuntimeUICC*` names are unfortunate but they *are* the public API — keep them as-is. + +The two underscore-prefixed default interfaces of each CoClass (`_CustomControlTimer`, `_CustomControlContext`, `_CustomFormContext`, `_CustomControlsCollection`, `_CustomControlTimerEvents`) are an implementation detail of the COM `[Default]`/`[Default, Source]` pattern — fold their members onto the CoClass page, **don't** give the interfaces their own pages. + +## Enumerations + +Public enums under `docs/Reference/CustomControls/Enumerations/`: + +- `CornerShape`, `FillPattern`, `TextAlignment`, `TextOverflowMode`, `DockMode`, `FontWeight`, `StartupPosition`, `BorderStyle`, `WindowState` — straightforward value enums. +- `Customtate` — **probable typo** for `CustomState`. Has the same three members as `WindowState` (`tbNormal` / `tbMinimized` / `tbMaximized`) and isn't referenced anywhere else in the package. Document it (since it's `Public`), but add a `> [!NOTE]` callout flagging the typo and pointing readers to `WindowState`. +- `ColorRGBA`, `PixelCount`, `PointSize` — these are declared as `Enum` only because twinBASIC doesn't yet have a `Type Foo = Long` alias syntax. Each carries a `FIXME` comment ("Substitute for an ALIAS to Long") and a single `[_MAX] = 0` placeholder member. Document them as **typedefs for `Long`** (the underlying storage type), not as real enums. Note in each that the alias is what user code actually sees on `Public Width As CustomControls.PixelCount` (etc.) — when the alias syntax lands, these enum stand-ins go away. + +Plus the two enums nested inside `WaynesSlider`: `SliderDirection` and `SliderDisplayValueFormat`. They live on the `WaynesSlider/index.md` page rather than under `Enumerations/` (locally scoped to the slider). diff --git a/WIP.WebView2.md b/WIP.WebView2.md new file mode 100644 index 0000000..89f3ead --- /dev/null +++ b/WIP.WebView2.md @@ -0,0 +1,26 @@ +# WebView2 Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. + +The user-facing surface is the `WebView2` control plus a small set of wrapper classes plus the `wv2…` enumerations. + +| Class | Role | +|-----------------------------|-------------------------------------------------------------------------------------------------------| +| `WebView2` | the control itself (`Inherits VB.BaseControlFocusableNoFont`, `[WindowsControl(...)]`) | +| `WebView2Header` | one HTTP header (Name / Value); value type returned by header iteration | +| `WebView2HeadersCollection` | enumerable wrapper used by `For Each` over request / response headers | +| `WebView2Request` | request side of a `WebResourceRequested` event — Method, Uri, Headers, ContentBytes, ContentUTF8 | +| `WebView2RequestHeaders` | mutable request-header collection — `GetHeader`, `Contains`, `AppendHeader`, `RemoveHeader`, … | +| `WebView2Response` | response side of a `WebResourceRequested` event — StatusCode, ReasonPhrase, Headers, ContentBytes… | +| `WebView2ResponseHeaders` | mutable response-header collection | + +`WebView2EnvironmentOptions` is declared `Private Class`, **but** the `WebView2` control exposes it via `Public EnvironmentOptions As WebView2EnvironmentOptions = New WebView2EnvironmentOptions`. It is documented as a sub-page of the `WebView2` control class — its `Public` fields (`BrowserExecutableFolder`, `UserDataFolder`, `AdditionalBrowserArguments`, `Language`, `TargetCompatibleBrowserVersion`, `AllowSingleSignOnUsingOSPrimaryAccount`, `ExclusiveUserDataFolderAccess`, `EnableTrackingPrevention`) are user-set before / during the `Create` event. + +The `WebView2` control class uses the **folder-style** layout (`WebView2/index.md`) because of its size and to host the `EnvironmentOptions` sub-page. + +Enumerations (ten of them: `wv2PermissionKind`, `wv2PermissionState`, `wv2ErrorStatus`, `wv2KeyEventKind`, `wv2WebResourceContext`, `wv2ProcessFailedKind`, `wv2ScriptDialogKind`, `wv2HostResourceAccessKind`, `wv2PrintOrientation`, `wv2DefaultDownloadCornerAlign`) live under `WebView2/Enumerations/`. `COREWEBVIEW2_PHYSICAL_KEY_STATUS` is a public `Type` (used in the `AcceleratorKeyPressed` event arguments) and lives under `WebView2/Types/`. + +Pre-existing site cross-references: + +- [`docs/Tutorials/WebView2/`](docs/Tutorials/WebView2) — task-oriented tutorial set; cross-link from / to the reference pages where useful. +- [`docs/Reference/VBRUN/Constants/ControlTypeConstants.md`](docs/Reference/VBRUN/Constants/ControlTypeConstants.md) — lists `vbWebView2 = 18`. diff --git a/WIP.WinEventLogLib.md b/WIP.WinEventLogLib.md new file mode 100644 index 0000000..6a32c81 --- /dev/null +++ b/WIP.WinEventLogLib.md @@ -0,0 +1,102 @@ +# WinEventLogLib Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. Sister packages: [WinServicesLib](WIP.WinServicesLib.md), [WinNamedPipesLib](WIP.WinNamedPipesLib.md). + +The user-facing surface is the generic `EventLog(Of T1, T2)` class plus a single helper module. The package's `Constants.twin` declares `Public Enum EventLogTypeConstants` inside a `Private Module`, so the enum does not actually surface — and the `EventLogHelperPrivate` module in `Helper.twin` is *intended-private* despite the `Public` modifier (only used by `EventLog.LogArray` internally). The Win32 API wrappers in `APIs.twin` are pure plumbing. + +Public user-facing surface (one generic class + one helper module): + +| Symbol | Kind | Role | +|---------------------------------|---------------------|---------------------------------------------------------------------------------------| +| `EventLog(Of T1, T2)` | Generic class | Main user-facing class. `T1` is the event-ID enum, `T2` is the category enum. | +| `EventLogHelperPublic` | Public module | Holds the low-level `RegisterEventLogInternal` helper. | +| `RegisterEventLogInternal` | `Sub` on the module | Registry-write helper; `EventLog.Register()` is the normal entry point. | + +`EventLog(Of T1, T2)` public members: + +- `Public Sub New(LogName As String)` — constructor. `LogName` is either a leaf name (`"MyService"`, registered under `Application\MyService`) or a full path (`"System\MyService"`, registered under `System\MyService`). +- `Public Sub LogSuccess(ByVal EventId As T1, ByVal CategoryId As T2, ParamArray AdditionalStrings())` — writes an **Information**-type event (`EVENTLOG_SUCCESS = 0`). The name "Success" is the Win32 SDK constant's literal name, *not* the audit-success category — the underlying event type is **Information**. +- `Public Sub LogFailure(ByVal EventId As T1, ByVal CategoryId As T2, ParamArray AdditionalStrings())` — writes an **Error**-type event (`EVENTLOG_ERROR_TYPE = 1`). +- `Public Sub Register()` — writes the registry entries under `HKLM\SYSTEM\CurrentControlSet\Services\EventLog\…` to declare this EXE as the message provider for the source. Calls `RegisterEventLogInternal(LogName, GetDeclaredMaxEnumValue(Of T2))` — the category count is derived from `T2`'s declared maximum value at compile time. + +Class-level decoration on `EventLog`: `[COMCreatable(False)]`, `[ClassId("4AEA12E8-…-EAEAEAEAEAEA")]` (the `EA` suffix triggers special compiler handling for generic classes). The `[Description]` attribute on the class is the basis for the page intro: *"This is the main event log (generic) class."* + +`EventLogHelperPublic` public members: + +- `Public Sub RegisterEventLogInternal(ByVal LogPath As String, ByVal CategoryCount As Long)` — the registry-write helper. Prepends `"Application\"` to *LogPath* if no backslash is present, opens `HKLM\SYSTEM\CurrentControlSet\Services\EventLog\` with `KEY_WRITE`, then writes `EventMessageFile` and `CategoryMessageFile` (both set to `App.ModulePath`) and `CategoryCount`. Requires admin rights (registry writes to HKLM). Raises run-time error 5 with the message *"Failed to register event log source (\)"* if the open fails. Normally callers use `EventLog.Register()`, which fills *CategoryCount* automatically. + +**Gaps and quirks** to surface on the docs (drawn from a static read of the source): + +- The `EventLogTypeConstants` enum has five values (`Success`, `Warning`, `Error`, `AuditSuccess`, `AuditFailure`) but the public class only exposes Information and Error event types — Warning and Audit events are not currently reachable. +- Method names follow the Win32 SDK constants verbatim: `LogSuccess` writes an *Information* event (because `EVENTLOG_SUCCESS = 0` is the Win32 spelling for the information type), and `LogFailure` writes an *Error* event. Call this out on the per-method entries. +- Message resources: the registry entries point at `App.ModulePath` (the running EXE) for both `EventMessageFile` and `CategoryMessageFile`. Windows therefore expects a message-table resource keyed by the `T1` and `T2` enum values to be embedded in the EXE. The `.twin` source does not itself synthesise that resource; whatever mechanism populates the resource sits in the compiler's special-handling path for the `[ClassId("…EAEAEAEAEAEA")]` magic-byte pattern. The docs describe what Windows expects without making strong claims about how the compiler delivers it. +- `Register()` requires elevation. Normal usage is to call it once during install (from an elevated installer), then call `LogSuccess` / `LogFailure` at runtime without elevation. + +## Canonical usage idiom — composition-delegation onto a service class + +The package's intended usage pattern — *not* obvious from the bare API — is composition-delegation: + +```tb +Class TBSERVICE001 + Implements ITbService + Implements EventLog(Of MESSAGETABLE.EVENTS, MESSAGETABLE.CATEGORIES) Via _ + EventLog = New EventLog(Of MESSAGETABLE.EVENTS, MESSAGETABLE.CATEGORIES)("Application\" & CurrentComponentName) + … + LogSuccess(service_started, status_changed, CurrentComponentName) ' surfaces directly +End Class +``` + +The `Implements Via = ` form is twinBASIC's composition-delegation syntax (see [`docs/Features/Language/Delegation.md`](docs/Features/Language/Delegation.md) if/once that page exists, or the [`CustomControls` mixin pattern](docs/Reference/CustomControls/index.md) for an analogous use). The class declares it `Implements EventLog(Of …)` and gives the compiler a private field plus a constructor expression; the compiler then auto-forwards every `Public` member of `EventLog` (`LogSuccess`, `LogFailure`, `Register`) through that field. The result: a service class that *contains* an `EventLog` instance and exposes its logging methods as if they were its own. + +Surface this on the `EventLog` page (and on the package index) as the **recommended pattern** for service / long-running classes. Spell out: + +- The constructor expression evaluates *once* (the first time the delegating class is instantiated, per twinBASIC's `Implements ... Via` semantics). +- The `T1` / `T2` type arguments must be identical at the `Implements` declaration and the constructor (the compiler enforces this). +- The `LogPath` is typically `"Application\" & CurrentComponentName` — `CurrentComponentName` is the compile-time class name, so the log path automatically tracks renames. +- The delegating class transparently inherits all three of `LogSuccess` / `LogFailure` / `Register`. Calling code can use them unqualified. + +## Message-table backing: `[PopulateFrom("json", …)]` on the enums + +The `T1` / `T2` enums are typically auto-populated from a JSON resource via the `[PopulateFrom]` attribute: + +```tb +Module MESSAGETABLE + [PopulateFrom("json", "/Resources/MESSAGETABLE/Strings.json", "events", "name", "id")] + Enum EVENTS + End Enum + + [PopulateFrom("json", "/Resources/MESSAGETABLE/Strings.json", "categories", "name", "id")] + Enum CATEGORIES + End Enum +End Module +``` + +…with `Resources\MESSAGETABLE\Strings.json`: + +```json +{ + "events": [ + { "id": -1073610751, "name": "service_started", "LCID_0000": "%1 service started" }, + { "id": -1073610750, "name": "service_startup_failed", "LCID_0000": "%1 service startup failed" }, + … + ], + "categories": [ + { "id": 1, "name": "status_changed", "LCID_0000": "Status Changed" } + ] +} +``` + +Two things are happening here: + +1. **The enum bodies are populated at compile time** — `Enum EVENTS` starts empty in the source, but after compilation it has members `service_started = -1073610751`, `service_startup_failed = -1073610750`, … (one per `"events"` entry in the JSON, keyed `name → id`). +2. **The same JSON is consumed by the compiler's `mc.exe`-equivalent** that emits the message-table resource into `App.ModulePath`. The `LCID_0000` strings are the message-table entries, and the `%1`, `%2`, … placeholders are filled at log time from the `AdditionalStrings` `ParamArray` to `LogSuccess` / `LogFailure`. The `CategoryCount` registry value (written by `Register()`) is the highest declared `id` in the `categories` block, which is what `GetDeclaredMaxEnumValue(Of T2)` recovers at compile time. + +So the round-trip is: JSON → compile-time enum population + message-table resource emission → registry entries that point Windows at the EXE → runtime `LogSuccess(EventId, CategoryId, …)` writes an event the Event Viewer can format using the embedded message-table strings. + +Surface this on the index page (under "Setting up message resources" or similar) with the JSON skeleton and the cross-reference to `[PopulateFrom]` (which is documented under `docs/Features/`, not in the reference set — link to that page if it exists, otherwise describe in-place). + +The negative event-ID values in the JSON (`-1073610751`) are the standard Win32 event-ID encoding: the high bits encode severity (`0xC0000000` = Error), facility (`0x...`), and customer bit. Don't unpack this on the docs; just note that *"event IDs follow the Win32 documented encoding — see Microsoft's 'Event Identifiers' reference"*. + +## Why `T1` / `T2` and not separate `EventIds` / `Categories` classes + +A class can only `Implements EventLog(Of T1, T2) Via …` *once*. If a service needs events from multiple unrelated message tables, it can compose multiple `EventLog` instances **as named fields** (no `Via`), accepting a small loss of ergonomics (calls become `MyEventLog.LogSuccess(…)` instead of `LogSuccess(…)`). Surface this as a one-line note on the index — most services share a single `MESSAGETABLE` module across all their classes, so the limitation rarely bites. diff --git a/WIP.WinNamedPipesLib.md b/WIP.WinNamedPipesLib.md new file mode 100644 index 0000000..a2fa50d --- /dev/null +++ b/WIP.WinNamedPipesLib.md @@ -0,0 +1,199 @@ +# WinNamedPipesLib Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. Sister packages: [WinServicesLib](WIP.WinServicesLib.md), [WinEventLogLib](WIP.WinEventLogLib.md). + +An IOCP-based async pipe framework. Four user-facing classes — `NamedPipeServer` and `NamedPipeServerConnection` on the server side, `NamedPipeClientManager` and `NamedPipeClientConnection` on the client side. The Win32 API wrappers, the package-internal `OverlappedTypeConstants` enum, the IOCP helper module, and the four `INamedPipe*Internal` interfaces (each declared alongside its matching public class as a refcount / dispatch helper for the IOCP worker threads) are all plumbing and get no doc page. + +The four classes are each tagged `[COMCreatable(False)]` — only the manager / server classes can be instantiated by user code (with `New`); the two `Connection` classes are constructed internally and handed back through events / return values. + +Public user-facing surface (four classes — two on each side): + +| Class | Role | +|-----------------------------|---------------------------------------------------------------------------------------------------------------| +| `NamedPipeServer` | The server. User-instantiated. Sets `PipeName`, calls `Start`, listens for events; one server hosts many clients. | +| `NamedPipeServerConnection` | One server-side per-client connection. Surfaced through `NamedPipeServer` events; carries `AsyncRead` / `AsyncWrite` / `AsyncClose`. | +| `NamedPipeClientManager` | The client-side coordinator. User-instantiated. Owns the IOCP worker threads; the `Connect` method returns a `NamedPipeClientConnection`. | +| `NamedPipeClientConnection` | One client-side connection. Returned by `NamedPipeClientManager.Connect`; carries `Connected` / `Disconnected` / `MessageReceived` / `MessageSent` events and `AsyncRead` / `AsyncWrite` / `AsyncClose`. | + +## `NamedPipeServer` public members + +Tagged `[COMCreatable(False)]`, `[InterfaceId(...)]`, `[EventInterfaceId(...)]`, `[ClassId(...)]`. No `[Description("...")]` on the class itself. + +**Public fields** (each carries a `[Description("...")]`): + +- `PipeName As String` — *"the discoverable pipe name"*. Set this before `Start()` or `Start()` raises run-time error 5 (*"cannot start without specifying a pipe name"*). The Win32 pipe namespace path is `\\.\pipe\` (the package prepends `\\.\pipe\` itself). +- `NumThreadsIOCP As Long = 1` — *"the number of IOCP worker threads that will be created"*. Read once when `Start()` is called; the in-source `FIXME` notes that this should become read-only once started. +- `FreeThreadingEvents As Boolean = False` — *"set to TRUE to allow the server events ClientConnected / ClientReceivedDataAsync etc to be fired directly from the IOCP worker threads. set to FALSE to ensure the events get fired on the main UI thread."* The free-threaded path skips a Win32 message-loop round-trip; the marshalled path is safer because the events fire on the UI thread. +- `ContinuouslyReadFromPipe As Boolean = True` — *"set to TRUE to ensure ClientReceivedDataAsync events always fire without having to call AsyncRead manually."* When `False`, the consumer must call `AsyncRead` after each `ClientMessageReceived` to keep receiving. +- `MessageBufferSize As Long = 131072` — *"sets the initial size for ReadFile() buffers. does not affect the maximum message receive size, but can affect performance."* On `ERROR_MORE_DATA` the IOCP loop allocates a larger overflow buffer and re-issues the read, so messages larger than this size do work — but with one extra allocation per overflowed message. + +**Public events**: + +- `ServerReady()` — fires once after `Start()` when every IOCP worker has joined. +- `ClientConnected(Connection As NamedPipeServerConnection)` — a new client connection has completed. +- `ClientDisconnected(Connection As NamedPipeServerConnection)` — the connection has dropped and every outstanding async operation has returned. +- `ClientMessageReceived(Connection As NamedPipeServerConnection, ByRef Cookie As Variant, ByRef Data() As Byte)` — a message arrived. *Data* is a transient view over the IOCP read buffer (a hand-rolled `SAFEARRAY` whose backing memory is reused after the event); copy it if you need to keep it past the event handler. +- `ClientMessageSent(Connection As NamedPipeServerConnection, ByRef Cookie As Variant)` — a previously-issued `AsyncWrite` has completed. + +**Public methods**: + +- `Sub New()` — constructor; creates the hidden marshalling-window used for UI-thread event delivery. +- `Public Sub Start()` — creates the IOCP completion port and `NumThreadsIOCP` worker threads, then issues the first connection listener. Idempotent: calling `Start()` while already started is a no-op. +- `Public Sub Stop()` — cancels every outstanding I/O, joins the IOCP threads, closes pipe handles. Idempotent. Called automatically from `Class_Terminate`. +- `Sub AsyncBroadcast(ByRef Data() As Byte, Optional ByRef Cookie As Variant = Empty)` — issues `AsyncWrite` against every currently-connected `NamedPipeServerConnection`. +- `Public Sub ManualMessageLoopEnter()` / `Public Sub ManualMessageLoopLeave()` — drive a Win32 message loop manually (rare; only needed when the host process does not naturally pump messages — e.g. an unattended Windows service that wants the marshalled-event semantics rather than the free-threaded ones). `Leave` posts `WM_USER_QUITTING`, which `Enter` reads to break the loop. + +## `NamedPipeServerConnection` public members + +Tagged `[COMCreatable(False)]`. Not directly user-instantiable. + +**Public fields**: + +- `Handle As LongPtr` — the underlying Win32 pipe handle. Exposed but not normally needed; useful for low-level operations or debugging. +- `IsOpening As Boolean` — true while `Open()` is in progress (race-condition window between adding to the linked list and finishing `ConnectNamedPipe`). +- `IsConnected As Boolean` — true between the client connecting and the connection dropping. +- `CustomData As Variant` — *"free for use"*: opaque per-connection slot the consumer can attach state to. + +**Public methods**: + +- `Sub New(...)` — internal constructor; takes the parent server + pipe info. Never called by user code. +- `Public Sub AsyncClose()` — cancels outstanding I/O and closes the pipe handle. Called automatically from `Class_Terminate`. +- `Public Sub AsyncWrite(ByRef Data() As Byte, Optional ByRef Cookie As Variant = Empty)` — writes a message back to this specific client. Returns immediately; `NamedPipeServer.ClientMessageSent` fires when the write completes. +- `Public Sub AsyncRead(Optional ByRef Cookie As Variant = Empty, Optional OverlappedStruct As LongPtr)` — manually issues a read. Only needed when `NamedPipeServer.ContinuouslyReadFromPipe = False`; otherwise the server keeps the read pump fed automatically. + +No public events — message-received and connection-dropped notifications come through the parent `NamedPipeServer`. The class declares an internal `INamedPipeServerConnectionInternal` interface that the IOCP loop uses for refcounting; that interface is `Private` and gets no doc page. + +## `NamedPipeClientManager` public members + +Tagged `[COMCreatable(False)]`, `[InterfaceId(...)]`, `[EventInterfaceId(...)]`, `[ClassId(...)]`. + +**Public fields** (each carries `[Description("...")]`, mirror the server's): + +- `NumThreadsIOCP As Long = 1` +- `MessageBufferSize As Long = 131072` +- `FreeThreadingEvents As Boolean = False` +- `ContinuouslyReadFromPipe As Boolean = True` + +These four are read once on the first `Connect()` call and propagated to every `NamedPipeClientConnection` created through that manager; subsequent changes do not affect connections that already exist. Source comment in `NamedPipeClientConnection` confirms: *"tip: set it in NamedPipeClientConnections before Connect()"*. + +**Public methods**: + +- `Sub New()` — constructor; creates the hidden marshalling window. +- `Public Function Connect(ByVal PipeName As String) As NamedPipeClientConnection` — opens a connection to a server (`\\.\pipe\`). Lazy on first call: creates the IOCP port and the worker threads. Returns a connection object that fires `Connected` once the async `CreateFileW` completes. +- `Public Sub Stop()` — cancels every outstanding I/O on every managed connection, joins the IOCP threads, frees the resources. Idempotent. Called automatically from `Class_Terminate`. +- `Public Function FindNamedPipes(Optional Pattern As String = "*") As Collection` — enumerates the named pipes currently published on the local machine (via `FindFirstFileW("\\.\pipe\")`). Returns a `Collection` of `String`. Useful as a discovery helper before calling `Connect`. + +No events on the manager itself — per-connection events live on the returned `NamedPipeClientConnection` objects. + +## `NamedPipeClientConnection` public members + +Tagged `[COMCreatable(False)]`, `[InterfaceId(...)]`, `[ClassId(...)]`, `[EventInterfaceId(...)]`. Not directly user-instantiable — `NamedPipeClientManager.Connect` returns it. + +**Public fields**: + +- `PipeName As String` — the pipe name the connection targets. +- `Handle As LongPtr` — the underlying Win32 file handle. Same caveats as on the server-side connection. +- `CustomData As Variant` — *"free for use"*. + +**Public events**: + +- `Connected()` — the async `CreateFileW` has succeeded. +- `Disconnected()` — the connection has dropped and every outstanding async operation has returned. +- `MessageReceived(ByRef Cookie As Variant, ByRef Data() As Byte)` — a message arrived. *Data* has the same transient-view semantics as on the server. +- `MessageSent(ByRef Cookie As Variant)` — a previously-issued `AsyncWrite` has completed. + +**Public methods**: + +- `Sub New(...)` — internal constructor; never called by user code directly. +- `Public Sub AsyncClose()` — **critical:** the README says *"you MUST call AsyncClose on the client side, otherwise the connection is left alive when the object goes out of scope"*. Surface this on every relevant page. +- `Public Sub AsyncWrite(ByRef Data() As Byte, Optional ByRef Cookie As Variant = Empty)` — sends a message to the server. +- `Public Sub AsyncRead(Optional ByRef Cookie As Variant = Empty, Optional OverlappedStruct As LongPtr)` — manually issues a read. Same gating as on the server-side: only call this when `ContinuouslyReadFromPipe = False`. + +**Documented gaps / TODOs from `_README.txt`** (surface on the landing page): + +- *"we need a method to allow closing a client connection from the server side"* — there is no `NamedPipeServerConnection.Disconnect` or `.Close` user-method today. The server can stop the whole pipe (`NamedPipeServer.Stop`) but cannot selectively drop one client. +- *"named pipe error should be raised via Error events (rather than throwing an error on the worker threads)"* — internal IOCP errors currently bubble up as VBA run-time errors on worker threads rather than as `Error` events. No `Error` event exists on any of the four classes yet. +- *"remove max size 131072 of messages"* — the `MessageBufferSize` initial-buffer default is 131072 bytes. The IOCP overflow path (`ERROR_MORE_DATA` → larger buffer → re-issue read) does handle larger messages, but there may be a hard cap somewhere the author wants to remove; surface this as *"see TODO list in `_README.txt`"* rather than making a stronger claim. +- *"currently a lot of duplicate code in server + client"* — internal-refactor note. **Not** surfaced on the docs. + +**Cookie pattern.** Every `AsyncRead` and `AsyncWrite` accepts an optional *Cookie* (`Variant`). Whatever the consumer passes in flows through the IOCP completion buffer and is handed back out on the matching `MessageReceived` / `MessageSent` event. This is the package's mechanism for correlating individual writes with their completion notifications when many are in flight. + +**`Data() As Byte` transience.** Inside `MessageReceived` / `ClientMessageReceived`, *Data* is **not** a real `Byte` array — it is a hand-rolled `SAFEARRAY` whose `pvData` field points at the IOCP overlapped buffer. The buffer is recycled back into a free-list at the end of the event handler. Copy the bytes out (`ReDim`-and-copy, or `CStrConv` for text payloads) if you need them after returning from the handler. The source uses `PutMemPtr(VarPtr(safeArrayPtr), VarPtr(safeArrayPsuedo))` and clears it afterwards — surface this lifetime caveat on every event-page entry that carries *Data*. + +**Hidden message window.** Each `NamedPipeServer` and `NamedPipeClientManager` instance creates an invisible `STATIC`-class window with a subclassed `WndProc`, used to marshal IOCP-thread completions back to the UI thread when `FreeThreadingEvents = False`. Mention this on each class's intro paragraph — it explains why the consumer's process must be pumping a message loop for the default event-delivery semantics to work, and why `ManualMessageLoopEnter` / `ManualMessageLoopLeave` exist on `NamedPipeServer` for service / console hosts. + +## Canonical service-host idiom — `ManualMessageLoopEnter` paired with `ChangeState` + +`tbServiceTest2`'s `Sources\SERVICES\TBSERVICE001.twin` shows the standard pattern for a Windows service that hosts a `NamedPipeServer`. Surface this on the `NamedPipeServer.md` page (under a "Hosting inside a Windows service" sub-heading) and on the index landing: + +```tb +' On the service thread (ITbService.EntryPoint): +Set NamedPipeServer = New NamedPipeServer +NamedPipeServer.PipeName = "WaynesPipe_" & CurrentComponentName +ServiceManager.ReportStatus(vbServiceStatusRunning) + +NamedPipeServer.Start() +NamedPipeServer.ManualMessageLoopEnter() ' blocks until ManualMessageLoopLeave +NamedPipeServer.Stop() + +ServiceManager.ReportStatus(vbServiceStatusStopped) + +' On the dispatcher thread (ITbService.ChangeState): +Select Case dwControl + Case vbServiceControlStop, vbServiceControlShutdown + ServiceManager.ReportStatus(vbServiceStatusStopPending) + NamedPipeServer.ManualMessageLoopLeave() ' wakes the service thread +End Select +``` + +Key facts that aren't obvious from the per-method `[Description]`s: + +- The service-thread `EntryPoint` and the dispatcher-thread `ChangeState` are **different threads**. The `NamedPipeServer` member field is shared between them; the dispatcher-thread `ChangeState` calls `ManualMessageLoopLeave` on it to wake the service thread out of `ManualMessageLoopEnter`. +- `ManualMessageLoopLeave` is the **only** way to wake `ManualMessageLoopEnter` cleanly. There is no timeout, no second blocking primitive. If the service needs to react to other wake-up sources (paused state, custom control codes), it sets a shared flag *then* calls `ManualMessageLoopLeave` to break out, inspects the flag, and decides whether to re-enter the loop or proceed to shutdown. The `TBSERVICE002` variant in the same example demonstrates this with `IsPaused` / `IsStopping` shared `Public` fields and a `While IsStopping = False` outer loop. +- Pause / continue support uses the same pattern: `ChangeState` flips `IsPaused = True` and calls `ManualMessageLoopLeave`; the service thread sees the flag, reports `vbServiceStatusPaused`, enters a `Do While IsPaused : Sleep(500) : Loop`, then re-enters `ManualMessageLoopEnter` once `Continue` flips the flag back. +- `FreeThreadingEvents = False` (the default) is **required** for this pattern — events are marshalled to whichever thread is currently inside `ManualMessageLoopEnter`. Setting `FreeThreadingEvents = True` would deliver events on the IOCP worker thread instead and bypass the manual loop entirely (advanced; not the documented service idiom). + +The non-service equivalent — hosting the same `NamedPipeServer` inside a Form — is in `Sources\FORMS\InProcessNamedPipeServerForm.twin`: the Form's regular message loop pumps the marshalling window automatically, so the Form just calls `Server.Start()` in `Form_Load` and `Server.Stop` in `Form_Unload` without ever touching `ManualMessageLoopEnter` / `Leave`. Cross-reference both patterns on the `NamedPipeServer.md` page so the reader sees the choice point. + +## PropertyBag as the canonical message carrier + +Every example serialises structured payloads through the pipe as a `PropertyBag.Contents` `Byte()`: + +```tb +' Sender: +Dim propertyBag As New PropertyBag +propertyBag.WriteProperty("CommandID", "WHAT_TIME_IS_IT") +propertyBag.WriteProperty("Data", payload) +SelectedNamedPipe.AsyncWrite propertyBag.Contents + +' Receiver (inside MessageReceived event): +Dim propertyBag As New PropertyBag +propertyBag.Contents = Data ' deep-copies the bytes; safe past the event handler +Dim commandID As String = propertyBag.ReadProperty("CommandID") +… +``` + +Two reasons this pattern matters and should be surfaced on the docs: + +1. **The transient-`Data()` problem is solved by `PropertyBag`.** Assigning to `PropertyBag.Contents` deep-copies the byte buffer; once the assignment returns, the original IOCP buffer can be recycled without invalidating the data. This is the cleanest answer to *"how do I keep the data past the event handler?"* — call out on every `MessageReceived` / `ClientMessageReceived` page entry as the recommended capture mechanism. +2. **`PropertyBag` provides typed multi-field payloads** without the consumer having to design a wire protocol. Both sides agree on the property names (`"CommandID"`, `"ResponseCommandID"`, `"ResponseData"`, `"Data"`) and `PropertyBag` handles the encoding / decoding. Cross-link [`PropertyBag` reference](docs/Reference/VBRUN/PropertyBag/index.md) from the index landing. + +Surface as the **recommended** carrier; nothing in the package mandates it, raw `Byte()` works too, but every worked example uses `PropertyBag` and the integration story reads much more cleanly with it. + +## Discovery loop — `FindNamedPipes` + +`tbServiceTest2`'s `MainForm` shows the canonical client-side discovery pattern: a low-frequency `Timer` (the form uses `timerRefreshNamedPipes` with a multi-second interval) that calls `NamedPipeClients.FindNamedPipes("WaynesPipe_*")`, repopulates a `ListBox`, and preserves the user's current selection: + +```tb +For Each namePipeName In NamePipeClients.FindNamedPipes("WaynesPipe_*") + If namePipeName = NamedPipeSelected Then namedPipeSelectedIndex = Index + lstNamedPipes.AddItem(namePipeName) + Index += 1 +Next +``` + +Surface on the `NamedPipeClientManager.md` page (under the `FindNamedPipes` entry) as the recommended polling loop — the underlying `FindFirstFileW("\\.\pipe\…")` call is cheap enough to invoke every few seconds without measurable cost, and pipes appear / disappear too quickly for any event-driven discovery to be reliable. Don't claim there's no faster API; just say *"polling is the documented approach"*. + +## Service-side broadcast + +`AsyncBroadcast` (on `NamedPipeServer`) accepts a `Byte()` payload and issues `AsyncWrite` against every currently-connected `NamedPipeServerConnection`. Useful when the server has multiple concurrent connections and wants to push an update to all of them; the alternative is iterating the connections manually. diff --git a/WIP.WinNativeCommonCtls.md b/WIP.WinNativeCommonCtls.md new file mode 100644 index 0000000..3e93209 --- /dev/null +++ b/WIP.WinNativeCommonCtls.md @@ -0,0 +1,130 @@ +# WinNativeCommonCtls Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. + +A VB6-compatible replacement for **Microsoft Common Controls 6.0** (`MSCOMCTL.OCX`), written on top of the Win32 ComCtl32 controls (`COMCTL32.DLL` / `MSFTEDIT.DLL`). Ships eight controls that mirror the MSCOMCTL surface name-for-name where possible. First released v0.0.1.0 on 18-FEB-2023; independent of (but co-versioned with) the VB compatibility package. + +Each control is a heavy `BaseCtl` (where every event / method / property is implemented, tagged `[COMCreatable(False)]` + `[EventsUseDispInterface]`) plus a thin `` leaf (`Inherits BaseCtl`, tagged `[WindowsControl("/miscellaneous/ICONS??/??.png")]`). The leaf adds only a `Class_BeforeFirstMethodAccess` that calls `[_HiddenModule].EnsureContainerIsLoaded(Me)` — same `BaseCtl` / `` leaf split that CEF and the VB controls use. + +Eight controls (one `.twin` per pair): + +| File | `BaseCtl` inherits | Role | +|-------------------|---------------------------------|-----------------------------------------------------------------------------------------------| +| `DTPicker.twin` | `VB.BaseControlFocusable` | Date / time picker — calendar drop-down, single-date `Value`, custom format strings | +| `ImageList.twin` | `VB.BaseControlNotFocusable` | Off-screen image collection — feeds `ListView` / `TreeView` icons via `Icons` / `ImageList` properties | +| `ListView.twin` | `VB.BaseControlFocusable` | Multi-column list — four `View` modes (Icon / SmallIcon / List / Report), label-edit, checkboxes | +| `MonthView.twin` | `VB.BaseControlFocusable` | Full-month calendar grid — multi-select, bold-day callbacks, week-number / today display | +| `ProgressBar.twin`| `VB.BaseControlNotFocusable2` | Standard / Smooth / Marquee progress indicator with three visual states (Normal / Error / Paused) | +| `Slider.twin` | `VB.BaseControlFocusableNoFont` | Trackbar / slider — tick marks, range selection, vertical or horizontal orientation | +| `TreeView.twin` | `VB.BaseControlFocusable` | Hierarchical tree of `Node` objects — sorting, label-edit, checkboxes, image lists | +| `UpDown.twin` | `VB.BaseControlFocusableNoFont` | Spin control (up / down arrows) — pure Increment / Min / Max / Value; no auto-buddy binding | + +Every `BaseCtl` carries `[WithDispatchForwarding] Implements Control` (where `Control` is `Private Interface` in `Interfaces.twin`, marked `[COMExtensible]` — essentially an `Object` alias that makes the dispatch forwarding behave). They also implement a chorus of `VB.IWindowsControl`, `VB.IWindowElementEventsCommon`, `VB.IWindowElementEventsCommonControls`, `VB.IWindowElementEventsUC`, `VB.IWindowElementEventsAX` — these are the VB-package event-dispatch interfaces; do **not** surface them on the docs. Each control also implements one private `TbPrivate` interface (declared `[ComImport(True)]` inside the same `.twin`) that the package's collection sub-objects use to refcount and reach internal state without taking a strong reference; **no doc page** for those. + +## Public user-facing surface + +The eight leaf classes `DTPicker`, `ImageList`, `ListView`, `MonthView`, `ProgressBar`, `Slider`, `TreeView`, `UpDown` are what user code references at design time (via `[WindowsControl(...)]`) and at run time (`Dim lv As ListView`). The `BaseCtl` base classes are the implementation half — `[COMCreatable(False)]` and not user-instantiable, but **the entire user-visible surface is declared on them**. Document on the leaf's name (`ListView.md`), describe the full surface, and don't surface the `BaseCtl` split. + +The package also surfaces eight sub-object classes — collection plus item: + +| Class | Reached via | Notes | +|-----------------|----------------------------------------------------------|------------------------------------------------------------------------------------| +| `ListImages` | `ImageList.ListImages` (Get-only) | Enumerable; `Item(Index or Key)` default member; `Add`, `Remove`, `Clear`, `Exists` | +| `ListImage` | element of `ListImages` (returned from `Add`, indexed) | `Index` (read-only), `Key`, `Picture`, `Tag`, plus `Draw(hDC, x, y, Style)` and `ExtractIcon` | +| `ListItems` | `ListView.ListItems` (Get-only) | Enumerable; `Item(Index or Key)` default member; `Add`, `Remove`, `Clear` | +| `ListItem` | element of `ListItems` | `Text` (default), `SubItems(Index)`, `Icon`, `SmallIcon`, `Checked`, `Selected`, `Ghosted`, `Bold`, `BackColor`, `ForeColor`, `Tag`, `ToolTipText`, `EnsureVisible`, `Left` / `Top` / `Width` / `Height`, `Index` (RO), `Key`, `CreateDragImage` (`[Unimplemented]`) | +| `ColumnHeaders` | `ListView.ColumnHeaders` (Get-only) | Same shape as `ListItems`; `Add(Index, Key, Text, Width, Alignment, Icon)` returns `ColumnHeader` | +| `ColumnHeader` | element of `ColumnHeaders` | `Text` (default), `Width`, `Left` (RO), `Alignment` (typed `ListColumnAlignmentConstants`), `Position`, `SubItemIndex`, `Icon`, `Index` (RO), `Key`, `Tag` | +| `Nodes` | `TreeView.Nodes` (Get-only) | Enumerable; `Item(Index or Key)` default; `Add(Relative, Relationship, Key, Text, Image, SelectedImage)` returns `Node` | +| `Node` | element of `Nodes` | `Text` (default), `Parent`, `Child`, `Next`, `Previous`, `Root`, `FirstSibling`, `LastSibling`, `Children` (count), `Expanded`, `Selected`, `Checked`, `Bold`, `BackColor`, `ForeColor`, `Image`, `SelectedImage`, `Tag`, `FullPath`, `Visible` (RO), `Sorted`, `SortOrder`, `SortType`, `EnsureVisible`, `Index` (RO), `Key` | + +Every sub-object is `[COMCreatable(False)]` — its constructor takes a `BaseCtl` reference, so user code never instantiates these directly. They are returned from container methods (`Add`, `Item`) and reached through container properties. + +Container cross-references (typed as the `BaseCtl` parent, since the controls accept either the base or the leaf — but document the parameter as the **leaf**): + +- `TreeView.ImageList` / `Let` / `Set` — typed `As ImageListBaseCtl`; the user assigns an `ImageList`. +- `ListView.Icons` / `Let` / `Set`, `ListView.SmallIcons` / `Let` / `Set`, `ListView.ColumnHeaderIcons` / `Let` / `Set` — all three typed `As ImageListBaseCtl`; the user assigns an `ImageList`. + +`ListView.BorderStyle` is unusually typed `As TreeBorderStyleConstants` (declared in `TreeViewPublic`, not in a `ListView*` module) — the enum is shared across both controls. Surface this on the `BorderStyle` entry without trying to rationalise it. + +## Per-control highlights + +These are the points worth surfacing on each control's page that are *not* obvious from a flat property list: + +- **DTPicker** — the only control where most behaviour is in the calendar drop-down, not the inline display. The `Calendar*` colour properties (`CalendarBackColor`, `CalendarForeColor`, `CalendarTitleBackColor`, `CalendarTitleForeColor`, `CalendarTrailingForeColor`) act on the dropped-down month grid via `DTM_SETMCCOLOR`. `Format` (`DTPickerFormatConstants`) chooses between long-date / short-date / time / custom; when set to `dtpCustom`, the picker pulls `CustomFormat` (a `GetDateFormat`-style picture string). The control exposes `Year` / `Month` / `Week` / `Day` / `Hour` / `Minute` / `Second` accessors that decompose the current `Value`. `Value` is `Variant` — it can be `Null` (no date selected) when `CheckBox = True` and the user unchecks the box. Events `Format`, `FormatSize`, `CallbackKeyDown` fire when `Format = dtpCustom` and the format string contains a callback token. +- **ImageList** — purely off-screen; `Visible` does nothing user-meaningful (it's a "store of pictures" control). The `ImageWidth` / `ImageHeight` properties are read/write **only while empty** — once any image is added, the setter raises run-time error 35611 (*"Property is read-only if image list contains images"*). `ColorDepth` is fixed at construction time. `MaskColor` + `UseMaskColor = True` makes the masked pixels transparent when rendered into a control that consumes the image list. `Overlay(Key1, Key2)` composes two list-images into a single `StdPicture`. Bound-count tracking: an `ImageList` cannot be modified (clear / remove) while any control has it bound as `Icons` / `SmallIcons` / `ColumnHeaderIcons` / `ImageList`, throwing error 35617. +- **ListView** — the largest of the eight. `View` switches the visual mode (`lvwIcon` / `lvwSmallIcon` / `lvwList` / `lvwReport`); `Arrange` (`lvwNone` / `lvwAutoLeft` / `lvwAutoTop`) auto-flows the icon mode; `Report` mode is the only one that shows the `ColumnHeaders`. `LabelEdit` defaults to `lvwAutomatic` — F2 / click-and-wait edits a label in place; `lvwManual` requires `StartLabelEdit()` and `lvwDisabled` blocks editing. `TextBackground` (`lvwTransparent` / `lvwOpaque`) acts on the *item* text rendering, not the control's `BackColor`. `MultiSelect = True` enables Ctrl+click / Shift+click range selection. `CheckBoxes = True` adds a leading checkbox per row and fires `ItemCheck`. `AllowColumnReorder` only matters in Report view. `BorderStyle` is `TreeBorderStyleConstants` (`ccNone` / `ccFixedSingle`). The control surfaces `hWnd` and `hWndHeader` (the embedded `SysHeader32` window) separately. `Scroll` is `[Unimplemented]` per the source. `GetFirstVisible() As ListItem`, `SelectedItem` / `SelectedItemIndex` — the latter is read-only (assign through `ListItem.Selected = True` instead). +- **MonthView** — `MonthColumns` / `MonthRows` lay out a grid of side-by-side month panels (`ResizeToFit` auto-sizes the control to fit them). `Day` / `Month` / `Week` / `Year` are the same decomposition pattern as DTPicker. `MaxSelCount` is the upper bound of a multi-day selection (default 7, max ≈ 366 per the Win32 control); `SelStart` and `SelEnd` are the inclusive range. `MinDate` / `MaxDate` bound the navigable range. `Value` is the *current selection's start date* (same as `SelStart` when `MultiSelect = False`). The control fires both `Click` (any click) and `DateClick` (only when a date cell is hit, with the date passed as a parameter); same split for `DblClick` / `DateDblClick`. `GetDayBold` is an event-driven callback — the control fires it for each visible month asking for a `State()` array of which days to render bold; this is the mechanism for highlighting holidays, schedule entries, etc. `DayBold` is an alternative per-date setter. `GetMonthRange(IncludeTrailing, StartDate, EndDate)` returns the visible date span. +- **ProgressBar** — three orthogonal axes. `Min` / `Max` / `Value` are the standard range. `Step` + `StepIt()` advances the bar by `Step` units (typical loop pattern: `Min = 0`, `Max = N`, then `StepIt()` per iteration). `Scrolling = PrbScrollingStandard` (default) animates the bar in segments; `PrbScrollingSmooth` is the continuous block; `PrbScrollingMarquee` is the indeterminate animation (drive with `MarqueeAnimation = True` + `MarqueeSpeed`). `State` (`PrbStateNormal` / `Error` / `Paused`) tints the bar red / yellow per the OS theme. `Orientation` is `PrbOrientation` (`Horizontal` / `Vertical`). The control has `Click` / `DblClick` / `Mouse*` events but **no** `Change` despite the declaration — verify with the source if surfaced (`Change` is declared in the events region but not fired by any Win32 progress-bar notification). +- **Slider** — `Min` / `Max` / `Value` like a scrollbar; `SmallChange` is the arrow-key step, `LargeChange` is the PgUp / PgDn step. `SelStart` + `SelLength` create a highlighted selection range (visible when `SelectRange = True`). `TickFrequency` controls how often tick marks appear; `TickStyle` (`sldBottomRight` / `sldTopLeft` / `sldBoth` / `sldNoTicks`) chooses which side(s) of the channel they render on. `TextPosition` (`sldAboveLeft` / `sldBelowRight`) is for the optional tip text. `HideThumb = True` removes the draggable indicator. `ShowTip = True` enables the floating tooltip showing the current value during drag. `Orientation` is `OrientationConstants` (the shared horizontal / vertical enum used also by `UpDown`). +- **TreeView** — the second-largest control. `Style` (`TreeStyleConstants`, 8 values) is a composite of *show / hide* flags for tree-lines / plus-minus boxes / icons / text — the values name what's shown. `LineStyle` chooses `tvwRootLines` (lines from root nodes) or `tvwTreeLines` (lines only from children). `Sorted` / `SortOrder` / `SortType` apply at the root level; each `Node` has its own per-subtree `Sorted` / `SortOrder` / `SortType`. `LabelEdit` is the same gating as `ListView.LabelEdit` (`tvwAutomatic` / `Manual` / `Disabled`). `CheckBoxes = True` adds per-node checkboxes; `FullRowSelect` extends the selection highlight across the full row width. `Indentation` is in twips. `HitTest(x, y)` returns the `Node` at a point (for hover effects, drag-drop). `SelectedItem` (`Get` / `Let` / `Set`) and `DropHighlight` (`Get` / `Let` / `Set`) are both `Node`-typed. `StartLabelEdit()` for `Manual` mode. `GetVisibleCount()` returns how many full nodes the visible area shows. `Scroll` event new to tB. +- **UpDown** — pure spin control: `Min` / `Max` / `Value` / `Increment`. `Orientation` is `OrientationConstants` (horizontal pair of arrows or vertical, the more common). Events are `Change` (any time `Value` changes), `UpClick`, `DownClick`. There is *no* auto-buddy / partner-control facility in this version (the Win32 `UDS_AUTOBUDDY` flag is in the source enums but not exposed) — user code wires `UpClick` / `DownClick` to update the partner control manually. + +Common surface across every control: `Public Opacity As Double = 100` (with the *"REQUIRES TARGET OS 6.2+ FOR CHILD CONTROLS."* description), `Public TransparencyKey As OLE_COLOR = -1` (same OS requirement), and (where `FEATURE_OLEDRAGDROP` is enabled at compile time) `Public OLEDropMode As VBRUN.OLEDropConstants` plus the `OLECompleteDrag` / `OLEDragDrop` / `OLEDragOver` / `OLEGiveFeedback` / `OLESetData` / `OLEStartDrag` events. `Public OLEDrag()` method on every control. `Public Property Get Parent() As Object` and `Public Property Get Object() As Object` on every control. The inherited surface from `VB.BaseControl*` includes `Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, `Visible`, `Enabled`, `BackColor` / `ForeColor` / `Font` (where focusable), `Appearance`, `MousePointer` / `MouseIcon`, `ToolTipText`, `DragMode` / `DragIcon`, `Drag()`, `Refresh()`, `SetFocus()` (focusable variants), `ZOrder()`, `CausesValidation`, `TabIndex` / `TabStop` (focusable variants), `VisualStyles`, `hWnd`, `HelpContextID` / `WhatsThisHelpID`. + +## Per-control nested enums (fold onto the declaring control's page) + +These enums are declared *inside* each `BaseCtl` (`Enum ` without `Public`, which still surfaces because the enclosing class is public). Following the CustomControls convention for `WaynesSlider.SliderDirection`, document each on its declaring control's page rather than under `Enumerations/`: + +| Enum | Declared on | Members | +|---------------------------------|----------------------------|-------------------------------------------------------------------------------| +| `ImageListColorDepth` | `ImageListBaseCtl` | `ColorDepth4Bit = 4`, `ColorDepth8Bit = 8`, `ColorDepth16Bit = 16`, `ColorDepth24Bit = 24`, `ColorDepth32Bit = 32` | +| `ListViewConstants` | `ListViewBaseCtl` | `lvwIcon = 0`, `lvwSmallIcon = 1`, `lvwList = 2`, `lvwReport = 3` | +| `ListArrangeConstants` | `ListViewBaseCtl` | `lvwNone = 0`, `lvwAutoLeft = 1`, `lvwAutoTop = 2` | +| `ListTextBackgroundConstants` | `ListViewBaseCtl` | `lvwTransparent = 0`, `lvwOpaque = 1` | +| `ListLabelEditConstants` | `ListViewBaseCtl` | `lvwAutomatic = 0`, `lvwManual = 1`, `lvwDisabled = 2` | +| `ListColumnAlignmentConstants` | `ColumnHeader` | `lvwColumnLeft = 0`, `lvwColumnRight = 1`, `lvwColumnCenter = 2` | +| `PrbOrientation` | `ProgressBarBaseCtl` | `PrbOrientationHorizontal = 0`, `PrbOrientationVertical = 1` | +| `PrbScrolling` | `ProgressBarBaseCtl` | `PrbScrollingStandard = 0`, `PrbScrollingSmooth = 1`, `PrbScrollingMarquee = 2` | +| `PrbState` | `ProgressBarBaseCtl` | `PrbStateNormal = 1`, `PrbStateError = 2`, `PrbStatePaused = 3` | +| `TickStyleConstants` | `SliderBaseCtl` | `sldBottomRight = 0`, `sldTopLeft = 1`, `sldBoth = 2`, `sldNoTicks = 3` | +| `TextPositionConstants` | `SliderBaseCtl` | `sldAboveLeft = 0`, `sldBelowRight = 1` | + +Source-side spelling note: every enum is named `` (no `Public` modifier) but the *member* names use the historical VB6 prefix conventions — `lvw` for ListView, `tvw` for TreeView, `sld` for Slider, `dtp` for DTPicker, `Prb` for ProgressBar, `cc` for cross-control. Mixed casing in member names (`SldAboveLeft` literal in the source defaults vs `sldAboveLeft` declaration) is a source-side issue; surface members with the declared casing. + +## Module-level enums (under `Enumerations/`) + +Five `Consts.twin` modules in `SUPPORT/` carry Win32 SDK plumbing (message IDs, notification IDs, style flags, Win32 types like `NMHDR` / `SYSTEMTIME` / `LVCOLUMNW`) **plus** a small fraction of user-facing enums. The plumbing is unreachable by user code (mostly inside `Private Module …Consts`); the user-facing enums are split into a separate `Public Module` (TreeView's clean case) or coexist with the plumbing in an effectively-public bare `Module` (the rest). Either way, surface only the user-facing enums: + +| Enum | Declared in / module | Members | +|-------------------------------|------------------------------------------------------------|----------------------------------------------------------------------| +| `DTPickerFormatConstants` | `DTPickerConsts.twin` (module `DTPickerConsts`) | `dtpLongDate = 0`, `dtpShortDate = 1`, `dtpTime = 2`, `dtpCustom = 3` | +| `TreeBorderStyleConstants` | `TreeViewConsts.twin` (`Public Module TreeViewPublic`) | `ccNone = 0`, `ccFixedSingle = 1` | +| `TreeLabelEditConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwAutomatic = 0`, `tvwManual = 1`, `tvwDisabled = 2` | +| `TreeLineStyleConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwTreeLines = 0`, `tvwRootLines = 1` | +| `TreeStyleConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | 8 members: `tvwTextOnly`, `tvwPictureText`, `tvwPlusMinusText`, `tvwPlusMinusPictureText`, `tvwTreelinesText`, `tvwTreelinesPictureText`, `tvwTreelinesPlusMinusText`, `tvwTreelinesPlusMinusPictureText` | +| `TreeRelationshipConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwFirst = 0`, `tvwLast = 1`, `tvwNext = 2`, `tvwPrevious = 3`, `tvwChild = 4` | +| `TreeSortOrderConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwAscending = 0`, `tvwDescending = 1` | +| `TreeSortTypeConstants` | `TreeViewConsts.twin` (`TreeViewPublic`) | `tvwBinary = 0`, `tvwText = 1` | +| `OrientationConstants` | `Misc.twin` (`Private Module Miscellaneous`) | `ccOrientationHorizontal = 0`, `ccOrientationVertical = 1` — used by both **Slider** and **UpDown** | +| `ImlDrawConstants` | `ImageListConsts.twin` (`Private Module ImageListConsts`) | `ImlDrawNormal = 1`, `ImlDrawTransparent = 2`, `ImlDrawSelected = 4`, `ImlDrawFocus = 8`, `ImlDrawNoMask = 16` — flag combination; used as `[TypeHint(ImlDrawConstants)]` on `ListImage.Draw`'s `Style` parameter | + +For `OrientationConstants` and `ImlDrawConstants` (declared `Public Enum` inside a `Private Module`): the enclosing module is unreachable by name from user code, but the enum members are reachable because they're tagged through `[TypeHint]` on the consuming method's parameter and are also surfaced by the IDE's "implicit member visibility" — i.e. user code writes `Slider1.Orientation = ccOrientationVertical` and `ListImage.Draw(hdc, 0, 0, ImlDrawTransparent Or ImlDrawSelected)`. Document the enum and don't worry about qualification — the user's call site never needs `Module.Enum.Member` form. + +The remaining `Consts.twin` modules (`ImageListConsts`, `ListViewConsts`, `ProgressBarConsts`, `TreeViewConsts.TreeViewConsts` (the private half), `UpDownConsts`, `SliderConsts`, `MonthViewConsts`, `DTPickerConsts` non-`DTPickerFormatConstants` content) are package-internal — Win32 message IDs, style flags, notification structures (`NMHDR`, `NMLISTVIEW`, `NMDATETIMECHANGE`, …) that the controls use to talk to ComCtl32 but that the user never references. **No doc pages** for those; do not document `LVMessages`, `TVMessages`, `MonthViewMessages`, `SliderMessages`, `UpDownMessages`, `DTPickerMessages` and the associated `*Notifications` / `*Styles` enums. + +## Private classes (no doc page) + +- `Private Class ListViewHeaderSubclasser` (in `ListView.twin`) — subclasses the embedded `SysHeader32` window to intercept `HDM_LAYOUT` notifications for the column-resize handler. Implementation detail. +- `Private Class TreeViewNodeCheckState` / `ListViewNodeCheckState` / `TreeViewNodeClick` / `TreeViewNodeDblClick` (in `TreeViewNodeCheckState.twin`) — four `IScheduledCallback`-implementing dispatch helpers that the controls schedule onto the message loop to fire `NodeCheck` / `ItemCheck` / `NodeClick` / `DblClick` events at the right point in the click-handling sequence. Same role as the `…Internal` classes in WinNamedPipesLib; no doc page. +- `Class ImageListPropertyPage` (in `ImageListPropertyPage.twin`) — `[FormDesignerId]` `[PredeclaredId]` `[COMCreatable(False)]` Form class that's the IDE's design-time property editor for `ImageList` (the "Custom Properties..." button). Invoked from `ImageListBaseCtl.HandleInvokePropertyExtension`. Pure design-time tooling, never appears at run-time; no doc page. +- `Private Interface Control` / `IScheduledCallback` / `ITwinBasicDesignerExtensions` (in `Interfaces.twin`) — internal interfaces. `Control` is the empty marker interface that `[WithDispatchForwarding]` resolves names through. No doc pages. +- `Private Module Miscellaneous` (in `Misc.twin`) — `StrPtrSafe`, `CommonTreeViewGetNodeFromHandle`, `SyncBorderStyle` — internal helpers. `OrientationConstants` does surface from this module (see above) but the module itself doesn't get a doc page. +- `Private Module ImagesHelper` (in `ImagesHelper.twin`) — `GetBitsPerPixelFromPic`. Internal helper. No doc page. +- `Private Module ImageListConsts`, `ListViewConsts`, `ProgressBarConsts`, `TreeViewConsts` (the private half), and the bare `Module DTPickerConsts` / `MonthViewConsts` / `SliderConsts` / `UpDownConsts` (effectively public but Win32-plumbing-only) — covered above; no per-module doc page. + +## `[Unimplemented]` and `[Hidden]` members to flag + +- **DTPicker.RightToLeft** — tagged `[Unimplemented]`; flag with `> [!NOTE]`. +- **MonthView.RightToLeft** — same. +- **ListView.Scroll** event — tagged `[Unimplemented]`; flag on the event entry. +- **ListItem.CreateDragImage** — tagged `[Unimplemented]`; flag with `> [!NOTE]`. +- **ListImages.ControlDefault** — tagged `[Unimplemented]` and `[Hidden]`; **do not document** (`[Hidden]` means the IDE intentionally suppresses it). +- **ListView.AllowColumnReorder** — implemented but only takes effect in Report view; surface as a note on the property. + +Layout: folder-style for `ImageList/`, `ListView/`, `TreeView/` (each has 2–4 sub-object companions — `ListImage` + `ListImages`; `ListItem` + `ListItems` + `ColumnHeader` + `ColumnHeaders`; `Node` + `Nodes` — that are 1:1 with the container, same pattern as `CustomControls/WaynesButton/WaynesButtonState.md`). Single-file for the remaining five (`DTPicker.md`, `MonthView.md`, `ProgressBar.md`, `Slider.md`, `UpDown.md`). An `Enumerations/` folder holds the 10 module-level / shared enums; the 11 per-control nested enums fold onto their declaring control's page. + +## Pre-existing cross-references on the site + +- [`docs/Reference/VBRUN/Constants/ControlTypeConstants.md`](docs/Reference/VBRUN/Constants/ControlTypeConstants.md) already lists every control's `vb` constant: `vbProgressBar = 21`, `vbTreeView = 22`, `vbSlider = 26`, `vbUpDown = 27`, `vbDTPicker = 28`, `vbMonthView = 29`, `vbListView = 30`, `vbImageList = 31`. Each control's reference page should link back to its constant. +- [`docs/Reference/VBRUN/Constants/OLEDropConstants.md`](docs/Reference/VBRUN/Constants/OLEDropConstants.md) and the `OLEDragDrop` events are inherited surface — link the `OLEDropMode` entries to the constant. diff --git a/WIP.WinServicesLib.md b/WIP.WinServicesLib.md new file mode 100644 index 0000000..969c2a2 --- /dev/null +++ b/WIP.WinServicesLib.md @@ -0,0 +1,132 @@ +# WinServicesLib Package — Working Notes + +See [WIP.md](WIP.md) for the cross-package maintenance guide. Sister packages: [WinEventLogLib](WIP.WinEventLogLib.md), [WinNamedPipesLib](WIP.WinNamedPipesLib.md). + +A thin OS-services wrapper. The `[PredeclaredId]` singleton `Services` is the entry point; `ServiceManager` carries per-service configuration; `ServiceCreator(Of T)` is the generic factory; `ServiceState` is a read-only snapshot. The Win32 API wrappers, the `Private Module ServicesConstants` half of the constants file, the `ServiceControlHandlerCallback_Trampoline` helper, and the `IServiceCreator` / `IServiceManagerInternal` private interfaces are plumbing and get no doc page. The user-facing enums live in `Public Module ServicesConstantsPublic`. + +Public user-facing surface (three concrete classes + one generic class + one interface + four enums): + +| Symbol | Kind | Role | +|---------------------------------|-----------------------|---------------------------------------------------------------------------------------------------| +| `Services` | `[PredeclaredId]` Class | The singleton coordinator: `ConfigureNew`, `RunServiceDispatcher`, `InstallAll`, `UninstallAll`, `LaunchService`, `ControlService`, `QueryStateOfService`, `GetConfiguredService`, `_NewEnum`. Used as `Services.X` without `New`. | +| `ServiceManager` | Class | Per-service configuration + runtime status reporting. Returned by `Services.ConfigureNew()`. | +| `ServiceCreator(Of T)` | Generic class | The dispatcher's factory: `T` must implement `ITbService`; `CreateInstance` returns `New T`. | +| `ServiceState` | Class | Read-only state snapshot. Constructor (called via `Services.QueryStateOfService(Name)`) queries the SCM. | +| `ITbService` | Public Interface | The contract every service class implements: `EntryPoint`, `StartupFailed`, `ChangeState`. | +| `ServiceTypeConstants` | Enum | `tbServiceTypeOwnProcess`, `tbServiceTypeShareProcess`, etc. | +| `ServiceStartConstants` | Enum | `tbServiceStartAuto`, `tbServiceStartOnDemand`, etc. | +| `ServiceControlCodeConstants` | Enum | `vbServiceControlStop`, `vbServiceControlPause`, `vbServiceControlContinue`, etc. | +| `ServiceStatusConstants` | Enum | `vbServiceStatusRunning`, `vbServiceStatusStartPending`, `vbServiceStatusStopped`, etc. | + +The two private interfaces (`IServiceCreator`, `IServiceManagerInternal`) are pure implementation detail — same situation as `WinNamedPipesLib`'s `INamedPipe*Internal` interfaces. **No doc page**, and don't surface the underscored implementing members on the concrete classes either. + +The two `Private Module` declarations (`ServicesAPIs`, `ServicesConstants`) and the `Private Module ServicesHelper` are all internal — **no doc page**. + +## `Services` public members + +`[PredeclaredId]` Class. The compiler instantiates a singleton named `Services` at program start; user code calls `Services.X` directly without `New`. The class also doubles as an enumerable collection of the `ServiceManager` instances that have been configured (`For Each manager In Services`). + +**Public methods**: + +- `Function ConfigureNew() As ServiceManager` — *"Use this method to configure a service. Usually used during app startup."* Allocates a new `ServiceManager`, adds it to the internal collection, returns it. Typical use: `With Services.ConfigureNew : .Name = "MyService" : .InstanceCreator = New ServiceCreator(Of MyServiceClass) : End With`. +- `Sub RunServiceDispatcher()` — *"This method hands over to the OS for managing the starting/stopping of services via the main thread. This is a BLOCKING call, until the OS wants to shutdown the service EXE."* Builds a `SERVICE_TABLE_ENTRYW` from every configured `ServiceManager` and calls `StartServiceCtrlDispatcherW`. Returns only when the OS terminates the service host. Raises run-time error 5 if the dispatcher cannot start (typically when the EXE was launched normally rather than by the SCM). +- `Sub InstallAll()` — *"This method tries to register ALL of the configured services onto the system."* Iterates the configured `ServiceManager`s and calls `.Install()` on each. Requires admin. +- `Sub UninstallAll()` — *"This method tries to unregister ALL of the configured services off the system."* Iterates and calls `.Uninstall()` on each. Requires admin. +- `Function QueryStateOfService(ByVal ServiceName As String) As ServiceState` — returns a fresh `ServiceState` snapshot. Raises run-time error 5 if the service isn't installed. +- `Sub LaunchService(ByVal ServiceName As String, ParamArray LaunchArgs())` — start an installed service by name, optionally passing launch arguments through to its `ServiceManager.LaunchArgs()` field. Wraps `OpenServiceW(SERVICE_START)` + `StartServiceW`. Raises run-time error 5 on permission / not-installed / already-running. +- `Sub ControlService(ByVal ServiceName As String, ByVal ControlCode As ServiceControlCodeConstants)` — send an SCM control code to a running service. The required SCM permission is derived from the control code automatically (`SERVICE_STOP` for `vbServiceControlStop`, `SERVICE_PAUSE_CONTINUE` for the pause / continue / netbind / paramchange family, `SERVICE_INTERROGATE` for `vbServiceControlInterrogate`, `SERVICE_USER_DEFINED_CONTROL` for codes 128–255, `SERVICE_ALL_ACCESS` otherwise). For `vbServiceControlStop` the wrapper fills `SERVICE_CONTROL_STATUS_REASON_PARAMSW` with `SERVICE_STOP_REASON_FLAG_PLANNED | MAJOR_NONE | MINOR_NONE` — there is a `FIXME` to allow customising the reason code. + +**Public properties**: + +- `Property Get GetConfiguredService(ByVal Name As String) As ServiceManager` — look up a previously-configured `ServiceManager` by its `Name`. Raises run-time error 5 if not found. (Despite the `Get` syntax the lookup is parameterised by name; it's a property in name only.) + +**Public enumerator**: + +- `Property Get _NewEnum() As Variant` — `[Enumerator]`-tagged; enables `For Each manager In Services` over the configured `ServiceManager`s. *"Provides For-Each support for the services collection, exposing each configured service as a ServiceManager instance."* + +## `ServiceManager` public members + +`[COMCreatable(False)]`. User code never instantiates this directly — `Services.ConfigureNew()` returns it. The source-side constructor carries `[Description("For internal use. Dont create instances of ServiceManager manually, use Services.ConfigureNew instead")]` — surface that on the page intro. + +**Public field** (one): + +- `LaunchArgs() As String` — populated by `ServiceEntryPoint` from the `argv` the SCM hands over. `LaunchArgs(0)` is the *first user-supplied* argument (the SCM-supplied service name at `argv[0]` is dropped). The example uses it to gate startup: `If Join(ServiceManager.LaunchArgs) <> "MySecretPassword" Then …`. + +**Public properties** (each carries a `[Description("...")]`): + +- `InstanceCreator As IServiceCreator` (Get / Let / Set) — *"Set this to an instance of the ServiceCreator class to allow the OS to launch the instance of your service."* Typically `.InstanceCreator = New ServiceCreator(Of MyServiceClass)`. +- `Name As String` (Get / Let) — *"The name of the service, as listed in the OS services database."* +- `Description As String` (Get / Let) — *"The description of the service, as listed in the OS services database."* Applied via `ChangeServiceConfig2W(SERVICE_CONFIG_DESCRIPTION)` on every successful `Install()`. +- `Type As ServiceTypeConstants` (Get / Let) — *"The type of the service, typically `tbServiceTypeOwnProcess` or `tbServiceTypeShareProcess`."* Defaults to `tbServiceTypeOwnProcess`. +- `InstallStartMode As ServiceStartConstants` (Get / Let) — *"The start-mode of the service, typically `tbServiceStartOnDemand` or `tbServiceStartAuto`."* Defaults to `tbServiceStartOnDemand`. +- `InstallCmdLine As String` (Get / Let) — *"The command line arguments passed to the service EXE when the OS launches the service."* Defaults to `""""""`. **Usually overridden to add a discriminator argument** like `-startService` so the EXE knows whether it was launched by the SCM (run dispatcher) or by a user (show UI). Example: `.InstallCmdLine = """" & App.ModulePath & """ -startService"`. +- `DependentServices() As Variant` (Get / Let) — *"A list of dependent services that this service requires to be started before this service is launched (dependent services are auto-launched by the OS)."* Pass an `Array("OtherSvc1", "OtherSvc2")`. The setter stashes it; `Install()` packs it into a double-null-terminated string and hands it to `CreateServiceW`. +- `AutoInitializeCOM As Boolean` (Get / Let) — *"When TRUE, COM will be initialized for you on the new service thread in STA mode."* Defaults to `True`. Set to `False` if your service needs a different apartment model (call `CoInitializeEx` yourself from `EntryPoint`). +- `SupportsPausing As Boolean` (Get / Let) — *"When TRUE, the SCM will send `SERVICE_CONTROL_PAUSE` / `SERVICE_CONTROL_CONTINUE` notifications."* Defaults to `False`. The setter calls `ResyncStatus()` so toggling it mid-run takes effect immediately. (Most services set this to `True` once inside `EntryPoint` and then handle `vbServiceControlPause` / `vbServiceControlContinue` in `ChangeState`.) + +**Public methods**: + +- `Sub Install()` — *"This method attempts to install the configured service on the system."* Opens the SCM with `SC_MANAGER_CONNECT Or SC_MANAGER_CREATE_SERVICE`, calls `CreateServiceW`. If the service already exists, deletes it (via `OpenServiceW(SERVICE_DELETE)` + `DeleteService`) and **retries** the create — so `Install()` is effectively re-entrant / safe to call multiple times. On successful create, sets the description via `ChangeServiceConfig2W`. Raises run-time error 5 on permissions failure or unrecoverable create failure. **Requires admin elevation.** +- `Sub Uninstall()` — *"This method attempts to uninstall the configured service on the system."* Opens the SCM, opens the service with `SERVICE_DELETE`, calls `DeleteService`. Raises run-time error 5 if the service isn't registered or on permissions failure. **Requires admin elevation.** +- `Sub ReportStatus(ByVal dwCurrentState As ServiceStatusConstants, Optional ByVal dwWin32ExitCode As Long = ERRORCODE_NO_ERROR, Optional ByVal dwWaitHint As Long = 0)` — *"This method informs the OS of the current state of the service."* The user's `EntryPoint` is **required** to call `ReportStatus(vbServiceStatusRunning)` once steady-state is reached and `ReportStatus(vbServiceStatusStopped)` once shut-down completes; long start-up sequences should also call `ReportStatus(vbServiceStatusStartPending, , )` periodically to keep the SCM from killing the service. The `dwControlsAccepted` field of `SERVICE_STATUS` is filled automatically from the state and from `SupportsPausing` (Stop is always accepted except during `StartPending`; Pause/Continue is gated on `SupportsPausing`). The `dwCheckPoint` field auto-increments for pending states and resets on `Running`/`Stopped`. +- `Sub ResyncStatus()` — re-applies the cached `SERVICE_STATUS` to the SCM via `SetServiceStatus`. Called automatically from `ReportStatus` and from the `SupportsPausing` setter. User code rarely needs to call this directly; mention it for completeness. + +The class also carries two methods that are technically `Public`-by-default (no modifier) but are invoked only by the OS dispatcher / the package's own trampoline — `ServiceEntryPoint(ByVal dwArgc As Long, ByVal lpszArgv As LongPtr)` and `ServiceControlHandlerCallback(ByVal dwControl As Long, ByVal dwEventType As Long, ByVal lpEventData As LongPtr)`. **Do not list these as user-facing methods**; mention them at the very end of the page under "Internal hooks" with a `> [!NOTE]` saying the OS / package infrastructure invokes them and user code never calls them. + +## `ServiceCreator(Of T)` public members + +Generic class. `[COMCreatable(False)]`. `[Description("This class allows the service manager to create an instance of a particular service on-demand as needed")]` is the source intro. Tagged with the EA magic-byte `[ClassId("66170220-FEF3-4257-8FBA-EAEAEAEAEAEA")]` — same compiler-special-handling treatment as `WinEventLogLib`'s `EventLog(Of T1, T2)`. Do not surface the `ClassId` on the page. + +Type parameter constraint: `T` must implement `ITbService`. There is no syntactic `Where T : ITbService` constraint expressed in the source, but `Function CreateInstance() As ITbService` returning `New T` only compiles when `T` implements `ITbService` — flag this as the practical constraint on the page. + +**Public method**: + +- `Function CreateInstance() As ITbService` — `Implements IServiceCreator.CreateInstance`. Returns `New T`. Called once per service start by the package's dispatcher trampoline. User code never calls this directly; the typical usage is `.InstanceCreator = New ServiceCreator(Of MyServiceClass)` on a freshly-allocated `ServiceManager`. + +The page should be small (the surface is one method) and largely focused on explaining the `Of T` parameterisation + the `T : ITbService` constraint + how it slots into `ServiceManager.InstanceCreator`. + +## `ServiceState` public members + +`[COMCreatable(False)]`. Returned by `Services.QueryStateOfService(Name)`. The constructor takes the service name, opens the SCM with `SC_MANAGER_CONNECT`, opens the service with `SERVICE_QUERY_STATUS`, calls `QueryServiceStatusEx(SC_STATUS_PROCESS_INFO, ...)`, and snapshots a `SERVICE_STATUS_PROCESS` struct. **The snapshot is taken once at construction time and never refreshed** — to see updated state, call `Services.QueryStateOfService` again. + +The constructor raises run-time error 5 with descriptive messages on three failure modes: SCM open failed (*"Unable to open the Service manager..."*), service not installed (*"Service '' is not installed on this system"*), status query failed (*"Unable to query the service state"*). + +**Public properties** (all read-only `Get`): + +- `Type As ServiceTypeConstants` — the SCM-reported service type. +- `CurrentState As Long` — the SCM-reported state, but typed `Long` rather than `ServiceStatusConstants`. **Source carries a `' FIXME` comment** — surface as a `> [!NOTE]` that this returns the underlying `Long` value (which happens to match the `ServiceStatusConstants` enum values), and that callers wanting type-safety can `CType(state.CurrentState, ServiceStatusConstants)`. +- `CurrentStateText As String` — human-readable text: `"RUNNING"`, `"STOPPED"`, `"STARTING"`, `"STOPPING"`, `"PAUSED"`, `"PAUSING"`, `"CONTINUING"`, `"UNKNOWN STATE ()"`. +- `ControlsAccepted As Long` — bitmask of `SERVICE_ACCEPT_*` flags. **Source carries a `' FIXME` comment** — surface the same way as `CurrentState`. +- `ExitCode As Long` — the `dwWin32ExitCode` field. The Win32 documented sentinel `ERROR_SERVICE_SPECIFIC_ERROR` (`1066`) means "see `ServiceSpecificExitCode`". +- `ServiceSpecificExitCode As Long` — the service-defined exit code when `ExitCode = ERROR_SERVICE_SPECIFIC_ERROR`. Otherwise meaningless. +- `CheckPoint As Long` — the `dwCheckPoint` field; increments while the service is in a pending state and resets at steady state. +- `WaitHint As Long` — the `dwWaitHint` milliseconds field. +- `ProcessId As Long` — the OS process ID hosting the service (0 if not running). +- `Flags As Long` — the `dwServiceFlags` field (currently `SERVICE_RUNS_IN_SYSTEM_PROCESS = 1` is the only documented bit). + +## `ITbService` public members + +`Public Interface`. Tagged `[InterfaceId("5F137E12-5164-452E-911A-6FD9BF20EC81")]`. Description: *"All services must implement `ITbService`."* The contract is three subs: + +- `Sub EntryPoint(ByVal ServiceContext As ServiceManager)` — the main service body. Called by the package's dispatcher trampoline once the SCM has finished start-up handshaking. **Runs on the service thread** (a separate thread from the dispatcher). Inside this sub the implementor: + 1. Optionally validates startup conditions (e.g. checks `ServiceContext.LaunchArgs`). + 2. Calls `ServiceContext.ReportStatus(vbServiceStatusRunning)` once steady-state is reached (the dispatcher trampoline reports `vbServiceStatusStartPending` automatically before calling `EntryPoint`). + 3. Runs the long-running work loop. For pipe-server services this is the `NamedPipeServer.ManualMessageLoopEnter()` blocking call; for other services it might be a `Do While IsStopping = False` loop with a wait primitive. + 4. Calls `ServiceContext.ReportStatus(vbServiceStatusStopped)` before returning. +- `Sub StartupFailed(ByVal ServiceContext As ServiceManager)` — called if `RegisterServiceCtrlHandlerExW` failed (the control handler couldn't be hooked, e.g. the service was launched outside the SCM context). Typical implementation: log a failure event. Don't try to `ReportStatus` from here — the status handle is invalid. +- `Sub ChangeState(ByVal ServiceContext As ServiceManager, ByVal dwControl As ServiceControlCodeConstants, ByVal dwEventType As Long, ByVal lpEventData As LongPtr)` — the control-code dispatcher. **Runs on the main (dispatcher) thread**, not on the service thread. Typical pattern: `Select Case dwControl` over `vbServiceControlStop` / `vbServiceControlShutdown` / `vbServiceControlPause` / `vbServiceControlContinue`, set shared `Public` flags (`IsStopping`, `IsPaused`), call `ServiceContext.ReportStatus` to acknowledge the transition, signal the service thread to react (e.g. `NamedPipeServer.ManualMessageLoopLeave()`). The `dwEventType` + `lpEventData` parameters carry the event-specific payload for the codes that need it (`SERVICE_CONTROL_DEVICEEVENT`, `SERVICE_CONTROL_POWEREVENT`, `SERVICE_CONTROL_SESSIONCHANGE`, `SERVICE_CONTROL_HARDWAREPROFILECHANGE` — see Microsoft's `HandlerEx` documentation for the data layouts). + +**The two-thread split is the single most important fact about the interface** — every page entry should reinforce it. The example uses `Public IsPaused As Boolean` + `Public IsStopping As Boolean` shared fields on the service class to ferry state between the two threads, which is the documented pattern. + +## Enumerations + +Public enums (in `Public Module ServicesConstantsPublic`), one page each under `docs/Reference/WinServicesLib/Enumerations/`: + +- `ServiceTypeConstants` — `tbServiceTypeAdapter`, `tbServiceTypeSystemDriver`, `tbServiceTypeKernelDriver`, `tbServiceTypeRecognizerDriver`, `tbServiceTypeOwnProcess`, `tbServiceTypeShareProcess`, `tbServiceTypeOwnProcessInteractive`, `tbServiceTypeShareProcessInteractive`. The driver values (`tbServiceTypeSystemDriver`, `tbServiceTypeKernelDriver`, `tbServiceTypeRecognizerDriver`, `tbServiceTypeAdapter`) are only meaningful when registering a kernel-mode driver — twinBASIC services compile to a user-mode EXE and should use `tbServiceTypeOwnProcess` (one service per EXE) or `tbServiceTypeShareProcess` (multiple services hosted in one EXE; the example uses this). The `Interactive` variants are kept for compatibility but Windows Vista and later disallow them; flag with a `> [!NOTE]`. +- `ServiceStartConstants` — `tbServiceStartAuto`, `tbServiceStartBoot`, `tbServiceStartOnDemand`, `tbServiceStartDisabled`, `tbServiceStartDriverSystem`. `tbServiceStartBoot` and `tbServiceStartDriverSystem` only apply to kernel drivers. +- `ServiceControlCodeConstants` — 18 values mirroring the Win32 `SERVICE_CONTROL_*` constants. Source-side prefix is `vbServiceControl*` (carried over from VB6 — note the prefix is `vb`, not `tb`, in this enum; surface as-is, don't try to rationalise). +- `ServiceStatusConstants` — `vbServiceStatusStopped`, `vbServiceStatusStartPending`, `vbServiceStatusStopPending`, `vbServiceStatusRunning`, `vbServiceStatusContinuePending`, `vbServiceStatusPausePending`, `vbServiceStatusPaused`. Same `vb` prefix. + +Format pages like `WebView2/Enumerations/wv2PrintOrientation.md` — single intro paragraph, a value table with `{: #vbServiceXxx }` anchors per row for deep-linking. + +The package's `index.md` walks the reader through: (1) what a Windows service is; (2) the lifecycle — configure (`Services.ConfigureNew`) → install (elevated) → run (`Services.RunServiceDispatcher` blocks the main thread, SCM launches the service thread on demand); (3) the two-thread split between `EntryPoint` and `ChangeState`; (4) integration cross-links to `WinEventLogLib` (composition-delegation idiom) and `WinNamedPipesLib` (the `ManualMessageLoopEnter` / `Leave` service-hosting idiom). diff --git a/WIP.md b/WIP.md index 82ac075..3f581cd 100644 --- a/WIP.md +++ b/WIP.md @@ -21,9 +21,7 @@ Reference documentation is **complete** for all twelve packages, adapted from pr | tbIDE | done | — | | WinNativeCommonCtls | done | — | -The rest of this file is the maintenance guide for adding new pages or updating existing ones — primary-source paths, page templates, cross-section linking conventions, the per-symbol workflow, and the integrity check. - -When working from a primary source: always read it first — **never paraphrase from memory.** +The rest of this file is the maintenance guide for updating existing pages or adding new ones — high-level package surface notes, page templates, cross-section linking conventions, and the integrity check. ## Where things live @@ -44,1384 +42,21 @@ When working from a primary source: always read it first — **never paraphrase - `docs/Reference/Procedures and Functions.md` — alphabetical index of procedures/functions. - `docs/_includes/footer_custom.html` — overrides the theme's footer slot; renders the copyright line and, when `vba_attribution: true` is set in a page's frontmatter, an additional CC-BY-4.0 attribution line beneath it. -## VBA-Docs source (read-only) - -Cloned as a sibling of this repo. All paths below are relative to the repo root: - -``` -../VBA-Docs/Language/Reference/User-Interface-Help/-.md -``` - -Common kinds: `-statement`, `-function`, `-property`, `-method`, `-object`, `-operator`. Find the file with `ls ../VBA-Docs/Language/Reference/User-Interface-Help/ | grep -i ` before drafting. - -Used for: Core statements/keywords, the VBA package, and the VBRUN package. - -## TwinBASIC Package source (read-only) - -All of twinbasic's package sources are at: - -``` -..\tb-export\NewProject\Packages\VB\Sources\CONTROLS\STANDARD\.twin -..\tb-export\NewProject\Packages\VB\Sources\CONTROLS\OTHER\.twin -..\tb-export\NewProject\Packages\VB\Sources\BASE\Base*.twin -..\tb-export\NewProject\Packages\VBA\Sources\ -..\tb-export\NewProject\Packages\VBRUN\Sources\ -..\tb-export\NewProject\Packages\WebView2Package\Sources\ -..\tb-export\NewProject\Packages\Assert\Sources\ -..\tb-export\NewProject\Packages\CustomControls\Sources\ -..\tb-export\NewProject\Packages\CustomControlsPackage\Sources\ -..\tb-export\NewProject\Packages\cefPackages\Sources\ -..\tb-export\NewProject\Packages\WinEventLogLib\Sources\ -..\tb-export\NewProject\Packages\WinNamedPipesLib\Sources\ -..\tb-export\NewProject\Packages\WinServicesLib\Sources\ -..\tb-export\NewProject\Packages\WinNativeCommonCtls\Sources\ -etc. -``` - -For the **tbIDE** package, the sources are not in `..\tb-export\`. They live inside one of the six addin sample projects (any will do — the package is a binary-only compiler package and ships its `.twin` declarations alongside each sample). Use sample10's copy as the canonical path: - -``` -..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Sources\ ← 24 flat .twin files -..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\LICENCE.md ← MIT, Wayne Phillips, 2022 -..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Settings ← project.name = "tbIDE", buildType = Package TWINPACK -``` - -The six matching consumer-side example addins live at: - -``` -..\tbrepro\sample10\WaynesWorldAddInTest1\Sources\MainModule.twin ← kitchen-sink: toolbar / toolwindow / DOM / events / Evaluate / ActiveEditors -..\tbrepro\sample11\WaynesWorldCPUMonitorTest1\Sources\MainModule.twin ← AddinTimer + chartjs custom-element + onClose cleanup -..\tbrepro\sample12\WaynesWorldMonacoEditorTest1\Sources\MainModule.twin ← monaco custom-element + .editor.AddEventListener -..\tbrepro\sample13\WaynesListViewAddIn\Sources\MainModule.twin ← listview custom-element + ApplyCss + raiseEvent() from inline HTML -..\tbrepro\sample14\WaynesVirtualListViewAddIn\Sources\MainModule.twin ← virtuallistview + onAsyncGetItemHTML + setAsyncResult + notifyChangedItem -..\tbrepro\sample15\tbGlobalSearchAddIn1\Sources\MainModule.twin ← real-world: FS traversal + ReadText + ActiveEditors.Open + persistent settings -``` - -Read them in roughly that order — sample10 introduces the addin idioms one by one, samples 11–14 each focus on a single advanced custom-element widget (chartjs / monaco / listview / virtuallistview), and sample15 is a complete, polished addin that exercises the file system + editor-navigation surface. - -For the CEF package, the examples live in a different folder: - -``` -..\tbrepro\cef\CEFSampleProject\Sources\ ← four worked examples + MainForm -``` - -For the WinServicesLib package — and the canonical integration story across **all three** "winlibs" packages (services + event log + named pipes wired together end-to-end) — the worked example lives at: - -``` -..\tbrepro\winlibs\tbServiceTest2\Sources\ - Startup.twin ← Sub Main: configures two services + dispatches - SERVICES\TBSERVICE001.twin, TBSERVICE002.twin ← user-implemented ITbService classes - FORMS\MainForm.twin ← non-service mode: control-panel UI - FORMS\InProcessNamedPipeServerForm.twin ← in-process pipe server (no service) - MISC\MESSAGETABLE.twin ← [PopulateFrom("json",...)] enums for the event log -..\tbrepro\winlibs\tbServiceTest2\Resources\MESSAGETABLE\Strings.json ← message-table backing JSON -``` - -Read this project end-to-end before extending the docs for any of WinServicesLib, WinEventLogLib, or WinNamedPipesLib — the three packages share a load-bearing set of idioms (composition-delegation on `EventLog(Of T1, T2)`, the manual-message-loop pattern coupling `NamedPipeServer` to a service's `ChangeState` handler, `PropertyBag` as the canonical pipe payload) that only become visible when you see them used together. - -### VB Controls - -The `STANDARD/` folder holds the leaf control classes. The `BASE/` folder defines the inheritance chain (e.g. `BaseControlWindowlessNoFocus` → `BaseControlRectDockable` → `BaseControlRect` → `BaseControl`); read those alongside the leaf class to know which `Public` members are actually visible. Members marked `Protected` or hidden behind `[Unimplemented]` should be flagged with a `> [!NOTE]` callout. - -These pages are fully original content — **omit** the `vba_attribution: true` frontmatter flag. - -### WebView2Package - -Layout of `..\tb-export\NewProject\Packages\WebView2Package\Sources\`: - -- `Classes/` — the implementation classes. Only a few are part of the user-facing surface; the rest are `Private` plumbing. -- `Abstract/` — raw `ICoreWebView2*` COM interfaces. Every one is declared `Private Interface`; these are pure implementation detail and get **no documentation page**. -- `Support/Enumerations.twin` — the `wv2…` enumerations, all in the `WebViewEnums` module. -- `Support/Types.twin` — the `WebViewTypes` module; currently only `COREWEBVIEW2_PHYSICAL_KEY_STATUS`. -- `Support/WebView2Misc.twin` — `Private Module`; helpers, no public surface. -- `EventCallbacks/` — currently empty. - -Public user-facing surface (from `grep '^Public Class' Classes/*.twin` plus the top-level `WebView2` class which has no explicit modifier): - -| Class | Role | -|-----------------------------|-------------------------------------------------------------------------------------------------------| -| `WebView2` | the control itself (`Inherits VB.BaseControlFocusableNoFont`, `[WindowsControl(...)]`) | -| `WebView2Header` | one HTTP header (Name / Value); value type returned by header iteration | -| `WebView2HeadersCollection` | enumerable wrapper used by `For Each` over request / response headers | -| `WebView2Request` | request side of a `WebResourceRequested` event — Method, Uri, Headers, ContentBytes, ContentUTF8 | -| `WebView2RequestHeaders` | mutable request-header collection — `GetHeader`, `Contains`, `AppendHeader`, `RemoveHeader`, … | -| `WebView2Response` | response side of a `WebResourceRequested` event — StatusCode, ReasonPhrase, Headers, ContentBytes… | -| `WebView2ResponseHeaders` | mutable response-header collection | - -`WebView2EnvironmentOptions` is declared `Private Class`, **but** the `WebView2` control exposes it via `Public EnvironmentOptions As WebView2EnvironmentOptions = New WebView2EnvironmentOptions`. Document it as a sub-page of the `WebView2` control class — its `Public` fields (`BrowserExecutableFolder`, `UserDataFolder`, `AdditionalBrowserArguments`, `Language`, `TargetCompatibleBrowserVersion`, `AllowSingleSignOnUsingOSPrimaryAccount`, `ExclusiveUserDataFolderAccess`, `EnableTrackingPrevention`) are user-set before / during the `Create` event. - -The other `Private Class` files (`WebView2DeferredCallback`, `WebView2DeferredRaiseEvent`, `WebView2DevToolsProtocolCallback`, `WebView2ExecuteScriptCompleteHandler`, `WebView2ExecuteScriptCompleteHandler2`) and their helper interfaces (`IDeferredCallback`, `IExecuteScriptCompleteCallback`) are deferral / callback plumbing — skip. - -The `WebView2` class itself is large (~1450 lines) and exposes Properties / Methods / Events plus the `EnvironmentOptions` member. Use the **folder-style** layout (`WebView2/index.md`) like `CheckBox/index.md` so the page can carry a TOC and the optional sub-pages (`WebView2/EnvironmentOptions.md`, etc.) sit beside it. - -Enumerations live in `Support/Enumerations.twin` (module `WebViewEnums`) and currently number ten: `wv2PermissionKind`, `wv2PermissionState`, `wv2ErrorStatus`, `wv2KeyEventKind`, `wv2WebResourceContext`, `wv2ProcessFailedKind`, `wv2ScriptDialogKind`, `wv2HostResourceAccessKind`, `wv2PrintOrientation`, `wv2DefaultDownloadCornerAlign`. Group them under `WebView2/Enumerations/` following the VBRUN `Constants/` precedent — one page per enum, with `AlignConstants.md` as the formatting model. - -`COREWEBVIEW2_PHYSICAL_KEY_STATUS` is a public `Type` in the `WebViewTypes` module; it surfaces through the `AcceleratorKeyPressed` event arguments. One page (`WebView2/Types/COREWEBVIEW2_PHYSICAL_KEY_STATUS.md`) is enough. - -The package is licensed **MIT** (copyright Wayne Phillips T/A iTech Masters, 2022) — independent of the CC-BY-4.0 VBA-Docs sources. These pages are fully original content; **omit** the `vba_attribution: true` flag, the same as VB-package pages. - -**Naming:** the source-side package symbol is `WebView2Package` (and the source folder is `..\tB-export\NewProject\Packages\WebView2Package\`), but the doc folder, URL segment, and page title all drop the doubled "Package" suffix — folder `docs/Reference/WebView2/`, permalink `/tB/Packages/WebView2/`, title `WebView2 Package` (space-separated, the same ` Package` convention VB / VBRUN / Assert use). Every child page sets `parent: WebView2 Package` (matching the title, not the URL segment). - -Pre-existing `WebView2` references on the site to keep aligned: - -- [`docs/Tutorials/WebView2/`](docs/Tutorials/WebView2) — task-oriented tutorial; new reference pages should cross-link to it where useful, and vice versa. -- [`docs/Reference/VBRUN/Constants/ControlTypeConstants.md`](docs/Reference/VBRUN/Constants/ControlTypeConstants.md) — already lists `vbWebView2 = 18`; the new `WebView2` reference page should link to that constant. - -### Assert - -Layout of `..\tb-export\NewProject\Packages\Assert\Sources\` is flat — three sibling files, no sub-folders, no `Abstract\` / `Support\` plumbing: - -- `Exact.twin` — module **Exact** -- `Strict.twin` — module **Strict** -- `Permissive.twin` — module **Permissive** - -All three modules expose the **same 15-member API**; only the comparison semantics differ. The differentiating semantics are spelled out in the module-level `[Description("...")]` block at the top of each `.twin`: - -| Module | String comparisons | Other comparisons | -|---------------|--------------------|--------------------------------------------------------------------------------| -| `Exact` | case-sensitive | no implicit conversions; datatypes must match exactly (`5 <> 5.0`); `vbNullString <> ""`; `Empty` distinct from `0` / `False` / `""`; object default members **not** evaluated | -| `Strict` | case-sensitive | evaluated as if written directly in twinBASIC; object default members **not** evaluated | -| `Permissive` | case-insensitive | evaluated as if written directly in twinBASIC | - -`Null` is never equal to anything (not even itself) under any of the three flavours — use `IsNull` / `IsNotNull` to test for it. - -The 15 members per module (identical signatures across all three): - -| Member | Purpose | -|-------------------------------------------------|------------------------------------------------------------------| -| `Succeed()` | unconditionally records a pass | -| `Fail([Message])` | unconditionally records a failure | -| `Inconclusive([Message])` | records an inconclusive / skipped result | -| `AreEqual(Expected, Actual, [Message])` | value-equality assertion | -| `AreNotEqual(Expected, Actual, [Message])` | inverse of `AreEqual` | -| `AreSame(Expected, Actual, [Message])` | reference-identity (`Is`) assertion for objects | -| `AreNotSame(Expected, Actual, [Message])` | inverse of `AreSame` | -| `IsTrue(Condition, [Message])` | asserts the condition is `True` | -| `IsFalse(Condition, [Message])` | asserts the condition is `False` | -| `IsNothing(Value, [Message])` | asserts the object reference is `Nothing` | -| `IsNotNothing(Value, [Message])` | inverse of `IsNothing` | -| `IsNull(Value, [Message])` | asserts the value is `Null` | -| `IsNotNull(Value, [Message])` | inverse of `IsNull` | -| `SequenceEquals(Expected, Actual, [FailMessage])` | element-by-element comparison of two sequences / arrays | -| `NotSequenceEquals(Expected, Actual, [FailMessage])` | inverse of `SequenceEquals` | - -Source-side every member is declared as `Public DeclareWide PtrSafe Sub Lib "" Alias "#N" (...)` and tagged `[DebugOnly(True), MustBeQualified(True), PreserveSig(False), UseGetLastError(False)]`. The `Lib` / `Alias` / `PreserveSig` / `UseGetLastError` decoration is internal pseudo-DLL plumbing for the runtime — **do not** surface it on the doc pages. The `[DebugOnly(True)]` tag matters to users (assertions compile out of release builds) and **should** be called out. The `[MustBeQualified(True)]` tag means callers must write the module name, e.g. `Strict.IsTrue(x)`. - -The module-level `[Description("…")]` is the only non-empty description on the source side — the per-member `[Description("")]` blocks are all empty placeholders, so member descriptions are fully original prose. - -**Layout decision** — deviation from the per-symbol pattern used elsewhere: - -Because the three modules share an *identical* API, replicating 15 member-pages × 3 modules = 45 near-duplicate pages would add noise without value. Use **one page per module** instead, listing all 15 members inline under `## ` headings (deep-linkable as `…/Strict#areequal`). The package landing page collects the three module pages and shows the semantics comparison table. - -So the layout is: - -- `docs/Reference/Assert/index.md` — package landing page; the three modules + side-by-side semantics table + a one-paragraph "what is this for" intro pointing at unit testing -- `docs/Reference/Assert/Exact.md` — single-file module page, all 15 members -- `docs/Reference/Assert/Strict.md` — same shape -- `docs/Reference/Assert/Permissive.md` — same shape - -That's 4 pages total. (If a future release of the package adds more modules or non-module classes, revisit.) - -**Naming:** - -- Folder / URL segment: `Assert/` (the package name is `Assert` per `Settings` → `project.name`; no doubled "Package" awkwardness like WebView2Package had). -- Index title: `Assert Package` — same ` Package` convention as VB / VBRUN / WebView2. -- Module page title: `` (just `Exact`, `Strict`, `Permissive` — they're modules, not classes, and "Module" is implied by context). -- Permalinks: `/tB/Packages/Assert/` for the index, `/tB/Packages/Assert/` for each module page. -- `parent: Assert Package` on each module page (matching the index `title:`, the same split VB / VBRUN / WebView2 use). - -**License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2022) — same situation as WebView2Package. Pages are fully original; **omit** the `vba_attribution: true` flag. - -### CustomControls / CustomControlsPackage - -Two source-side packages, **one** doc-side package. They always ship together and are co-versioned with twinBASIC — `CustomControlsPackage` lists `CustomControls` as a `isCompilerPackage` reference in its `Settings`, and neither is usable without the other. Document the union as `docs/Reference/CustomControls/`. - -The split on the source side is by *role*: - -- `..\tb-export\NewProject\Packages\CustomControls\Sources\CustomControls.twin` — the **DESIGNER** framework. A single file with `Module Constants` (the enums + the `SerializeInfo` / `Canvas` UDTs) plus the abstract surface a custom control hooks into (`ICustomControl`, `ICustomForm` interfaces; `CustomControlContext`, `CustomFormContext`, `CustomControlTimer`, `CustomControlsCollection` CoClasses). Project `appTitle` is `"CustomControls DESIGNER Package"`. -- `..\tb-export\NewProject\Packages\CustomControlsPackage\Sources\` — the **runtime**: eight concrete `Waynes…` controls plus `zTemporarySupport.twin`, a bag of shared appearance helpers and three mixin base classes. Project `appTitle` is `"Custom Controls Package"`. - -Public user-facing surface, grouped by role. - -#### Concrete controls (`CustomControlsPackage\Sources\Waynes*.twin`) - -Each is `Class ` (no `Public` modifier — implicitly public), tagged `[CustomControl("/miscellaneous/frm.png")]` (designer icon) and `[COMCreatable(False)]` (cannot be `New`'d through COM; instantiated by the designer). - -| Control | Implements | Co-located public types | -|------------------|-----------------------------------------------------------|--------------------------------------------------| -| `WaynesButton` | `ICustomControl` + `BaseControlFocusable` (mixin) | `WaynesButtonState` (private, but exposed) | -| `WaynesForm` | `ICustomControl` + `BaseForm` (mixin) | — (uses `WindowsFormOptions` from support file) | -| `WaynesFrame` | `ICustomControl` + `BaseControl` (mixin) | — | -| `WaynesGrid` | `ICustomControl` + `BaseControlFocusable` (mixin) | `Column`, `CellRenderingOptions` | -| `WaynesLabel` | `ICustomControl` + `BaseControl` (mixin) | — | -| `WaynesSlider` | `ICustomControl` + `BaseControlFocusable` (mixin) | `WaynesSliderState`, `SliderDirection` & `SliderDisplayValueFormat` (nested enums) | -| `WaynesTextBox` | `ICustomControl` + `BaseControlFocusable` (mixin) | `WaynesTextBoxState` | -| `WaynesTimer` | `ICustomControl` + `BaseControl` (mixin) | — | - -The "mixin" base classes (`BaseControl`, `BaseControlFocusable`, `BaseForm`) are declared `Private Class` in `zTemporarySupport.twin` and pulled into each control via the twinBASIC `Implements Via _BaseControl = New ` syntax. The base classes themselves get **no doc page** (they're private and never named by user code), but the inherited members **must be folded into each control's Properties listing** the same way VB-package controls list their inherited surface. The visible inherited surface, by mixin: - -- `BaseControl` → `Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, `Visible`. -- `BaseControlFocusable` → all of `BaseControl` + `TabIndex`, `TabStop`. -- `BaseForm` → `FormDesignerId`, `Name`, `Left`, `Top`, `Width`, `Height`, `Controls`. - -The state-holder classes (`WaynesButtonState`, `WaynesSliderState`, `WaynesTextBoxState`) and `WindowsFormOptions` are declared `Private Class` but are exposed on the parent control via `Public WithEvents NormalState As WaynesButtonState` (etc.). Same situation as `WebView2EnvironmentOptions` — document them as **sub-pages** of the parent control using the folder-style layout. - -#### Shared appearance helpers (`CustomControlsPackage\Sources\zTemporarySupport.twin`) - -Every helper in this file is `Private Class`, but the ones below are reachable through `Public WithEvents …` properties on one or more of the eight controls and **must be documented**: - -| Class | Reached as | -|-----------------|-------------------------------------------------------------------------------------| -| `Anchors` | `.Anchors` (via the mixin base) | -| `Corners` | `.Corners`, `CellRenderingOptions.Corners`, `.BackgroundCorners`, `BlockCorners` | -| `Corner` | `Corners.TopLeft` / `.TopRight` / `.BottomLeft` / `.BottomRight` | -| `Borders` | `.Borders`, `CellRenderingOptions.Borders`, `.BackgroundBorders`, `BlockBorders` | -| `Border` | element of `Borders.Elements()`; also `TextRendering.Outlines()` | -| `Fill` | `.BackgroundFill`, `.BlockFill`, `CellRenderingOptions.Fill`, `Border.Fill`, `Line.Fill`, `TextRendering.Fill` | -| `FillColorPoint` | element of `FillColorPoints.Values()` | -| `FillColorPoints` | `Fill.ColorPoints` | -| `Line` | `WaynesGrid.VerticalLineOptions` / `.HorizontalLineOptions` / `.ResizerBar` | -| `Padding` | `TextRendering.Padding` | -| `TextRendering` | `.TextRendering`, `WaynesLabel.TextRendering`, `CellRenderingOptions.TextRendering` | -| `FontStyle` | `TextRendering.Font` | -| `WindowsFormOptions` | `WaynesForm.WindowsOptions` (only one consumer) | - -Document these under `docs/Reference/CustomControls/Styles/`. Pair small helpers with their containers on a single page (the pairings happen to be self-evident from the table — `Corner` inlines under `Corners.md`, `Border` under `Borders.md`, `FillColorPoint` and `FillColorPoints` under `Fill.md`, `FontStyle` under `TextRendering.md`). `WindowsFormOptions` is the exception: it has exactly one consumer (`WaynesForm`), so put it as a sub-page of `WaynesForm/` (folder-style), parallel to how WebView2 carries `EnvironmentOptions`. - -The remaining `Private Class` / `Private Module` content in `zTemporarySupport.twin` is implementation-detail and gets **no doc page**: - -- `TextDecorator`, `TextDecorators` — used only inside `ElementDescriptor`, not surfaced on any control property. -- `UDTs` — a wrapper class whose `Public Type` declarations (`MouseEvent`, `KeyEvent`, `FocusEvent`, `ElementDescriptor`) and `Public Enum` declarations (`CaretPosition`, `SpecialKeyCodes`) only matter to someone writing a *new* custom control (they're passed to `AddressOf`-registered callbacks on `ElementDescriptor`). Defer documenting these until / unless an "authoring a custom control" tutorial calls for them. -- `BaseControl`, `BaseControlFocusable`, `BaseForm` — the mixin bases (covered above; members surface on each control, but the bases themselves are private). -- `Private Module MathSupport` / `Private Module ColorSupport` — internal. - -#### DESIGNER framework surface (`CustomControls\Sources\CustomControls.twin`) - -The framework half — what a *control author* writes against. Document at `docs/Reference/CustomControls/Framework/`: - -| Symbol | Kind | Role | -|------------------------------|---------------------|---------------------------------------------------------------------------------------| -| `ICustomControl` | `Interface` | what every concrete control implements: `Initialize(Context)`, `Destroy()`, `Paint(Canvas)` | -| `ICustomForm` | `Interface` | analogous surface for form-class custom controls | -| `CustomControlContext` | `CoClass` | passed to `ICustomControl.Initialize`; offers `GetSerializer()`, `Repaint()`, `CreateTimer()`, `ChangeFocusedElement()` | -| `CustomFormContext` | `CoClass` | extends `CustomControlContext` with `Show()` / `Close()` | -| `CustomControlTimer` | `CoClass` | returned by `CustomControlContext.CreateTimer()`; `Interval`, `Enabled`, `OnTimer` event | -| `CustomControlsCollection` | `CoClass` | the `Controls` collection on a form — `Count`, `Item`, `Add`, `Remove`, `_NewEnum` | -| `SerializeInfo` | UDT | obtained from `Context.GetSerializer()`; exposes `RuntimeUISrz*` operations (deserialize, mode flags, …) | -| `Canvas` | UDT | parameter to `ICustomControl.Paint`; exposes `RuntimeUICCCanvasAddElement` + DPI / size getters | - -Both UDTs follow a pattern unique to twinBASIC: a `Pointer As LongPtr` field plus `Public DeclareWide PtrSafe Function/Sub … Lib "" Alias "#N"` pseudo-DLL declarations bound directly into the type. From a *caller* perspective these read as instance methods on the UDT (`Canvas.RuntimeUICCCanvasAddElement(descriptor)`); document them as methods, and **do not** surface the `Lib "<…>"` / `Alias "#N"` / `PreserveSig` / `DLLStackCheck` decoration (same treatment as Assert's pseudo-DLL plumbing). The verbose `RuntimeUISrz*` / `RuntimeUICC*` names are unfortunate but they *are* the public API — keep them as-is. - -The two underscore-prefixed default interfaces of each CoClass (`_CustomControlTimer`, `_CustomControlContext`, `_CustomFormContext`, `_CustomControlsCollection`, `_CustomControlTimerEvents`) are an implementation detail of the COM `[Default]`/`[Default, Source]` pattern — fold their members onto the CoClass page, **don't** give the interfaces their own pages. - -#### Enumerations (`CustomControls.twin`, module `Constants`) - -Public enums to surface, one page each, under `docs/Reference/CustomControls/Enumerations/`: - -- `CornerShape`, `FillPattern`, `TextAlignment`, `TextOverflowMode`, `DockMode`, `FontWeight`, `StartupPosition`, `BorderStyle`, `WindowState` — straightforward value enums. -- `Customtate` — **probable typo** for `CustomState`. Has the same three members as `WindowState` (`tbNormal` / `tbMinimized` / `tbMaximized`) and isn't referenced anywhere else in the package. Document it (since it's `Public`), but add a `> [!NOTE]` callout flagging the typo and pointing readers to `WindowState`. -- `ColorRGBA`, `PixelCount`, `PointSize` — these are declared as `Enum` only because twinBASIC doesn't yet have a `Type Foo = Long` alias syntax. Each carries a `FIXME` comment ("Substitute for an ALIAS to Long") and a single `[_MAX] = 0` placeholder member. Document them as **typedefs for `Long`** (the underlying storage type), not as real enums. Note in each that the alias is what user code actually sees on `Public Width As CustomControls.PixelCount` (etc.) — when the alias syntax lands, these enum stand-ins go away. - -Plus the two enums nested inside `WaynesSlider`: `SliderDirection` and `SliderDisplayValueFormat`. Document them on the `WaynesSlider/index.md` page rather than under `Enumerations/` (they're locally scoped to the slider). - -#### Doc-side layout (folders / files) - -Compact form: - -``` -docs/Reference/CustomControls/ - index.md ← package landing; intro + role split + cross-links to the four groups - WaynesButton/index.md, WaynesButton/WaynesButtonState.md - WaynesForm/index.md, WaynesForm/WindowsFormOptions.md - WaynesFrame.md - WaynesGrid/index.md, WaynesGrid/Column.md, WaynesGrid/CellRenderingOptions.md - WaynesLabel.md - WaynesSlider/index.md, WaynesSlider/WaynesSliderState.md - WaynesTextBox/index.md, WaynesTextBox/WaynesTextBoxState.md - WaynesTimer.md - Styles/index.md, Styles/Anchors.md, Styles/Borders.md, Styles/Corners.md, - Styles/Fill.md, Styles/Line.md, Styles/Padding.md, Styles/TextRendering.md - Framework/index.md, Framework/Canvas.md, Framework/CustomControlContext.md, - Framework/CustomControlsCollection.md, Framework/CustomControlTimer.md, - Framework/CustomFormContext.md, Framework/ICustomControl.md, - Framework/ICustomForm.md, Framework/SerializeInfo.md - Enumerations/index.md, Enumerations/BorderStyle.md, Enumerations/ColorRGBA.md, - Enumerations/CornerShape.md, Enumerations/Customtate.md, Enumerations/DockMode.md, - Enumerations/FillPattern.md, Enumerations/FontWeight.md, Enumerations/PixelCount.md, - Enumerations/PointSize.md, Enumerations/StartupPosition.md, Enumerations/TextAlignment.md, - Enumerations/TextOverflowMode.md, Enumerations/WindowState.md -``` - -**Naming:** - -- Folder / URL segment: `CustomControls/` (drops the "Package" suffix; collapses the two source packages, same simplification WebView2 used). -- Index title: `CustomControls Package` — the ` Package` convention. -- Permalinks: `/tB/Packages/CustomControls/` for the landing; `/tB/Packages/CustomControls/` and `/tB/Packages/CustomControls//` for single-file vs folder-style controls; `/tB/Packages/CustomControls/Styles/`, `/tB/Packages/CustomControls/Framework/`, `/tB/Packages/CustomControls/Enumerations/` for the three sub-groups. -- `parent: CustomControls Package` on every child page (matching the index `title:`). -- The `Styles/`, `Framework/`, `Enumerations/` index pages set `parent: CustomControls Package` and `has_children: true`; their children set `parent: ` (the grouped-page pattern). Mirror exactly the structure WebView2 uses for its `Enumerations/`. - -**License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2022) — same situation as WebView2Package and Assert. Pages are fully original; **omit** the `vba_attribution: true` flag. - -### cefPackage (CEF) - -Layout of `..\tb-export\NewProject\Packages\cefPackages\Sources\`: - -- `CefControl.twin` — contains three classes: the private `CefBrowserBaseCtl` (where every event / method / property is declared), the private `CefEnvironmentOptions` (a bare-field options class), and the public `CefBrowser` control which only inherits from `CefBrowserBaseCtl` and sets the design-time icon. This single file is the entire user-facing surface of the package. -- `CefControlGlobalWnd.twin` — private internal message-window plumbing; no doc page. -- `APIs.twin` — private Win32 API declarations; no doc page. -- `MainModule.twin` — `PreSubMain` module that intercepts CEF sub-process launches; no doc page. -- `Registry.twin` — Win32 registry helpers; no doc page. -- `SpecialFolders.twin` — `KnownFolders_CSIDLs` enum + `GetSpecialFolder` helper; `Private Module`, no doc page (used only by the sample project, not exported from the package). -- `CEF/Aliases.twin`, `CEF/ApiEntryPoints.twin`, `CEF/Globals.twin`, `CEF/Initialize.twin`, `CEF/Misc.twin` — `Private Module` implementation detail; no doc pages. -- `CEF/Enums/_cef_*_t.twin` — internal C-ABI enums wrapped in `Private Module _cef_*_t`; one file (`_cef_log_severity_t.twin`) also declares the user-facing `CefLogSeverity` enum. The other 29 `_cef_*_t` enums never surface in the public API and get **no doc page**. -- `CEF/Structs/_cef_*_t.twin` — internal C-ABI structs (`cef_browser_settings_t`, `cef_pdf_print_settings_t`, …) wrapped in `Private Module`; no doc pages. `Structs/TODO.twin` declares an empty placeholder `Class CefRequestHeaders` — also no doc page (see the alias note below). -- `CEF/CrossProcessIPC/BrowserOM.twin`, `RendererOM.twin`, `RendererAsyncOM.twin` — `Private Class` (or unmarked-but-effectively-private) classes that broker IPC between the host and the CEF browser / renderer processes; no doc pages. **Exception:** `BrowserOM.twin` declares the user-facing `cefPrintOrientation` enum inline (used by `CefBrowser.PrintToPdf`); document it under `Enumerations/`. -- `CEF/Implementations/*.twin` — every file is `Private Class` / `Private Module`; CEF callback handlers (`Client`, `ClientLifeSpanHandler`, `ClientLoadHandler`, `AppRender…`, the `Exposed*Javascript*` and `*Task` classes, the `PrintToPDFCallback`, …). All implementation detail; no doc pages. - -Public user-facing surface (one control + one options class + two enums): - -| Symbol | Kind | Role | -|------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------| -| `CefBrowser` | Class (inherits `CefBrowserBaseCtl`) | the control itself, tagged `[WindowsControl("/Miscellaneous/cef64.png")]` | -| `CefEnvironmentOptions` | `Private Class`, exposed | reached via `Public EnvironmentOptions As CefEnvironmentOptions = New CefEnvironmentOptions` on the control | -| `CefLogSeverity` | `Enum` (in `_cef_log_severity_t.twin`)| used by `CefEnvironmentOptions.LogSeverity` | -| `cefPrintOrientation` | `Enum` (in `BrowserOM.twin`) | used by the `Orientation` parameter of `CefBrowser.PrintToPdf` | - -`CefBrowserBaseCtl` is `Private Class` but is where every public member is *declared* — `CefBrowser` itself adds nothing beyond inheriting from it. Document everything on the `CefBrowser/index.md` page (folder-style, parallel to `WebView2/`) and treat the base class as an internal split that doesn't surface. - -`CefBrowser` inherits from `VB.BaseControlRectDockable`, so its Properties listing must fold in the dockable-rect surface (`Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, ...) the same way VB-package control pages and CustomControls control pages do. - -`CefBrowserRequestHeaders` is declared at the top of `CefControl.twin` as `Alias CefBrowserRequestHeaders As Object` and appears in the `NavigationStarting` event signature. The underlying `Class CefRequestHeaders` lives at the bottom of `Structs/TODO.twin` with an empty body — it's a placeholder for a future header collection. **No doc page;** mention on the `NavigationStarting` event entry that the parameter is currently typed `Object` and reserved for future use. - -The `CefBrowser` public surface, from `grep '^\s*Public' CefControl.twin` plus a walk through `CefBrowserBaseCtl`: - -- **Events (12):** `Create`, `Ready`, `Error`, `NavigationStarting`, `NavigationComplete`, `SourceChanged`, `DocumentTitleChanged`, `DOMContentLoaded`, `PrintToPdfCompleted`, `PrintToPdfFailed`, `JsAsyncResult`, `JsMessage`. -- **Methods (14):** `Initialize`, `Navigate`, `NavigateToString`, `Reload`, `GoBack`, `GoForward`, `ExecuteScript`, `JsRun`, `JsRunAsync`, `PostWebMessage`, `SetVirtualHostNameToFolderMapping`, `ClearVirtualHostNameToFolderMapping`, `OpenDevToolsWindow`, `PrintToPdf`. -- **Properties (12):** `DocumentURL` (Get/Let), `DocumentTitle` (Get), `ZoomFactor` (Get/Let), `UserAgent` (Get/Let), `CanGoBack` (Get), `CanGoForward` (Get), `CefMajorVersion` (Get), `Visible` (Get/Let), `hWnd` (Get), `Parent` (Get), `EnvironmentOptions` (field), `CreateInitialized` (field, `Boolean`, defaults `True`). Plus a `Hidden` `Align` (Get/Let) inherited boilerplate. Plus the inherited rect-dockable surface (`Name`, `Left`, `Top`, `Width`, `Height`, `Anchors`, `Dock`, …). - -`CefEnvironmentOptions` is four bare `Public` fields: - -| Field | Type | -|----------------------------|------------------| -| `BrowserExecutableFolder` | `String` | -| `UserDataFolder` | `String` | -| `LogFilePath` | `String` | -| `LogSeverity` | `CefLogSeverity` | - -`CefLogSeverity` members: `CefLogDisable = 0`, `CefLogVerbose = 1`, `CefLogInfo = 2`, `CefLogWarning = 3`, `CefLogError = 4`, `CefLogFatal = 5`. - -`cefPrintOrientation` members: `cefPrintPortrait = 0`, `cefPrintLandscape = 1`. - -**WebView2-parity gap list** (call out on `CEF/index.md`, drawn from `Sources\Example1..4.twin` where these are commented out as *"Sorry, this feature is not yet available in the CEF package"*): - -- Methods: `OpenTaskManagerWindow`, `AddObject` (host-object publication), the request-filter machinery (`AddWebResourceRequestedFilter`). -- Events: `AcceleratorKeyPressed`, `PermissionRequested`, `WebResourceRequested`, `ProcessFailed`, `ScriptDialogOpening`, `UserContextMenu`, `SuspendCompleted`, `SuspendFailed`, `DownloadStarting`, `NewWindowRequested`. - -`CefBrowser.NavigationComplete` carries `IsSuccess` and `WebErrorStatus` parameters, but the source (`OnNavigationComplete_UI` in `CefControl.twin`) currently hard-codes `IsSuccess = True` and `WebErrorStatus = 0` with `FIXME` comments — note this on the event page. - -**Multi-version source.** The same `.twin` sources compile against three CEF runtimes (v49 / v109 / v145) selected via the `CEF_VERSION` conditional-compilation argument on the project. At runtime, `CefBrowser.CefMajorVersion` returns the value picked at compile time. The user picks a runtime at deploy time by downloading the matching ZIP from `github.com/twinbasic/cef-runtimes` and extracting to `%LocalAppData%\twinBASIC_CEF_Runtime\`, or by overriding `CefBrowser.EnvironmentOptions.BrowserExecutableFolder` before / during the `Create` event. The wiki entry will redirect to the new docs, so the runtime download + version-picking section lives on `CEF/index.md`. - -#### Doc-side layout (folders / files) - -Six pages total: - -``` -docs/Reference/CEF/ - index.md ← package landing; intro + WebView2 comparison + version table + runtime install + WebView2-parity gap list + class & enum lists - CefBrowser/index.md ← the control (folder-style, like WebView2/WebView2/index.md) - CefBrowser/EnvironmentOptions.md ← parallel to WebView2/WebView2/EnvironmentOptions.md - Enumerations/index.md - Enumerations/CefLogSeverity.md - Enumerations/cefPrintOrientation.md -``` - -**Naming:** - -- Folder / URL segment: `CEF/` (uppercase acronym, matches the wiki spelling; drops the `Package` suffix, same simplification as WebView2 and Assert). -- Index title: `CEF Package` — the ` Package` convention. -- Permalinks: `/tB/Packages/CEF/` for the landing, `/tB/Packages/CEF/CefBrowser/` for the control (folder-style), `/tB/Packages/CEF/CefBrowser/EnvironmentOptions` for the sub-page, `/tB/Packages/CEF/Enumerations/` for the enums. -- `parent: CEF Package` on every top-level child page. The two enum pages set `parent: Enumerations` and `grand_parent: CEF Package` (the grouped-page pattern; mirror exactly the structure WebView2 uses for its `Enumerations/`). The `EnvironmentOptions` sub-page sets `parent: CefBrowser` and `grand_parent: CEF Package`. - -**License:** MIT (Wayne Phillips T/A iTech Masters) — same situation as WebView2Package, Assert, and CustomControls. The Settings file doesn't carry an explicit `licence:` field but every other package by this author is MIT and the wiki implies the same. Pages are fully original; **omit** the `vba_attribution: true` flag. - -### WinEventLogLib - -Layout of `..\tb-export\NewProject\Packages\WinEventLogLib\Sources\` is flat — four `.twin` files plus three text files (`_README.txt`, `_LICENCE.txt`, `_RELEASE_HISTORY.txt`): - -- `APIs.twin` — `Private Module EventLogAPIs` wrapping six `advapi32.dll` entry points (`RegCreateKeyExW`, `RegSetValueExW`, `RegCloseKey`, `RegDeleteKeyExW`, `RegisterEventSourceW`, `ReportEventW`). No doc page. -- `Constants.twin` — `Private Module EventLogConstants` carrying `Public Enum EventLogTypeConstants` (`vbEventLogTypeSuccess`, `vbEventLogTypeAuditFailure`, `vbEventLogTypeAuditSuccess`, `vbEventLogTypeError`, `vbEventLogTypeWarning`). The module is `Private`, so the enum does not surface in the public API; no doc page. -- `EventLog.twin` — the generic `Public Class EventLog(Of T1, T2)`. The only user-facing class. -- `Helper.twin` — two modules: `Public Module EventLogHelperPrivate` (one helper `VariantArrayToStringArray`, used only by `EventLog.LogArray` internally — its name signals *intended* private but the module is declared `Public`; treat as internal and skip) and `Public Module EventLogHelperPublic` (one user-facing helper `RegisterEventLogInternal`). - -Public user-facing surface (one generic class + one helper module): - -| Symbol | Kind | Role | -|---------------------------------|---------------------|---------------------------------------------------------------------------------------| -| `EventLog(Of T1, T2)` | Generic class | Main user-facing class. `T1` is the event-ID enum, `T2` is the category enum. | -| `EventLogHelperPublic` | Public module | Holds the low-level `RegisterEventLogInternal` helper. | -| `RegisterEventLogInternal` | `Sub` on the module | Registry-write helper; `EventLog.Register()` is the normal entry point. | - -`EventLog(Of T1, T2)` public members: - -- `Public Sub New(LogName As String)` — constructor. `LogName` is either a leaf name (`"MyService"`, registered under `Application\MyService`) or a full path (`"System\MyService"`, registered under `System\MyService`). -- `Public Sub LogSuccess(ByVal EventId As T1, ByVal CategoryId As T2, ParamArray AdditionalStrings())` — writes an **Information**-type event (`EVENTLOG_SUCCESS = 0`). The name "Success" is the Win32 SDK constant's literal name, *not* the audit-success category — the underlying event type is **Information**. -- `Public Sub LogFailure(ByVal EventId As T1, ByVal CategoryId As T2, ParamArray AdditionalStrings())` — writes an **Error**-type event (`EVENTLOG_ERROR_TYPE = 1`). -- `Public Sub Register()` — writes the registry entries under `HKLM\SYSTEM\CurrentControlSet\Services\EventLog\…` to declare this EXE as the message provider for the source. Calls `RegisterEventLogInternal(LogName, GetDeclaredMaxEnumValue(Of T2))` — the category count is derived from `T2`'s declared maximum value at compile time. - -Class-level decoration on `EventLog`: `[COMCreatable(False)]`, `[ClassId("4AEA12E8-…-EAEAEAEAEAEA")]` (the `EA` suffix triggers special compiler handling for generic classes). The `[Description]` attribute on the class is the basis for the page intro: *"This is the main event log (generic) class."* - -`EventLogHelperPublic` public members: - -- `Public Sub RegisterEventLogInternal(ByVal LogPath As String, ByVal CategoryCount As Long)` — the registry-write helper. Prepends `"Application\"` to *LogPath* if no backslash is present, opens `HKLM\SYSTEM\CurrentControlSet\Services\EventLog\` with `KEY_WRITE`, then writes `EventMessageFile` and `CategoryMessageFile` (both set to `App.ModulePath`) and `CategoryCount`. Requires admin rights (registry writes to HKLM). Raises run-time error 5 with the message *"Failed to register event log source (\)"* if the open fails. Normally callers use `EventLog.Register()`, which fills *CategoryCount* automatically. - -**Gaps and quirks** to surface on the docs (drawn from a static read of the source): - -- The `EventLogTypeConstants` enum has five values (`Success`, `Warning`, `Error`, `AuditSuccess`, `AuditFailure`) but the public class only exposes Information and Error event types — Warning and Audit events are not currently reachable. -- Method names follow the Win32 SDK constants verbatim: `LogSuccess` writes an *Information* event (because `EVENTLOG_SUCCESS = 0` is the Win32 spelling for the information type), and `LogFailure` writes an *Error* event. Call this out on the per-method entries. -- Message resources: the registry entries point at `App.ModulePath` (the running EXE) for both `EventMessageFile` and `CategoryMessageFile`. Windows therefore expects a message-table resource keyed by the `T1` and `T2` enum values to be embedded in the EXE. The `.twin` source does not itself synthesise that resource — there is no `mc.exe` invocation or message-table emit visible in `WinEventLogLib\Sources\`; whatever mechanism populates the resource sits in the compiler's special-handling path for the `[ClassId("…EAEAEAEAEAEA")]` magic-byte pattern. The docs describe what Windows expects without making strong claims about how the compiler delivers it. -- `Register()` requires elevation. Normal usage is to call it once during install (from an elevated installer), then call `LogSuccess` / `LogFailure` at runtime without elevation. -- The package is described in `_README.txt` with a copy-pasted "NAMED PIPES PACKAGE" header (clearly an unintentional carry-over from another sister package); the body correctly says *"A simple framework for creating Windows event log entries from twinBASIC"*. Use the body, not the header. - -#### Canonical usage idiom — composition-delegation onto a service class - -`tbServiceTest2` (`..\tbrepro\winlibs\tbServiceTest2\Sources\`) shows the package's intended usage pattern, which is *not* obvious from the bare API: - -```tb -Class TBSERVICE001 - Implements ITbService - Implements EventLog(Of MESSAGETABLE.EVENTS, MESSAGETABLE.CATEGORIES) Via _ - EventLog = New EventLog(Of MESSAGETABLE.EVENTS, MESSAGETABLE.CATEGORIES)("Application\" & CurrentComponentName) - … - LogSuccess(service_started, status_changed, CurrentComponentName) ' surfaces directly -End Class -``` - -The `Implements Via = ` form is twinBASIC's composition-delegation syntax (see [`docs/Features/Language/Delegation.md`](docs/Features/Language/Delegation.md) if/once that page exists, or the [`CustomControls` mixin pattern](docs/Reference/CustomControls/index.md) for an analogous use). The class declares it `Implements EventLog(Of …)` and gives the compiler a private field plus a constructor expression; the compiler then auto-forwards every `Public` member of `EventLog` (`LogSuccess`, `LogFailure`, `Register`) through that field. The result: a service class that *contains* an `EventLog` instance and exposes its logging methods as if they were its own. - -Surface this on the `EventLog` page (and on the package index) as the **recommended pattern** for service / long-running classes. Spell out: - -- The constructor expression evaluates *once* (the first time the delegating class is instantiated, per twinBASIC's `Implements ... Via` semantics). -- The `T1` / `T2` type arguments must be identical at the `Implements` declaration and the constructor (the compiler enforces this). -- The `LogPath` is typically `"Application\" & CurrentComponentName` — `CurrentComponentName` is the compile-time class name, so the log path automatically tracks renames. -- The delegating class transparently inherits all three of `LogSuccess` / `LogFailure` / `Register`. Calling code can use them unqualified. - -#### Message-table backing: `[PopulateFrom("json", …)]` on the enums - -The `T1` / `T2` enums are typically auto-populated from a JSON resource via the `[PopulateFrom]` attribute. `tbServiceTest2`'s `Sources\MISC\MESSAGETABLE.twin`: - -```tb -Module MESSAGETABLE - [PopulateFrom("json", "/Resources/MESSAGETABLE/Strings.json", "events", "name", "id")] - Enum EVENTS - End Enum - - [PopulateFrom("json", "/Resources/MESSAGETABLE/Strings.json", "categories", "name", "id")] - Enum CATEGORIES - End Enum -End Module -``` - -…with `Resources\MESSAGETABLE\Strings.json`: - -```json -{ - "events": [ - { "id": -1073610751, "name": "service_started", "LCID_0000": "%1 service started" }, - { "id": -1073610750, "name": "service_startup_failed", "LCID_0000": "%1 service startup failed" }, - … - ], - "categories": [ - { "id": 1, "name": "status_changed", "LCID_0000": "Status Changed" } - ] -} -``` - -Two things are happening here: - -1. **The enum bodies are populated at compile time** — `Enum EVENTS` starts empty in the source, but after compilation it has members `service_started = -1073610751`, `service_startup_failed = -1073610750`, … (one per `"events"` entry in the JSON, keyed `name → id`). -2. **The same JSON is consumed by the compiler's `mc.exe`-equivalent** that emits the message-table resource into `App.ModulePath`. The `LCID_0000` strings are the message-table entries, and the `%1`, `%2`, … placeholders are filled at log time from the `AdditionalStrings` `ParamArray` to `LogSuccess` / `LogFailure`. The `CategoryCount` registry value (written by `Register()`) is the highest declared `id` in the `categories` block, which is what `GetDeclaredMaxEnumValue(Of T2)` recovers at compile time. - -So the round-trip is: JSON → compile-time enum population + message-table resource emission → registry entries that point Windows at the EXE → runtime `LogSuccess(EventId, CategoryId, …)` writes an event the Event Viewer can format using the embedded message-table strings. - -Surface this on the index page (under "Setting up message resources" or similar) with the JSON skeleton and the cross-reference to `[PopulateFrom]` (which is documented under `docs/Features/`, not in the reference set — link to that page if it exists, otherwise describe in-place). - -The negative event-ID values in the JSON (`-1073610751`) are the standard Win32 event-ID encoding: the high bits encode severity (`0xC0000000` = Error), facility (`0x...`), and customer bit. Don't unpack this on the docs; just note that *"event IDs follow the Win32 documented encoding — see Microsoft's 'Event Identifiers' reference"*. - -#### Why `T1` / `T2` and not separate `EventIds` / `Categories` classes - -A class can only `Implements EventLog(Of T1, T2) Via …` *once*. If a service needs events from multiple unrelated message tables, it can compose multiple `EventLog` instances **as named fields** (no `Via`), accepting a small loss of ergonomics (calls become `MyEventLog.LogSuccess(…)` instead of `LogSuccess(…)`). Surface this as a one-line note on the index — most services share a single `MESSAGETABLE` module across all their classes (as the example does), so the limitation rarely bites. - -**Layout decision** — three pages total, mirroring the small-package approach used by Assert: - -- `docs/Reference/WinEventLogLib/index.md` — landing page: intro, lifecycle (define enums → instantiate → Register once → LogSuccess / LogFailure), gaps and quirks, the class and module lists. -- `docs/Reference/WinEventLogLib/EventLog.md` — the generic class, single-file (no sub-pages — the surface is small). -- `docs/Reference/WinEventLogLib/EventLogHelperPublic.md` — the helper module, single-file (one Sub listed under a `## RegisterEventLogInternal` heading, same shape as Assert per-member sections). - -**Naming:** - -- Folder / URL segment: `WinEventLogLib/` (matches the source-side package name; no `Package` suffix to drop, the package isn't named with that suffix in `Settings`). -- Index title: `WinEventLogLib Package` — the ` Package` convention used by every other package landing. -- Permalinks: `/tB/Packages/WinEventLogLib/` for the landing; `/tB/Packages/WinEventLogLib/EventLog` and `/tB/Packages/WinEventLogLib/EventLogHelperPublic` for the two member pages. -- `parent: WinEventLogLib Package` on each child page (matching the index `title:`, the same split every other package uses). - -**License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2025; first release v0.1, 04-FEB-2025) — same situation as WebView2Package, Assert, CustomControls, and CEF. Pages are fully original content; **omit** the `vba_attribution: true` flag. - -### WinNamedPipesLib - -Layout of `..\tb-export\NewProject\Packages\WinNamedPipesLib\Sources\` is flat — seven `.twin` files plus three text files (`_README.txt`, `_LICENCE.txt`, `_RELEASE_HISTORY.txt`): - -- `APIs.twin` — `Private Module NamedPipesAPIs` wrapping Win32 declarations from `kernel32.dll`, `user32.dll`, and `oleaut32.dll` (`CreateNamedPipeW`, `ConnectNamedPipe`, `ReadFile` / `WriteFile`, `CreateIoCompletionPort`, `GetQueuedCompletionStatus`, `PostQueuedCompletionStatus`, `CreateThread`, …) plus the supporting `Type` declarations (`POINT`, `MSG`, `SAFEARRAYBOUND`, `SAFEARRAY_1D`, `OVERLAPPED_CUSTOM`, `FILETIME`, `WIN32_FIND_DATAW`) and a 32/64-bit `SetWindowLongPtrW` alias. No doc page. -- `Constants.twin` — `Private Module NamedPipesConstants` carrying Win32 constants (`PIPE_ACCESS_DUPLEX`, `PIPE_TYPE_MESSAGE`, `FILE_FLAG_OVERLAPPED`, `ERROR_IO_PENDING`, `ERROR_MORE_DATA`, …) and the package-internal `Enum OverlappedTypeConstants` (`tbOverlappedConnect`, `tbOverlappedRead`, `tbOverlappedWrite`). Module is private, so none of these surface in the public API; no doc page. -- `Helper.twin` — `Private Module NamedPipesHelper` with `ObjectFromPointer_*` and `ObjPtrRef` / `ObjReleaseRef` reference-counting helpers used internally to ferry COM pointers across IOCP worker threads. Module is private; no doc page. -- `NamedPipeServer.twin` — `Public Class NamedPipeServer`. The server-side entry point. Also declares the private `INamedPipeServerInternal` interface (implementation detail; no doc page). -- `NamedPipeServerConnection.twin` — `Public Class NamedPipeServerConnection`. Per-client connection object surfaced through `NamedPipeServer` events. Also declares the private `INamedPipeServerConnectionInternal` interface (no doc page). -- `NamedPipeClientManager.twin` — `Public Class NamedPipeClientManager`. Owns the client-side IOCP machinery and the `Connect` / `Stop` / `FindNamedPipes` entry points. Also declares the private `INamedPipeClientManagerInternal` interface (no doc page). -- `NamedPipeClientConnection.twin` — `Public Class NamedPipeClientConnection`. Returned by `NamedPipeClientManager.Connect`. Carries the `Connected` / `Disconnected` / `MessageReceived` / `MessageSent` events and the `AsyncWrite` / `AsyncRead` / `AsyncClose` methods. Also declares the private `INamedPipeClientConnectionInternal` interface (no doc page). - -The four classes are each tagged `[COMCreatable(False)]` — only the manager / server classes can be instantiated by user code (with `New`); the two `Connection` classes are constructed internally and handed back through events / return values. - -The private `INamedPipe*Internal` interfaces serve a marshalling-only role: each public class implements its matching internal interface so that the IOCP worker threads can refcount and dispatch through `stdole.IUnknown` without taking a strong reference to the parent class. They are *not* user-facing surface and get no documentation page. - -Public user-facing surface (four classes — two on each side): - -| Class | Role | -|-----------------------------|---------------------------------------------------------------------------------------------------------------| -| `NamedPipeServer` | The server. User-instantiated. Sets `PipeName`, calls `Start`, listens for events; one server hosts many clients. | -| `NamedPipeServerConnection` | One server-side per-client connection. Surfaced through `NamedPipeServer` events; carries `AsyncRead` / `AsyncWrite` / `AsyncClose`. | -| `NamedPipeClientManager` | The client-side coordinator. User-instantiated. Owns the IOCP worker threads; the `Connect` method returns a `NamedPipeClientConnection`. | -| `NamedPipeClientConnection` | One client-side connection. Returned by `NamedPipeClientManager.Connect`; carries `Connected` / `Disconnected` / `MessageReceived` / `MessageSent` events and `AsyncRead` / `AsyncWrite` / `AsyncClose`. | - -#### `NamedPipeServer` public members - -Tagged `[COMCreatable(False)]`, `[InterfaceId(...)]`, `[EventInterfaceId(...)]`, `[ClassId(...)]`. No `[Description("...")]` on the class itself. - -**Public fields** (each carries a `[Description("...")]`): - -- `PipeName As String` — *"the discoverable pipe name"*. Set this before `Start()` or `Start()` raises run-time error 5 (*"cannot start without specifying a pipe name"*). The Win32 pipe namespace path is `\\.\pipe\` (the package prepends `\\.\pipe\` itself). -- `NumThreadsIOCP As Long = 1` — *"the number of IOCP worker threads that will be created"*. Read once when `Start()` is called; the in-source `FIXME` notes that this should become read-only once started. -- `FreeThreadingEvents As Boolean = False` — *"set to TRUE to allow the server events ClientConnected / ClientReceivedDataAsync etc to be fired directly from the IOCP worker threads. set to FALSE to ensure the events get fired on the main UI thread."* The free-threaded path skips a Win32 message-loop round-trip; the marshalled path is safer because the events fire on the UI thread. -- `ContinuouslyReadFromPipe As Boolean = True` — *"set to TRUE to ensure ClientReceivedDataAsync events always fire without having to call AsyncRead manually."* When `False`, the consumer must call `AsyncRead` after each `ClientMessageReceived` to keep receiving. -- `MessageBufferSize As Long = 131072` — *"sets the initial size for ReadFile() buffers. does not affect the maximum message receive size, but can affect performance."* On `ERROR_MORE_DATA` the IOCP loop allocates a larger overflow buffer and re-issues the read, so messages larger than this size do work — but with one extra allocation per overflowed message. - -**Public events**: - -- `ServerReady()` — fires once after `Start()` when every IOCP worker has joined. -- `ClientConnected(Connection As NamedPipeServerConnection)` — a new client connection has completed. -- `ClientDisconnected(Connection As NamedPipeServerConnection)` — the connection has dropped and every outstanding async operation has returned. -- `ClientMessageReceived(Connection As NamedPipeServerConnection, ByRef Cookie As Variant, ByRef Data() As Byte)` — a message arrived. *Data* is a transient view over the IOCP read buffer (a hand-rolled `SAFEARRAY` whose backing memory is reused after the event); copy it if you need to keep it past the event handler. -- `ClientMessageSent(Connection As NamedPipeServerConnection, ByRef Cookie As Variant)` — a previously-issued `AsyncWrite` has completed. - -**Public methods**: - -- `Sub New()` — constructor; creates the hidden marshalling-window used for UI-thread event delivery. -- `Public Sub Start()` — creates the IOCP completion port and `NumThreadsIOCP` worker threads, then issues the first connection listener. Idempotent: calling `Start()` while already started is a no-op. -- `Public Sub Stop()` — cancels every outstanding I/O, joins the IOCP threads, closes pipe handles. Idempotent. Called automatically from `Class_Terminate`. -- `Sub AsyncBroadcast(ByRef Data() As Byte, Optional ByRef Cookie As Variant = Empty)` — issues `AsyncWrite` against every currently-connected `NamedPipeServerConnection`. -- `Public Sub ManualMessageLoopEnter()` / `Public Sub ManualMessageLoopLeave()` — drive a Win32 message loop manually (rare; only needed when the host process does not naturally pump messages — e.g. an unattended Windows service that wants the marshalled-event semantics rather than the free-threaded ones). `Leave` posts `WM_USER_QUITTING`, which `Enter` reads to break the loop. - -#### `NamedPipeServerConnection` public members - -Tagged `[COMCreatable(False)]`. Not directly user-instantiable. - -**Public fields**: - -- `Handle As LongPtr` — the underlying Win32 pipe handle. Exposed but not normally needed; useful for low-level operations or debugging. -- `IsOpening As Boolean` — true while `Open()` is in progress (race-condition window between adding to the linked list and finishing `ConnectNamedPipe`). -- `IsConnected As Boolean` — true between the client connecting and the connection dropping. -- `CustomData As Variant` — *"free for use"*: opaque per-connection slot the consumer can attach state to. - -**Public methods**: - -- `Sub New(...)` — internal constructor; takes the parent server + pipe info. Never called by user code. -- `Public Sub AsyncClose()` — cancels outstanding I/O and closes the pipe handle. Called automatically from `Class_Terminate`. -- `Public Sub AsyncWrite(ByRef Data() As Byte, Optional ByRef Cookie As Variant = Empty)` — writes a message back to this specific client. Returns immediately; `NamedPipeServer.ClientMessageSent` fires when the write completes. -- `Public Sub AsyncRead(Optional ByRef Cookie As Variant = Empty, Optional OverlappedStruct As LongPtr)` — manually issues a read. Only needed when `NamedPipeServer.ContinuouslyReadFromPipe = False`; otherwise the server keeps the read pump fed automatically. - -No public events — message-received and connection-dropped notifications come through the parent `NamedPipeServer`. The class declares an internal `INamedPipeServerConnectionInternal` interface that the IOCP loop uses for refcounting; that interface is `Private` and gets no doc page. - -#### `NamedPipeClientManager` public members - -Tagged `[COMCreatable(False)]`, `[InterfaceId(...)]`, `[EventInterfaceId(...)]`, `[ClassId(...)]`. - -**Public fields** (each carries `[Description("...")]`, mirror the server's): - -- `NumThreadsIOCP As Long = 1` -- `MessageBufferSize As Long = 131072` -- `FreeThreadingEvents As Boolean = False` -- `ContinuouslyReadFromPipe As Boolean = True` - -These four are read once on the first `Connect()` call and propagated to every `NamedPipeClientConnection` created through that manager; subsequent changes do not affect connections that already exist. Source comment in `NamedPipeClientConnection` confirms: *"tip: set it in NamedPipeClientConnections before Connect()"*. - -**Public methods**: - -- `Sub New()` — constructor; creates the hidden marshalling window. -- `Public Function Connect(ByVal PipeName As String) As NamedPipeClientConnection` — opens a connection to a server (`\\.\pipe\`). Lazy on first call: creates the IOCP port and the worker threads. Returns a connection object that fires `Connected` once the async `CreateFileW` completes. -- `Public Sub Stop()` — cancels every outstanding I/O on every managed connection, joins the IOCP threads, frees the resources. Idempotent. Called automatically from `Class_Terminate`. -- `Public Function FindNamedPipes(Optional Pattern As String = "*") As Collection` — enumerates the named pipes currently published on the local machine (via `FindFirstFileW("\\.\pipe\")`). Returns a `Collection` of `String`. Useful as a discovery helper before calling `Connect`. - -No events on the manager itself — per-connection events live on the returned `NamedPipeClientConnection` objects. - -#### `NamedPipeClientConnection` public members - -Tagged `[COMCreatable(False)]`, `[InterfaceId(...)]`, `[ClassId(...)]`, `[EventInterfaceId(...)]`. Not directly user-instantiable — `NamedPipeClientManager.Connect` returns it. - -**Public fields**: - -- `PipeName As String` — the pipe name the connection targets. -- `Handle As LongPtr` — the underlying Win32 file handle. Same caveats as on the server-side connection. -- `CustomData As Variant` — *"free for use"*. - -**Public events**: - -- `Connected()` — the async `CreateFileW` has succeeded. -- `Disconnected()` — the connection has dropped and every outstanding async operation has returned. -- `MessageReceived(ByRef Cookie As Variant, ByRef Data() As Byte)` — a message arrived. *Data* has the same transient-view semantics as on the server. -- `MessageSent(ByRef Cookie As Variant)` — a previously-issued `AsyncWrite` has completed. - -**Public methods**: - -- `Sub New(...)` — internal constructor; never called by user code directly. -- `Public Sub AsyncClose()` — **critical:** the README says *"you MUST call AsyncClose on the client side, otherwise the connection is left alive when the object goes out of scope"*. Surface this on every relevant page. -- `Public Sub AsyncWrite(ByRef Data() As Byte, Optional ByRef Cookie As Variant = Empty)` — sends a message to the server. -- `Public Sub AsyncRead(Optional ByRef Cookie As Variant = Empty, Optional OverlappedStruct As LongPtr)` — manually issues a read. Same gating as on the server-side: only call this when `ContinuouslyReadFromPipe = False`. - -**Documented gaps / TODOs from `_README.txt`** (surface on the landing page): - -- *"we need a method to allow closing a client connection from the server side"* — there is no `NamedPipeServerConnection.Disconnect` or `.Close` user-method today. The server can stop the whole pipe (`NamedPipeServer.Stop`) but cannot selectively drop one client. -- *"named pipe error should be raised via Error events (rather than throwing an error on the worker threads)"* — internal IOCP errors currently bubble up as VBA run-time errors on worker threads rather than as `Error` events. No `Error` event exists on any of the four classes yet. -- *"remove max size 131072 of messages"* — the `MessageBufferSize` initial-buffer default is 131072 bytes. The IOCP overflow path (`ERROR_MORE_DATA` → larger buffer → re-issue read) does handle larger messages, but there may be a hard cap somewhere the author wants to remove; surface this as *"see TODO list in `_README.txt`"* rather than making a stronger claim. -- *"currently a lot of duplicate code in server + client"* — internal-refactor note. **Not** surfaced on the docs. - -**Cookie pattern.** Every `AsyncRead` and `AsyncWrite` accepts an optional *Cookie* (`Variant`). Whatever the consumer passes in flows through the IOCP completion buffer and is handed back out on the matching `MessageReceived` / `MessageSent` event. This is the package's mechanism for correlating individual writes with their completion notifications when many are in flight. - -**`Data() As Byte` transience.** Inside `MessageReceived` / `ClientMessageReceived`, *Data* is **not** a real `Byte` array — it is a hand-rolled `SAFEARRAY` whose `pvData` field points at the IOCP overlapped buffer. The buffer is recycled back into a free-list at the end of the event handler. Copy the bytes out (`ReDim`-and-copy, or `CStrConv` for text payloads) if you need them after returning from the handler. The source uses `PutMemPtr(VarPtr(safeArrayPtr), VarPtr(safeArrayPsuedo))` and clears it afterwards — surface this lifetime caveat on every event-page entry that carries *Data*. - -**Hidden message window.** Each `NamedPipeServer` and `NamedPipeClientManager` instance creates an invisible `STATIC`-class window with a subclassed `WndProc`, used to marshal IOCP-thread completions back to the UI thread when `FreeThreadingEvents = False`. Mention this on each class's intro paragraph — it explains why the consumer's process must be pumping a message loop for the default event-delivery semantics to work, and why `ManualMessageLoopEnter` / `ManualMessageLoopLeave` exist on `NamedPipeServer` for service / console hosts. - -#### Canonical service-host idiom — `ManualMessageLoopEnter` paired with `ChangeState` - -`tbServiceTest2`'s `Sources\SERVICES\TBSERVICE001.twin` shows the standard pattern for a Windows service that hosts a `NamedPipeServer`. Surface this on the `NamedPipeServer.md` page (under a "Hosting inside a Windows service" sub-heading) and on the index landing: - -```tb -' On the service thread (ITbService.EntryPoint): -Set NamedPipeServer = New NamedPipeServer -NamedPipeServer.PipeName = "WaynesPipe_" & CurrentComponentName -ServiceManager.ReportStatus(vbServiceStatusRunning) - -NamedPipeServer.Start() -NamedPipeServer.ManualMessageLoopEnter() ' blocks until ManualMessageLoopLeave -NamedPipeServer.Stop() - -ServiceManager.ReportStatus(vbServiceStatusStopped) - -' On the dispatcher thread (ITbService.ChangeState): -Select Case dwControl - Case vbServiceControlStop, vbServiceControlShutdown - ServiceManager.ReportStatus(vbServiceStatusStopPending) - NamedPipeServer.ManualMessageLoopLeave() ' wakes the service thread -End Select -``` - -Key facts that aren't obvious from the per-method `[Description]`s: - -- The service-thread `EntryPoint` and the dispatcher-thread `ChangeState` are **different threads**. The `NamedPipeServer` member field is shared between them; the dispatcher-thread `ChangeState` calls `ManualMessageLoopLeave` on it to wake the service thread out of `ManualMessageLoopEnter`. -- `ManualMessageLoopLeave` is the **only** way to wake `ManualMessageLoopEnter` cleanly. There is no timeout, no second blocking primitive. If the service needs to react to other wake-up sources (paused state, custom control codes), it sets a shared flag *then* calls `ManualMessageLoopLeave` to break out, inspects the flag, and decides whether to re-enter the loop or proceed to shutdown. The `TBSERVICE002` variant in the same example demonstrates this with `IsPaused` / `IsStopping` shared `Public` fields and a `While IsStopping = False` outer loop. -- Pause / continue support uses the same pattern: `ChangeState` flips `IsPaused = True` and calls `ManualMessageLoopLeave`; the service thread sees the flag, reports `vbServiceStatusPaused`, enters a `Do While IsPaused : Sleep(500) : Loop`, then re-enters `ManualMessageLoopEnter` once `Continue` flips the flag back. -- `FreeThreadingEvents = False` (the default) is **required** for this pattern — events are marshalled to whichever thread is currently inside `ManualMessageLoopEnter`. Setting `FreeThreadingEvents = True` would deliver events on the IOCP worker thread instead and bypass the manual loop entirely (advanced; not the documented service idiom). - -The non-service equivalent — hosting the same `NamedPipeServer` inside a Form — is in `Sources\FORMS\InProcessNamedPipeServerForm.twin`: the Form's regular message loop pumps the marshalling window automatically, so the Form just calls `Server.Start()` in `Form_Load` and `Server.Stop` in `Form_Unload` without ever touching `ManualMessageLoopEnter` / `Leave`. Cross-reference both patterns on the `NamedPipeServer.md` page so the reader sees the choice point. - -#### PropertyBag as the canonical message carrier - -Every example serialises structured payloads through the pipe as a `PropertyBag.Contents` `Byte()`: - -```tb -' Sender: -Dim propertyBag As New PropertyBag -propertyBag.WriteProperty("CommandID", "WHAT_TIME_IS_IT") -propertyBag.WriteProperty("Data", payload) -SelectedNamedPipe.AsyncWrite propertyBag.Contents - -' Receiver (inside MessageReceived event): -Dim propertyBag As New PropertyBag -propertyBag.Contents = Data ' deep-copies the bytes; safe past the event handler -Dim commandID As String = propertyBag.ReadProperty("CommandID") -… -``` - -Two reasons this pattern matters and should be surfaced on the docs: - -1. **The transient-`Data()` problem is solved by `PropertyBag`.** Assigning to `PropertyBag.Contents` deep-copies the byte buffer; once the assignment returns, the original IOCP buffer can be recycled without invalidating the data. This is the cleanest answer to *"how do I keep the data past the event handler?"* — call out on every `MessageReceived` / `ClientMessageReceived` page entry as the recommended capture mechanism. -2. **`PropertyBag` provides typed multi-field payloads** without the consumer having to design a wire protocol. Both sides agree on the property names (`"CommandID"`, `"ResponseCommandID"`, `"ResponseData"`, `"Data"`) and `PropertyBag` handles the encoding / decoding. Cross-link [`PropertyBag` reference](docs/Reference/VBRUN/PropertyBag/index.md) from the index landing. - -Surface as the **recommended** carrier; nothing in the package mandates it, raw `Byte()` works too, but every worked example uses `PropertyBag` and the integration story reads much more cleanly with it. - -#### Discovery loop — `FindNamedPipes` - -`tbServiceTest2`'s `MainForm` shows the canonical client-side discovery pattern: a low-frequency `Timer` (the form uses `timerRefreshNamedPipes` with a multi-second interval) that calls `NamedPipeClients.FindNamedPipes("WaynesPipe_*")`, repopulates a `ListBox`, and preserves the user's current selection: - -```tb -For Each namePipeName In NamePipeClients.FindNamedPipes("WaynesPipe_*") - If namePipeName = NamedPipeSelected Then namedPipeSelectedIndex = Index - lstNamedPipes.AddItem(namePipeName) - Index += 1 -Next -``` - -Surface on the `NamedPipeClientManager.md` page (under the `FindNamedPipes` entry) as the recommended polling loop — the underlying `FindFirstFileW("\\.\pipe\…")` call is cheap enough to invoke every few seconds without measurable cost, and pipes appear / disappear too quickly for any event-driven discovery to be reliable. Don't claim there's no faster API; just say *"polling is the documented approach"*. - -#### Service-side broadcast - -`InProcessNamedPipeServerForm.twin` demonstrates `Server.AsyncBroadcast("BROADCAST")` (string coerced to `Byte()` via twinBASIC's implicit `String → Byte()` conversion). Useful when the same server has multiple concurrent connections and wants to push an update to all of them — the alternative is iterating over a user-maintained list of `NamedPipeServerConnection`s and calling `AsyncWrite` on each. The package handles the iteration internally. Mention on the `NamedPipeServer.AsyncBroadcast` entry. - -**Layout decision** — five pages total, one per public class plus the landing page: - -``` -docs/Reference/WinNamedPipesLib/ - index.md ← package landing; intro + IOCP model + cookie + transient-data caveat + gap list + class table - NamedPipeServer.md ← single-file: fields, events, methods - NamedPipeServerConnection.md ← single-file - NamedPipeClientManager.md ← single-file - NamedPipeClientConnection.md ← single-file -``` - -All four class pages are single-file (no folder-style — no natural sub-pages; the surface per class is medium-small, on the order of WebView2 wrapper classes). - -**Naming:** - -- Folder / URL segment: `WinNamedPipesLib/` (matches the source-side package name; no `Package` suffix to drop, same as `WinEventLogLib`). -- Index title: `WinNamedPipesLib Package` — the ` Package` convention. -- Permalinks: `/tB/Packages/WinNamedPipesLib/` for the landing; `/tB/Packages/WinNamedPipesLib/` for each of the four class pages. -- `parent: WinNamedPipesLib Package` on each child page (matching the index `title:`). - -**License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2025; first release v0.1, 04-FEB-2025) — same situation as WebView2Package, Assert, CustomControls, CEF, and WinEventLogLib. Pages are fully original content; **omit** the `vba_attribution: true` flag. - -### WinServicesLib - -Layout of `..\tb-export\NewProject\Packages\WinServicesLib\Sources\` is flat — eight `.twin` files plus three text files (`_README.txt`, `_LICENCE.txt`, `_RELEASE_HISTORY.txt`): - -- `APIs.twin` — `Private Module ServicesAPIs` wrapping fourteen `advapi32.dll` / `kernel32.dll` / `ole32.dll` / `oleaut32.dll` entry points (`StartServiceCtrlDispatcherW`, `OpenSCManagerW`, `CreateServiceW`, `RegisterServiceCtrlHandlerExW`, `SetServiceStatus`, `OpenServiceW`, `DeleteService`, `CloseServiceHandle`, `QueryServiceStatusEx`, `StartServiceW`, `ControlServiceExW`, `ChangeServiceConfig2W`, `CoInitializeEx`, `SysAllocStringPtr`) plus the supporting `Type` declarations (`SERVICE_STATUS`, `SERVICE_STATUS_PROCESS`, `SERVICE_CONTROL_STATUS_REASON_PARAMSW`, `SERVICE_CONFIG_DESCRIPTION`). No doc page. -- `Constants.twin` — two modules. `Private Module ServicesConstants` carries the Win32 access-flag constants (`SC_MANAGER_*`, `SERVICE_*` permission bits, `SERVICE_CONTROL_*` control codes, `SERVICE_ACCEPT_*` accepted-controls flags, etc.) plus the `SC_STATUS_TYPE` enum. **Public** module `ServicesConstantsPublic` carries the four user-facing enumerations (`ServiceTypeConstants`, `ServiceStartConstants`, `ServiceControlCodeConstants`, `ServiceStatusConstants`). The private module is internal; the public module's enums surface and need their own doc pages. -- `Helper.twin` — `Private Module ServicesHelper` with the IOCP-style trampoline (`ServiceControlHandlerCallback_Trampoline`) used in place of class-`AddressOf` plus a `VariantArrayToStringArray` helper. No doc page. -- `Interfaces.twin` — three interfaces: `Public Interface ITbService` (user-implemented), `Private Interface IServiceCreator` (internal — the public `ServiceCreator(Of T)` class implements it), `Private Interface IServiceManagerInternal` (internal). Only `ITbService` gets a doc page. -- `Services.twin` — the predeclared `Class Services` (no `Public`/`Private` modifier — `Class` defaults to public; tagged `[PredeclaredId]`, so it's used singleton-style as `Services.ConfigureNew`). The package's main entry point. -- `ServiceManager.twin` — `Public Class ServiceManager`. Per-service configuration + runtime status reporting. `[COMCreatable(False)]`. User code never instantiates this directly — it's returned by `Services.ConfigureNew()`. -- `ServiceCreator.twin` — `Public Class ServiceCreator(Of T)`. Generic factory `T → New T As ITbService`. `[COMCreatable(False)]`. Has the EA magic-byte `ClassId("66170220-FEF3-4257-8FBA-EAEAEAEAEAEA")` pattern, same compiler-special-handling as `WinEventLogLib`'s `EventLog(Of T1, T2)`. -- `ServiceState.twin` — `Class ServiceState` (no modifier — public by default). Read-only state snapshot for an installed service. `[COMCreatable(False)]`. Returned by `Services.QueryStateOfService`. - -Public user-facing surface (three concrete classes + one generic class + one interface + four enums): - -| Symbol | Kind | Role | -|---------------------------------|-----------------------|---------------------------------------------------------------------------------------------------| -| `Services` | `[PredeclaredId]` Class | The singleton coordinator: `ConfigureNew`, `RunServiceDispatcher`, `InstallAll`, `UninstallAll`, `LaunchService`, `ControlService`, `QueryStateOfService`, `GetConfiguredService`, `_NewEnum`. Used as `Services.X` without `New`. | -| `ServiceManager` | Class | Per-service configuration + runtime status reporting. Returned by `Services.ConfigureNew()`. | -| `ServiceCreator(Of T)` | Generic class | The dispatcher's factory: `T` must implement `ITbService`; `CreateInstance` returns `New T`. | -| `ServiceState` | Class | Read-only state snapshot. Constructor (called via `Services.QueryStateOfService(Name)`) queries the SCM. | -| `ITbService` | Public Interface | The contract every service class implements: `EntryPoint`, `StartupFailed`, `ChangeState`. | -| `ServiceTypeConstants` | Enum | `tbServiceTypeOwnProcess`, `tbServiceTypeShareProcess`, etc. | -| `ServiceStartConstants` | Enum | `tbServiceStartAuto`, `tbServiceStartOnDemand`, etc. | -| `ServiceControlCodeConstants` | Enum | `vbServiceControlStop`, `vbServiceControlPause`, `vbServiceControlContinue`, etc. | -| `ServiceStatusConstants` | Enum | `vbServiceStatusRunning`, `vbServiceStatusStartPending`, `vbServiceStatusStopped`, etc. | - -The two private interfaces (`IServiceCreator`, `IServiceManagerInternal`) are pure implementation detail — same situation as `WinNamedPipesLib`'s `INamedPipe*Internal` interfaces. **No doc page**, and don't surface the underscored implementing members on the concrete classes either. - -The two `Private Module` declarations (`ServicesAPIs`, `ServicesConstants`) and the `Private Module ServicesHelper` are all internal — **no doc page**. - -#### `Services` public members - -`[PredeclaredId]` Class. The compiler instantiates a singleton named `Services` at program start; user code calls `Services.X` directly without `New`. The class also doubles as an enumerable collection of the `ServiceManager` instances that have been configured (`For Each manager In Services`). - -**Public methods**: - -- `Function ConfigureNew() As ServiceManager` — *"Use this method to configure a service. Usually used during app startup."* Allocates a new `ServiceManager`, adds it to the internal collection, returns it. Typical use: `With Services.ConfigureNew : .Name = "MyService" : .InstanceCreator = New ServiceCreator(Of MyServiceClass) : End With`. -- `Sub RunServiceDispatcher()` — *"This method hands over to the OS for managing the starting/stopping of services via the main thread. This is a BLOCKING call, until the OS wants to shutdown the service EXE."* Builds a `SERVICE_TABLE_ENTRYW` from every configured `ServiceManager` and calls `StartServiceCtrlDispatcherW`. Returns only when the OS terminates the service host. Raises run-time error 5 if the dispatcher cannot start (typically when the EXE was launched normally rather than by the SCM). -- `Sub InstallAll()` — *"This method tries to register ALL of the configured services onto the system."* Iterates the configured `ServiceManager`s and calls `.Install()` on each. Requires admin. -- `Sub UninstallAll()` — *"This method tries to unregister ALL of the configured services off the system."* Iterates and calls `.Uninstall()` on each. Requires admin. -- `Function QueryStateOfService(ByVal ServiceName As String) As ServiceState` — returns a fresh `ServiceState` snapshot. Raises run-time error 5 if the service isn't installed. -- `Sub LaunchService(ByVal ServiceName As String, ParamArray LaunchArgs())` — start an installed service by name, optionally passing launch arguments through to its `ServiceManager.LaunchArgs()` field. Wraps `OpenServiceW(SERVICE_START)` + `StartServiceW`. Raises run-time error 5 on permission / not-installed / already-running. -- `Sub ControlService(ByVal ServiceName As String, ByVal ControlCode As ServiceControlCodeConstants)` — send an SCM control code to a running service. The required SCM permission is derived from the control code automatically (`SERVICE_STOP` for `vbServiceControlStop`, `SERVICE_PAUSE_CONTINUE` for the pause / continue / netbind / paramchange family, `SERVICE_INTERROGATE` for `vbServiceControlInterrogate`, `SERVICE_USER_DEFINED_CONTROL` for codes 128–255, `SERVICE_ALL_ACCESS` otherwise). For `vbServiceControlStop` the wrapper fills `SERVICE_CONTROL_STATUS_REASON_PARAMSW` with `SERVICE_STOP_REASON_FLAG_PLANNED | MAJOR_NONE | MINOR_NONE` — there is a `FIXME` to allow customising the reason code. - -**Public properties**: - -- `Property Get GetConfiguredService(ByVal Name As String) As ServiceManager` — look up a previously-configured `ServiceManager` by its `Name`. Raises run-time error 5 if not found. (Despite the `Get` syntax the lookup is parameterised by name; it's a property in name only.) - -**Public enumerator**: - -- `Property Get _NewEnum() As Variant` — `[Enumerator]`-tagged; enables `For Each manager In Services` over the configured `ServiceManager`s. *"Provides For-Each support for the services collection, exposing each configured service as a ServiceManager instance."* - -#### `ServiceManager` public members - -`[COMCreatable(False)]`. User code never instantiates this directly — `Services.ConfigureNew()` returns it. The source-side constructor carries `[Description("For internal use. Dont create instances of ServiceManager manually, use Services.ConfigureNew instead")]` — surface that on the page intro. - -**Public field** (one): - -- `LaunchArgs() As String` — populated by `ServiceEntryPoint` from the `argv` the SCM hands over. `LaunchArgs(0)` is the *first user-supplied* argument (the SCM-supplied service name at `argv[0]` is dropped). The example uses it to gate startup: `If Join(ServiceManager.LaunchArgs) <> "MySecretPassword" Then …`. - -**Public properties** (each carries a `[Description("...")]`): - -- `InstanceCreator As IServiceCreator` (Get / Let / Set) — *"Set this to an instance of the ServiceCreator class to allow the OS to launch the instance of your service."* Typically `.InstanceCreator = New ServiceCreator(Of MyServiceClass)`. -- `Name As String` (Get / Let) — *"The name of the service, as listed in the OS services database."* -- `Description As String` (Get / Let) — *"The description of the service, as listed in the OS services database."* Applied via `ChangeServiceConfig2W(SERVICE_CONFIG_DESCRIPTION)` on every successful `Install()`. -- `Type As ServiceTypeConstants` (Get / Let) — *"The type of the service, typically `tbServiceTypeOwnProcess` or `tbServiceTypeShareProcess`."* Defaults to `tbServiceTypeOwnProcess`. -- `InstallStartMode As ServiceStartConstants` (Get / Let) — *"The start-mode of the service, typically `tbServiceStartOnDemand` or `tbServiceStartAuto`."* Defaults to `tbServiceStartOnDemand`. -- `InstallCmdLine As String` (Get / Let) — *"The command line arguments passed to the service EXE when the OS launches the service."* Defaults to `""""""`. **Usually overridden to add a discriminator argument** like `-startService` so the EXE knows whether it was launched by the SCM (run dispatcher) or by a user (show UI). Example: `.InstallCmdLine = """" & App.ModulePath & """ -startService"`. -- `DependentServices() As Variant` (Get / Let) — *"A list of dependent services that this service requires to be started before this service is launched (dependent services are auto-launched by the OS)."* Pass an `Array("OtherSvc1", "OtherSvc2")`. The setter stashes it; `Install()` packs it into a double-null-terminated string and hands it to `CreateServiceW`. -- `AutoInitializeCOM As Boolean` (Get / Let) — *"When TRUE, COM will be initialized for you on the new service thread in STA mode."* Defaults to `True`. Set to `False` if your service needs a different apartment model (call `CoInitializeEx` yourself from `EntryPoint`). -- `SupportsPausing As Boolean` (Get / Let) — *"When TRUE, the SCM will send `SERVICE_CONTROL_PAUSE` / `SERVICE_CONTROL_CONTINUE` notifications."* Defaults to `False`. The setter calls `ResyncStatus()` so toggling it mid-run takes effect immediately. (Most services set this to `True` once inside `EntryPoint` and then handle `vbServiceControlPause` / `vbServiceControlContinue` in `ChangeState`.) - -**Public methods**: - -- `Sub Install()` — *"This method attempts to install the configured service on the system."* Opens the SCM with `SC_MANAGER_CONNECT Or SC_MANAGER_CREATE_SERVICE`, calls `CreateServiceW`. If the service already exists, deletes it (via `OpenServiceW(SERVICE_DELETE)` + `DeleteService`) and **retries** the create — so `Install()` is effectively re-entrant / safe to call multiple times. On successful create, sets the description via `ChangeServiceConfig2W`. Raises run-time error 5 on permissions failure or unrecoverable create failure. **Requires admin elevation.** -- `Sub Uninstall()` — *"This method attempts to uninstall the configured service on the system."* Opens the SCM, opens the service with `SERVICE_DELETE`, calls `DeleteService`. Raises run-time error 5 if the service isn't registered or on permissions failure. **Requires admin elevation.** -- `Sub ReportStatus(ByVal dwCurrentState As ServiceStatusConstants, Optional ByVal dwWin32ExitCode As Long = ERRORCODE_NO_ERROR, Optional ByVal dwWaitHint As Long = 0)` — *"This method informs the OS of the current state of the service."* The user's `EntryPoint` is **required** to call `ReportStatus(vbServiceStatusRunning)` once steady-state is reached and `ReportStatus(vbServiceStatusStopped)` once shut-down completes; long start-up sequences should also call `ReportStatus(vbServiceStatusStartPending, , )` periodically to keep the SCM from killing the service. The `dwControlsAccepted` field of `SERVICE_STATUS` is filled automatically from the state and from `SupportsPausing` (Stop is always accepted except during `StartPending`; Pause/Continue is gated on `SupportsPausing`). The `dwCheckPoint` field auto-increments for pending states and resets on `Running`/`Stopped`. -- `Sub ResyncStatus()` — re-applies the cached `SERVICE_STATUS` to the SCM via `SetServiceStatus`. Called automatically from `ReportStatus` and from the `SupportsPausing` setter. User code rarely needs to call this directly; mention it for completeness. - -The class also carries two methods that are technically `Public`-by-default (no modifier) but are invoked only by the OS dispatcher / the package's own trampoline — `ServiceEntryPoint(ByVal dwArgc As Long, ByVal lpszArgv As LongPtr)` and `ServiceControlHandlerCallback(ByVal dwControl As Long, ByVal dwEventType As Long, ByVal lpEventData As LongPtr)`. **Do not list these as user-facing methods**; mention them at the very end of the page under "Internal hooks" with a `> [!NOTE]` saying the OS / package infrastructure invokes them and user code never calls them. - -#### `ServiceCreator(Of T)` public members - -Generic class. `[COMCreatable(False)]`. `[Description("This class allows the service manager to create an instance of a particular service on-demand as needed")]` is the source intro. Tagged with the EA magic-byte `[ClassId("66170220-FEF3-4257-8FBA-EAEAEAEAEAEA")]` — same compiler-special-handling treatment as `WinEventLogLib`'s `EventLog(Of T1, T2)`. Do not surface the `ClassId` on the page. - -Type parameter constraint: `T` must implement `ITbService`. There is no syntactic `Where T : ITbService` constraint expressed in the source, but `Function CreateInstance() As ITbService` returning `New T` only compiles when `T` implements `ITbService` — flag this as the practical constraint on the page. - -**Public method**: - -- `Function CreateInstance() As ITbService` — `Implements IServiceCreator.CreateInstance`. Returns `New T`. Called once per service start by the package's dispatcher trampoline. User code never calls this directly; the typical usage is `.InstanceCreator = New ServiceCreator(Of MyServiceClass)` on a freshly-allocated `ServiceManager`. - -The page should be small (the surface is one method) and largely focused on explaining the `Of T` parameterisation + the `T : ITbService` constraint + how it slots into `ServiceManager.InstanceCreator`. - -#### `ServiceState` public members - -`[COMCreatable(False)]`. Returned by `Services.QueryStateOfService(Name)`. The constructor takes the service name, opens the SCM with `SC_MANAGER_CONNECT`, opens the service with `SERVICE_QUERY_STATUS`, calls `QueryServiceStatusEx(SC_STATUS_PROCESS_INFO, ...)`, and snapshots a `SERVICE_STATUS_PROCESS` struct. **The snapshot is taken once at construction time and never refreshed** — to see updated state, call `Services.QueryStateOfService` again. - -The constructor raises run-time error 5 with descriptive messages on three failure modes: SCM open failed (*"Unable to open the Service manager..."*), service not installed (*"Service '' is not installed on this system"*), status query failed (*"Unable to query the service state"*). - -**Public properties** (all read-only `Get`): - -- `Type As ServiceTypeConstants` — the SCM-reported service type. -- `CurrentState As Long` — the SCM-reported state, but typed `Long` rather than `ServiceStatusConstants`. **Source carries a `' FIXME` comment** — surface as a `> [!NOTE]` that this returns the underlying `Long` value (which happens to match the `ServiceStatusConstants` enum values), and that callers wanting type-safety can `CType(state.CurrentState, ServiceStatusConstants)`. -- `CurrentStateText As String` — human-readable text: `"RUNNING"`, `"STOPPED"`, `"STARTING"`, `"STOPPING"`, `"PAUSED"`, `"PAUSING"`, `"CONTINUING"`, `"UNKNOWN STATE ()"`. -- `ControlsAccepted As Long` — bitmask of `SERVICE_ACCEPT_*` flags. **Source carries a `' FIXME` comment** — surface the same way as `CurrentState`. -- `ExitCode As Long` — the `dwWin32ExitCode` field. The Win32 documented sentinel `ERROR_SERVICE_SPECIFIC_ERROR` (`1066`) means "see `ServiceSpecificExitCode`". -- `ServiceSpecificExitCode As Long` — the service-defined exit code when `ExitCode = ERROR_SERVICE_SPECIFIC_ERROR`. Otherwise meaningless. -- `CheckPoint As Long` — the `dwCheckPoint` field; increments while the service is in a pending state and resets at steady state. -- `WaitHint As Long` — the `dwWaitHint` milliseconds field. -- `ProcessId As Long` — the OS process ID hosting the service (0 if not running). -- `Flags As Long` — the `dwServiceFlags` field (currently `SERVICE_RUNS_IN_SYSTEM_PROCESS = 1` is the only documented bit). - -#### `ITbService` public members - -`Public Interface`. Tagged `[InterfaceId("5F137E12-5164-452E-911A-6FD9BF20EC81")]`. Description: *"All services must implement `ITbService`."* The contract is three subs: - -- `Sub EntryPoint(ByVal ServiceContext As ServiceManager)` — the main service body. Called by the package's dispatcher trampoline once the SCM has finished start-up handshaking. **Runs on the service thread** (a separate thread from the dispatcher). Inside this sub the implementor: - 1. Optionally validates startup conditions (e.g. checks `ServiceContext.LaunchArgs`). - 2. Calls `ServiceContext.ReportStatus(vbServiceStatusRunning)` once steady-state is reached (the dispatcher trampoline reports `vbServiceStatusStartPending` automatically before calling `EntryPoint`). - 3. Runs the long-running work loop. For pipe-server services this is the `NamedPipeServer.ManualMessageLoopEnter()` blocking call; for other services it might be a `Do While IsStopping = False` loop with a wait primitive. - 4. Calls `ServiceContext.ReportStatus(vbServiceStatusStopped)` before returning. -- `Sub StartupFailed(ByVal ServiceContext As ServiceManager)` — called if `RegisterServiceCtrlHandlerExW` failed (the control handler couldn't be hooked, e.g. the service was launched outside the SCM context). Typical implementation: log a failure event. Don't try to `ReportStatus` from here — the status handle is invalid. -- `Sub ChangeState(ByVal ServiceContext As ServiceManager, ByVal dwControl As ServiceControlCodeConstants, ByVal dwEventType As Long, ByVal lpEventData As LongPtr)` — the control-code dispatcher. **Runs on the main (dispatcher) thread**, not on the service thread. Typical pattern: `Select Case dwControl` over `vbServiceControlStop` / `vbServiceControlShutdown` / `vbServiceControlPause` / `vbServiceControlContinue`, set shared `Public` flags (`IsStopping`, `IsPaused`), call `ServiceContext.ReportStatus` to acknowledge the transition, signal the service thread to react (e.g. `NamedPipeServer.ManualMessageLoopLeave()`). The `dwEventType` + `lpEventData` parameters carry the event-specific payload for the codes that need it (`SERVICE_CONTROL_DEVICEEVENT`, `SERVICE_CONTROL_POWEREVENT`, `SERVICE_CONTROL_SESSIONCHANGE`, `SERVICE_CONTROL_HARDWAREPROFILECHANGE` — see Microsoft's `HandlerEx` documentation for the data layouts). - -**The two-thread split is the single most important fact about the interface** — every page entry should reinforce it. The example uses `Public IsPaused As Boolean` + `Public IsStopping As Boolean` shared fields on the service class to ferry state between the two threads, which is the documented pattern. - -#### Enumerations - -Public enums (in `Public Module ServicesConstantsPublic`), one page each under `docs/Reference/WinServicesLib/Enumerations/`: - -- `ServiceTypeConstants` — `tbServiceTypeAdapter`, `tbServiceTypeSystemDriver`, `tbServiceTypeKernelDriver`, `tbServiceTypeRecognizerDriver`, `tbServiceTypeOwnProcess`, `tbServiceTypeShareProcess`, `tbServiceTypeOwnProcessInteractive`, `tbServiceTypeShareProcessInteractive`. The driver values (`tbServiceTypeSystemDriver`, `tbServiceTypeKernelDriver`, `tbServiceTypeRecognizerDriver`, `tbServiceTypeAdapter`) are only meaningful when registering a kernel-mode driver — twinBASIC services compile to a user-mode EXE and should use `tbServiceTypeOwnProcess` (one service per EXE) or `tbServiceTypeShareProcess` (multiple services hosted in one EXE; the example uses this). The `Interactive` variants are kept for compatibility but Windows Vista and later disallow them; flag with a `> [!NOTE]`. -- `ServiceStartConstants` — `tbServiceStartAuto`, `tbServiceStartBoot`, `tbServiceStartOnDemand`, `tbServiceStartDisabled`, `tbServiceStartDriverSystem`. `tbServiceStartBoot` and `tbServiceStartDriverSystem` only apply to kernel drivers. -- `ServiceControlCodeConstants` — 18 values mirroring the Win32 `SERVICE_CONTROL_*` constants. Source-side prefix is `vbServiceControl*` (carried over from VB6 — note the prefix is `vb`, not `tb`, in this enum; surface as-is, don't try to rationalise). -- `ServiceStatusConstants` — `vbServiceStatusStopped`, `vbServiceStatusStartPending`, `vbServiceStatusStopPending`, `vbServiceStatusRunning`, `vbServiceStatusContinuePending`, `vbServiceStatusPausePending`, `vbServiceStatusPaused`. Same `vb` prefix. - -Format pages like `WebView2/Enumerations/wv2PrintOrientation.md` — single intro paragraph, a value table with `{: #vbServiceXxx }` anchors per row for deep-linking. - -#### Doc-side layout (folders / files) - -Ten pages total: - -``` -docs/Reference/WinServicesLib/ - index.md ← package landing; lifecycle + dual-thread model + install / launch flows + integration cross-links (event log + named pipes) - Services.md ← the predeclared singleton - ServiceManager.md ← per-service configuration + ReportStatus - ServiceCreator.md ← Of T generic factory - ServiceState.md ← read-only state snapshot - ITbService.md ← interface every service implements - Enumerations/index.md - Enumerations/ServiceTypeConstants.md - Enumerations/ServiceStartConstants.md - Enumerations/ServiceControlCodeConstants.md - Enumerations/ServiceStatusConstants.md -``` - -All five concrete pages are single-file (no folder-style — no natural sub-pages; each class's surface is medium-sized). - -The `index.md` should be substantial and walk the reader through: - -1. **What a Windows service is** (one paragraph: long-running background process supervised by the SCM, started before/independently of user logon, controlled via the Services control panel applet or `sc.exe`). -2. **Lifecycle**: configure (`Services.ConfigureNew`) → install (`Services.InstallAll` or per-manager `.Install`, **elevated**) → run (`Services.RunServiceDispatcher` blocks the EXE's main thread; SCM launches the service thread on demand). The example's `If InStr(Command, "-startService") > 0` branch is the canonical "same EXE for install-time UI and run-time service" pattern. -3. **The two-thread split**: `EntryPoint` and `ChangeState` run on different threads; surface this prominently with a small diagram or numbered explanation. -4. **Integration with the sister packages**: cross-link `Implements EventLog(Of …) Via EventLog = New EventLog(…)` (see `WinEventLogLib`) and the `NamedPipeServer.ManualMessageLoopEnter`/`Leave` service-hosting idiom (see `WinNamedPipesLib`). The worked example at `..\tbrepro\winlibs\tbServiceTest2\Sources\` ties all three together. - -**Naming:** - -- Folder / URL segment: `WinServicesLib/` (matches the source-side package name; no `Package` suffix to drop, same as `WinEventLogLib` and `WinNamedPipesLib`). -- Index title: `WinServicesLib Package` — the ` Package` convention. -- Permalinks: `/tB/Packages/WinServicesLib/` for the landing; `/tB/Packages/WinServicesLib/` for each class page; `/tB/Packages/WinServicesLib/Enumerations/` for each enum page. -- `parent: WinServicesLib Package` on each top-level child page. The four enum pages set `parent: Enumerations` and `grand_parent: WinServicesLib Package` (the grouped-page pattern; same shape as the WebView2 / CEF / CustomControls `Enumerations/` directories). - -**License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2025; first release v0.1, 04-FEB-2025) — same situation as every other Wayne Phillips package. Pages are fully original content; **omit** the `vba_attribution: true` flag. - -### tbIDE - -The **addin SDK** — a type-only compiler package. Every public symbol is an interface or a CoClass; the actual implementations live in the IDE binary, and an addin DLL binds against the type declarations and lets the IDE marshal calls into its implementations at run time. Twenty-four flat `.twin` files in `..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Sources\`, each 8–57 lines (501 lines total) — there is no plumbing to skip, every file declares user-facing types. - -The `CHANGELOG.md` shipped in the package is a leftover copy-paste from a different package ("twinBASIC WinNativeForms") and is **not** about tbIDE — **do not** propagate that history onto the docs. - -#### How an addin is built and loaded - -From any of the six sample `Settings` files (the structure is identical across them): - -- `project.buildType`: **Standard DLL** — addins are not packages, they are DLLs that the IDE loads. -- `project.buildPath`: `${IdePath}\addins\${Architecture}\${ProjectName}.${FileExtension}` — the build output drops directly into `\addins\Win32\` or `\addins\Win64\`. The IDE scans this folder on startup. -- `project.references` includes the tbIDE compiler package: `id: {99DEC38C-75F6-4488-8EE7-2D52D83881D2}`, `isCompilerPackage: true`, `publisher: TWINBASIC-COMPILER`, `symbolId: tbIDE`. Same shape that `CustomControls` uses. - -The DLL **must** export a single factory function the IDE will call: - -```tb -Module MainModule - [DllExport] - Public Function tbCreateCompilerAddin(ByVal Host As Host) As AddIn - Return New MyAddinClass(Host) - End Function -End Module -``` - -The returned object must implement the [`AddIn`](#addin) interface (a single read-only `Name` property). The IDE releases the object when the addin is disabled or the IDE shuts down. Every sample uses this exact `tbCreateCompilerAddin` skeleton — surface it on the index landing as the canonical entry point. - -#### Public user-facing surface - -Twenty-four files declaring twenty-three public CoClasses + one concrete `Class` + one interface-only declaration: - -| File | Public symbols | Role | -|------------------------------|-----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -| `Addin.twin` | `IAddInV1` + `AddIn` CoClass | The contract every addin's main class implements. One member: `Property Get Name() As String`. | -| `Host.twin` | `IHostV1` + `ItbHostEventsV1/V2/V3` + `Host` CoClass | Root of the API — passed to `tbCreateCompilerAddin`. Versioned events (see "Versioned event interfaces" below). | -| `AddinTimer.twin` | `Class AddinTimer` (no CoClass) | **The only concrete instantiable class** in the package. `New AddinTimer`; sets `Interval` (ms) + `Enabled`; fires `Timer` event. Internally wraps `SetTimer`/`KillTimer`. | -| `Button.twin` | `IButtonV1` + `IButtonEventsV1` + `Button` CoClass | Toolbar button. Returned by `Toolbar.AddButton`. `OnClick` event. | -| `CodeEditor.twin` | `ICodeEditorV1 Extends IEditorV1` + `CodeEditor` CoClass | A code-pane editor — selection, text, Monaco passthrough, `AddMonacoWidget` for inline overlay HTML. Nested `RevealArea` enum. | -| `DebugConsole.twin` | `IDebugConsoleV1` + `DebugConsole` CoClass | The DEBUG CONSOLE pane. `PrintText` (with optional colour), `Clear`, `SetFocus`. | -| `Editor.twin` | `IEditorV1` + `Editor` CoClass | Base interface for editors. Source-side comment: *"Castable to CodeEditor etc."* — i.e. `Dim ce As CodeEditor = editor` works because the underlying object implements both. | -| `Editors.twin` | `IEditorsV1` + `Editors` CoClass | Collection of active editors. `Item(Index)` default member, `Count`, `Open(Path, Line, Col, Options)`. Source-side note: *"There is currently only ONE active editor, accessible via Editors(0) syntax"*. | -| `File.twin` | `IFileV1` + `IFileV2 Extends IFileV1` + `File` CoClass | A virtual-FS file. V1: `Data` / `DataLen` / `Text` / `IsDirty`. V2 adds `ReadText(ReadTextFlags)`. Nested `ReadTextFlags` enum (one flag: `CommentsToWhitespace`). | -| `FileSystem.twin` | `IFileSystemV1` + `FileSystem` CoClass | Tiny — `RootFolder` and `ResolvePath(Path)` (path needs the `twinbasic:/` prefix). | -| `FileSystemItem.twin` | `IFileSystemItemV1` + `FileSystemItem` CoClass | Base for `File` and `Folder`. `Name`, `Path`, `Type`, `Parent`. Nested `FileSystemItemType` enum (`Folder`, `FileVIRTUALDOC`, `FileOTHER`, `FileTWIN`, `FileBAS`, `FileCLS`, `FileUIDESIGNER`, `FileJSON`). | -| `Folder.twin` | `IFolderV1 Extends IFileSystemItemV1` + `Folder` CoClass | `Count`, `Item(IndexOrName)`, `IsPackagesFolder`, plus for-each enumeration over `FileSystemItem` children. | -| `HtmlElement.twin` | `IHtmlElementV1` + `HtmlElement` CoClass | A DOM element inside a tool window. `Properties` (default member — see below), `ChildDomElements`, `Remove`, `AddEventListener(DomEventName, CallbackFunc, Optional Data)`. Plus one `[Hidden]` legacy `AddEventListenerOLD1`. | -| `HtmlElementProperties.twin` | `IHtmlElementPropertiesV1` (`[COMExtensible(True)]`) + CoClass | The dynamic property bag on a DOM element. `Item(DomPropertyName)` is the default member; the `[COMExtensible(True)]` attribute is what makes `.style.display = "flex"` resolve through `Item("style").Item("display")` at run time. | -| `HtmlElementProperty.twin` | `IHtmlElementPropertyV1` (`[COMExtensible(True)]`) + CoClass | One value in the bag. `Value` (Get/Let, default member), plus nested `Properties` for chained access (`.style.color = "white"`). | -| `HtmlElements.twin` | `IHtmlElementsV1` + `HtmlElements` CoClass | The `ChildDomElements` collection. `Item(ID)` default, `Add(ElementID, TagName)` returns the new child. Note `TagName` accepts both standard HTML tags AND the IDE's custom widget tags `chartjs` / `monaco` / `listview` / `virtuallistview`. | -| `IHtmlEventProperties.twin` | `IHtmlEventPropertiesV1` (`[COMExtensible(True)]`) + `HtmlEventProperties` CoClass | The event-payload bag. Like `HtmlElementProperties` but read-only and used inside event handler callbacks. | -| `IHtmlEventProperty.twin` | `IHtmlEventPropertyV1` (`[COMExtensible(True)]`) + `HtmlEventProperty` CoClass | One value in the event bag. | -| `KeyboardShortcuts.twin` | `IKeyboardShortcutsV1` + `KeyboardShortcuts` CoClass | Single member: `Add(keyString, Callback As LongPtr)`. `keyString` is a literal key like `"{CTRL}{SHIFT}d"` (prefixes `{CTRL}` / `{SHIFT}` / `{ALT}`). | -| `Project.twin` | `IProjectV1` + `Project` CoClass | The currently-loaded project. Lifecycle (`Save`, `Close`, `Build`, `Clean`), introspection (`Path`, `Name`, `BaseFolderPath`, `ProjectID`, version + architecture + build-output info), `Evaluate(ExprString)` (debug-console-style expression evaluation), `RootFolder` (entry into the virtual FS), and `LoadMetaData`/`SaveMetaData` (persistent per-addin key/value storage inside the `.twinproj`). Nested `VbBuildType` enum. | -| `Themes.twin` | `IThemesV1` + `Themes` CoClass | `ActiveThemeName` ("Classic" / "Dark" / "Light"), `ActiveThemeNameGroup` ("dark" / "light"). The `Host` events interface fires `OnChangedTheme` when this flips. | -| `ToolWindow.twin` | `IToolWindowV1` + `IToolWindowEventsV1` + `ToolWindow` CoClass | A dockable / floating tool window. `Title`, `Visible`, `Resizable`, `Close`, `ApplyCss(stylesString)`, `RootDomElement` (default member — the entry into the DOM tree). `OnClose` event. | -| `ToolWindows.twin` | `IToolWindowsV1` + `ToolWindows` CoClass | Single member: `Add(Name, Optional UniqueIdForPositionPersistance) As ToolWindow`. The second argument lets the IDE remember the floating-window position across IDE restarts. | -| `Toolbar.twin` | `IToolbarV1` + `Toolbar` CoClass | `AddSplitter` (vertical-bar separator), `AddButton(Id, Caption, Optional IconData)`. | -| `Toolbars.twin` | `IToolbarsV1` + `Toolbars` CoClass | `Item(Index)` default, `Count`. Source-side note: *"There is currently only ONE toolbar, accessible via the Toolbars(0) syntax"*. | - -#### The interface/CoClass split — what it means for the doc layout - -Almost every `.twin` declares one or two `Public Interface IV1 Extends stdole.IUnknown` followed by `Public CoClass ` with `[Default] Interface IV1` (and optionally `[Default, Source] Interface IEventsV1`). The pattern is the standard COM idiom for late-binding-friendly extensibility — the IDE implements the interfaces, the addin holds references typed at the CoClass. - -**The interfaces themselves get no doc page.** Users type their variables `As Host` / `As Button` / `As ToolWindow` (the CoClass), not `As IHostV1`. Fold the interface's members onto the CoClass's page; do not list both. Same convention CustomControls uses for its `_…` default interfaces. - -**Versioning is conveyed by interface chains.** Two cases visible in the source: - -- `IFileV1` → `IFileV2 Extends IFileV1` (V2 adds `ReadText(ReadTextFlags)`). The `File` CoClass declares `[Default] Interface IFileV2`. Document the V2 surface as the canonical `File` page; do not split V1 vs V2. (Mention in passing that `ReadText` is V2-only and consequently won't bind against very early IDE builds — though in practice every shipping IDE is V2+.) -- `IHostV1` → `ItbHostEventsV1` → `ItbHostEventsV2 Extends V1` → `ItbHostEventsV3 Extends V2`. The `Host` CoClass declares `[Default, Source] Interface ItbHostEventsV3`. The new members on V2 / V3 (`OnChangedActiveEditor`, `OnChangedTheme`) are each tagged **`[AllowUnpopulatedVtableEntry]`**, which is the mechanism that lets a newer addin compile against `ItbHostEventsV3` and still load against an older IDE that only implements `V1` — the IDE doesn't have to provide the V2/V3 entries. - -Document all `Host` events together on the `Host.md` page (the per-version split is a compatibility detail, not a user-facing concept). - -#### `AddinTimer` is the only user-instantiable class - -Every other public symbol is a CoClass exposed *to* the addin by the IDE — the addin receives instances via `Host`, never constructs them. `AddinTimer` is the exception: it's a concrete `Class AddinTimer` (not a CoClass) and the addin instantiates it with `New AddinTimer`. Internally it wraps `SetTimer` / `KillTimer` with a private `TimerCallback`, exposes `Interval` (ms) + `Enabled`, and fires a `Timer` event. - -Sample 11's CPU-monitor demonstrates the typical pattern: - -```tb -Private WithEvents Timer As AddinTimer -… -Set Timer = New AddinTimer -Timer.Interval = 500 -Timer.Enabled = True -… -Private Sub Timer_Timer() - ' fires every 500 ms -End Sub -Private Sub myToolWindow_OnClose() - Set Timer = Nothing ' stop the timer when the window closes -End Sub -``` - -The class uses the `Handles` syntax internally (`Private Sub Changed() Handles Enabled.OnPropertyLet, Interval.OnPropertyLet`) so any change to `Enabled` or `Interval` re-arms the underlying timer — surface this as *"set `Enabled = False` to stop, change `Interval` at any time"*, not as an implementation detail. - -`Class_Terminate` calls `KillTimer` so a dropped reference is sufficient to stop. Sample 15 demonstrates that direct Win32 `SetTimer` / `KillTimer` is also fine if `AddinTimer` doesn't fit — both patterns are valid; the package doesn't *require* the helper. - -#### The HTML / DOM surface - -Tool windows are rendered as HTML inside the IDE (the same browser surface the IDE uses for its own panes). The `HtmlElement` / `HtmlElements` / `HtmlElementProperty` / `HtmlElementProperties` quartet is the addin's keyhole into the DOM. - -Three things make this surface unusual and have to be surfaced on the docs: - -1. **`[COMExtensible(True)]` on `HtmlElementProperties` / `HtmlElementProperty` / `HtmlEventProperties` / `HtmlEventProperty`.** The attribute opts the interface into IDispatch dynamic-member resolution, which is what makes `.style.display = "flex"` work — at compile time the right-hand `.style.display` resolves to `Item("style").Item("display").Value = "flex"` (the default-member dance), and the IDE resolves the names against the live DOM at run time. No member named `style` is *declared* on `IHtmlElementPropertiesV1`. Surface this on each of the four pages with a `> [!IMPORTANT]` callout: the property names accepted are **every DOM property of the underlying tag** (standard HTML attributes, CSS-style properties under `.style.…`, plus any custom-element-specific surface like `.chart.data.datasets(0).borderWidth` on a `chartjs` element or `.editor.setOption(...)` on a `monaco` element). The docs cannot enumerate them — refer the reader to the relevant DOM / library reference. -2. **The custom-element tags.** `HtmlElements.Add(id, tagName)` accepts standard HTML tags (`"div"`, `"input"`, `"span"`, `"h1"`, …) AND four IDE-specific widget tags: `"chartjs"` (Chart.js wrapper — surfaces a `.chart` property), `"monaco"` (the Monaco editor — surfaces a `.editor` property), `"listview"` (the IDE's listview widget — surfaces a `.listview` property with `addItem` / `removeItem` / etc.), and `"virtuallistview"` (the same with `setItemCount` + the `onAsyncGetItemHTML` event). All four are demonstrated in samples 11–14. Surface as *"the tag name is forwarded to the IDE's tool-window renderer, which understands the standard HTML tags plus the custom widget tags … see sample 11 / 12 / 13 / 14"*. -3. **`AddEventListener(DomEventName As String, CallbackFunc As LongPtr, Optional Data As Variant)`.** The callback is passed as `AddressOf SomeSub`, and `SomeSub` must have the signature `Sub(ByVal eventInfo As HtmlEventProperties)`. The `eventInfo` parameter is the IDE-marshalled equivalent of the JavaScript `Event` object — `eventInfo.key` / `eventInfo.target.value` / `eventInfo.target.id` are the usual fields, but again the property names are resolved against the *actual* event object at run time, not declared statically. Sample 13 also demonstrates **custom event names raised from inline HTML** via the IDE-side `raiseEvent("name", event, stopPropagation, …customData)` JavaScript helper; the custom-data values become `eventInfo.customData0`, `eventInfo.customData1`, … and are demonstrated in sample 15's `ClickedMatch` handler. Sample 14 demonstrates **async events** via `eventInfo.setAsyncResult("")` (the listener returns the requested HTML asynchronously back into the virtual list view's render cycle). - -Document the four `Html*` classes as the *contract* (`Item` default member, the `Value` accessor, the `Properties` chaining) and use the samples to illustrate the dynamic-resolution mechanism. Do not try to enumerate the resolved property surface — it's open-ended. - -The `HtmlEvent*` half of the quartet declares `Value` as **read-only Get** (vs `HtmlElementProperty`'s `Value` which has Get + Let) — that's the contract distinction between an inbound event payload and an outbound DOM property setter. Note this on each page. - -#### ToolWindow as a jQuery-style selector - -`ToolWindow` is the *root* of a tool window's DOM and **also doubles as a member-by-ID accessor**: `myToolWindow("#dataEntry").Value` (see sample 13) returns the `Value` of the child element whose `id` is `dataEntry`. There is no explicit member on `IToolWindowV1` that takes a string argument — the source-side `RootDomElement` carries `[DefaultMember]`, so `myToolWindow("#dataEntry")` resolves to `RootDomElement.Properties.Item("#dataEntry")`, which the dynamic resolver then interprets as a CSS-style selector against the rendered DOM. Surface this as *"the tool window's default member is `RootDomElement`, which is COM-extensible — string indexing accepts CSS selectors"* and call out the `#id` (single element by ID) form as the most common case. - -`ApplyCss(styles As String)` injects a `