DVX BASIC IDE Guide
DVX BASIC is a BASIC development environment for DVX. It provides a VB3-style integrated development environment with a code editor, form designer, project system, and a full interactive debugger -- all integrated into DVX itself.
This guide covers every feature of the IDE: menus, toolbar, editor, form designer, project management, debugger, and auxiliary windows.
IDE Windows
The DVX BASIC IDE is modeled after Visual Basic 3.0. It consists of several floating windows arranged on the DVX desktop:
- Main Toolbar Window -- anchored at the top of the screen. Contains the menu bar, toolbar buttons, and status bar.
- Code Editor -- the primary editing surface for BASIC source code, with Object/Event dropdowns, syntax highlighting, and line numbers.
- Form Designer -- a visual design surface for .frm files, showing a WYSIWYG preview of the form with grab handles for resizing controls.
- Project Explorer -- a tree view listing all files in the project.
- Toolbox -- a palette of available controls for placing on forms.
- Properties Panel -- a tree of controls and a list of editable properties for the selected control.
- Output Window -- displays PRINT output and runtime errors.
- Immediate Window -- an interactive REPL for evaluating expressions and modifying variables at runtime.
- Debug Windows -- Locals, Call Stack, Watch, and Breakpoints windows that appear automatically when debugging.
The IDE compiles BASIC source into bytecode and runs it in an integrated virtual machine. Programs execute in cooperative slices, yielding to DVX between slices so the GUI remains responsive. Call DoEvents in long-running loops to keep the event loop flowing.
Toolbar
The toolbar is organized into four groups separated by vertical dividers. Each button has a tooltip showing its name and keyboard shortcut.
File Group
Button Shortcut Action ------ -------- ------ Open Ctrl+O Add a file to the project (same as File > Add File). Save Ctrl+S Save the active file.
Run Group
Button Shortcut Action ------ -------- ------ Run F5 Compile and run the program. Stop Esc Stop the running program.
Debug Group
Button Shortcut Action ------ -------- ------ Debug Shift+F5 Start or resume a debug session. Step Into F8 Step into the next statement. Step Over Shift+F8 Step over the next statement. Step Out Ctrl+Shift+F8 Step out of the current procedure. Run to Cursor Ctrl+F8 Run to the cursor position.
View Group
Button Shortcut Action ------ -------- ------ Code F7 Switch to Code view. Design Shift+F7 Switch to Design view.
Code Editor
The Code Editor is the primary editing window for BASIC source code. It occupies the center of the screen, below the toolbar and above the Output and Immediate windows.
Object and Function Dropdowns
At the top of the Code Editor are two dropdown lists:
- Object -- lists (General) plus all objects (form name, control names, menu item names, and the WithEvents variables the file declares). Selecting an object filters the Function dropdown.
- Function -- lists all event handlers (procedures) for the selected object. Implemented handlers are listed first (plain text); unimplemented handlers follow in brackets (e.g., [Click]). Selecting an unimplemented event creates a new event handler stub. Only the events the object can get are listed: Paint, for example, appears for the form and for PictureBoxes, the objects a program draws on.
A WithEvents variable (Dim WithEvents acct As Account, also Private or Public, in a module, a form, or a class module) is listed as an object; its events are the ones its class module declares with Event. Picking one of them creates the handler acct_EventName with the event's own parameters, so it matches what the class raises. Selecting the variable when it has no handler yet creates the handler for the class's first event, as a control's default event is created.
The editor shows one procedure at a time. Each procedure has its own buffer, and switching between them is instantaneous. The (General) section contains module-level declarations and code.
Text outside the procedure being shown is never lost: lines typed above or below the SUB move to (General), a second SUB typed into the buffer becomes a procedure of its own, and a procedure whose SUB line was deleted becomes (General) text. When you leave an event handler that was generated and never edited it is removed; empty SUBs and FUNCTIONs you wrote yourself are kept. After a save the editor shows exactly what was stored (blank lines around a procedure are normalized). A section too large for the editor opens read-only, with a message in the status bar.
Syntax Highlighting
The editor applies real-time syntax coloring as you type. Seven categories are highlighted in distinct colors; the colors themselves are editable in Tools > Preferences > Colors.
Category Examples -------- -------- Default Text Identifiers, whitespace. Keywords IF, THEN, FOR, NEXT, SUB, FUNCTION, DIM, PRINT, SELECT, CASE, DO, LOOP, WHILE, WEND, END, EXIT, CALL, GOSUB, GOTO, RETURN, DECLARE, CONST, TYPE, AND, OR, NOT, XOR, MOD, etc. Types INTEGER, LONG, SINGLE, DOUBLE, STRING, BOOLEAN, DATE, VARIANT, OBJECT, COLLECTION, TRUE, FALSE, NOTHING, EMPTY, NULL Strings "Hello, World!" Comments ' This is a comment, REM This is a comment Numbers 42, 3.14, &HFF, date literals such as #2026-09-30# and #1/15/2026 10:30 AM# Operators =, <, >, +, -, *, /, \, &
Date literals are colored as numbers only when they are real dates, as the compiler reads them; the # of a file number (CLOSE #1) is not. Some words are colored like keywords without being reserved -- built-in type names, Empty, Null, EACH, EVENT, WITHEVENTS, and the console and graphics statements such as CLS, LOCATE, COLOR, SCREEN, PSET, CIRCLE, PAINT, PCOPY, and PMAP. A program may use them as its own names, and after a dot (Text1.Width, Picture1.Point) they are shown as ordinary names.
Editor Features
- Line numbers -- displayed in the gutter on the left side.
- Auto-indent -- new lines are automatically indented to match the previous line.
- Tab handling -- the Tab key is captured by the editor. Tab width and whether to insert spaces or tab characters are configurable in Preferences (default: 3 spaces).
- Gutter click -- clicking in the line number gutter toggles a breakpoint on that line.
- Line decorations -- breakpoint lines show a red dot in the gutter. The current debug line (when paused) is highlighted with a yellow background.
Form Designer
The Form Designer provides a visual design surface for editing .frm files. Switch to it with Shift+F7 or the Design toolbar button. It opens in a separate window showing a WYSIWYG preview of the form.
Design Surface
- Grid snapping -- controls snap to an 8-pixel grid when placed or resized.
- Selection -- click a control to select it. The selected control is highlighted with grab handles.
- Grab handles -- 6x6 pixel handles appear on the right edge (E), bottom edge (S), and bottom-right corner (SE) of the selected control. Drag a handle to resize the control.
- Reordering -- drag a control to reorder it among its siblings in the layout (VBox/HBox). A container moves together with its contents.
- Placing controls -- select a control type in the Toolbox, then click on the form to place a new instance. The control is auto-named (e.g., Command1, Command2), skipping names already used by a control, a menu item, or the form. Clicking the same tool again deselects it (toggles back to pointer mode). Controls nest at most 15 levels deep; a drop into a container that is already that deep goes into its parent container.
- Menu bar preview -- if the form has menu items (defined via the Menu Editor), a preview menu bar is rendered on the design window.
- Delete key -- removes the selected control and everything inside it.
Opening a form in the designer does not mark it modified. A control whose Visible property is False is still shown on the design surface (the setting applies when the program runs). Disabling a container greys the controls inside it; a control's own Enabled = False is kept when the container is enabled again. A .frm file without an AutoSize line opens without AutoSize; AutoSize = True wins over Width and Height lines. Text values (Caption, Text, and so on) are limited to 379 characters.
Menu Editor
The Menu Editor (View > Menu Editor, Ctrl+E) edits the active form's menu bar and popup menus. Menus nest at most 15 levels (0-14); indenting refuses when any item below would go deeper. OK rejects an item nested more than one level below the item above it, and a blank item that has sub-items (it must be named); blank items without sub-items are dropped. The window's close box cancels. Shortcut is a key that runs the item (F2, Ctrl+X, Alt+Enter ...), shown at its right; Text is key text shown without making a shortcut (see Menu Item Properties); OK rejects a Shortcut it does not know. Renaming a menu item renames its event handlers in the code and updates the ContextMenu setting of the form and its controls; inserting, deleting, and moving items does not rename anything.
Form Properties
Forms have the following design-time properties: Name, Caption, Width, Height, Left, Top, Layout (VBox or HBox), Centered, AutoSize, Resizable, HelpTopic, ContextMenu, and the font (the Font row; see Properties Panel).
Project System
Project Files (.dbp)
A DVX BASIC project is stored as a .dbp file (DVX BASIC Project). The project file records:
- Name -- the project display name (up to 32 characters).
- Startup Form -- which form to show automatically when the program starts.
- Metadata -- Author, Company, Version, Copyright, Description (a single line), and Icon Path (for compiled binaries).
- Arguments -- the command line the program gets (COMMAND$) when it is run from the IDE, for trying out a program that opens the file it is given. It is not built into the compiled program: there COMMAND$ returns whatever the program was started with.
- More Than One Copy -- "Allow more than one copy to run at once" ([Settings] MultiInstance = True): the compiled program can be started again while it is running, and each copy runs on its own (like the Clock and the File Manager). Without it, starting the program again only says that it is already running.
- Help File -- a compiled help file (.hlp) for the program. It is copied next to the compiled program, and F1 in the running program opens it, at the HelpTopic of the focused control or, failing that, of the form. Add help.bas to the project and call HelpView to open it from a Help menu.
- Type -- what the project makes: a program (an .app, the default) or, with Type = Control, a control file (a .wgt holding the project's UserControls for every program). See UserControls in the IDE.
- File list -- relative paths of all the project's files: modules (.bas) in the [Modules] section, class modules (.cls) in the [Classes] section, UserControls (.ctl) in the [Controls] section, and forms (.frm) in the [Forms] section.
- Fonts -- the .DFN fonts the program carries, in the [Fonts] section (see Fonts a Program Carries).
The file sections all have the same shape: one FileN line per file, numbered from 0, and a Count line:
[Modules]
File0 = main.bas
Count = 1
[Classes]
File0 = account.cls
File1 = ledger.cls
Count = 2
[Forms]
File0 = form1.frm
Count = 1
Fonts a Program Carries
A program can carry fonts of its own, so its text looks the same whatever fonts were installed. In Project > Properties, Add... in the Fonts row picks a .DFN font (one outside the project's directory can be copied into it); Remove takes the chosen one off the list (the file stays). The fonts are listed in the [Fonts] section of the project file. Make builds them into the program as font resources, and the program has them whenever it runs; running it from the IDE gives it the same fonts until it stops. A program carries at most 64 fonts.
A program's fonts are its own: other programs never see them. Where a font it carries is of a family that is installed too, its sizes and styles come first and the installed family fills in the rest. Fonts are made with the SDK's mkfont from TrueType, Type 1, and bitmap fonts -- one size and only the characters the program uses keeps a font small -- or copied from DVX's FONTS folder. Each .DFN carries the notice and licence of the font it was made from.
How Projects are Compiled
When the project is compiled, all source files are concatenated into a single source stream: the modules first, then the class modules, then the UserControls, then the forms. The IDE tracks which lines belong to which file so error messages and debugger locations point to the correct .bas, .cls, or .frm file. The code section of each .frm file is preceded by a hidden BEGINFORM marker that ties its code to its form, and the code of each class module and UserControl is likewise marked with its class name; the header lines of a class module or UserControl are not compiled.
Compile-Time Validation
Before the source is handed to the compiler, the IDE scans every .frm file and cross-checks the forms against the widget registry and the statements that reference them. Three classes of error are caught and reported without producing a binary:
- Unknown widget types -- any Begin TypeName ... End block whose TypeName does not match a registered widget is flagged. The error message names the offending form, control, and type, and hints that DVX uses VB6-style names (SpinButton, not Spinner).
- Unknown properties -- statements of the form CtrlName.Property = value, or reads of CtrlName.Property, are checked against the widget's declared property list. A typo such as btn.Captoin triggers a compile error with the form, control, and property name. A control name that is used with different types on different forms is not checked.
- Unknown methods -- method calls CtrlName.Method ... are checked the same way against the widget's method list, catching mistakes like list.AddTiem before the program ever runs.
- Menu items -- only Name (read), Checked, and Enabled can be used on a menu item in code; Caption, Visible, RadioCheck, Shortcut, and ShortcutText are design-time settings.
These checks use the same metadata the Properties panel and Toolbox read from each installed control, so property and method names always match the current widget set without any separate list to maintain.
Project Operations
Operation Description --------- ----------- New Project Creates a blank project with a name and directory. A default .frm file is added automatically. Open Project Opens a .dbp file and loads all referenced files into memory. A project file that is 64 KB or larger, or cannot be read, is not loaded; opening it later reports "Cannot load <file>: unreadable or larger than 65535 bytes." Carriage returns are removed when files are loaded. Save Project Writes the .dbp file to disk. Close Project Closes the project, prompting to save unsaved changes. Add File Adds a .bas, .frm, .cls, or .ctl file to the project. Opening a file without a project auto-creates an implicit project. New Class Module Adds a new class module to the project and opens it (see Class Modules below). New UserControl Adds a new UserControl to the project and opens it (see UserControls in the IDE). Rename File Gives a file a new name on disk and in the project file (see Renaming a File below). Remove File Removes a file from the project (prompts to save if modified). Removing the startup form clears the Startup Form setting. Project Properties Opens a dialog for editing project metadata (name, type, author, company, version, copyright, description, icon, help file, startup form), the fonts the program carries, whether more than one copy may run at once, and the Arguments for runs from the IDE.
Renaming a File
Select a file in the Project Explorer and choose File > Rename File. Type the new file name; the extension may be left out (a module stays .bas, a class module .cls, a UserControl .ctl, and a form .frm, and cannot be given another kind's extension). The file is renamed in the project's directory and the project file is saved at once with the new name, so the two always agree. Nothing changes, and a message says why, when the name is not a valid file name, when another project file or any file in the directory already has it, or when the file or the project file cannot be written -- an existing file is never overwritten. A file that has never been saved is simply given the new name, and is written under it when you save.
A class module named after its file (it has no Attribute VB_Name line, or one that matches the file name, as File > New Class Module writes it) takes the new name as its class name: its Attribute VB_Name line is changed to match (an edit that is saved with the file, like any other). The new name must then be a valid name that no other form, class module, or UserControl uses. Code that uses the old class name is not changed. A UserControl named after its file (as File > New UserControl makes it) takes the new name too, on its Begin UserControl line; forms that use the old name are not changed. A form keeps its Name property; rename the form itself in the Properties window.
Project Explorer
The Project Explorer is a tree view window listing all project files, in four groups: Forms, Modules, Classes, and Controls (UserControls). Double-click a file to open it: .bas, .cls, and .ctl files open in Code view, .frm files open in Design view. The selected file in the Project Explorer determines the target for View > Code and View > Designer commands.
Class Modules
A class module (.cls file) defines a kind of object for your program: its member variables, properties, and methods. The BASIC Language Reference describes how to write one (see Objects and Class Modules); this section covers working with them in the IDE.
- Create -- choose File > New Class Module. The IDE adds a new class module named Class1 (Class2, and so on, when that name is taken) to the Classes group, saved in the project's directory as Class1.cls, and opens it in the Code Editor. The new file holds only its header and Option Explicit; it is written to disk when you save it.
- Add an existing one -- choose File > Add File and pick a .cls file (the Class Modules file type). Class files from Visual Basic can be added as they are.
- Open -- double-click it in the Project Explorer's Classes group. It opens in Code view; a class module has no designer.
- Save -- File > Save File or Save All, as for any other file. The project file lists class modules in its [Classes] section.
- Remove -- select it in the Project Explorer and choose File > Remove File. The file itself stays on disk.
- Rename -- a class's name is the one in its Attribute VB_Name line, not the file name. File > Rename File renames the file, and a class named after its file (as a new class module is) takes the new name too; see Renaming a File. To give a class a name different from its file, edit its Attribute VB_Name line (and the code that uses the old name) and save. A class module without an Attribute VB_Name line takes its name from the file name.
A new class module starts with these header lines:
VERSION DVX 1.00 CLASS
Attribute VB_Name = "Class1"
Option Explicit
Leave the VERSION and Attribute lines at the top of the file; they are not compiled as code. Each class name must be different from the other class names, the UserControl names, and the form names in the project.
UserControls in the IDE
A UserControl is a control you write in BASIC (see UserControls: Your Own Controls in the BASIC Language Reference for how to write one). This topic covers making one in the IDE, editing it, trying it on a form, and turning it into a control file every program can use.
Making a UserControl
Choose File > New UserControl. The IDE adds a new UserControl named UserControl1 (UserControl2, and so on, when that name is taken) to the project's Controls group, saved in the project's directory as UserControl1.ctl, and opens it in the Code Editor. It is written to disk when you save it. A new UserControl starts like this:
VERSION DVX 1.00
Begin UserControl UserControl1
Width = 64
Height = 64
End
Option Explicit
Private Sub UserControl_Paint ()
End Sub
The lines up to End are the control's header; the code follows. To use a .ctl file you already have, choose File > Add File and pick it (the UserControls file type). Open, save, rename, and remove .ctl files as you do class modules: a UserControl named after its file takes the new name when the file is renamed.
Writing the Code
In the Code Editor, the Object list of a UserControl holds UserControl (the control itself) beside (General). Its Function list has the events the control gets: Initialize, Terminate, Resize, Paint, Click, DblClick, MouseDown, MouseMove, MouseUp, MouseWheel, KeyDown, KeyPress, KeyUp, GotFocus, and LostFocus. Picking one that has no code yet writes its procedure with the right parameters, for example:
Private Sub UserControl_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single)
End Sub
These procedures are Private: a Public one would be a method of the control.
The Header in the Properties Window
While a UserControl is the active file, the Properties window shows its header instead of a form's controls: Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, and DefaultEvent, each with the value the control takes (its default when the header leaves it out). Double-click a row to change it:
Row How it is changed --- ----------------- Name Type a new name. It must be a valid name that no form, class module, or other UserControl of the project uses. Forms that use the old name are not changed. Width, Height Enter a whole number of pixels (at least 1): the control's natural size. CanGetFocus Each double-click switches between True and False. BackColor Pick a color in the color dialog. ToolboxIcon Type the name of a 24 x 24 picture file in the project's folder. Description Type the toolbox tooltip. DefaultEvent Pick one of the control's Events (or none): the event the designer opens when a placed control is double-clicked.
The change is written into the header lines of the file (the rest of the file stays as it is) and saved with the file. The Code Editor shows the new header; breakpoints in the code move with the lines below it. You can also edit the header lines in the Code Editor by hand.
Trying It on a Form
While the project is open, its UserControls are in the Toolbox like the controls DVX comes with: with the picture of their ToolboxIcon (or their name on the button), and their Description as the tooltip. Place one on a form as any other control. The control on the design surface is a real one: its code runs, with UserControl.UserMode False, so Initialize, Resize, and Paint draw it as it will look, while your clicks and keys go to the designer.
- The Properties window lists the control's properties that can be set (its Public variables and Property Let procedures), with the values its code gives them. A value you change there is passed to its Property Let and saved in the .frm; when the control's code calls UserControl.PropertyChanged, the window shows the new values.
- The Code Editor's Function list for the placed control has the events the control raises, then the events a form gets for any of its controls (GotFocus, LostFocus, Validate, DragDrop, and DragOver); picking one writes its handler with the event's parameters (Sub Gauge1_Changed (ByVal Level As Integer)).
- Double-clicking the control on the design surface opens its DefaultEvent's handler.
The designer compiles the project's UserControls (with its modules and class modules, but not its forms) when the project opens and again when a .ctl file is saved, added, removed, or renamed, or its header is changed in the Properties window; the controls on the design surface are made again then. While the controls have a compile error the designer cannot show them (the status line says so); compile with F5 to see the error. The code of the project's modules (outside their procedures) does not run in the designer, so module variables start empty there. A UserControl whose name is the name of a control installed for every program cannot be shown in the designer, and the program stops when it is run ("Control cannot be registered"): give it another name.
When the program runs, its UserControls are its own: they are registered again for the program, separately from the copies in the designer.
Control Projects and Make Control
A project whose controls every program should be able to use is a control project: in File > Project Properties set Type to Control. A control project needs no forms. File > Make Executable becomes File > Make Control: it compiles the project, asks where to save the control file (the project's name with .wgt) and for a debug or release build, and writes the file -- the same file the BASIC compiler makes from the project. Run (F5) only compiles a control project: it is not a program.
To install the control file, copy it into DVX's WIDGETS folder (in a folder of its own, like the controls DVX comes with). When DVX starts next, every program can use its controls, and the Toolbox shows them with their ToolboxIcon pictures and Descriptions.
A program project can hold UserControls too (list them under Controls): the program built from it (Make Executable) carries them, and only that program uses them.
Properties Panel
The Properties panel (Window > Properties) has two sections:
- Control tree -- a TreeView at the top listing the form and all its controls in layout order. Click a control name to select it in both the Properties panel and the Form Designer. Drag items in the tree to reorder controls in the form's layout.
- Property list -- a two-column ListView below the tree showing property names and values for the selected control. Double-click a property value to edit it via an InputBox dialog. Changes take effect immediately in the designer preview.
Each control type exposes different properties (e.g., Caption, Text, Width, Height, MaxWidth, MaxHeight, Weight, Alignment, Enabled, Visible, and type-specific properties like DataSource and DataField for data-bound controls). A UserControl placed on a form lists the properties its code defines.
The Font row shows the font the selected control, or the form, draws with: its family, size in pixels, and styles, such as "DejaVu Sans, 14, Bold". Double-click it to choose a font in the Font dialog; the design surface shows it at once. The choice is kept as the six Font properties (FontName, FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru), which then appear in the list and can be changed one at a time too. Choosing the font the control would have anyway (its container's or the form's) removes them, so the control follows the form's font again. A font chosen for the form is used by every control that has none of its own. For a TextBox, TextArea, ComboBox, SpinButton, or Terminal the dialog lists only fixed-pitch families.
While a UserControl (.ctl) is the active file, the panel shows the control's header instead: Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, and DefaultEvent (see UserControls in the IDE).
Toolbox
The Toolbox (Window > Toolbox) is a floating palette of buttons, one for each available control type. Every widget registered with DVX that has a BASIC name appears in the toolbox: the controls DVX comes with, the controls written in BASIC that are installed for every program (control files in the WIDGETS folder), and the open project's own UserControls (see UserControls in the IDE). The buttons are in alphabetical order of the control's name. A button shows the control's picture, or its name when it has none; its tooltip is the control's description.
The Toolbox opens four buttons across and as tall as its buttons need, down to the bottom of the screen. Resize it like any window: the buttons rearrange to fill its width, and when they do not all fit, a scroll bar reaches the rest.
Click a tool to select it (the active tool name is stored in the designer state), then click on the form to place a new instance. Click the same tool again to deselect it and return to pointer mode.
Available Controls
VB Name Description ------- ----------- CheckBox On/off checkbox. ComboBox Drop-down combo box. CommandButton Push button that triggers a Click event. Data Data control for binding to a database. DBGrid Data-bound grid for displaying database query results. DropDown Simple drop-down list. Frame Grouping container with a labeled border. HBox Horizontal layout container. HelpPane One topic of a help file, as the Help Viewer shows it. HScrollBar Horizontal slider/scrollbar. Label Static text label. Line Horizontal or vertical separator line. ListBox Scrollable list of items. ListView Multi-column list with headers. OptionButton Radio button (mutually exclusive within a group). PictureBox Canvas for drawing and images. ProgressBar Progress indicator bar. RichEdit Text box for text in many fonts, styles, and colors. RichLabel Text in many fonts, styles, and colors, in aligned paragraphs. ScrollPane Scrollable container for large content. Spacer Invisible spacing element for layout. SpinButton Numeric up/down spinner. Splitter Resizable split between two child panes. StatusBar Status bar at the bottom of a form. TabPage One page of a TabStrip, holding the controls shown on that tab. TabStrip Tabbed container with multiple pages. Terminal ANSI terminal emulator control. TextArea Multi-line text editor. TextBox Single-line text input field. Timer Non-visual timer that fires periodic events. Toolbar Toolbar container for buttons. TreeView Hierarchical tree control. VBox Vertical layout container. WrapBox Flow layout container that wraps items to the next row.
Image and ImageButton controls are not in the Toolbox; they can still be written into a .frm file by hand and are then edited in the Properties panel like any other control.
Debugger
The DVX BASIC IDE includes a full interactive debugger. The debugger has three states:
State Description ----- ----------- Idle No program loaded or running. Running The program is executing. Paused Execution is paused at a breakpoint or step point. The IDE is fully interactive -- you can inspect and change variables, set or clear breakpoints, step, continue, or stop.
Starting a Debug Session
- Shift+F5 (Debug) -- compiles the project and starts execution in debug mode. Breakpoints are active but execution does not pause at the first statement.
- F8 (Step Into) -- if idle, starts a debug session and breaks at the first statement.
- F5 (Run) -- compiles and runs without the debugger. No breakpoints are active. If already paused, resumes execution with debugging disabled.
Breakpoints
Setting Breakpoints
- Press F9 to toggle a breakpoint on the current editor line.
- Click in the line number gutter to toggle a breakpoint on that line.
Breakpoint Validation
Not every line can have a breakpoint. The IDE validates the line content and silently refuses to set breakpoints on:
- Blank lines
- Comment lines (' or REM)
- SUB and FUNCTION declaration lines
- END SUB and END FUNCTION lines
Breakpoint Storage
Each breakpoint records the file it belongs to, the line number within that file, and the procedure name (Object.Event). Breakpoints are stored in the project and survive compilation.
Visual Indicators
- Breakpoint lines show a red dot in the gutter.
- The current debug line (when paused) has a yellow background.
Breakpoint Adjustment on Edit
Breakpoints follow their statements when lines are added, removed, joined, split, or pasted. A breakpoint whose line is deleted is removed.
Stepping
Action Shortcut Behavior ------ -------- -------- Step Into F8 Execute one statement. If the statement is a SUB/FUNCTION call, step into it. Step Over Shift+F8 Execute one statement. If the statement is a SUB/FUNCTION call, execute the entire call and break at the next line in the current scope. Step Out Ctrl+Shift+F8 Run until the current SUB/FUNCTION returns to its caller. Run to Cursor Ctrl+F8 Run until execution reaches the line under the cursor.
Debug Run Behavior
When a program is running under the debugger:
- The program runs in short cooperative slices so the IDE remains responsive.
- When the program hits a breakpoint, execution pauses immediately. The IDE switches to Code View, navigates to the breakpoint line, highlights it in yellow, and opens the Locals and Call Stack windows if they are not already visible.
- While paused, you can inspect variables in Locals and Watch, evaluate expressions in the Immediate window, assign new values to variables, toggle breakpoints, step, continue, or stop. A pause in module-level code works the same way as a pause in an event handler.
- Resuming (F5, Shift+F5, or any Step command) returns the program to Running state.
Stopping
Press Esc or click the Stop toolbar button at any time to halt execution. The program is terminated, the debugger returns to Idle, and the IDE restores any designer or code windows that were hidden at the start of the run.
Locals Window
When the debugger pauses, the Locals and Call Stack windows are auto-opened (if not already visible). The Watch and Breakpoints windows can be opened manually from the Window menu.
Shows variables for the current execution scope. Displayed as a three-column ListView:
Column Content ------ ------- Name Variable name (internal mangled names like static variable placeholders are filtered out). Type Data type: Integer, Long, Single, Double, String, Boolean, Date, Variant, Array, UDT, or the type of object. Array types show the element type (e.g., Integer()). Value Current value. Strings are shown in quotes. Booleans as True/False. Arrays show bounds and element count (e.g., Integer(0 To 9) [10]). Uninitialized arrays show (uninitialized).
Variants show the value they currently hold, or Empty or Null; an OPTIONAL Variant parameter the caller left out shows Missing. Object variables show Nothing, or which object they refer to: the class name of a class instance, a Collection with its number of items, or the form or control.
The Locals window displays:
- Local variables of the current procedure, including its STATIC variables and the module variables it names in a SHARED statement.
- Module-level variables. While execution is paused inside a SUB or FUNCTION, only the variables declared with DIM SHARED (or REDIM SHARED) are listed, because the procedure cannot see the others. This also applies inside a DEF FN function, although a DEF FN can read every module variable.
- Form-scoped variables for the current form (if the program is executing within a form context), with the same SHARED rule.
- Member variables of the object, while execution is paused in a procedure of a class module: the variables of the very object the procedure was called for (Me), Private ones included.
The Watch window and the Immediate window follow the same rules. The window is resizable.
Call Stack Window
Shows the current call chain as a two-column ListView:
Column Content ------ ------- Procedure Procedure name (or (module) for module-level code). Line Line number where execution is paused (shown for the topmost frame).
The current location is shown first, followed by each caller in the call stack (walking from the deepest frame back to the module entry point). Up to 32 frames are displayed.
Watch Window
Allows monitoring arbitrary expressions while debugging. The window has a text input at the top and a two-column ListView below:
Column Content ------ ------- Expression The watch expression text. Value Evaluated result (or <error> if evaluation fails). Blank when not paused.
Adding Watch Expressions
Type an expression in the text input and press Enter. Up to 16 watch expressions can be active at once.
Watch Expression Syntax
Watch expressions support:
- Simple variable names: x, count
- Array subscripts: arr(5), matrix(2, 3)
- UDT field access: player.name
- Combined: items(i).price
- Arbitrary BASIC expressions (compiled and evaluated against the paused VM's state): x + y * 2, Len(name$)
Editing and Deleting
- Double-click or press Enter on a watch entry to move it back into the input box for editing.
- Press Delete to remove the selected watch expression.
Breakpoints Window
Lists all set breakpoints as a three-column ListView:
Column Content ------ ------- File Project file path. Procedure Procedure name (Object.Event format, or (General)). Line Code line number within the file.
- Double-click a breakpoint to navigate the code editor to that location.
- Press Delete to remove selected breakpoints (multi-select is supported).
Immediate Window
The Immediate window is an interactive REPL at the bottom-right of the screen. Type a line of BASIC and press Enter to evaluate it. Results appear inline below your input. Enter evaluates the line it is pressed on (a line split by Enter is joined back first); Enter on an empty line does nothing. A statement that does not finish (for example DO: LOOP) is stopped with "Error: Statement did not finish; stopped."
Expression Evaluation
If the input is not a recognized statement keyword, it is automatically wrapped in a PRINT statement. For example, typing 2 + 2 evaluates as PRINT 2 + 2 and displays 4.
You can also type full statements:
- PRINT x * 2 -- evaluate and print an expression.
- DIM tmp As Integer -- declare a temporary variable.
- LET x = 42 -- explicit assignment (see below).
Parse or runtime errors are displayed inline with an Error: prefix.
Inspecting Variables While Paused
When the debugger is paused at a breakpoint, the Immediate window has access to the running program's state. Expressions like count or name$ & " test" display live values.
Assigning Variables While Paused
When paused, you can modify variables in the running program directly from the Immediate window using assignment syntax:
variableName = newValue
The optional LET keyword is also accepted:
LET variableName = newValue
Assignment works for:
- Scalar variables -- x = 42, name$ = "test", count = count + 1 (any expression is allowed on the right)
- Array elements -- arr(5) = 100, matrix(2, 3) = 7.5
- UDT fields -- player.score = 1000
- Combined -- items(0).price = 9.99
The new value is written directly into the running program's variable (local, global, or form scope). A confirmation message is displayed, and the Locals and Watch windows update automatically to reflect the change.
If the assignment target cannot be resolved (unknown variable, out-of-bounds index, wrong type), an error message is displayed.
Output Window
The Output window is a read-only TextArea at the bottom-left of the screen. It displays:
- PRINT output -- PRINT statement output from the running program is appended here, until the program uses a console or graphics statement (CLS, LOCATE, COLOR, SCREEN, LINE, INKEY$, and the others): from then on PRINT and INPUT use the program's own screen window instead (see Console and Graphics in the language reference).
- Runtime errors -- if the VM encounters a runtime error (division by zero, out-of-bounds, etc.), the error message and line number are displayed in the output with an Error on line N: prefix.
- Compile errors -- if compilation fails, the error message and location are shown.
The output buffer holds up to 32,768 characters; when it is full the oldest lines are dropped. The window always shows the newest output. Use Run > Clear Output to clear it.
INPUT statements prompt the user via a modal InputBox dialog; the prompt text is also echoed to the Output window. A program that has its own screen window types its INPUT there instead.
The Program's Screen Window
A program that uses the console or graphics statements gets a window of its own, the size of its screen mode (80x25 text, or 640x480 or 320x200 graphics), titled with the project's name. Alt+Enter in that window (or FULLSCREEN in the program) shows it on the whole screen; Alt+Tab brings the desktop back and the program continues. Keys typed in the window go to the program (INKEY$ and INPUT). The window stays open after the program's code has finished, so its output can be read; closing it ends the program.
Find / Replace
Open with Ctrl+F (Find) or Ctrl+H (Replace). The Find/Replace dialog is modeless -- it stays open while you continue editing.
Dialog Controls
Control Description ------- ----------- Find input The text to search for. Replace checkbox + input Enable replacement mode and enter replacement text. Scope Radio group: Function, Object, File, or Project. Default is Project. Direction Radio group: Forward or Backward. Match Case Checkbox: case-sensitive search.
Buttons
Button Action ------ ------ Find Next Find the next occurrence. Function scope searches the current procedure, Object scope the selected object's procedures, File scope the current file, and Project scope every file. At the end of the current procedure the search wraps around to its start. Replace Replace the current match and find the next one. Replace All Replace all occurrences within the selected scope. Close Close the dialog.
Keyboard Shortcut
F3 repeats the last search (Find Next) without opening the dialog, also after the dialog was closed (then searching the whole project, ignoring case). Scope and direction chosen with the keyboard take effect like mouse clicks.
Preferences
Open via Tools > Preferences. Settings are saved to dvxbasic.ini in the app's config directory. The dialog has two tabs.
General Tab
Editor Section
Setting Description Default ------- ----------- ------- Skip comments/strings when renaming When renaming a control or form, skip occurrences inside comments and string literals. On Require variable declaration (OPTION EXPLICIT) When enabled, newly created projects have OPTION EXPLICIT on by default. Off Tab width Number of spaces per tab stop. 3 Insert spaces instead of tabs When enabled, pressing Tab inserts spaces. When disabled, inserts a real tab character. On
New Project Defaults Section
These fields set the default values for new project metadata:
Field Description Default ----- ----------- ------- Author Default author name. (empty) Publisher Default publisher/company name. (empty) Version Default version string. 1.0 Copyright Default copyright notice. (empty) Description Default project description (one line). (empty)
Colors Tab
Customizes the syntax-highlighting colors used in the code editor. The left side is a list of seven color entries (Default Text, Keywords, Strings, Comments, Numbers, Operators, Types). Select an entry to see its color, and its red, green, and blue amounts, on the right. Click Change... (or double-click the entry) to pick a new color in the color box: click a basic color or mix one with the Red, Green, and Blue sliders or number boxes, then click OK. Changes are applied to the open editor when you click OK in the Preferences dialog.
Keyboard Shortcuts
Shortcut Action -------- ------ Ctrl+O Add File Ctrl+S Save File Ctrl+A Select All Ctrl+X Cut Ctrl+C Copy Ctrl+V Paste Ctrl+F Find Ctrl+H Replace Ctrl+E Menu Editor F1 DVX BASIC Help F3 Find Next F5 Run Shift+F5 Debug Ctrl+F5 Run Without Recompile Esc Stop F7 Code View Shift+F7 Design View F8 Step Into Shift+F8 Step Over Ctrl+Shift+F8 Step Out Ctrl+F8 Run to Cursor F9 Toggle Breakpoint Del Delete
Changes in This Release
This release fixes a large number of problems in the language, the runtime, the controls, and the IDE. Most programs keep working unchanged. Three changes need attention in existing programs: variable scope (see Module Variables Need SHARED), the order of the bytes in a color, and text stored in numbers (see Colors and Numbers Follow Visual Basic).
Module Variables Need SHARED
DVX BASIC now uses the QBasic scope rules. A SUB or FUNCTION sees a variable declared at module level (including the code section of a form) only when it is declared with DIM SHARED, or named in a SHARED statement inside the procedure. Before, every module variable was visible everywhere.
- In an existing program, change DIM to DIM SHARED for every module-level variable that a SUB, FUNCTION, or event handler uses.
- Without that change such a name silently becomes a local variable of the procedure (or a compile error with OPTION EXPLICIT, or when the procedure indexes or dots it).
- New: the SHARED statement inside a procedure, and REDIM SHARED at module level. DIM SHARED inside a procedure is a compile error.
- CONSTs, TYPEs, procedures, forms, and controls are visible everywhere as before; a DEF FN still sees every module variable.
See: Variable Scope and SHARED
Colors and Numbers Follow Visual Basic
- A color is &HBBGGRR, as in Visual Basic: RGB(255, 0, 0) is &HFF (red) and &HFF0000 is blue. RGB, GETRED / GETGREEN / GETBLUE, ForeColor, BackColor, FillColor, the drawing methods, POINT, the Canvas control, and DlgColor all use this form. A program that wrote colors as &HRRGGBB numbers must swap the red and blue bytes, or build its colors with RGB. New: QBCOLOR(n) and the constants vbBlack, vbRed, vbGreen, vbYellow, vbBlue, vbMagenta, vbCyan, and vbWhite.
- Text used as a number must be a number, as in Visual Basic: n = "12" stores 12, but n = "abc", "abc" + 2, or a READ of DATA abc into a number raise error 13 (Type mismatch) and leave the variable as it was. VAL still reads the number at the start of any text, and INPUT reads numbers the way VAL does.
- A Long result too large for a Long raises error 6 (Overflow), as in Visual Basic, instead of becoming a Double (an Integer result still becomes a Long). Use CDBL on an operand when a larger result is expected.
Language
- GOSUB and RETURN work inside SUB and FUNCTION: each procedure call has its own GOSUB stack. RETURN with no pending GOSUB in the procedure raises error 3 (RETURN without GOSUB); before, RETURN inside a procedure left the procedure (use EXIT SUB / EXIT FUNCTION for that).
- Labels belong to their SUB or FUNCTION (or to the module level). GOTO, GOSUB, ON GOTO/GOSUB, and ON ERROR GOTO reach only labels of the same procedure; the same label name can be used in several procedures, and a label may share a name with a variable. An undefined label, or a jump to a SUB or variable name, is a compile error. "SubName:" at the start of a line is an error instead of a silent label.
- A CONST inside a SUB or FUNCTION is local and no longer overrides a module constant. CONST name AS type converts the literal (Integer and Long round and range-check); a string/number mismatch or an overflow is a compile error.
- DEF FN must be defined before it is used; its free names are module variables.
- DEFINT, DEFLNG, and the other DEFtype statements also apply to parameters, STATIC variables, and FUNCTION results. A typed FUNCTION converts the value assigned to it (FUNCTION F AS INTEGER with F = 2.5 returns 2). STATIC variables convert on store and their names no longer leak out of the procedure.
- DIM a, b AS STRING, c(3) declares several variables in one statement.
- name(...) in an expression only calls FUNCTIONs; a SUB there, or indexing a scalar or CONST, is a compile error.
- A FUNCTION can be called as a statement (F 3), discarding its result.
- DECLARE must match the definition's BYVAL and OPTIONAL flags too.
- SWAP and GET #n, , arr(i) evaluate each subscript exactly once. A BYREF argument written as an expression such as arr(i) + 1 is passed by value.
- Numeric literals: an Integer/Long suffix on a literal with a fraction or exponent rounds (1E3% is 1000); -32768% and -2147483648& are accepted; 1. is a valid literal; an over-long literal is an error.
- DATA: unquoted items are raw text up to the next comma, colon, comment, or end of line.
- READ, INPUT, INPUT #, and LINE INPUT # accept array elements and TYPE fields; PRINT #n, alone writes an empty line.
- PRINT USING: ; and , both separate values, and a trailing ; or , suppresses the newline.
- More than 8 array dimensions, or an LBOUND/UBOUND dimension outside 1..8, is a compile error.
- A missing END SUB, END FUNCTION, END TYPE, or END DECLARE is a compile error.
- FOR: a constant fractional STEP with an Integer or Long counter is a compile error, and a computed STEP is rounded to the counter's type. A fractional STEP no longer turns a Single counter into a Double.
- DECLARE LIBRARY arguments are always passed by value, converted to the declared parameter type (a Single parameter receives a Single).
- Name(idx).Member works with any member name (including keywords such as Name and Text), with method calls, and in chains; Me.Method (x) is a method call.
- Compile errors read "Line N, Col C: message" and point at what is wrong (the column is left out only at the end of the source). A SUB called but never defined is reported where it is called.
- Built-in function names are not reserved: a variable, constant, SUB, or FUNCTION the program declares with the same name is used instead (see How Names Are Found in the language reference).
- Assigning a whole TYPE value to a TYPE variable, array element, or field copies it into the record that is there, as in Visual Basic, so a SUB that was passed one of its fields by reference sees the new value.
- A program's compiled code is no longer limited to 64 KB, and programs compile faster with less memory (a program with many procedures twice as fast).
- New: COMMAND$ returns the arguments the program was started with (for example the document it was opened with), and COMMAND$(n) returns the nth argument without its quotes. The DVX programs that open documents (Notepad, Image Viewer, Icon Editor, Resource Editor, Help Editor) open the file named there when they start. In the IDE, set the arguments under Project Properties. A program that uses COMMAND$ does not run on an older DVX.
- A SUB or FUNCTION may use at most 64 local slots (parameters, local variables, and hidden temporaries). More is a compile error "Too many local variables in X (n, limit is 64)" instead of a runtime error 9; READ and INPUT # into many array elements no longer use a slot per target.
- SHARED naming a variable that does not exist at module level creates it there; in a form's code it becomes a variable of that form, not a program-wide one.
- New types Byte (0 to 255) and Currency (an exact amount with four decimal places, suffix @), with CBYTE, CCUR, DEFBYTE, and DEFCUR. A variable declared with a type has that type before anything is stored in it (VARTYPE of an unassigned Long is vbLong).
- Named arguments, as in Visual Basic: Report title:="Sales", width:=60 for SUBs, FUNCTIONs, class methods, and the built-in functions that have argument names (MsgBox, InputBox, Mid, InStr, Format, DateAdd, and others).
- QBasic line numbers: a line may start with a number from 0 to 65529, which GOTO, GOSUB, ON GOTO, IF ... THEN 100, RESTORE, and RESUME can name; ERL gives the last line number before the error. RESTORE label and RESUME label are new too.
- STATIC arrays and STATIC TYPE variables keep their contents between calls.
- Members of an object whose class the compiler knows (declared As the class or As Collection, New, Me in a class) are checked when the program is compiled: a misspelled or Private member, the wrong number of arguments, a Sub used as a value, a property used as a statement or assigned without a Property Let are compile errors that point at the member, and the calls run faster. Through As Object and As Variant members are still found when the program runs. A class's own member variables can be used anywhere in the class, and Me reaches its Private members. Passing a number, text, or an object of another class to an object parameter of a known class is a compile error.
Objects, Classes, and Variants
- New: class modules (.cls files) define your own objects, with Public and Private member variables, Subs and Functions, Property Get / Let / Set, Class_Initialize and Class_Terminate, Me, and a default member. Objects are made with NEW (DIM x AS NEW Account, SET x = NEW Account), compared with Is and tested with TypeOf ... Is. An object is freed, and its Class_Terminate run, as soon as nothing refers to it any more.
- New: the Collection object (Add, Remove, Item, Count, with optional text keys) and FOR EACH ... NEXT over Collections and arrays.
- New: the Variant type (DIM v AS VARIANT) with the special values Empty and Null, and the functions VARTYPE, TYPENAME, ISEMPTY, ISNULL, ISNUMERIC, ISOBJECT, ISARRAY, and CVAR, with the vb... VarType constants and vbObjectError.
- New: PUBLIC and PRIVATE at module level (PUBLIC x is DIM SHARED x, PRIVATE x is DIM x).
- New error numbers: 94 Invalid use of Null, 424 Object required, 438 Object doesn't support this property or method, 450 Wrong number of arguments or invalid property assignment, 451 Object not a collection, 457 duplicate Collection key.
- Changed: CHOOSE with an index outside its list returns Null instead of raising error 5.
- Changed: a PARAMARRAY without an AS clause is an array of Variants (TYPENAME "Variant()").
- A form or control name can be used directly wherever an object is expected: SET c = Text1, either side of Is, TypeOf, and an argument for an object parameter.
- NEW, PROPERTY, PUBLIC, PRIVATE, and TYPEOF are now reserved words: a variable, SUB, or FUNCTION with one of these names must be renamed. VARIANT, OBJECT, COLLECTION, EMPTY, NULL, and EACH are colored like keywords but are not reserved.
- IDE: File > New Class Module, .cls files in Add File, a Classes group in the Project Explorer, and a [Classes] section in the project file. The debugger shows Variants, objects, and the member variables of the object whose code is running.
- New: events -- EVENT and RAISEEVENT in class modules, DIM WithEvents variables in modules and forms with handlers named variable_EventName.
- New: IMPLEMENTS -- a class can provide the members of another class (the interface) and its objects can be used through variables of the interface type.
- New: ISMISSING for OPTIONAL Variant parameters; arguments can be left out in the middle of a call (Report a, , c); FORMAT$ without a format string; LSET of whole TYPE records.
- Changed: objects that only refer to each other in a loop are found and freed while the program runs (their Class_Terminate runs first) instead of staying until the program ends.
- Changed: an array stored in a Variant is a copy, not shared; ParamArray arguments that are variables are passed by reference; a left-out Variant argument of a method is Missing instead of Empty.
- New error numbers: 448 Named argument not found (a Missing value used), 449 Argument not optional.
- EVENT, RAISEEVENT, WITHEVENTS, and IMPLEMENTS are colored like keywords but are not reserved.
- New: default values for OPTIONAL parameters (Optional count As Integer = 10), in procedures, class methods, and Property procedures.
- New: a class module can declare WithEvents member variables and handle the events of the objects it holds; listening never keeps an object alive.
- Changed: ParamArray arguments that are array elements, TYPE fields, or a class's member variables are passed by reference too, and so are variables and array elements given to a class's methods through As Object or As Variant variables (for parameters that are not BYVAL).
- New: PLAY's X command and "=" numbers with VARPTR$, as in QBasic; each screen page keeps its own text cursor; a PictureBox fires Paint after its size changes.
- New: OPTIONAL object parameters can have the default Nothing (Optional target As Object = Nothing).
- New: TYPE fields given to a class's methods (obj.Move rec.x) are passed by reference; values a method stores in a variable passed by reference, or copies back from its ParamArray, are converted to the variable's type. The methods of forms and controls take their arguments by value.
- Changed: REDIM (or ERASE of a dynamic array) while one of the array's elements is passed by reference to a running procedure raises error 10 (This array is fixed or temporarily locked), as in Visual Basic, instead of leaving the old element behind.
- New: LOADPICTURE returns a Picture object for DragIcon and the Picture of Image and ImageButton (file names still work); new error 481 Invalid picture.
- New: while a control is dragged, the pointer shows the "no drop" sign over objects that have no DragDrop event procedure and outside the program's forms.
- IDE: File > Rename File; WithEvents variables and their class's events in the code window's dropdowns; date literals and the console and graphics statement names are colored.
- Changed: a parameter passed by reference is the caller's variable itself, as in Visual Basic. A TYPE field given to a SUB or FUNCTION (Bump rec.count, Bump recs(2).count) is now passed by reference (it was passed by value); a write reaches the variable at once instead of when the call returns, also through a ParamArray, a class's method, and RaiseEvent, so it stays when the procedure later stops with an error and every handler of an event writes to the same variable; a STRING * n variable or field keeps its length at every write. The debugger shows (and changes) the caller's variable for such a parameter.
See: Objects and Class Modules
Sound, Joysticks, and Frames
- New statements from QBasic: BEEP, SOUND frequency, duration (in clock ticks, 18.2 a second), and PLAY with the whole music macro language (notes, octaves, lengths, dots, pauses, tempo, MN/ML/MS, and MF/MB for foreground or background music), plus the PLAY(n) function (notes still waiting in the background). They play on whatever sound hardware the computer has, or silently when there is none.
- PLAY's new I command plays the notes that follow on a General MIDI instrument (I1 to I128) through the music synthesizer; I0 goes back to tones.
- New functions from QBasic: STICK(n) (joystick position, 1 to 200) and STRIG(n) (joystick buttons), on any joystick DVX knows, or the keyboard when there is none.
- New statement FRAMEWAIT rate: waits for the next frame of a steady clock, keeping the desktop running, so a game loop runs at the same speed on every computer.
- BEEP, SOUND, PLAY, and FRAMEWAIT are now keywords, and STICK and STRIG built-in functions: a variable, SUB, or FUNCTION with one of these names must be renamed.
- Message boxes (MsgBox) play the desktop sound chosen for their kind on the Control Panel's Sounds tab.
See: Sound, Joysticks, and Frames
Runtime
- Nested BYREF parameters reach the original variable or array element.
- ON ERROR is per procedure. A procedure called from a handler can trap its own errors, returning from a procedure that only installed a handler leaves the caller's ERR alone, and an error inside a running handler passes to the caller's handler.
- RESUME NEXT after an error in an IF/ELSEIF/WHILE/DO/LOOP condition skips the block or leaves the loop; after an error in a FOR statement the loop is skipped; after an error in the SELECT CASE expression the whole SELECT CASE block is skipped; after an error in a CASE test that test counts as false and the next value or CASE is tried; after an error in an ON selector no branch is taken.
- New error numbers: 3 RETURN without GOSUB, 14 Out of string space, 28 Out of stack space (was reported as 7), 63 Bad record number, 91 Object variable not set. ERR is a Long, and ERROR n reports the standard message for known numbers.
- Using a property or method (or Show, Hide, Unload) on a value that is not a form or control raises error 91 instead of doing nothing.
- EXP, ^, and other math that leaves the numeric range raise error 6 (Overflow); an undefined result raises error 5.
- INT, FIX, and SGN return an Integer (Long or Double when needed); RND returns a Double and follows QBasic (RND(0) repeats the last number, a negative argument reseeds). RANDOMIZE with a negative or very large seed, and RND with a very large negative argument, seed predictably (the seed is taken modulo 2^32), so the sequence for a negative RANDOMIZE seed may differ from before. -x of the most negative Integer or Long widens like 0 - x.
- INSTR follows OPTION COMPARE TEXT. INSTR and the MID$ statement reject a start below 1 (error 5); MID$ with a length of 0 replaces nothing.
- PRINT # writes a space after numbers, as PRINT does. PRINT USING and FORMAT$ show a value too wide for the field in scientific notation.
- SEEK on a RANDOM file works in records (statement and function); a position below 1 is error 63. PUT of a value longer than the record length raises error 59 before writing anything.
- SLEEP keeps the desktop and the program's forms running, SLEEP 0 just processes events, and closing the program ends the sleep.
- DIR$ returns only files. GETATTR and SETATTR on a missing file raise error 53; SETATTR raises 75 when it cannot change the attributes.
- IniRead and IniWrite use the same INI reader as the DVX system settings. IniWrite keeps the text of every other line (of any length) unchanged, ends every line of the saved file with CR LF, and raises error 75 when it cannot read the existing file or write the new one (a write failure used to be ignored); a failed write leaves the old file intact. IniRead no longer finds keys in the tail of a long line, and keys above the first section header are read with section "". Section names are matched with surrounding blanks ignored.
- VAL keeps the magnitude of very long digit strings. Running out of memory while building a string raises error 14.
- REDIM PRESERVE of a TYPE array keeps the existing elements.
- Appending to a String variable with s = s + x or s = s & x no longer copies the whole string each time, so building long strings in a loop is much faster. When x is InputBox$, MsgBox, CreateForm, CreateControl, or Shell, s is still read before x runs (left to right).
- And, Or, Xor, Eqv, and Imp of two Booleans give a Boolean, and Not of a Boolean gives a Boolean, as in Visual Basic 4 and later: Print (a > 1) Or (b > 1) prints True instead of -1, and TypeName gives "Boolean". With a number on either side they still work on the bits and give a number.
- Big disks and long file names: where the disk takes long file names (DVX under Windows, or a long file name driver loaded before DVX), OPEN, KILL, NAME, MKDIR, DIR$, and the other file statements use them, and the File Manager, the file dialogs, and Terminal downloads keep them. A file can be bigger than 2 GB on a FAT32 disk; LOF, FILELEN, LOC, and SEEK raise error 6 (Overflow) for a size or position past 2 GB rather than give a wrong number, and EOF stays right below it. FileOpsFreeBytes and FileOpsTotalBytes are right for disks past 2 GB.
- Paths may be written with / or \ wherever a path is taken (OPEN, KILL, NAME, MKDIR, CHDIR, DIR$, FILELEN, and the file and folder functions of the libraries), mixed too; paths DVX gives back (CURDIR$, App.Path, App.Data, the file dialogs, the FileOps results) use \, as DOS shows them. New: FileOpsPathNative$ gives a path of your own that form. DIR$("C:\*.*") lists the root of C: (before, it listed C:'s current folder). SHELL commands still go to DOS as written, where / starts a switch.
- The IDE shows a project's file paths with \ and keeps writing them with / in the .dbp file, so a project reads the same everywhere.
- The Open and Save As dialogs (DlgFileOpen$, DlgFileSave$, and the IDE's own) have a Drive list beside the Path box (Alt+D); a drive with no disk in it says it is not ready.
- Changed: FileOpsValidName takes the folder the name is for, FileOpsValidName(folder, itemName), since the rules depend on its disk; XferSafeName$ takes the folder a file is received into, XferSafeName$(name, folder). New: FileOpsLongNames, FileOpsShortPath$, FileOpsValidLongName, and FileOpsValidShortName (fileops.bas), and ShellArgQuote$ (shell.bas), which puts a path with spaces in quotes for a command line.
- Programs compiled with an older release must be rebuilt. A program whose procedure table records more than 64 locals for a procedure is refused when it is loaded.
- A class may have at most 65536 member variables and a form at most 65536 form-level variables (as many as the compiled program can number); more is a compile error ("Too many member variables in X", "Too many form-level variables"), and a program file that records more is refused when it is loaded.
Forms and Controls
- A reference to a removed control or to a control of an unloaded form raises an "unknown control" runtime error. CreateControl with a parent that is not a container on the same form raises "CreateControl: invalid parent".
- SetEvent works on forms (Me) and menu items; a handler without parameters is called without the event's arguments.
- Frame: the Caption from the .frm file or set by code now shows on the frame's border, and reading Caption gives it back (it was ignored and read back "").
- A control's Index below 0, in a .frm file or typed in the designer, means the control is not in a control array, in the designer, the form loader, and the saved file alike (a negative Index was kept, saved without an Index line, and read back as a different control).
- ListBox: a horizontal scroll bar appears when the box is narrower than its longest item (its MaxWidth, or a narrow form); Left and Right scroll it a character at a time.
- Padding = 0 and Spacing = 0 now mean no padding and no space (before, 0 meant the default). A form, box, or ScrollPane that sets none keeps its own (4 pixels for forms, VBox, and HBox; a Frame's Spacing is 0, a ScrollPane's both 0), and reading Padding or Spacing gives the pixels in use. In the designer, an empty Padding or Spacing on the form means the layout's own.
- MaxWidth and MaxHeight win over MinWidth / MinHeight and over the size a control's contents ask for, and the space above the cap goes to the other controls instead of staying empty.
- New drawing members on forms and PictureBoxes: FontScale (Print, TextWidth, and TextHeight up to 8 times as large, for titles) and ScrollBarsNeeded (which scroll bars content of a size needs in an area, as the controls decide it; vbSBNone, vbHorizontal, vbVertical, vbBoth).
- Fonts: every control and form has FontName, FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru, as in Visual Basic. A control without a font of its own uses its container's or its form's. Print, TextWidth, TextHeight, PrintAccel, and TextWidthAccel on forms and PictureBoxes use the object's font. The new DlgFont in commdlg.bas shows the font dialog.
- Form.Visible = True/False behaves exactly like Show/Hide, but only on a real change: Visible = True on a form that is already shown does nothing (a modal form stays modal), and on a form that has no window it is ignored. MinWidth and MinHeight read back the value that was set. Size properties are limited to 0..32767.
- Disabling a container (for example Frame.Enabled = False) blocks mouse and keyboard input to every control inside it (also inside a disabled ScrollPane or Splitter), and SetFocus on such a control does nothing.
- OptionButton.Value is a read/write Boolean (True = selected); the button's index is the new read-only OptionIndex. Setting Value raises no Click.
- HScrollBar has Min and Max properties. SpinButton's RealMode uses the range set with SetRange. PictureBox has read-only ImageWidth and ImageHeight.
- ListBox SetItems "" empties the list, and every | separates items. ListView SetColumns has no length limit. TreeView Clear resets scrolling. TextArea FindNext finds a match that starts at the cursor, and the new SetCursor method moves the caret without selecting.
- TabStrip/TabControl: a negative tab index selects the first tab and an index past the end the last one.
- The mouse wheel over a DBGrid scrolls the rows without moving the current record, and over a Terminal scrolls its history locally.
- .frm files: End followed by blanks or a comment (also End'comment) closes its block while End followed by a word does not, in the compiler, the build, and the release renamer exactly as in the loader, Begin followed by a tab is accepted, Enabled = False on popup-menu items is applied, properties after a nested child's End apply to the parent, and Layout on a control that is not a container is reported.
- Include libraries: sql.bas, comm.bas, and commdlg.bas take and return Long (SQLFieldInt and SQLAffected no longer wrap above 32767); resource.bas string functions are declared AS STRING. CommHandshake takes an optional passphrase, and the new CommSetPlain accepts unencrypted data on a channel.
- commdlg.bas functions are renamed to match the other include libraries: basFileOpen is now DlgFileOpen$, basFileSave is DlgFileSave$, basInputBox2 is DlgInputBox$, basInputCancelled is DlgInputCancelled, basChoiceDialog is DlgChoice, basIntInput is DlgIntInput, and basPromptSave is DlgPromptSave. The old names are gone; programs that use them must be updated and rebuilt.
- New in comm.bas: CommRecvEncrypted(handle, channel) returns True when every byte the last CommRecv$ on that channel returned arrived encrypted and authenticated (also when it returned nothing), and False when any arrived unencrypted (before CommHandshake or on a CommSetPlain channel).
- KeyDown: Shift, Ctrl, or Alt with F1-F12, and Alt with Home, End, PgUp, PgDn, Ins, Del, and the arrow keys, report the plain key's KeyCode with the modifier in Shift, even when the modifier is released before the program sees the key.
- Data control: while its Validate handler runs, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, Refresh, and Update on the same Data control are ignored (they used to validate the same record again without end); Delete is allowed. Only unloading the Data control itself from Validate or Reposition stops the operation that fired it; removing other controls does not.
- Data control: writing a field back with the value it already has is not an edit, so leaving a bound control no longer fires Validate. With a SELECT query as RecordSource, edits are dropped when the current record changes. Update and Delete still find a record whose key is a number stored without a declared column type, or is NULL, when the key names exactly one record; Delete keeps the record shown when the database row could not be deleted.
- DBGrid re-sorts and re-measures its columns when the Data control's records change (Refresh, AddNew, Delete, or an edit), keeping the chosen sort column, and not on a plain record move.
- Image and ImageButton: Picture = "" clears the picture (Picture then reads back ""); a file that cannot be loaded leaves the picture and Picture unchanged. A disabled Image keeps its transparent color transparent.
- Timer: a Timer whose handler shows a modal dialog no longer fires a second time from inside that dialog.
- TextArea: the horizontal scroll bar appears as soon as the longest line fills the visible width, and the caret stays visible while typing at the right edge.
- Terminal: pasting with Ctrl+V returns the view to the live screen and sends the text in small pieces.
- ListBox and ListView: changing the items while an item is being dragged ends the drag.
- TabControl: switching tabs closes an open drop-down list only when it belongs to a control on the page being hidden. A Label on a Toolbar is drawn on the toolbar's color.
- Compile errors from the command-line compiler name the file and line. A .frm without a Begin Form line is an error. A release build that runs out of memory while renaming names fails instead of producing a broken program.
- A form shown modal has no minimize gadget.
- Menu items accept only Name (read-only), Checked, and Enabled in code; Caption, Visible, RadioCheck, Shortcut, and ShortcutText are design-time settings, and using them in code is a compile error in the IDE.
IDE
- Immediate window: Enter evaluates the line it is pressed on, assignments accept any expression, and a statement that does not finish is stopped.
- A compile error puts the caret on the column of the error, not just its line.
- Output window: always shows the newest output and drops the oldest lines when full; Output Window to Log also logs errors.
- Find/Replace: Object and File scopes search only that object or file; F3 works after the dialog is closed.
- Edit commands act on the window last worked in (designer control or text window). A pasted control keeps all its properties.
- The code editor never drops text outside the shown procedure; only untouched generated event handlers are removed.
- Files are saved exactly as written, with LF line endings, through a temporary file (for FORM1.FRM, FORM1.$FR, with the old file kept as FORM1.~FR until the save is complete; your own name.BAK is never touched); carriage returns are removed when files are loaded. Don't Save discards Project Properties changes too. A project file may hold up to 64 KB of text not counting carriage returns; a file of twice that size or more is refused without being read.
- Debugger: while paused inside a SUB or FUNCTION, Locals and Watch show only the variables the procedure can see (its locals, its STATIC and SHARED names, and DIM SHARED module variables), and STATIC variables are now listed.
- Compile-time checks of control properties and methods use the control on the form whose code is being compiled, so a control name used for different control types on different forms is checked too.
- Form designer: Image and ImageButton are no longer in the Toolbox, controls nest at most 15 levels, Delete removes a control with its contents, AutoSize matches the runtime, TabStrip TabIndex and the form's ContextMenu are kept on save, control arrays inside containers no longer duplicate controls, and a control keeps any number of design-time properties.
- Error and debugger line numbers in a form's code match the editor exactly (the indentation of the first code line is kept).
- Menu Editor: at most 15 levels, the close box cancels, and only real renames rewrite event handlers.
- Project Properties: Description is a single line.
- A Sub or Function whose name already exists (typed or pasted) stays its own procedure, and the compiler reports the duplicate; before, it was moved into (General).
- Breakpoints and the caret follow lines that the editor moves when it tidies a procedure (blank lines after End Sub, text typed outside a Sub, a deleted Sub line); a breakpoint whose line disappears is removed.
- Immediate window: output leaves room for typing, and Enter in a completely full window evaluates nothing and says so in the status bar.
- Debugger: a breakpoint reached while a stopped program is unwinding no longer pauses it.
- The title bar's * also shows for a form changed in the designer but not yet saved.
- Form designer: a .frm whose container has properties after the controls inside it (Index, Caption ...; DVX writes them before) opens as it runs: the properties are kept and the controls stay in that container. Before, they were dropped, and such a file with another control of the container's name opened as a blank form. Menu items' properties after their submenus are kept too. FrmOpen reads .frm files the same way.
- The Toolbox lists the controls in alphabetical order, as CtlTypeName$ does.
- Properties window: the Font row sets a control's or the form's font in the Font dialog, and the design surface shows the fonts.
- Toolbox: it can be resized, its buttons rearrange to fit, and a scroll bar appears when they do not all fit (with many controls installed, the bottom rows used to be off the screen).
- UserControls: File > New UserControl, a Controls group in the Project Explorer, the UserControl object and its events in the Code Editor, the header in the Properties window, the project's UserControls in the Toolbox and on the design surface (their code runs, with UserMode False), and control projects with File > Make Control. A program built with Make Executable carries its UserControls. See UserControls in the IDE.
- Form designer: a property value may be as long as a full .frm line (511 characters); a value too long for the line it is saved on (deeply nested controls) stops the save with "A property value is too long to save at its position in the form". Making a container part of a control array keeps its contents. Only one OptionButton per container keeps Value = True: setting it on one (in the property grid or by pasting a selected one) clears the others.
DVX BASIC 1.0 -- Copyright 2026 Scott Duensing
Data Types
DVX BASIC supports the following data types. Each type has a corresponding type suffix character that can be appended to variable names.
Primary Types
Type Size Suffix Range / Description ---- ---- ------ ------------------- Byte 1 byte (none) 0 to 255 Integer 2 bytes % -32768 to 32767 Long 4 bytes & -2147483648 to 2147483647 Single 4 bytes ! 32-bit float, approximately 7 digits precision Double 8 bytes # 64-bit float, approximately 15 digits precision Currency 8 bytes @ -922337203685477.5808 to 922337203685477.5807, exact to four decimal places String variable $ Variable-length, reference-counted, dynamic string Boolean 2 bytes (none) True (-1) or False (0) Date 8 bytes (none) A date and time of day from 1/1/100 to 12/31/9999 Variant varies (none) Any value at all: a number, text, a date, an object, an array, or the special values Empty and Null (see Variants)
Object Types
An object variable does not hold a value of its own; it refers to an object that lives elsewhere, so two variables can refer to the same object. Store a reference with Set, use the object's properties and methods with a dot, and set the variable to Nothing when you are done with it.
Declared As Holds ----------- ----- Control A control on a form (any type of control) Form A form Object Any object: a form, a control, an instance of a class module, or a Collection ClassName An instance of that class module (see Objects and Class Modules) Collection A Collection object (see Collection Object)
Dim c As Control
Set c = Text1
c.Text = "Hello"
Dim people As New Collection
people.Add "Ann"
An object variable that has not been Set holds Nothing. Control, Form, and Object variables accept each other's values; a variable declared As a class name or As Collection accepts only an object of that kind, or Nothing: an object of another kind raises error 13 (Type mismatch), and a value that is not an object at all raises error 424 (Object required). The Source parameter of the DragDrop and DragOver events is a Control.
See also: Objects and Class Modules
Internal Types
These types are not directly declarable but are used internally by the runtime.
Internal Type Description ------------- ----------- Array Reference-counted multi-dimensional array (up to 8 dimensions) UDT User-defined type instance (created with TYPE...END TYPE) Object Reference to a form, a control, a class instance, or a Collection; declarable as Control, Form, Object, a class name, or Collection Ref Reference to a variable, array element, TYPE field, or class member variable (what a ByRef parameter holds)
Type Suffixes
Type suffixes can be appended to variable names to declare their type implicitly:
count% = 42 ' Integer
total& = 100000 ' Long
rate! = 3.14 ' Single
pi# = 3.14159265 ' Double
price@ = 19.99 ' Currency
name$ = "Hello" ' String
Numeric Literals
Form Example Description ---- ------- ----------- Decimal integer 42 Values -32768..32767 are Integer; larger are Long Hex integer &HFF, &H1234 Hexadecimal literal (0-9, A-F) Octal integer &O10, &O77 Octal literal (0-7) Binary integer &B1010, &B11111111 Binary literal (0-1) Integer suffix 42% Force Integer type Long suffix 42&, &HFF& Force Long type Floating-point 3.14, 1.5E10, 2.5D3 Any number containing a decimal point or exponent is Double by default Single suffix 3.14! Force Single type Double suffix 3.14# Force Double type Currency suffix 19.99@, 5@ Force Currency type (more than four decimal places round, halves to even)
Note: Both E and D can introduce an exponent (e.g. 1.5E10 and 2.5D3 are equivalent forms of a scientific-notation double).
A literal with an Integer or Long suffix and a fraction or exponent is rounded (1E3% is 1000, 2.5% is 2). The most negative values -32768% and -2147483648& are valid. A trailing decimal point is allowed (1. is the Double 1). A numeric literal too long to read is a compile error.
Type Promotion
When mixing types in expressions, values are automatically promoted to a common type: Byte -> Integer -> Long -> Single -> Double. A Currency with a Byte, Integer, Long, or Currency stays Currency (see Byte and Currency below). Whole-number arithmetic keeps the type of its operands: an Integer result too large for an Integer becomes a Long (32767 + 1 is the Long 32768) and a Byte result outside 0 to 255 becomes an Integer, but a Long result too large for a Long raises error 6 (Overflow), as in Visual Basic. Convert an operand when a larger result is expected: CDBL(2147483647) + 1 is 2147483648.
+ joins two strings. When a string meets a number in an arithmetic operator (including +) or a comparison, the string must hold a number, as in Visual Basic: "1" + 2 is 3 and " 2.5 " * 2 is 5, but text that is not a number ("abc" + 2, "12abc" + 2) raises error 13 (Type mismatch). VAL reads the number at the start of any text instead (VAL("12abc") is 12). Use & to join text and numbers, and VAL / STR$ to convert explicitly.
Assignment Conversion
Storing a value into a variable declared as Byte, Integer, Long, Single, Double, Currency, String, Boolean, or Date (by suffix, by AS clause, by DEFtype, or as a typed parameter, STATIC variable, FUNCTION result, TYPE field, or array element) converts the value to that type: a number stored in a String variable becomes its text as STR$ gives it without the leading space ("5"). Text stored in a numeric variable must be a number, as in Visual Basic: "12" and " 3.5 " convert, while text that is not a number ("abc", "12abc") raises error 13 (Type mismatch) and the variable keeps its value. DEFtype statements also give parameters, STATIC variables, and FUNCTION results their default type. Byte, Integer, and Long round to the nearest whole number, with halves rounding to the nearest even number (2.5 becomes 2, 3.5 becomes 4), and raise error 6 (Overflow) when the result does not fit; Currency rounds to four decimal places the same way. Single keeps about 7 significant digits, so PRINT shows at most 7 digits of it. Boolean stores True for any non-zero number or non-empty string and False otherwise. Date takes a number as a date's number of days and reads a string as a date (error 13 when the text is not a date, error 6 outside 1/1/100 to 12/31/9999); INPUT into a Date variable reads the date the same way. A variable declared with DIM but without a type is Single (or the DEFtype for its first letter). A variable that is never declared keeps whatever type the assigned value has. A variable declared with a type holds that type from the start, before anything is stored in it: VARTYPE of a Long that was never assigned is 3 (vbLong) and of a String 8 (vbString).
Dim n As Integer
n = 3.7 ' n is 4
n = 40000 ' Error 6: Overflow
n = "12" ' n is 12
n = "twelve" ' Error 13: Type mismatch (n is still 12)
Dim b As Boolean
b = 3 ' b is True
Dim s
s = 1 / 3 ' s is Single: prints 0.3333333
x = 1 / 3 ' x was never declared and keeps the Double: prints 0.333333333333333
Boolean Values
Boolean values use -1 for True and 0 for False. Any non-zero numeric value is treated as True in a conditional context. The keywords True and False are reserved and may be used anywhere a Boolean value is expected.
Byte and Currency
A Byte holds a whole number from 0 to 255 in one byte. Storing a value outside that range raises error 6 (Overflow). Arithmetic on two Bytes gives a Byte when the result is 0 to 255 and an Integer otherwise; negating a Byte gives an Integer. CBYTE converts a value to a Byte.
A Currency holds an amount of money exactly: it counts ten-thousandths, so it has four decimal places and adding, subtracting, or multiplying amounts never drifts (adding 0.1@ ten times gives exactly 1). A value with more decimal places is rounded to four, halves to the nearest even digit (CCUR(2.00005) is 2, CCUR(2.00015) is 2.0002), and a result outside the Currency range raises error 6 (Overflow). +, -, and * on a Currency and a Byte, Integer, Long, or Currency give a Currency; with a Single or Double they give a Double, and / always gives a Double. Comparisons of Currency values are exact. PRINT and STR$ show a Currency with only the decimals it needs (12.5); FORMAT$ lays it out like any other number. A FOR loop with a Currency counter steps exactly. CCUR converts a value to a Currency.
Dim total As Currency
Dim i As Integer
For i = 1 To 3
total = total + 19.99@
Next
Print total ' 59.97
Print FORMAT$(total, "0.00")
GET and PUT store a Byte in 1 byte and a Currency in 8 (see GET / PUT).
Date Values
A Date holds a calendar date and a time of day, as in Visual Basic: the whole part of its number counts days from 12/30/1899 and the fraction is the time (0.5 is noon, so 36526.5 is noon on 1/1/2000). Days before 12/30/1899 are negative numbers whose time still counts forward from midnight: -1.25 is 6:00 AM on 12/29/1899. CDBL gives a date's number and CDATE turns a number back into a date.
Write a date literal between # signs. The month comes before the day; a year can also come first, and month names (full or abbreviated) work too. A time can follow the date or stand alone:
Dim d As Date
d = #1/15/2000# ' January 15, 2000
d = #2000-01-15 15:30# ' year first, 24-hour time
d = #Jan 15, 2000 3:30:00 PM#
d = #3:30 PM# ' a time alone is on 12/30/1899
Two-digit years 00 to 29 are 2000 to 2029 and 30 to 99 are 1930 to 1999. Date is not a reserved word, so a program may still use Date (or any of the date function names) for its own variables and procedures; the program's own name wins.
PRINT, STR$, CSTR, and the & operator show a date as m/d/yyyy h:mm:ss AM (1/15/2000 3:30:00 PM). A date at midnight shows only the date and a time on 12/30/1899 only the time; a Date variable that was never assigned is 12:00:00 AM. Use FORMAT$ for any other layout.
Adding a number of days to a date, or subtracting one, gives a date (fractions are parts of a day, so d + 0.25 is six hours later); subtracting one date from another gives the number of days between them as a Double. Comparisons put dates in time order, and a string compared with a date is read as a date:
Dim due As Date
due = Date + 30 ' thirty days from today
If due > #12/31/2025# Then Print "Next year"
Print #3/1/2000# - #2/1/2000# ' 29 (2000 is a leap year)
If due = "1/15/2026" Then Print "Match"
A Date variable has no Empty or Null state (a Variant does): a date that is out of range is error 6 (Overflow), and text that is not a date is error 13 (Type mismatch).
Variants
A Variant is a variable that can hold any kind of value: a number, text, a date, True or False, an object, or a whole array. It takes on the type of whatever is stored in it, and can change type each time something new is stored. A Variant can also hold two special values that no other type has: Empty (nothing has been stored yet) and Null (the value is unknown or missing).
Dim v As Variant
Print TypeName(v) ' Empty
v = 42
Print TypeName(v) ' Integer
v = "forty-two"
Print TypeName(v) ' String
v = #1/15/2000#
Print TypeName(v) ' Date
v = Null
Print IsNull(v) ' True
Declare a Variant with AS VARIANT. Arrays of Variants (DIM list(10) AS VARIANT), Variant parameters, and FUNCTIONs and properties AS VARIANT work the same way; each element of a Variant array starts Empty. A variable declared with DIM but no AS clause, or never declared at all, is not a Variant: it keeps the older rules described under Assignment Conversion.
Storing a Variant in a variable of another type converts the value to that type, as for any other assignment. A Variant that is given an array gets its own copy of the array, as in Visual Basic: after v = list, changing list(1) does not change v(1), and w = v copies again. The same holds when a Variant holding an array is passed BYVAL, stored in an object's member, or added to a Collection. Index it like the array itself, v(1).
Dim list(2) As Integer, v As Variant
list(0) = 1
v = list ' v holds a copy
list(0) = 5
Print v(0); list(0) ' 1 5
Empty
Empty means that nothing has been stored in the Variant yet. A new Variant is Empty, and assigning Empty makes it Empty again. In arithmetic Empty counts as 0 and in text as "", so a counter kept in a Variant needs no starting value; PRINT shows nothing for it.
Dim total As Variant
total = total + 5 ' Empty counts as 0: total is 5
If IsEmpty(v) Then Print "Nothing stored yet"
Null
Null means "no valid value" -- for example a field that was never filled in. Null spreads through most expressions: anything calculated from a Null is Null too, so a missing value is not mistaken for a real one.
Expression Result ---------- ------ Null + 1, Null * x, -Null, Not Null Null Null = 1, Null < x, Null = Null Null (a comparison with Null is never True) Abs(Null), Int(Null), Fix(Null), Sgn(Null) Null Left(Null, 1), UCase(Null), Len(Null), ... Null (the Variant string functions; see below) "Name: " & Null "Name: " (& treats Null as "") Null & Null Null False And Null False True Or Null True False Imp Null, Null Imp True True Any other And, Or, Xor, Eqv, or Imp with Null Null
An IF, ELSEIF, WHILE, DO, or LOOP condition that is Null counts as False. Any other use that needs a real number -- storing Null in a numeric, Boolean, or Date variable, CINT(Null) and the other conversion functions, a string function ending in $ such as UCASE$ or LEFT$, or an array subscript -- raises error 94 (Invalid use of Null).
As in Visual Basic, the string functions without $ are Variant functions that pass Null through: LEFT, RIGHT, MID, UCASE, LCASE, TRIM, LTRIM, RTRIM, CHR, HEX, OCT, STR, SPACE, and STRING give Null when their text (or number) argument is Null, and so do LEN, INSTR, INSTRREV, STRCOMP, LIKE, and the date parts YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, and DATEPART. Their $ forms (LEFT$, UCASE$ ...) always give text and raise error 94 for Null.
Because Null = Null is itself Null, test for Null with ISNULL, never with =:
If IsNull(price) Then
Print "No price yet"
Else
Print "Price: "; price
End If
Empty and Null are not reserved words: a program that declares its own variable or constant named Empty or Null keeps using it.
Variant Functions
Function Returns Description
-------- ------- -----------
VARTYPE(v) Integer A number for the kind of value v holds (see VarType Constants): vbEmpty (0), vbNull (1), vbInteger (2), vbString (8), vbObject (9), and so on. For an array it is vbArray (8192) plus the element type, so an Integer array is 8194 and a Variant array 8204.
TYPENAME(v) String The name of the kind of value: "Byte", "Integer", "Long", "Single", "Double", "Currency", "String", "Boolean", "Date", "Empty", "Null", "Error" (a left-out OPTIONAL argument), the name of a TYPE, "Integer()" for an array, and for objects the class name, "Collection", "Nothing", or the type of a form or control.
ISEMPTY(v) Boolean True when v is Empty.
ISNULL(v) Boolean True when v is Null.
ISNUMERIC(v) Boolean True when v is a number, True or False, Empty, or text that reads as a number ("12", "1.5"). False for Null, dates, and other text.
ISOBJECT(v) Boolean True when v is an object reference, including Nothing.
ISARRAY(v) Boolean True when v is an array.
ISMISSING(v) Boolean True when v is an OPTIONAL Variant parameter the caller left out (see Procedures).
ISDATE(v) Boolean True for a Date or text holding a date (see Date and Time Functions).
CVAR(v) Variant v unchanged, as a Variant.
ARRAY(a, ...) Variant A Variant holding an array of copies of the values, numbered from OPTION BASE (0 unless OPTION BASE 1); ARRAY() is empty (UBOUND -1).
Dim days As Variant
days = Array("Mon", "Tue", "Wed")
Print days(1) ' Tue
For Each d In Array(1, 2, 3)
Print d;
Next
Sub Describe(ByVal v As Variant)
Select Case VarType(v)
Case vbEmpty
Print "(empty)"
Case vbNull
Print "(unknown)"
Case vbString
Print "Text: "; v
Case Else
Print TypeName(v); ": "; v
End Select
End Sub
Operators
Operators listed from highest precedence (evaluated first) to lowest precedence (evaluated last). Parentheses override precedence and may be nested freely.
Precedence Operator Description ---------- -------- ----------- 1 (highest) ^ Exponentiation 2 - (unary), + (unary) Negation (unary plus is a no-op) 3 * / Multiply, float divide 4 \ Integer divide 5 MOD Modulus 6 + - Addition, subtraction (+ also concatenates strings) 7 & String concatenation 8 = <> < > <= >= Is Like Comparison (returns Boolean) 9 NOT Logical/bitwise NOT 10 AND Logical/bitwise AND 11 XOR Logical/bitwise exclusive-or 12 OR Logical/bitwise OR 13 EQV Logical/bitwise equivalence 14 (lowest) IMP Logical/bitwise implication
Arithmetic Operators
Operator Description -------- ----------- + Addition (or string concatenation when both operands are strings) - Subtraction (or negation when used as a unary prefix) * Multiplication / Floating-point division (result is always Single or Double) \ Integer division (result is Long, fractional part discarded) MOD Integer modulus (remainder) ^ Exponentiation (result is Double)
Comparison Operators
Comparison operators return True (-1) or False (0).
Operator Description -------- ----------- = Equal to <> Not equal to < Less than > Greater than <= Less than or equal to >= Greater than or equal to Is Both sides refer to the same object (objects only) Like The text on the left matches the pattern on the right
String comparisons are controlled by OPTION COMPARE. With OPTION COMPARE BINARY (the default) strings are compared by byte value. With OPTION COMPARE TEXT comparisons are case-insensitive. A comparison with Null gives Null (see Variants).
Is compares object references, not values: a Is b is True when both refer to the very same object, and x Is Nothing tests for an object variable that holds no object. Either side may be a form or control name. Using Is on something that is not an object raises error 424 (Object required).
text Like pattern is True when the whole of text matches the pattern. In the pattern, ? stands for any one character, * for any run of characters (none too), # for one digit 0 to 9, [list] for one character in the list, and [!list] for one character not in it; anything else stands for itself. A list holds characters and ranges such as a-z ([A-Za-z0-9_]); to match a literal ?, *, #, or [, put it in brackets ([*]). OPTION COMPARE TEXT makes the match ignore case. Like with Null gives Null, and a pattern with a [ that has no ] raises error 93.
If name$ Like "A*" Then Print "Starts with A"
If code$ Like "[A-Z]##" Then Print "A letter and two digits"
If file$ Like "*.[!b]*" Then Print "Not a .b... extension"
TypeOf object Is TypeName is True when the object is of that type. TypeName can be a class module's name, Collection, Form, Control (any control), Object (any object that is not Nothing), or a control type such as TextBox. It is False for Nothing. Use Not TypeOf ... Is ... for the opposite test.
If player Is Nothing Then Set player = New Player
If a Is b Then Print "Same object"
If TypeOf Source Is TextBox Then Print Source.Text
Logical / Bitwise Operators
The logical operators work on Boolean values and also perform bitwise operations on integer values. As in Visual Basic, two Booleans give a Boolean (True And False is False, Not True is False, and Print (a > 1) Or (b > 1) prints True); when either side is a number they work on the bits and give a number (an Integer, or a Long when a side is Long): True And 3 is 3. With Null they give Null unless the other side decides the answer (False And Null is False, True Or Null is True, a Boolean as the other side is); see Variants.
Operator Description -------- ----------- NOT Logical/bitwise complement AND Logical/bitwise AND OR Logical/bitwise OR XOR Logical/bitwise exclusive-or EQV Logical/bitwise equivalence (same as NOT (a XOR b)) IMP Logical/bitwise implication (same as (NOT a) OR b)
String Concatenation
Use & to concatenate strings. The + operator also concatenates when both operands are strings. Prefer & because it does not depend on operand types.
result$ = "Hello" & " " & "World"
result$ = firstName$ & " " & lastName$
' + also concatenates when both operands are strings
greeting$ = "Hi " + name$
Statements
Multiple statements can appear on one line separated by : (colon). A line can be continued onto the next physical line by ending it with an underscore (_) preceded by whitespace. Comments start with ' (apostrophe) or the REM keyword and run to the end of the line. The character ? is a shorthand for PRINT.
a = 10 : b = 20 : c = a + b ' three statements on one line
total = price * quantity _
+ shipping + tax ' one statement continued across lines
REM This is a comment
' So is this
? "Hello" ' same as PRINT "Hello"
Declaration Statements (DIM, REDIM, CONST, TYPE, DECLARE)
Conditional Statements (IF, SELECT CASE)
Loop Statements (FOR, DO, WHILE)
Procedures (SUB, FUNCTION, DEF FN)
Flow Control (EXIT, CALL, GOTO, GOSUB, ON)
Input/Output (PRINT, INPUT, DATA/READ)
Miscellaneous Statements (ON ERROR, SHELL, SLEEP, END)
Sound, Joysticks, and Frames (BEEP, SOUND, PLAY, STICK, STRIG, FRAMEWAIT)
Objects and Class Modules (NEW, PROPERTY, Class_Initialize, Me)
Declaration Statements
DIM
Declares variables and arrays with an explicit type. If no type is given, the type defaults to whatever DEFtype (if any) is in effect for the variable's first letter; without a DEFtype statement the default is Single.
DIM variable AS type
DIM variable(upperBound) AS type
DIM variable(lower TO upper) AS type
DIM variable(dim1, dim2, ...) AS type
DIM variable() AS type
DIM variable AS UdtName
DIM variable AS STRING * n
DIM variable AS NEW ClassName
DIM SHARED variable AS type
DIM variable1 [AS type], variable2 [AS type], ...
An array declared with bounds is a fixed array. DIM variable() with empty parentheses declares a dynamic array that has no storage until a REDIM gives it bounds; using it before then raises error 13. An array that is first created by REDIM, and an array parameter, are dynamic too (see ERASE for the difference).
One DIM can declare several variables, separated by commas. Each one takes its own AS clause; a name without one gets its suffix or DEFtype type (Single by default), so DIM a, b AS STRING makes only b a String. Arrays can have up to 8 dimensions; more is a compile error. A variable declared with type suffix does not need the AS clause:
Dim name$, count%, total& ' suffixes infer the types
Dim first, last As String, list(10) As Integer
Examples:
Dim name As String
Dim count As Integer
Dim values(100) As Double
Dim matrix(1 To 10, 1 To 10) As Single
Dim Shared globalFlag As Boolean
Dim record As PersonType
Dim fixedStr As String * 20
Note: A module-level variable is visible inside a SUB or FUNCTION only when it is declared with DIM SHARED (or named in a SHARED statement in that procedure). See Variable Scope below. Inside a SUB or FUNCTION, DIM declares a local variable that is recreated on each call (use STATIC to retain its value between calls). A SUB or FUNCTION holds at most 64 parameters and local variables together, including a few hidden ones that statements such as ON ... GOTO, SWAP, and READ into array elements use while they run; a procedure that needs more fails to compile with "Too many local variables". A program holds at most 512 module-level variables, a form at most 65536 form-level variables, and a class at most 65536 member variables; more fails to compile.
Fixed-length strings (STRING * n) are padded with spaces and truncated when assigned so their length is always exactly n.
DIM variable AS NEW ClassName (or AS NEW Collection) creates a new object at the DIM and stores it in the variable; without NEW an object variable starts as Nothing. See Objects and Class Modules. DIM variable AS NEW FormName makes a new copy of a form of the project (see Several Copies of a Form in the Form topic); DIM variable AS FormName declares a variable for that form or a copy of it.
Variable Scope and SHARED
DVX BASIC follows the QBasic scope rules:
- Variables declared (or first used) at module level -- outside every SUB and FUNCTION -- are module variables. The code section of a form (.frm) is module-level code of that form.
- A SUB or FUNCTION sees a module variable only when it was declared with DIM SHARED at module level, or when the procedure names it in a SHARED statement. This applies to arrays as well.
- Any other name used inside a SUB or FUNCTION is a local variable of that procedure, even when a module variable of the same name exists. With OPTION EXPLICIT, using such a name without a DIM is a compile error that says the module variable is not SHARED.
- Indexing (name(...)) or dotting (name.member) a module variable the procedure cannot see is a compile error.
- CONSTs declared at module level, TYPEs, SUBs, FUNCTIONs, class modules, forms, and controls are visible everywhere; no SHARED is needed for them. PUBLIC at module level is the same as DIM SHARED (see PUBLIC / PRIVATE).
- A DEF FN is module-level code: its expression sees every module variable.
DIM SHARED total AS LONG ' visible in every SUB and FUNCTION
DIM count AS INTEGER ' module code only
SUB AddUp(n AS INTEGER)
total = total + n ' the module variable
count = 1 ' a local variable, not the module's count
END SUB
SUB ShowCount
SHARED count ' this procedure uses the module's count
PRINT count
END SUB
DIM SHARED and REDIM SHARED are only allowed at module level. Inside a procedure use the SHARED statement:
SHARED variable [()] [AS type] [, variable [()] [AS type] ...]
SHARED gives the procedure access to the named module variables (write name() for an array). The AS type, when given, must match the module variable's declaration. Naming a variable that does not exist at module level creates it there (in a form's code, as a variable of that form).
How Names Are Found
Every name in a program is looked up the same way, in this order:
- A name the program declares is that declaration: first the procedure's own parameters, local variables, and STATIC and SHARED names; then, in a class module, the class's own members; then the variables of the form whose code it is; then the module's variables, SUBs, FUNCTIONs, constants, TYPEs, ENUMs, and classes, and the predefined vb constants. Inside a procedure a module variable counts only when it is shared (see above); a class's members always do.
- Otherwise a built-in function of that name is used. Built-in names are not reserved: a variable, constant, SUB, or FUNCTION the program declares with the same name wins (a FUNCTION named StrReverse replaces the built-in one).
- Otherwise, where an object is expected -- before a dot (name.Caption), after SET, around Is or TypeOf, or as an argument for an Object or class parameter -- the name is a control of the current form, or a form of that name. Form1.Text1.Text names the control Text1 on the form Form1.
- Otherwise the name is a new variable: local inside a procedure, a module variable elsewhere (a DEF FN expression's names are module variables). With OPTION EXPLICIT this is a compile error.
A SUB or FUNCTION can be called before its definition, and a TYPE, ENUM, or class -- with its member variables and procedures -- can be used before it is declared: the compiler reads every declaration first. A name after a dot is a member of the object before it: when that object's class is known (see Objects and Class Modules) the member is looked up in the class when the program is compiled, else when it runs.
PUBLIC / PRIVATE
At module level, PUBLIC and PRIVATE can be used in place of DIM, as in Visual Basic. PUBLIC declares a variable that every SUB and FUNCTION can use (the same as DIM SHARED); PRIVATE declares one that only module-level code sees (the same as DIM).
Public highScore As Long ' same as Dim Shared highScore As Long
Private tries As Integer ' same as Dim tries As Integer
PUBLIC and PRIVATE may also be written in front of SUB and FUNCTION. Outside a class module they change nothing, because procedures are visible to the whole program anyway. In a class module they decide what code outside the class can use; see Objects and Class Modules. Inside a procedure, use DIM.
REDIM
Reallocates an array, optionally preserving existing data. The array may have been declared with DIM name() or with bounds, or not declared at all (REDIM then declares a dynamic array).
REDIM array(newBounds) AS type
REDIM PRESERVE array(newBounds) AS type
REDIM SHARED array(newBounds) AS type
REDIM SHARED (module level only) also makes the array visible in every SUB and FUNCTION, like DIM SHARED.
ReDim items(newSize) As String
ReDim Preserve scores(1 To newCount) As Integer
CONST
Declares a named constant. The value is worked out once, when the program is compiled, and can never change -- you cannot assign a new value to a constant or pass it by reference. Several constants can be declared in one CONST statement, separated by commas.
CONST name = expression
CONST name AS type = expression
CONST name = expression, name = expression, ...
Const MAX_SIZE = 100
Const PI = 3.14159265
Const APP_NAME = "DVX App"
Const DEBUG_MODE = True
Const HEADER As String = "=== Report ==="
Const LAUNCH = #7/20/1969 8:17 PM#
Const LAST_INDEX = MAX_SIZE - 1, HALF = MAX_SIZE \ 2
Const TITLE = APP_NAME & " " & "1.0"
Const CRLF = Chr$(13) & Chr$(10)
Const TEAL = RGB(0, 128, 128)
Const BIG = MAX_SIZE > 50
The value can be any expression built from:
- literals (numbers, strings, True and False, #date# literals);
- constants declared earlier, including ENUM members (Member or EnumName.Member);
- the arithmetic operators + - * / \ MOD ^, the comparison operators, AND OR NOT XOR EQV IMP, and string joining with & or +;
- these built-in functions: ABS, ASC, ATN, CBOOL, CDATE, CDBL, CHOOSE, CHR$, CINT, CLNG, COS, CSNG, CSTR, DATEADD, DATEDIFF, DATEPART, DATESERIAL, DATEVALUE, DAY, EXP, FIX, FORMAT$, GETBLUE, GETGREEN, GETRED, HEX$, HOUR, IIF, INSTR, INSTRREV, INT, ISDATE, LCASE$, LEFT$, LEN, LOG, LTRIM$, MID$, MINUTE, MONTH, MONTHNAME, OCT$, REPLACE$, RGB, RIGHT$, RTRIM$, SECOND, SGN, SIN, SPACE$, SQR, STR$, STRCOMP, STRING$, STRREVERSE$, TAN, TIMESERIAL, TIMEVALUE, TRIM$, UCASE$, VAL, WEEKDAY, WEEKDAYNAME, and YEAR.
Anything else -- a variable, a FUNCTION of your own, or a built-in whose answer can change from run to run such as TIMER, NOW, RND, or DIR$ -- is a compile error ("Constant expression required"). An expression that fails, such as a division by zero or a result too big for its type, is also reported when the program is compiled. Text comparisons in a constant follow the OPTION COMPARE setting in effect where the constant is declared. A string constant can be at most 255 characters long.
Without an AS clause the constant takes the type of its value: a whole number makes a Long constant, a number with a fraction a Double constant, and strings, Booleans, and dates keep their type. With an AS clause the value is converted to that type: Integer and Long values are rounded and must fit the type, and a string for a numeric type (or a number for String) is a compile error.
Constants declared at module level are visible to all procedures. A CONST declared inside a SUB or FUNCTION is local to that procedure; it may reuse the name of a module constant without changing it.
PUBLIC CONST and PRIVATE CONST work as in Visual Basic. A Private constant, and any constant of a form or class module, belongs to the file it is declared in: another module, form, or class of the project can declare its own constant of the same name, and does not see this one. A Public constant, or a plain CONST in a module, is seen by the whole program. A class module or form cannot have a Public constant ("Constants are not allowed as Public members of a class module or form").
Public Const APP_VERSION = "2.1" ' every file of the project
Private Const BUFFER_SIZE = 512 ' this file only
TYPE...END TYPE
Defines a user-defined type (record/structure).
TYPE TypeName
fieldName AS type
fieldName AS STRING * n
...
END TYPE
Type PersonType
firstName As String
lastName As String * 20
age As Integer
End Type
Dim p As PersonType
p.firstName = "Scott"
p.age = 30
UDT fields can themselves be UDTs (nested types), and field access chains through array elements too: shapes(i).origin.x = 5. A new instance starts with numeric fields at 0, Boolean fields False, variable strings empty, and STRING * n fields padded to n spaces. TYPE variables can be written to and read from RANDOM and BINARY files with PUT and GET (see GET / PUT).
ENUM...END ENUM
Defines a set of named whole-number constants, and a type name for variables that hold one of them.
ENUM EnumName
member
member = expression
...
END ENUM
Enum Direction
North ' 0
East ' 1
South = 10
West ' 11
End Enum
Enum Style
Bold = 1
Italic = Bold * 2
Underline = Style.Italic * 2
AllStyles = Bold Or Italic Or Underline
End Enum
Dim heading As Direction
heading = West
If heading = Direction.West Then Print "Going west"
The first member is 0 and each member without a value is one more than the member before it. A value may be any constant expression (see CONST), including members declared earlier in the same ENUM or in another one. Members are Long constants and can be used on their own (West) or with the ENUM name in front (Direction.West). If two ENUMs have a member with the same name, that name must be written with its ENUM name.
The ENUM name can be used as a type wherever a type is allowed: DIM, parameters, FUNCTION results, and TYPE fields. Such a variable is a Long, so it can hold any whole number, not only the members. ENUMs are declared at module level (not inside a SUB or FUNCTION) and are visible everywhere in the program; declare an ENUM before the code that uses its members. PRIVATE ENUM keeps the ENUM and its members to the file it is declared in, like PRIVATE CONST; PUBLIC ENUM (the same as a plain ENUM) is seen everywhere. Members are constants: Direction.West = 3 is a compile error ("Cannot assign to a constant").
DECLARE
Forward-declares a SUB or FUNCTION. This is rarely required because the compiler supports forward references within a module, but it is still accepted for compatibility:
DECLARE SUB name ([BYVAL] [OPTIONAL] param AS type, ...)
DECLARE FUNCTION name ([BYVAL] [OPTIONAL] param AS type, ...) AS returnType
A DECLARE must match the SUB or FUNCTION it declares: the same parameter types, BYVAL and OPTIONAL flags, and return type.
DECLARE LIBRARY
Declares external functions from a native library bundled with DVX. This is how BASIC programs reach operating-system services, serial ports, databases, and other native facilities. Rather than writing DECLARE LIBRARY blocks yourself, add one of the shipped include files (such as commdlg.bas or sql.bas) to your project; the include file contains the proper declarations.
DECLARE LIBRARY "libraryName"
DECLARE SUB name ([BYVAL] param AS type, ...)
DECLARE FUNCTION name ([BYVAL] param AS type, ...) AS returnType
END DECLARE
Declare Library "basrt"
Declare Function SQLOpen(ByVal path As String) As Long
Declare Sub SQLClose(ByVal db As Long)
End Declare
Arguments are always passed by value. Each argument is converted to the type its parameter is declared with before the call, so a Double passed to a Long parameter arrives as a whole number and a Single parameter receives a Single. Declare Integer only for native values that fit in 16 bits; use Long for 32-bit values, or large results are cut down to 16 bits. A Byte parameter or result is 8 bits. A Currency parameter receives the amount as a 64-bit whole number of ten-thousandths; a DECLARE FUNCTION cannot return a Currency.
A String parameter or result declared AS STRING is text: it ends at the first CHR$(0). To pass or receive any bytes, CHR$(0) included (network data, sound samples, encrypted data, checksums of binary data), the declaration says BINARY STRING instead. To the program it is an ordinary String; LEN, MID$, ASC, and the other string functions see every byte. BINARY STRING is allowed only in a DECLARE LIBRARY block, and the shipped include files already use it where a function works with bytes.
Declare Library "mylib"
Declare Function MyChecksum(ByVal bytes As Binary String) As Long
Declare Function MyRead$(ByVal handle As Long, ByVal maxLen As Long) As Binary String
End Declare
packet$ = "A" + Chr$(0) + "B"
Print MyChecksum(packet$)
See also: BASIC Runtime Libraries
STATIC
Declares a local variable that retains its value between calls.
STATIC variable AS type
Sub Counter()
Static count As Integer
count = count + 1
Print count
End Sub
Several variables can be declared in one STATIC statement, separated by commas, each with its own AS clause; STRING * n works as in DIM. Arrays and TYPE variables can be STATIC too and keep their contents between calls: a fixed-size array (Static hits(1 To 10) As Long) or a dynamic one sized later with REDIM (Static names() As String). In a class module each object has its own copy of a procedure's STATIC variables, arrays, and TYPE variables, as in Visual Basic: two objects counting calls count separately. A STATIC AS NEW object or WithEvents variable is not supported (a compile error); use a module-level or member variable for those.
Sub Remember(ByVal word As String)
Static seen(1 To 3) As String
Static count As Integer
count = count + 1
seen((count - 1) Mod 3 + 1) = word
Print seen(1); " "; seen(2); " "; seen(3)
End Sub
Writing STATIC after a SUB or FUNCTION header makes every local variable of the procedure static, the ones declared with DIM and the ones used without a declaration (QBasic and Visual Basic):
Sub Tally(points As Integer) Static
total = total + points ' keeps its value between calls
Dim calls As Integer ' a STATIC variable too
calls = calls + 1
Print calls; total
End Sub
OPTION
Sets compiler options. Must appear before any executable code.
OPTION BASE 0 ' Arrays start at index 0 (default)
OPTION BASE 1 ' Arrays start at index 1
OPTION COMPARE BINARY ' Case-sensitive string comparisons (default)
OPTION COMPARE TEXT ' Case-insensitive string comparisons
OPTION EXPLICIT ' All variables must be declared with DIM
DEFtype Statements
Set the default type for variables based on their first letter.
DEFINT letterRange
DEFLNG letterRange
DEFSNG letterRange
DEFDBL letterRange
DEFSTR letterRange
DEFBYTE letterRange
DEFCUR letterRange
DefInt I-N ' Variables starting with I through N default to Integer
DefStr S ' Variables starting with S default to String
Assignment
Assigns a value to a variable, array element, or UDT field.
variable = expression
array(index) = expression
udt.field = expression
LET variable = expression
The LET keyword is optional and supported for compatibility.
Assigning a whole TYPE value to a TYPE variable, array element, or field copies the value's fields into the record that is already there, as in Visual Basic: the two stay separate records (changing one later does not change the other), and a SUB that was passed one of the target's fields by reference sees the new value.
SWAP
Exchanges the values of two variables or array elements. The two operands should be the same type.
SWAP variable1, variable2
SWAP array(index), variable
Swap a, b
Swap names(i), names(j)
LSET / RSET
Stores a string into a string variable without changing the variable's length: LSET places the value at the left and RSET at the right, filling the rest with spaces. A value longer than the variable is cut off at the right.
LSET variable = expression
RSET variable = expression
Dim field As String
field = Space$(8)
LSet field = "Name" ' "Name "
RSet field = "42" ' " 42"
The target may be an array element or a TYPE field. It must hold a string; any other value raises error 13 (Type mismatch).
LSET also copies a whole record: when the target is a TYPE variable, the value must be another TYPE variable, and its bytes -- laid out as PUT writes the record to a file -- are copied into the target, even when the two are of different TYPEs. When the source is shorter the rest of the target is filled with spaces; when it is longer the extra bytes are dropped. This is meant for records of numbers and fixed-length strings (STRING * n), as used with random-access files; a variable-length string field does not copy meaningfully between different TYPEs. RSET cannot copy records.
Type Header
kind As String * 2
size As Integer
End Type
Type Raw
bytes As String * 4
End Type
Dim h As Header, r As Raw
r.bytes = "AB" + Chr$(10) + Chr$(0)
LSet h = r ' h.kind = "AB", h.size = 10
SET
Stores an object reference -- a form, a control, an instance of a class module, a Collection, or Nothing -- in a variable, array element, TYPE field, or property. After SET both names refer to the same object; nothing is copied.
SET variable = objectExpression
SET variable = NEW ClassName
SET variable = NEW FormName
SET variable = Nothing
Dim frm As Form
Set frm = CreateForm("MyForm", 320, 240)
frm.Caption = "Built in code"
Dim c As Control
Set c = Text1 ' a control on the form
c.Text = "Hello"
Dim list As Collection
Set list = New Collection ' a new, empty Collection
Set list = Nothing ' let it go
Dim w As Form1
Set w = New Form1 ' a second Form1 window
w.Show
The value must be an object or Nothing; anything else raises error 424 (Object required). SET is not used for ordinary numeric or string assignment.
ERASE
Frees the memory of a dynamic array (declared with DIM name(), created by REDIM, or an array parameter) and resets it to the undimensioned state; using it again before a REDIM raises error 13. A fixed array (declared with bounds) keeps its bounds and has every element reset to zero, an empty string, False, or a fresh TYPE instance.
ERASE arrayName
ERASE array1, array2, ...
One ERASE can name several arrays, separated by commas.
Conditional Statements
IF...THEN...ELSE...END IF
Conditional execution. Supports single-line and multi-line forms.
Single-line form
IF condition THEN statement
IF condition THEN statement ELSE statement
Multi-line form
IF condition THEN
statements
ELSEIF condition THEN
statements
ELSE
statements
END IF
If x > 10 Then
Print "Large"
ElseIf x > 5 Then
Print "Medium"
Else
Print "Small"
End If
If ready Then Print "Go!"
SELECT CASE
Multi-way branch based on an expression value.
SELECT CASE expression
CASE value
statements
CASE value1, value2
statements
CASE low TO high
statements
CASE IS operator value
statements
CASE ELSE
statements
END SELECT
Select Case grade
Case 90 To 100
Print "A"
Case 80 To 89
Print "B"
Case Is >= 70
Print "C"
Case 60, 65
Print "D (borderline)"
Case Else
Print "F"
End Select
CASE items can be combined with commas. The IS keyword allows comparison operators: <, >, <=, >=, =, <>.
Loop Statements
FOR...NEXT
Counted loop with an optional step value.
FOR variable = start TO limit [STEP step]
statements
NEXT [variable]
For i = 1 To 10
Print i
Next i
For x = 10 To 0 Step -2
Print x
Next
The variable name after NEXT is optional. Use EXIT FOR to break out early.
With an Integer or Long counter a constant STEP must be a whole number (STEP 0.5 is a compile error); a STEP computed at run time is rounded to the counter's type. A Single or Double counter takes any step.
FOR EACH...NEXT
Runs the loop once for every item in a Collection or every element of an array, without counting.
FOR EACH item IN group
statements
NEXT [item]
Dim names As New Collection
names.Add "Ann"
names.Add "Bob"
Dim person As Variant
For Each person In names
Print "Hello, "; person
Next person
Dim scores(1 To 3) As Integer
Dim s As Variant
For Each s In scores
total = total + s
Next
group can be a Collection, an array (of any number of dimensions; elements are visited in row order, the last subscript changing fastest), a Variant holding an array, or a ParamArray. On each pass item receives a copy of the next item: for a Collection of objects it refers to the object itself, but assigning a new value to item does not change the array or Collection. Declare item AS VARIANT, or as an object type for a Collection of objects. The number of items is checked on every pass, so items added or removed inside the loop are taken into account.
EXIT FOR leaves the loop early. Looping over something that is not an array or Collection raises error 13 (Type mismatch), or error 451 (Object not a collection) for an object that is not a Collection. EACH and IN are not reserved words.
DO...LOOP
General-purpose loop with pre-test, post-test, or infinite forms.
DO [WHILE condition | UNTIL condition]
statements
LOOP [WHILE condition | UNTIL condition]
' Pre-test
Do While count < 10
count = count + 1
Loop
' Post-test
Do
line$ = ReadLine()
Loop Until line$ = "quit"
' Infinite loop (exit with EXIT DO)
Do
DoEvents
If done Then Exit Do
Loop
WHILE...WEND
Simple pre-test loop (legacy form; prefer DO...LOOP).
WHILE condition
statements
WEND
While Not EOF(1)
Line Input #1, line$
Print line$
Wend
Procedures
SUB...END SUB
Defines a subroutine (no return value).
SUB name ([BYVAL | BYREF] [OPTIONAL] param AS type, ...) [STATIC]
SUB name (arrayParam() AS type, ...)
statements
END SUB
Sub Greet(ByVal name As String)
Print "Hello, " & name
End Sub
A SUB, FUNCTION, or Property procedure stands on its own in the program: defining one inside another procedure, or inside an IF, FOR, DO, WHILE, SELECT CASE, or WITH block, is a compile error.
Parameters are passed by reference by default (nested calls pass the same variable on, so a SUB called from a SUB can still change the original); BYREF may be written out to say so, and BYVAL selects value semantics. A by-reference parameter is the caller's variable itself, as in Visual Basic: a write to it changes the variable at once (the caller's code, or another parameter naming the same variable, sees the new value straight away, even if the procedure later stops with an error), and what is stored is converted to the variable's own type (a STRING * n keeps its length). This holds for a plain variable, an array element (`bump a(3)` changes `a(3)` in place), a TYPE field (`bump rec.count`, `bump recs(2).count`, `bump rec.inner.count`) and, inside a class, one of the class's own member variables. While an array element (or a field of one) is passed by reference, its array cannot be given a new size or erased: REDIM of that array, or ERASE of it when it is a dynamic array, raises error 10 (This array is fixed or temporarily locked), as in Visual Basic (ERASE of a fixed-size array only clears its elements and is allowed). The array is free again as soon as the call returns. Assigning a whole TYPE value to the variable (or element) whose field is being passed copies the value into the same record, so the parameter sees the new value (as in Visual Basic). A parameter written as name() receives a whole array by reference; the caller passes `arr()` (or just `arr`), and the SUB may index, REDIM, or ERASE it. Use EXIT SUB to return early. A SUB is called either with or without parentheses; when used as a statement, parentheses are optional:
Greet "World"
Greet("World")
Call Greet("World")
As in Visual Basic and QBasic, a single argument in parentheses after the name of a SUB called without CALL (Bump (n), or Bump(n)) is an expression in parentheses, so it is passed by value: the SUB gets a copy and n is not changed. Write Bump n, or Call Bump(n), to pass n by reference.
An argument is converted to the type of a typed parameter (one declared with AS, a type suffix, or a DEFtype rule). A value -- a literal, an expression, a variable in its own parentheses, or anything given to a BYVAL parameter -- is converted the way an assignment would convert it (Report 3.7 gives an Integer parameter 4). A variable, array element, or TYPE field given to a by-reference parameter must have the parameter's own type, as in Visual Basic: an Integer parameter cannot be the caller's Double variable ("ByRef argument type mismatch" when the program is compiled); pass CINT(d), or (d) in its own parentheses, to pass a converted copy. A variable declared without a type (a Variant) holds any value and is passed as it is. Through an object variable declared As Object the procedure is only found when the program runs, so a variable of another type is passed as a converted copy instead.
Sub Fill(v() As Integer, ByRef count As Integer)
ReDim v(9) As Integer
v(9) = 7
count = count + 1
End Sub
Dim a(3) As Integer
Dim n As Integer
Fill a(), n
Optional Parameters
Mark a parameter OPTIONAL to allow callers to omit it. An optional parameter must be positioned after all required parameters. A caller can leave out the optional parameters at the end, or any optional parameter in the middle by writing nothing between the commas: Report a, , c. A left-out parameter of a type receives that type's empty value (0, "", False, a date of 0, Nothing); a left-out Variant parameter is Missing, which ISMISSING detects. Leaving out a parameter that is not OPTIONAL is a compile error (Argument not optional).
Sub Announce(ByVal msg As String, Optional ByVal loud As Integer)
If loud Then
Print UCase$(msg)
Else
Print msg
End If
End Sub
Announce "hello" ' loud defaults to 0
Announce "hello", True ' loud is -1
An OPTIONAL parameter can also name the value it takes when it is left out, after an equals sign: Optional count As Integer = 10. The default is a constant expression, like the value of a CONST: numbers, strings, dates, other constants, and the built-in functions CONST allows, converted to the parameter's type (a value that does not fit is a compile error). A parameter with a default is never Missing -- ISMISSING is False for it -- whether it is left out at the end, skipped between commas, or the procedure is reached through an object. Defaults work for SUB and FUNCTION procedures and for the procedures and Property procedures of class modules, including calls written before the procedure itself. As in Visual Basic the default is the one the declaration being called gives: a call through an interface (a variable declared As the interface class) passes the interface's default, even when the implementing class declares another. An object parameter (As Object, As Control, As Form, or As a class) can only have the default Nothing, as in Visual Basic: Optional target As Object = Nothing; a Variant parameter may have Nothing too. A default cannot be given to a required parameter, an array, or a TYPE, or in DEF FN and DECLARE LIBRARY. A DECLARE SUB / FUNCTION line must say whether each parameter has a default, as its definition does.
Const SIDE = 10
Function Area(Optional w As Integer = SIDE, Optional h As Integer = SIDE * 2) As Long
Area = w * h
End Function
Print Area() ' 200
Print Area(2) ' 40
Print Area(, 3) ' 30
ISMISSING works as in Visual Basic: only an OPTIONAL Variant parameter without a default can be Missing, and a Missing value passed on to another procedure's OPTIONAL Variant parameter is still Missing there. ISMISSING of anything else is False. Using a Missing value in any other way -- in arithmetic, in text, or storing it in a typed variable -- raises error 448.
Sub Greet(ByVal name As String, Optional title As Variant)
If IsMissing(title) Then
Print "Hello, "; name
Else
Print "Hello, "; title; " "; name
End If
End Sub
Greet "Ada" ' Hello, Ada
Greet "Ada", "Countess" ' Hello, Countess Ada
Named Arguments
An argument can be given by the name of its parameter, followed by := and the value, as in Visual Basic. Named arguments come after any positional ones, in any order; the parameters left out must be OPTIONAL.
Sub Report(ByVal title As String, Optional ByVal width As Integer = 40, Optional ByVal footer As String = "")
Print title; width; footer
End Sub
Report "Sales", footer:="(end)" ' width is 40
Report width:=60, title:="Stock"
Print Mid$(String:="hello", Start:=2) ' ello
MsgBox Prompt:="Saved", Title:="Notes"
Named arguments work for SUB and FUNCTION procedures, DEF FN functions, DECLARE LIBRARY procedures, and the procedures of class modules (through a variable of the class, or through an Object or Variant variable), and for the built-in functions that Visual Basic gives argument names to: MsgBox (Prompt, Buttons, Title), InputBox (Prompt, Title, Default), Mid (String, Start, Length), Left and Right (String, Length), InStr (Start, String1, String2), InStrRev (StringCheck, StringMatch, Start, Compare), Replace (Expression, Find, Replace, Start, Count, Compare), Split (Expression, Delimiter, Limit, Compare), Join (SourceArray, Delimiter), Filter (SourceArray, Match, Include, Compare), StrComp (String1, String2, Compare), String (Number, Character), Format (Expression, Format), IIf (Expression, TruePart, FalsePart), DateAdd (Interval, Number, Date), DateDiff and DatePart (Interval, Date1 and Date2 or Date, FirstDayOfWeek, FirstWeekOfYear), DateSerial (Year, Month, Day), TimeSerial (Hour, Minute, Second), Weekday (Date, FirstDayOfWeek), WeekdayName (Weekday, Abbreviate, FirstDayOfWeek), MonthName (Month, Abbreviate), and a Collection's Add (Item, Key, Before, After), Item, and Remove (Index).
A name the procedure does not have is a compile error (Named argument not found), and so is a parameter given twice. When the procedure is only found as the program runs (a method called through an Object or Variant variable) the same mistakes raise error 448 (Named argument not found) and error 450 (Wrong number of arguments). The methods of forms and controls do not take named arguments (error 446, Object doesn't support named arguments), and neither does a ParamArray parameter.
ParamArray
The last parameter of a SUB or FUNCTION can be marked PARAMARRAY to accept any number of extra arguments. It is written as an array, name(), and receives every remaining argument as an array numbered from 0. With no extra arguments the array is empty: UBOUND returns -1.
Sub PrintAll(ByVal title As String, ParamArray items())
Dim i As Integer
Print title;
For i = 0 To UBound(items)
Print " "; items(i);
Next
Print
End Sub
PrintAll "Totals:", 1, "two", 3.5 ' three items: 0 To 2
PrintAll "Nothing:" ' UBound(items) is -1
Function Sum(ParamArray n() As Double) As Double
Dim i As Integer
Dim total As Double
For i = 0 To UBound(n)
total = total + n(i)
Next
Sum = total
End Function
Without an AS clause (and no type suffix or DEFtype rule for the name) the ParamArray is an array of Variants (TYPENAME gives "Variant()"): each element holds the argument that was passed with its own type, so numbers, strings, and objects can be mixed, and FOR EACH can walk it. With an AS clause every argument is converted to that type. An argument that is a variable, a whole array, an array element (a(i), m(i, j)), a TYPE field (rec.f, arr(i).f, rec.inner.f) or, inside a class, one of the class's own member variables is passed by reference, as in Visual Basic: the element of the ParamArray is that variable, so a write to it changes the variable at once, converted to the variable's type (a value that cannot be converted raises its error at the assignment), and passing the element on (Bump v(0)) passes the variable. With an AS clause an argument is passed by reference only when its own type is that type; others are converted and passed by value. The same holds for a class's methods, whether the object variable is declared as the class or As Object / As Variant. While the call runs, the arrays whose elements were given are locked: REDIM of one of them (or ERASE of a dynamic one) raises error 10, as for any array element passed by reference. Copying the ParamArray (keep = v) copies the values, not the variables. Expressions and object properties are passed by value. Leaving an argument out (Sum 1, , 3) gives a Missing element. A PARAMARRAY cannot be combined with OPTIONAL parameters, cannot be BYVAL or BYREF, cannot hold a TYPE, and takes at most 31 arguments. It is not available in DEF FN or DECLARE LIBRARY.
FUNCTION...END FUNCTION
Defines a function with a return value.
FUNCTION name ([BYVAL] [OPTIONAL] param AS type, ...) AS returnType [STATIC]
statements
name = returnValue
END FUNCTION
STATIC after the header keeps every local variable of the SUB or FUNCTION between calls (see STATIC).
Function Square(ByVal n As Double) As Double
Square = n * n
End Function
Assign to the function name to set the return value. The value is converted to the function's type (AS clause, type suffix, or DEFtype), so FUNCTION F AS INTEGER with F = 2.5 returns 2. Inside the function its name on its own reads the value set so far (Total = Total + n), as in Visual Basic; with an argument list -- F() or F(n - 1) -- it calls the function again. Use EXIT FUNCTION to return early.
A FUNCTION can also be called as a statement, with or without parentheses; its result is discarded:
Function Beep2(ByVal n As Integer) As Integer
Beep2 = n * 2
End Function
Beep2 3 ' result discarded
name(...) in an expression calls a FUNCTION; using a SUB there is a compile error.
Labels, GOSUB, and Variables in Procedures
Labels belong to the SUB or FUNCTION they are written in (or to the module level). GOTO, GOSUB, ON ... GOTO/GOSUB and ON ERROR GOTO can only reach a label of the same procedure, and different procedures can reuse the same label name. GOSUB and RETURN work inside a procedure; see GOTO / GOSUB / RETURN. A procedure sees module variables only when they are SHARED; see Variable Scope and SHARED.
DEF FN
Defines a single-expression function. The function name begins with FN and is invoked just like a normal function. DEF FN functions cannot span multiple statements. A DEF FN must be defined before it is used, and only at module level (not inside a SUB or FUNCTION). Names in the expression other than the parameters are module variables, SHARED or not; the parameters are always passed by value.
DEF FNname(params) = expression
Def FnSquare(x) = x * x
Print FnSquare(5) ' prints 25
Def FnCelsius(f As Double) = (f - 32) * 5 / 9
Print FnCelsius(212) ' prints 100
Flow Control
EXIT
Exits the current block early.
EXIT FOR
EXIT DO
EXIT SUB
EXIT FUNCTION
EXIT PROPERTY
EXIT PROPERTY leaves a Property Get, Let, or Set procedure of a class module (see Objects and Class Modules).
CALL
Explicitly calls a subroutine or function. The return value (if any) is discarded.
CALL name
CALL name(args)
Normally you can omit CALL and just use the name directly.
GOTO / GOSUB / RETURN
GOTO label
GOSUB label
RETURN
A label is a name followed by a colon at the start of a line, or a line number at the start of a line (see Line Numbers below). Labels belong to the SUB or FUNCTION they are in (module-level labels to the module level): a jump can only reach a label of the same procedure, the same label name can be used in several procedures, and a label may have the same name as a variable. A jump to a label that is not defined in the procedure, or to a SUB or variable name, is a compile error.
GOSUB records where it was called from, runs the code at the label, and RETURN goes back to the statement after the GOSUB. This works at module level and inside a SUB or FUNCTION alike. Each procedure call has its own set of pending GOSUBs: RETURN only returns from a GOSUB made in the same procedure call, and leaving the procedure (END SUB, EXIT SUB, EXIT FUNCTION, or an error handled in a caller) discards the ones still pending. RETURN with no pending GOSUB raises error 3 (RETURN without GOSUB). To leave a SUB or FUNCTION, use EXIT SUB / EXIT FUNCTION.
GoSub Initialize
Print "Done"
End
Initialize:
count = 0
name$ = ""
Return
Sub Report(n As Integer)
GoSub Heading
Print "value"; n
Exit Sub
Heading:
Print "--- report ---"
Return
End Sub
ON...GOTO / ON...GOSUB
Computed branch based on an integer expression.
ON expression GOTO label1, label2, ...
ON expression GOSUB label1, label2, ...
If the expression evaluates to 1, control goes to the first label; 2, the second; and so on. If out of range, execution falls through. The labels follow the same rules as GOTO and GOSUB (same procedure only).
Line Numbers
As in QBasic, a line can start with a line number, a whole number from 0 to 65529. A line number is a label: GOTO, GOSUB, ON ... GOTO / GOSUB, ON ERROR GOTO, RESUME, and RESTORE can name it, it belongs to its procedure like any other label, and the same number cannot start two lines of a procedure. Numbers need not be in order, lines without a number are fine, and the number is read as a value, so 010 and 10 name the same line. IF can branch to a line number directly: IF x THEN 100 ELSE 200 is IF x THEN GOTO 100 ELSE GOTO 200, and IF x GOTO 100 works too.
10 INPUT "Guess"; g
20 IF g = 7 THEN 50
30 PRINT "No"
40 GOTO 10
50 PRINT "Yes"
In a program with line numbers, ERL gives the number of the last numbered line at or before the statement where the error happened (0 when there is none); in a program without them it gives the source line.
Input/Output Statements
Writes expressions to the output window, or to a file channel. Once the program has used a console or graphics statement (see Console and Graphics), PRINT writes on the program's screen instead, at the cursor, in the current COLOR.
PRINT [expression [{; | ,} expression] ...]
PRINT #channel, expression [; expression] ...
PRINT USING format$; expression [; expression] ...
- ; between items -- no separator (items appear next to each other, or separated by a space for numbers)
- , between items -- advance to the next 14-column tab zone
- A trailing ; or , suppresses the newline normally written at the end of the statement (PRINT USING and PRINT # too)
- ? is an alias for PRINT
PRINT # writes numbers exactly as PRINT shows them, with the space after each number, so PRINT #1, 1; 2 writes "1 2 ". PRINT #n, with nothing after the comma writes an empty line.
Special functions inside PRINT:
- SPC(n) -- print n spaces
- TAB(n) -- advance to column n (first column is 1); when the cursor is already past column n, a new line is started first
Print "Name:"; Tab(20); name$
Print "X="; x, "Y="; y ' comma = next 14-column zone
Print #1, "Written to file"
LPRINT
Prints on the printer instead of the screen. Everything PRINT accepts works the same way: ; and , between items, SPC, TAB, and LPRINT USING. The printer keeps its own column for TAB and the 14-column zones.
LPRINT [expression [{; | ,} expression] ...]
LPRINT USING format$; expression [; expression] ...
The output goes to the printer chosen in the Control Panel (a printer port, or a file). Each LPRINT is sent at once, and lines end the way printers expect. Nothing starts a new page by itself: LPRINT CHR$(12) ejects the page. If the printer has a problem (out of paper, off line), the user is asked to fix it and choose Retry, or Cancel; after Cancel the LPRINT raises error 57. With no printer to print on, LPRINT raises error 68.
LPrint "Invoice"; Tab(40); Date$
LPrint Using "Total: $$####.##"; total
LPrint Chr$(12) ' eject the page
For a whole document with margins, a header, a footer, and page numbers, and for the Print dialog, use the Printing Library (print.bas, see Include Libraries).
PRINT USING
PRINT USING formats each following expression according to a format string and prints the result. The expressions after the format are separated by ; or , (both work the same here). The format string is a mix of fields and literal text: each expression consumes the next field, the literal text before it is printed as it is, and the literal text after the last field is printed at the end. If more expressions are supplied than there are fields, the format string is reused from the start.
Format character Meaning ---------------- ------- # Digit position; unused positions on the left become spaces 0 Digit position that is always printed (zero padded); the first 0 sets the minimum number of digits . Decimal-point position , Insert thousands separator + At start or end: always show a sign character - At end: show a trailing minus sign only when negative ** At start: fill leading spaces with asterisks $$ At start: floating dollar sign ^^^^ Anywhere: format the value in scientific notation ! First character of a string only & Entire string (variable length) \ ... \ Fixed-length string field (width = 2 + number of spaces between backslashes) _ The next character is printed literally
A numeric field is as wide as its format characters; the value is right-aligned in it and a negative sign (or a leading +) floats directly in front of the first digit. A value that does not fit is printed in full with a leading %; a value too long to print in full is shown in scientific notation behind the %. A trailing + in a format with no digit positions is printed as literal text. A string expression must meet a string field and a numeric expression a numeric field, otherwise error 13 (Type mismatch) is raised.
Print Using "###.##"; -3.14159 ' " -3.14"
Print Using "+###"; 42 ' " +42"
Print Using "$$#,##0.00"; 1234.5 ' " $1,234.50"
Print Using "**#,##0.00"; 42.5 ' "*****42.50"
Print Using "###"; 12345 ' "%12345"
Print Using "####.####^^^^"; 0.000123 ' scientific notation
Print Using "\ \"; "Hello" ' fixed-width 4-char: "Hell"
Print Using "& likes &"; name$; food$ ' "Scott likes pizza"
Print Using "Total: ###.## units"; 7.5 ' "Total: 7.50 units"
Print Using "## and ##"; 1; 2; 3 ' " 1 and 2 3 and "
INPUT
Reads a line of text from the user or from a file channel. The user types it in a dialog box, or on the program's screen once it has one (see Console and Graphics).
INPUT variable
INPUT "prompt"; variable
INPUT #channel, variable
Input "Enter your name: "; name$
Input #1, line$
The target may be a variable, an array element, or a TYPE field (INPUT, INPUT #, LINE INPUT #, and READ alike). INPUT and INPUT # read a number into a numeric target the way VAL does (12abc gives 12).
DATA / READ / RESTORE
Inline data pool for constants.
DATA value1, value2, "string", ...
READ variable1, variable2, ...
RESTORE [label]
DATA statements define a pool of values. READ reads the next value from the pool into a variable, array element, or TYPE field. RESTORE resets the read pointer to the beginning; RESTORE label (or a line number) makes the next READ take the first DATA item at or after that label. An item in quotes is a string; an unquoted item is taken as raw text up to the next comma, colon, comment, or end of line (so keywords, several words, and text such as 12abc are read as they are written) and converted when READ into a numeric variable, which raises error 13 (Type mismatch) when the item is not a number. The most negative Integer and Long values (-32768, -2147483648) are accepted.
Data 10, 20, 30, "Hello"
Read a, b, c, msg$
Print a; b; c; msg$
Restore
Miscellaneous Statements
Error Handling
ON ERROR GOTO label ' Enable error handler
ON ERROR GOTO 0 ' Disable error handler
ON ERROR RESUME NEXT ' Go on at the next statement after any error
RESUME ' Retry the statement that caused the error
RESUME NEXT ' Continue at the next statement after the error
RESUME label ' Continue at a label or line number (RESUME 0 is RESUME)
ERROR n ' Raise a runtime error with error number n
The ERR keyword returns the current error number (a Long) in expressions and ERL the source line the error was raised on, or in a program with line numbers the last line number at or before it (both are 0 when no error is active, and both reset after RESUME). RESUME or RESUME NEXT executed while no error is active raises error 20 (RESUME without error). ERROR n raises error n with its standard message (ERROR 11 reports "Division by zero"); numbers without a standard message report "User-defined error".
Each SUB and FUNCTION has its own ON ERROR handler:
- An error is handled by the handler of the procedure it happens in; if that procedure has none, the calling procedures are searched outward.
- A procedure called from a running handler can trap its own errors with its own handler.
- Returning from a procedure that merely installed a handler leaves the caller's ERR and handler alone.
- An error raised inside a running handler passes to the calling procedure's handler (untrapped if there is none).
- The label named by ON ERROR GOTO must be in the same procedure.
An error that no handler traps ends the program, as in Visual Basic, wherever it happens: in module-level code or in an event handler (a Click, a Timer, a form's Load ...). DVX shows it in a BASIC Runtime Error box -- the error number, the line, and the message -- and writes it to the DVX log; the program's windows close once the box is dismissed. In the IDE the Output window shows the error and the editor goes to its line.
ON ERROR RESUME NEXT, as in Visual Basic, needs no handler: a statement that fails is abandoned and the program goes on at the next one, with ERR (Err.Number) and Err.Description telling what went wrong. An error in a procedure that has no handler of its own leaves that procedure, and the program goes on after the statement that called it. The statement clears ERR, and so does the end of the procedure that used it; check ERR right after the statements that may fail:
On Error Resume Next
Kill "old.tmp"
If Err.Number <> 0 Then Print "Not deleted: "; Err.Description
Err.Clear
On Error GoTo 0
The Err Object
Err holds the current error the way ERR and ERROR do:
Member Meaning
------ -------
Err or Err.Number The error number (0 when there is none); Err.Number = n sets it without raising it
Err.Description The error's message ("" when there is none)
Err.Source The source text the last Err.Raise gave ("" otherwise)
Err.Raise number [, source [, desc]] Raises error number (1 to 65535) with that source and description; without a description the number's standard message
Err.Clear Clears the error: ERR becomes 0, the description and source ""
ERROR$(n) (or ERROR(n)) gives the standard message of error number n, "User-defined error" for a number without one, and "" for 0; without an argument, the current error's message.
On Error GoTo Failed
Err.Raise 1001, "Loader", "The file has no header"
Exit Sub
Failed:
Print Err.Number; Err.Description; " ("; Err.Source; ")"
Resume Next
RESUME NEXT continues after the failing statement. When the error happened in the condition of an IF, ELSEIF, WHILE, DO, or LOOP, the condition counts as false (the block is skipped, the loop is left); after an error in a FOR statement the loop is skipped; after an error in the SELECT CASE expression the whole SELECT CASE block is skipped; after an error in a CASE test that test counts as false (the next value or CASE is tried); after an error in an ON ... GOTO/GOSUB selector no branch is taken and execution continues with the next statement.
On Error GoTo ErrorHandler
Open "missing.txt" For Input As #1
Exit Sub
ErrorHandler:
Print "Error number:"; Err; "on line"; Erl
Resume Next
Common Error Numbers
Number Meaning ------ ------- 1 FOR loop error (NEXT without FOR, NEXT variable mismatch, FOR stack underflow) 3 RETURN without GOSUB 4 Out of DATA 5 Illegal function call (bad argument to a built-in function; an undefined math result such as a negative number to a fractional power) 6 Overflow (value does not fit the target type; EXP, ^, or other math leaving the numeric range; a file size or position past 2 GB in LOF, FILELEN, LOC, SEEK, GET, or PUT) 7 Out of memory 9 Subscript out of range / invalid variable or field index 10 This array is fixed or temporarily locked (REDIM, or ERASE of a dynamic array, while one of its elements, or a field of one, is passed by reference to a procedure that is still running) 11 Division by zero 13 Type mismatch (text that is not a number where a number is needed) / not an array / not a TYPE instance 14 Out of string space (no memory to build a string) 20 RESUME without error 26 FOR loop nesting too deep 28 Out of stack space (calls or expressions nested too deeply) 51 Internal error (bad opcode) 52 Bad file number or file not open 53 File not found (OPEN for INPUT, KILL, FILELEN, ...) 54 Bad file mode (also: a printer opened for anything but OUTPUT or APPEND) 57 Device I/O error (printing failed or was cancelled) 58 File already exists or rename failed 59 Bad record length (OPEN ... LEN must be 1 to 32767; GET/PUT data longer than LEN) 63 Bad record number (SEEK, GET, or PUT position below 1) 64 Bad file name (WIDTH of a device DVX does not have) 67 Too many files open 68 Device unavailable (no printer to print on) 75 Path/file access error 76 Path not found 91 Object variable not set (property, method, Show/Hide/Unload on a value that is not a form or control, or on Nothing) 93 Invalid pattern string (a LIKE pattern with a [ that has no ]) 94 Invalid use of Null (Null where a real number or text is needed; see Variants) 424 Object required (SET, Is, or TypeOf with a value that is not an object) 438 Object doesn't support this property or method (the object has no member of that name, or it is Private) 446 Object doesn't support named arguments (a form or control method called with Name:=value) 448 Named argument not found (a Name:= the method does not have; a left-out OPTIONAL Variant argument, Missing, used as a value) 449 Argument not optional (a method call through an object left out a parameter that is not OPTIONAL) 450 Wrong number of arguments or invalid property assignment 451 Object not a collection (FOR EACH over an object that is not a Collection) 453 Library or function not installed (a DECLARE LIBRARY function whose library is not on this computer; see Include Libraries) 457 This key is already associated with an element of this collection (Collection.Add with a key in use) 481 Invalid picture (LOADPICTURE of a file that is not a picture DVX can show) 1000 No form runtime (a form or control statement ran without the form runtime, e.g. under a headless runner)
SHELL
Executes an operating-system command.
SHELL "command"
When used as a function, SHELL returns the exit code of the command.
Shell "DIR /B"
exitCode = Shell("COPY A.TXT B.TXT")
The command goes to DOS as it is written. DOS commands read / as the start of a switch (DIR /B), so write the paths in a command with \ (SHELL "DEL C:\TEMP\X.TXT"); FileOpsPathNative$ (fileops.bas) turns a path built with / into that form. Everywhere else in DVX BASIC a path may use / or \.
SLEEP
Pauses execution for the given number of seconds. With no argument, sleeps for 1 second. The desktop and the program's own forms keep running while it sleeps, SLEEP 0 just lets pending events run, and closing the program ends the sleep.
SLEEP [seconds]
RANDOMIZE
Seeds the random number generator. Without an argument (not shown here) and without a prior RANDOMIZE, the sequence is the same on every run. Use RANDOMIZE TIMER to get a different sequence each run.
RANDOMIZE seed
RANDOMIZE TIMER ' Seed from system clock
END
Terminates program execution immediately. Any form that is loaded is unloaded before the program exits.
END
Sound, Joysticks, and Frames
These statements work as they do in QBasic, on whatever sound hardware the computer has: a sound card, the PC speaker, or none at all, in which case the program runs the same but silently. Several programs can make sound at once. For sound files, tones of any shape, and music files, see the sound.bas and music.bas include libraries; for more joystick detail, joystick.bas.
BEEP
Sounds a short beep (800 hertz for a quarter of a second).
BEEP
SOUND
Plays a tone of frequency hertz (37 to 32767) for duration clock ticks; there are 18.2 ticks in a second, and fractions are allowed. A duration of 0 stops every sound the program has queued. Frequencies too high to hear (above 20000) are silent. Anything out of range is error 5 (Illegal function call).
SOUND frequency, duration
SOUND 440, 18.2 ' A for one second
SOUND 440, 0 ' stop
PLAY
Plays a tune written in the music macro language. Letters may be upper or lower case and spaces are ignored. The settings (octave, length, tempo, style, instrument, foreground or background) stay from one PLAY to the next.
PLAY commands$
PLAY "T160 O3 L8 E D C D E E E4"
Command Meaning ------- ------- A to G Play the note in the current octave. Follow it with # or + for sharp, - for flat, then optionally a length (1 to 64, as for L) and dots. N n Play note n (1 to 84, seven octaves from the lowest C); N0 is a rest. O n Octave 0 to 6 (default 4). Octave 3 starts at middle C. > and < Go up or down one octave. L n Length of the notes that follow: 1 a whole note, 2 a half, 4 a quarter (the default) ... 64. P n Pause (rest) of length n (1 to 64). . A dot after a note or pause makes it half as long again; each further dot adds half again. T n Tempo in quarter notes a minute (32 to 255, default 120). MN Music normal: each note sounds for 7/8 of its length (the default). ML Music legato: notes sound for their whole length. MS Music staccato: notes sound for 3/4 of their length. MF Music foreground: PLAY, SOUND, and BEEP wait until the sound has finished (the default). MB Music background: the program goes on while the music plays; it only waits when more than 32 notes are waiting. I n DVX's own: play the notes that follow on General MIDI instrument n (1 to 128; 1 is the piano, 41 the violin, 74 the flute) through the music synthesizer. I0 goes back to plain tones (the default). X "X" + VARPTR$(s$) plays the commands in s$ at that point (s$ may use X itself, up to 16 levels deep). = Any number above may be written as "=" + VARPTR$(n), n a numeric expression: "T=" + VARPTR$(tempo).
A string with a mistake in it plays nothing and raises error 5 (Illegal function call). X and "=" work as in QBasic, with VARPTR$ as for DRAW (see the program screen's DRAW): the value is taken when VARPTR$ runs. A whole tune may be at most 32767 characters once X and "=" are filled in.
verse$ = "L8 E D C D E E E4"
tempo = 160
PLAY "T=" + VARPTR$(tempo) + "O3 X" + VARPTR$(verse$) + "X" + VARPTR$(verse$)
Building the string with + works too, such as PLAY "O" + STR$(n). Without I, the notes are square-wave tones like the PC speaker's; with it, they play on the same synthesizer as MIDI music.
PLAY "MB T120 L4 CDEF GAB>C" ' a scale in the background
PLAY "I74 O4 L8 GAB>C" ' on the flute
PLAY Function
PLAY(n) returns how many notes are still waiting to be played in the background (n is ignored). A game can use it to keep its music going:
If PLAY(0) < 4 Then PLAY "MB " + tune$
STICK and STRIG
STICK(n) returns where a joystick points, from 1 (all the way left or up) to 200 (all the way right or down), about 100 in the middle, or 0 when there is no such joystick. STICK(0) reads joystick A's X and remembers A's Y and joystick B's X and Y, which STICK(1), STICK(2), and STICK(3) then return; so call STICK(0) first.
STRIG(n) returns True (-1) or False (0) for the buttons:
n Returns True when - ----------------- 0 joystick A's first button was pressed since the last STRIG(0) 1 joystick A's first button is down 2 joystick B's first button was pressed since the last STRIG(2) 3 joystick B's first button is down 4 joystick A's second button was pressed since the last STRIG(4) 5 joystick A's second button is down 6 joystick B's second button was pressed since the last STRIG(6) 7 joystick B's second button is down
Joystick A is the first joystick; joystick B the second one, or the second stick of a joystick with four axes. When no joystick is connected the keyboard stands in for joystick A (see the Control Panel's Game Controllers tab). STRIG ON, STRIG OFF, and ON STRIG are not supported; test STRIG in a Timer or a loop instead.
x = STICK(0)
y = STICK(1)
If STRIG(0) Then FireShot
FRAMEWAIT
Waits until the next frame of a steady clock of rate frames a second begins, keeping the desktop and the program's forms running meanwhile. A game loop that calls it once a frame runs at the same speed on every computer. Frames the program was too busy for are skipped, not caught up; FrameMissed() in game.bas counts them. rate must be at least 1; rates above 1000 count as 1000. Changing the rate starts the clock over.
FRAMEWAIT rate
Do
MoveEverything
DrawEverything
FRAMEWAIT 30
Loop Until quitting
Console and Graphics
These statements work as they do in QBasic. The first one a program uses gives it a screen of its own: a window, titled with the program's name, the size of the screen mode. From then on PRINT and INPUT use that screen too (before it, PRINT writes to the output window and INPUT asks in a dialog box, as always). Keys typed in the window go to INKEY$ and INPUT. The window stays open when the program's code has finished, so its output can be read; closing it ends the program.
The names are not reserved: a program that has its own variable, SUB, or constant called Color, Cls, Draw, and so on keeps using it.
Screen Modes
Mode Size and colours Text (WIDTH choices) Pages ---- ---------------- -------------------- ----- SCREEN 0 Text only, 16 colours 80 or 40 x 25, 43 or 50 8 (4 at 80 x 43, 80 x 50) SCREEN 1 320 x 200, 4 colours (two palettes) 40 x 25 1 SCREEN 2 640 x 200, 2 colours 80 x 25 1 SCREEN 7 320 x 200, 16 colours 40 x 25 8 SCREEN 8 640 x 200, 16 colours 80 x 25 4 SCREEN 9 640 x 350, 16 of 64 colours 80 x 25 or 80 x 43 2 SCREEN 10 640 x 350, 4 shades (some blinking) 80 x 25 or 80 x 43 2 SCREEN 11 640 x 480, 2 colours 80 x 30 or 80 x 60 1 SCREEN 12 640 x 480, 16 colours 80 x 30 or 80 x 60 1 SCREEN 13 320 x 200, 256 colours 40 x 25 1
A program starts in SCREEN 0. SCREEN with a new mode (and WIDTH) clears the screen and restores the mode's colours: the 16 standard colours, and in SCREEN 13 the standard 256-colour palette. SCREEN with the mode the program is already in only changes the pages, so a program can flip pages in a loop. Other mode numbers, and graphics statements in SCREEN 0, are error 5 (Illegal function call). The colour switch argument is accepted and ignored.
Text uses 16-pixel-high characters in SCREEN 0 (25 rows), 11 and 12 (30 rows), 14-pixel ones in SCREEN 9 and 10 (25 rows), and 8-pixel ones everywhere else. The window shows each mode in the shape it has on a monitor: the 640 x 200 modes with every line doubled, 40-column text with every column doubled; SCREEN 9 and 10 show at 640 x 350 as they are. On the whole screen the 320 x 200 modes use the computer's 320 x 200 mode and the others the nearest mode, centred.
SCREEN mode [, [colorswitch] [, [activepage] [, visualpage]]]
WIDTH [columns] [, rows]
Screen Pages and PCOPY
The pages of a mode (see the table) are separate screens. Drawing and PRINT go to the active page; the window shows the visual page. Drawing on a page that is not shown and then showing it (or copying it to the shown page) gives smooth animation. A visual page left out follows the active page. In SCREEN 0 each page has its own text. Each page also keeps its own text cursor, as in QBasic: LOCATE and PRINT move the active page's cursor, and switching pages brings back the cursor where it was on that page.
SCREEN 7, , 1, 0 ' draw on page 1, show page 0
Do
CLS
CIRCLE (x, 100), 20, 14
PCOPY 1, 0 ' show the finished picture
x = (x + 2) MOD 320
FRAMEWAIT 30
Loop
PCOPY source, destination copies one page (text and pixels) onto another. A page number the mode does not have is error 5.
FULLSCREEN
Shows the program's screen on the whole screen, or back in its window. Alt+Enter in the window does the same. While the program has the whole screen, Alt+Tab brings the desktop back (the program keeps running in its window) and switching back to the program returns it to the whole screen. Other programs keep running either way. When the computer has no suitable video mode, the screen stays in its window.
FULLSCREEN [ON | OFF]
FULLSCREEN expression ' True: whole screen, False: window
Text: CLS, LOCATE, COLOR, CSRLIN, POS, SCREEN( )
CLS [n]
LOCATE [row] [, [column] [, cursor]]
COLOR [foreground] [, background]
row = CSRLIN
column = POS(0)
code = SCREEN(row, column [, colorflag])
- CLS clears the screen and puts the cursor in the top-left corner (the top of the text viewport). In a graphics mode with a VIEW set, CLS (or CLS 1) clears only the view; with a VIEW PRINT set, CLS (or CLS 2) clears only the text viewport; CLS 0 clears everything.
- LOCATE moves the cursor (rows and columns count from 1). cursor 1 shows the text cursor in SCREEN 0, 0 hides it (the default). QBasic's cursor size arguments are accepted and ignored.
- COLOR in SCREEN 0 takes a foreground 0 to 31 and a background 0 to 15. Foregrounds 16 to 31 blink (colour minus 16); as on a VGA, backgrounds 8 to 15 show as 0 to 7 and make the text blink too.
- COLOR in SCREEN 1 takes a background colour (0 to 15) and a palette: an even number gives green, red, and brown for colours 1 to 3, an odd one (the default) cyan, magenta, and white. SCREEN 2 has no COLOR statement. In SCREEN 7 to 10 COLOR takes a foreground and a background colour (the colour of attribute 0: 0 to 15 in SCREEN 7 and 8, an EGA colour 0 to 63 in SCREEN 9, a shade 0 to 8 in SCREEN 10). In SCREEN 11 to 13 it takes only a foreground. The foreground is also the default colour of the drawing statements.
- CSRLIN is the cursor's row and POS(0) its column. Before the program has a screen, POS(0) is the output window's column.
- SCREEN(row, column) is the character code shown there; with a colorflag of 1, its colour (in SCREEN 0, foreground + 16 * background).
- PRINT scrolls the screen (or the text viewport) up at the bottom. CHR$(13) and CHR$(10) start a new line and CHR$(9) moves to the next multiple of 8 columns.
VIEW PRINT
Makes rows top to bottom the text viewport: PRINT scrolls only those rows, so the rows around them stay as they are (a title, a status line). The cursor goes to the viewport's first row. VIEW PRINT alone makes the whole screen the viewport again. LOCATE can still reach every row.
VIEW PRINT [top TO bottom]
INKEY$
Returns the next key typed in the program's window without waiting: "" when there is none, one character for a character key, and CHR$(0) + CHR$(scan code) for other keys (arrows, function keys). Keys held with Alt, and function keys held with Shift, Ctrl, or Alt, give the codes QBasic gives: Alt+A is CHR$(0) + CHR$(30), Alt+1 CHR$(0) + CHR$(120), Shift+F1 CHR$(0) + CHR$(84), Ctrl+F1 CHR$(0) + CHR$(94), and Alt+F1 CHR$(0) + CHR$(104). Keys wait for INKEY$ in a queue of 64.
Do
k$ = INKEY$
Loop While k$ = ""
If k$ = Chr$(0) + Chr$(72) Then Print "Up arrow"
PEN
QBasic's light pen; the mouse on the program's screen stands in for it, as the mouse drivers of the time did. PEN ON lets the program read it, PEN OFF stops that (PEN STOP is accepted too). PEN(n) then tells:
n PEN(n) - ------ 0 True (-1) if the left button went down since PEN(0) was last read, else 0 1, 2 x and y (pixels) where it last went down 3 True (-1) while the left button is down, else 0 4, 5 x and y where the mouse last was over the screen 6, 7 text row and column where it last went down 8, 9 text row and column where it last was
PEN(n) before PEN ON, after PEN OFF, or with another n raises error 5.
Screen 12
Pen On
Do
If Pen(0) Then PSet (Pen(1), Pen(2)), 14
Loop While InKey$ = ""
INPUT and LINE INPUT on the Screen
On the program's screen INPUT shows its prompt at the cursor and reads what is typed there: Backspace takes back a character, Esc clears the line, and Enter ends it. LINE INPUT reads a whole line, commas included, without adding "? " to the prompt. Without the program's screen LINE INPUT asks in a dialog box, like INPUT.
LINE INPUT ["prompt";] variable$
Graphics Coordinates
Points are (x, y) in pixels from the top-left corner. STEP(x, y) is relative to the last point drawn (the centre of the screen at first). VIEW makes a smaller part of the screen the drawing area (coordinates start at its corner, and drawing stays inside it); WINDOW gives it coordinates of your own. Colour numbers are attributes: 0 to 3 in SCREEN 1 and 10, 0 and 1 in SCREEN 2 and 11, 0 to 15 in SCREEN 7, 8, 9, and 12, 0 to 255 in SCREEN 13; anything else is error 5.
VIEW [[SCREEN] (x1, y1)-(x2, y2) [, [fill] [, border]]]
WINDOW [[SCREEN] (x1, y1)-(x2, y2)]
value = PMAP(coordinate, n)
- VIEW with coordinates of its own sets the view; fill fills it and border draws a frame around it. VIEW SCREEN keeps coordinates counting from the screen's corner. VIEW alone is the whole screen again.
- WINDOW (x1, y1)-(x2, y2) maps the view to those coordinates with y going up; WINDOW SCREEN keeps y going down. WINDOW alone goes back to pixels.
- PMAP converts between WINDOW coordinates and pixels of the view: n = 0 gives the pixel column of an x, 1 the pixel row of a y, 2 the x of a pixel column, and 3 the y of a pixel row. Without a WINDOW both are pixels.
PSET, PRESET, POINT
PSET [STEP](x, y) [, color]
PRESET [STEP](x, y) [, color]
c = POINT(x, y)
v = POINT(n)
PSET draws a point in the given colour or the COLOR foreground, PRESET in the given colour or colour 0. POINT(x, y) is the colour at a point, or -1 outside the view. POINT(0) and POINT(1) are the last point in pixels, POINT(2) and POINT(3) in WINDOW coordinates.
LINE
LINE [[STEP](x1, y1)]-[STEP](x2, y2) [, [color] [, [B | BF] [, style]]]
Draws a line, a box (B), or a filled box (BF). Without a start point the line starts at the last point. style is a 16-bit pattern: each set bit (from the top) draws a pixel, each clear one skips it.
LINE (10, 10)-(100, 50), 4, BF
LINE -STEP(20, 0), 15
LINE (0, 100)-(319, 100), 7, , &HF0F0
CIRCLE
CIRCLE [STEP](x, y), radius [, [color] [, [start] [, [end] [, aspect]]]]
Draws a circle, an ellipse, or an arc. start and end are angles in radians (0 to 2 * pi, counterclockwise from the right); a negative angle also draws the radius to that end, so CIRCLE can draw pie slices. aspect is the height divided by the width; the default makes circles round on a 4:3 monitor (0.83 in SCREEN 13, 1 in SCREEN 12), so in SCREEN 13 a circle in the window looks slightly flat and a round one needs aspect 1.
CIRCLE (160, 100), 50, 14
CIRCLE (160, 100), 50, 4, -0.0001, -1.5708 ' a quarter pie slice
CIRCLE (160, 100), 80, 2, , , 0.5 ' an ellipse
PAINT
PAINT [STEP](x, y) [, [color] [, border]]
PAINT [STEP](x, y), tile$ [, [border] [, background$]]
Fills the area around the point with color, up to pixels of the border colour (by default the fill colour itself).
With a string instead of a colour, PAINT fills the area with a pattern, as in QBasic. Each row of the pattern is one byte for each colour plane of the mode, and each bit is a pixel (the first bit is the left pixel): in SCREEN 2 and 11 one byte is eight pixels, colour 0 or 1; in SCREEN 7, 8, 9, and 12 four bytes (the blue, green, red, and bright planes) make eight pixels of 16 colours; in SCREEN 10 two bytes. In SCREEN 1 each byte is four pixels of two bits, and in SCREEN 13 each byte is one pixel. A pattern has up to 64 rows and repeats over the screen. The fill stops at the border colour (by default the COLOR foreground) and covers the whole area once, even where it already has the pattern's colours. background$ is accepted as in QBasic: a pattern with more than two rows in a row equal to it is error 5.
SCREEN 12
LINE (0, 0)-(100, 100), 15, B
PAINT (50, 50), CHR$(&HAA) + CHR$(&H55) + CHR$(0) + CHR$(0), 15 ' blue and green stripes
DRAW
Draws with a small command language. Letters may be upper or lower case; spaces and semicolons are ignored.
Command Meaning ------- ------- U n Up n (without n: 1). D n Down n. L n Left n. R n Right n. E n Diagonally up and right. F n Diagonally down and right. G n Diagonally down and left. H n Diagonally up and left. M x,y Move to x, y; with a sign (M+10,-5) move by that much. B Before a move: move without drawing. N Before a move: draw, then come back to where the move started. C n Colour of the lines that follow. S n Scale: moves are n/4 pixels a unit (1 to 255, default 4). A n Turn the directions by n * 90 degrees (0 to 3). TA n Turn the directions by n degrees (-360 to 360). P c,b PAINT at the current point with colour c up to border colour b.
The pen starts at the last point and stays where it ends. The colour, scale, and turn stay from one DRAW to the next. A mistake in the string is error 5.
As in QBasic, X runs another DRAW string and "=" takes a number, both given with VARPTR$: DRAW "X" + VARPTR$(shape$) and DRAW "C=" + VARPTR$(c) + "R=" + VARPTR$(n). DVX BASIC has no memory addresses, so VARPTR$ packs the value itself into its result (taken when VARPTR$ runs), and it takes any expression. Building the string with + and STR$ works too, such as DRAW "R" + STR$(n).
DRAW "BM100,100 C14 R50 D50 L50 U50" ' a square
DRAW "BM160,100 TA45 C2 R40" ' a line at 45 degrees
box$ = "R10 D10 L10 U10"
FOR i = 1 TO 5
DRAW "BM" + STR$(i * 20) + ",20 X" + VARPTR$(box$)
NEXT i
PALETTE
PALETTE [attribute, color]
PALETTE USING array[(first)]
Changes the colour an attribute shows. In SCREEN 11, 12, and 13, color is blue * 65536 + green * 256 + red with each part 0 to 63; in SCREEN 0 and 9 it is an EGA colour number 0 to 63; in SCREEN 1, 2, 7, and 8 one of the 16 standard colours; in SCREEN 10 a shade 0 to 8 (0 off, 4 grey, 8 bright, and 1 to 3, 5 to 7 blinking between two of them). PALETTE alone restores the mode's colours. PALETTE USING sets every attribute of the mode (256 in SCREEN 13) from a one-dimensional LONG array starting at element first (or its first element); -1 leaves that attribute as it is. On the whole screen a palette change shows at once, like on a VGA card; in the window, whatever is already drawn in that attribute changes colour too.
SCREEN 13
PALETTE 1, 63 * 65536 ' attribute 1 becomes bright blue
FOR i = 0 TO 63
PALETTE 2, i ' a red that fades in
FRAMEWAIT 30
NEXT i
Example
SCREEN 13
CLS
COLOR 14
PRINT "Press a key"
CIRCLE (160, 100), 60, 4
PAINT (160, 100), 1, 4
LINE (0, 0)-(319, 199), 15, B
DRAW "BM150,90 C15 R20 D20 L20 U20"
Do
Loop While INKEY$ = ""
FULLSCREEN
Do
Loop While INKEY$ = ""
File I/O
OPEN
Opens a file for reading, writing, or appending.
OPEN filename$ FOR INPUT AS #channel
OPEN filename$ FOR OUTPUT AS #channel
OPEN filename$ FOR APPEND AS #channel
OPEN filename$ FOR RANDOM AS #channel [LEN = recordSize]
OPEN filename$ FOR BINARY AS #channel
Mode Description ---- ----------- INPUT Open for sequential reading. File must exist. OUTPUT Open for sequential writing. Creates or truncates. APPEND Open for sequential writing at end of file. RANDOM Open for random-access record I/O. LEN sets the record size in bytes (default 128). BINARY Open for raw binary I/O.
Printing Through OPEN
The names LPT1:, LPT2:, LPT3: (a printer port) and PRN (the printer chosen in the Control Panel) open a printer, in any case and with or without the colon. Only OUTPUT and APPEND work (anything else raises error 54). PRINT # and WRITE # collect the output, and CLOSE sends it to the printer; a printer problem the user cancels makes CLOSE raise error 57, and a printer that cannot be reached makes OPEN raise error 68.
Open "LPT1:" For Output As #1
Print #1, "Line one"
Print #1, "Line two"
Close #1 ' now it prints
CLOSE
Closes open file channels.
CLOSE #channel
CLOSE #channel, #channel, ...
CLOSE
RESET
CLOSE with a list closes each channel in it (the # is optional); CLOSE alone, and RESET, close every open file. Closing a channel that is not open does nothing; a number outside the channels DVX has raises error 52.
PRINT #
Writes text to a file, formatted exactly as PRINT shows it: numbers are followed by a space, a comma moves to the next 14-column zone, a semicolon keeps the line going, and TAB(n), SPC(n), and PRINT #channel, USING work as they do for PRINT. See PRINT.
PRINT #channel, expression [; | , expression ...]
PRINT #channel, USING format$; expression [; expression ...]
WIDTH #
Sets the line width of an open file, the screen, or the printer: output longer than the width goes on in a new line, as in QBasic. 255 means no limit (the default).
WIDTH #channel, width
WIDTH "SCRN:", width
WIDTH "LPT1:", width
WIDTH LPRINT width
WIDTH "SCRN:" is the same as WIDTH width for the program's screen. WIDTH "LPT1:" (LPT2:, LPT3:, PRN) and WIDTH LPRINT set the width of the printer's lines, for LPRINT and for files opened on that printer afterwards. A width outside 1 to 255 raises error 5, a file number that is not open error 52, and a device DVX does not have (such as "COM1:") error 64.
INPUT #
Reads comma-delimited data from a file, one field per variable. Quoted strings written by WRITE # are read back without their quotes; numeric variables receive the converted value.
INPUT #channel, variable [, variable ...]
Open "data.txt" For Input As #1
Input #1, name$, age%, score#
Close #1
LINE INPUT #
Reads an entire line from a file into a string variable.
LINE INPUT #channel, variable$
WRITE #
Writes comma-delimited data to a file. Strings are enclosed in quotes, numbers are undecorated. Each statement writes a newline at the end.
WRITE #channel, expr1, expr2, ...
Write #1, "Scott", 42, 3.14
' Output: "Scott",42,3.14
GET / PUT
Read and write records in RANDOM or BINARY mode files. When recordNum is omitted the transfer starts at the current file position.
GET #channel, [recordNum], variable
PUT #channel, [recordNum], variable
In RANDOM mode recordNum is a 1-based record number and each record is LEN bytes (see OPEN). Numbers are stored in their binary form (Byte 1 byte, Integer and Boolean 2, Long 4, Single 4, Double, Currency, and Date 8); a String is stored as a 2-byte length followed by its characters. PUT always writes a whole record, padding the unused part with zero bytes, and GET always advances to the next record; data longer than LEN raises error 59 before anything is written.
In BINARY mode recordNum is a 1-based byte position. Numbers are stored as in RANDOM mode. PUT writes the characters of a String with no length prefix, and GET reads as many bytes as the String variable currently holds, so set its length first (for example with SPACE$ or by declaring it as STRING * n).
The variable may be a TYPE variable. Its fields are packed in declaration order with the sizes above; a STRING * n field takes exactly n bytes (padded with spaces), a variable-length String field is stored with its 2-byte length prefix in both modes, and nested TYPE fields are packed inline. GET fills the fields of the existing variable in place.
The variable may also be an array element, arr(i), of any type including a TYPE: PUT writes that element and GET reads into it. The index expression is evaluated once.
Type RecT
id As Integer
score As Double
nm As String * 6
End Type
Dim r As RecT
Open "recs.dat" For Random As #1 Len = 32
r.id = 1
Put #1, 1, r ' 16 bytes of fields, padded to a 32-byte record
Get #1, 1, r
Close #1
Dim buf As String
Open "raw.bin" For Binary As #1
buf = Space$(16)
Get #1, 1, buf ' read bytes 1-16
Put #1, 33, "TAG" ' write 3 bytes at position 33
Close #1
SEEK
Sets the file position. As a function, returns the current position. On a RANDOM file the position is a record number (statement and function); on other files it is a 1-based byte position. A position below 1 raises error 63 (Bad record number).
SEEK #channel, position ' Statement: set position
pos = SEEK(channel) ' Function: get current position
File System Statements
DVX BASIC provides statements for working with the file system -- creating, deleting, renaming, and copying files and directories, and reading or setting file attributes.
A path may be written with / or \ between its parts, mixed too: OPEN "DATA/SCORES.TXT" and OPEN "DATA\SCORES.TXT" open the same file, and so do the FileOps, Shell, and other library functions that take a path (SHELL commands are the exception: see SHELL). Paths DVX gives back -- CURDIR$, App.Path, App.Data, the file dialogs, FileOpsPathJoin$, and the other FileOps results -- use \, the separator DOS shows; FileOpsPathNative$ gives a path of your own that form before you show it.
KILL
Deletes a file.
KILL filename$
Kill "temp.bak"
NAME ... AS
Renames (or moves) a file.
NAME oldName$ AS newName$
Name "draft.txt" As "final.txt"
FILECOPY
Copies a file. If the destination already exists it is overwritten.
FILECOPY source$, destination$
FileCopy "data.db", "data.bak"
MKDIR / RMDIR
Creates or removes a directory.
MKDIR path$
RMDIR path$
MkDir App.Data & "\backups"
RmDir "C:\OLD"
CHDIR / CHDRIVE
Changes the current working directory or drive.
CHDIR path$
CHDRIVE drive$
ChDir "C:\DVX\PROJECTS"
ChDrive "D"
GETATTR / SETATTR
GETATTR returns the attributes of a file (as a function). SETATTR sets them (as a statement). Attributes are a bitmask of the vb file-attribute constants. A missing file raises error 53 (File not found); SETATTR raises error 75 when the attributes cannot be changed.
attrs = GETATTR(filename$)
SETATTR filename$, attrs
Constant Value Attribute -------- ----- --------- vbNormal 0 Normal file (no attributes set) vbReadOnly 1 Read-only vbHidden 2 Hidden vbSystem 4 System file vbDirectory 16 Entry is a directory vbArchive 32 File has been modified since last backup
If (GetAttr("readme.txt") And vbReadOnly) <> 0 Then
Print "File is read-only."
End If
SetAttr "secret.dat", vbHidden + vbReadOnly
CURDIR$ / DIR$
CURDIR$ returns the current directory. DIR$ returns the first file matching a pattern; subsequent calls with no arguments return additional matches (or "" when done). DIR$ returns only files: never ".", "..", or subdirectories. DIR$("C:\*.*") lists the root of C: (DIR$("C:*.*") the drive's current folder).
cwd$ = CURDIR$
first$ = DIR$("*.txt")
WHILE first$ <> ""
Print first$
first$ = DIR$ ' next match, no argument
WEND
FILELEN
Returns the length of a file in bytes. A file that does not exist raises error 53 (File not found); one past 2 GB raises error 6 (Overflow).
bytes = FILELEN(filename$)
String Functions
Function Returns Description -------- ------- ----------- ASC(s$) Integer ASCII code of the first character of s$ (error 5 for an empty string) CHR$(n) String Character with ASCII code n FILTER(arr, match$ [, include [, compare]]) String array The elements of arr that contain match$ (or, with include False, that do not) FORMAT$(value [, fmt$]) String Formats a number or a date using a format string HEX$(n) String Hexadecimal representation of n (uppercase, no leading &H) INSTR(s$, find$) Long Position of find$ in s$ (1-based), 0 if not found INSTR(start, s$, find$) Long Search starting at position start (1-based) INSTRREV(s$, find$ [, start [, compare]]) Long Position of the last find$ in s$ that ends at or before start (default: the end), 0 if not found JOIN$(arr [, delim$]) String The elements of a one-dimensional array joined with delim$ between them (default: a space) LCASE$(s$) String Converts s$ to lowercase LEFT$(s$, n) String Leftmost n characters of s$ LEN(s$) Long Length of s$ in characters LTRIM$(s$) String Removes leading spaces from s$ MID$(s$, start) String Substring from start (1-based) to end of string MID$(s$, start, length) String Substring of length characters starting at start OCT$(n) String Octal representation of n (no leading &O) REPLACE$(s$, find$, repl$ [, start [, count [, compare]]]) String s$ from position start (default 1) on, with the first count (default all) occurrences of find$ replaced by repl$ RIGHT$(s$, n) String Rightmost n characters of s$ RTRIM$(s$) String Removes trailing spaces from s$ SPACE$(n) String String of n spaces (n up to 65535) SPLIT(s$ [, delim$ [, limit [, compare]]]) String array The pieces of s$ between occurrences of delim$ (default: a space), at most limit of them (default: all) STRCOMP(a$, b$ [, compare]) Integer -1 if a$ sorts before b$, 0 if they are equal, 1 if a$ sorts after b$ STR$(n) String Converts number n to string (leading space for non-negative) STRING$(n, char) String String of n copies of char (char can be an ASCII code or single-character string; n up to 65535) STRREVERSE$(s$) String s$ with its characters in reverse order TRIM$(s$) String Removes leading and trailing spaces from s$ UCASE$(s$) String Converts s$ to uppercase VAL(s$) Double Converts string s$ to a numeric value; stops at first non-numeric character
A negative count or position passed to LEFT$, RIGHT$, MID$, SPACE$, or STRING$, or a count above 65535 passed to SPACE$ or STRING$, raises error 5 (Illegal function call). INSTR follows OPTION COMPARE TEXT (a case-insensitive search), and an INSTR start position below 1 raises error 5.
REPLACE$, JOIN$, and STRREVERSE$ may also be written without the $. Items in square brackets may be left out.
Comparing Text
The compare argument of FILTER, INSTRREV, REPLACE$, SPLIT, and STRCOMP chooses how text is matched: vbBinaryCompare (0) matches exactly, vbTextCompare (1) ignores upper and lower case, and vbUseCompareOption (-1, the default) follows OPTION COMPARE. Any other value raises error 5.
Print StrComp("apple", "Apple") ' 1 ("a" sorts after "A")
Print StrComp("apple", "Apple", vbTextCompare) ' 0
Print Replace$("Red red RED", "red", "blue", 1, -1, vbTextCompare) ' blue blue blue
Print InStrRev("C:\DVX\APPS\EDIT.APP", "\") ' 12
REPLACE$ Details
REPLACE$ returns the text from position start on, so REPLACE$("abcabc", "b", "x", 3) is "caxc" -- the characters before start are left out, as in Visual Basic. A count of -1 replaces every occurrence. An empty find$ returns the text unchanged. A start below 1 or a count below -1 raises error 5.
SPLIT, JOIN$, and FILTER
SPLIT returns a string array numbered from 0. Assign it to a dynamic array (one declared with empty parentheses) and use UBOUND to find the last piece. The last piece keeps the rest of the text when limit stops the split. Splitting an empty string, or a limit of 0, gives an array with no elements: its UBOUND is -1. An empty delim$ gives the whole string as the only piece.
Dim parts() As String
Dim i As Integer
parts = Split("red,green,,blue", ",")
For i = 0 To UBound(parts)
Print i; "[" & parts(i) & "]" ' the empty third piece is kept
Next i
Print Join$(parts, " / ") ' red / green / / blue
JOIN$ works on any one-dimensional array; numbers are converted as STR$ would, without the leading space. FILTER returns a new string array holding the elements that contain match$ (include True, the default) or that do not contain it (include False), or an array with no elements when nothing matches. Passing JOIN$ or FILTER something that is not a one-dimensional array raises error 13 (Type mismatch).
INSTRREV Details
INSTRREV searches backward: INSTRREV("abcabc", "bc") is 5 and INSTRREV("abcabc", "bc", 4) is 2. A start of -1 (the default) searches from the end. It returns 0 when s$ is empty or start is past the end of s$, and start when find$ is empty. A start of 0 or below -1 raises error 5.
FORMAT$
FORMAT$ formats a numeric value using a BASIC-style format string. The format characters are the same as the ones used by PRINT USING.
s$ = FORMAT$(value, fmt$)
s$ = FORMAT$(value)
Print Format$(3.14159, "###.##") ' " 3.14"
Print Format$(-7, "+###") ' " -7"
Print Format$(42, "00000") ' "00042"
Print Format$(1234.5, "#,##0.00") ' "1,234.50"
Print Format$(5, "#,##0.00") ' " 5.00"
Print Format$(0.5, "PERCENT") ' "50%"
The whole format string is one numeric field (for a number). The accepted format characters are # (digit or pad space), 0 (digit that is always printed; zero pads up to the first 0), . (decimal point), , (thousands separator), + and - (sign placement), $$ (floating dollar sign), ** (asterisk fill), ^^^^ (scientific notation), and the literal word PERCENT (multiplies by 100 and appends %). Unlike PRINT USING, a value wider than the field is printed in full without a % marker. See PRINT USING for details on each character.
With no format string (or an empty one), FORMAT$ gives the value's plain text, as CSTR does: a number without the leading space STR$ puts before it (Format$(42) is "42"), a date in the usual m/d/yyyy h:mm:ss AM form, text as it is, True / False, and "" for Empty.
FORMAT (without the $) is the same function.
FORMAT$ with Dates
When the format string holds a date or time code, or is one of the named date formats, FORMAT$ formats its value as a date. The value may be a Date, a number (taken as a date's number of days), or a string that holds a date (anything else raises error 13). A Date with an empty format string gives the usual m/d/yyyy h:mm:ss AM form. The codes may be written in upper or lower case.
Code Result ---- ------ d / dd Day of the month (dd: two digits) ddd / dddd Weekday name, abbreviated (Mon) or full (Monday) ddddd Short date (m/d/yyyy) dddddd Long date (dddd, mmmm d, yyyy) w Day of the week (1 = Sunday) ww Week of the year (the week holding January 1 is week 1) m / mm Month number (mm: two digits); the minute when it follows an hour or comes before a second mmm / mmmm Month name, abbreviated (Jan) or full (January) q Quarter (1-4) y Day of the year (1-366) yy / yyyy Year as two or four digits h / hh Hour (hh: two digits); 1-12 when the format has AM/PM, else 0-23 n / nn Minute s / ss Second ttttt Long time (h:mm:ss AM/PM) c The usual date and time form AM/PM am/pm AM or PM in the case written; A/P and a/p give one letter; AMPM gives AM or PM "text" Text in double quotes is copied as it is \x The character after a backslash is copied as it is
Any other character, such as / : - , or a space, is copied as it is. These names give whole formats:
Name Format Example ---- ------ ------- General Date (the usual form) 1/5/2000 3:04:05 PM Long Date dddd, mmmm d, yyyy Wednesday, January 5, 2000 Medium Date dd-mmm-yy 05-Jan-00 Short Date m/d/yyyy 1/5/2000 Long Time h:mm:ss AM/PM 3:04:05 PM Medium Time hh:mm AM/PM 03:04 PM Short Time hh:mm 15:04
Dim d As Date
d = #1/5/2000 3:04:05 PM#
Print Format$(d, "yyyy-mm-dd hh:nn:ss") ' 2000-01-05 15:04:05
Print Format$(d, "dddd, mmmm d") ' Wednesday, January 5
Print Format$(d, "h:mm AM/PM") ' 3:04 PM
Print Format$(d, "Long Date") ' Wednesday, January 5, 2000
Print Format$(Now, "Short Time") ' the time now, e.g. 09:41
MID$ Assignment
MID$ can also be used on the left side of an assignment to replace a portion of a string without changing its length:
Mid$(s$, start [, length]) = replacement$
Dim s$
s$ = "Hello, World!"
Mid$(s$, 8, 5) = "DVX--"
Print s$ ' Hello, DVX--!
If length is omitted, MID$ replaces up to LEN(replacement$) characters; a length of 0 replaces nothing. The target string's total length never changes -- extra characters in replacement$ are truncated and shorter replacements leave trailing characters in place. A start position below 1 or a negative length raises error 5 (Illegal function call).
Math Functions
Function Returns Description -------- ------- ----------- ABS(n) Double Absolute value of n ATN(n) Double Arctangent of n (result in radians) COS(n) Double Cosine of n (n in radians) EXP(n) Double e raised to the power n FIX(n) Integer Truncates n toward zero (drops the fractional part) INT(n) Integer Largest integer less than or equal to n (floor) LOG(n) Double Natural logarithm (base e) of n RND[(n)] Double Random number in the range 0 <= RND < 1 SGN(n) Integer Sign of n: -1 if negative, 0 if zero, 1 if positive SIN(n) Double Sine of n (n in radians) SQR(n) Double Square root of n TAN(n) Double Tangent of n (n in radians) TIMER Double Seconds since midnight
RND follows QBasic: RND or RND(n) with n > 0 returns the next number in the sequence, RND(0) repeats the last number, and RND(n) with n < 0 reseeds the generator from n (the same n always gives the same number). The result is a Double, 0 <= RND < 1.
INT, FIX, and SGN return an Integer, widening to Long or Double when the value does not fit. EXP, ^, and the other functions raise error 6 (Overflow) when the result leaves the numeric range, and error 5 when it is undefined (for example a negative number to a fractional power).
Note: Use RANDOMIZE or RANDOMIZE TIMER to seed the generator so you don't get the same sequence each run.
Color Functions
The color functions work with 24-bit colors packed into a Long the way Visual Basic packs them, &HBBGGRR: red is the low byte, green the middle byte, and blue the high byte. RGB(255, 0, 0) and &HFF are red, RGB(0, 0, 255) and &HFF0000 are blue. ForeColor, BackColor, FillColor, the drawing methods of forms and picture boxes (PSet, Line, Circle, Point), the Canvas control, and the color dialog all use this form (the console graphics statements of SCREEN modes use QBasic color numbers instead), as do the color constants vbBlack, vbRed, vbGreen, vbYellow, vbBlue, vbMagenta, vbCyan, and vbWhite (see Predefined Constants).
Function Returns Description -------- ------- ----------- RGB(r, g, b) Long Combine red, green, and blue components (0-255) into a color: r + g * 256 + b * 65536 QBCOLOR(n) Long The color of QBasic color number n (0 to 15: 0 black, 1 blue, 2 green, 4 red, 7 white, 8 gray, 15 bright white, ...); error 5 for any other number GETRED(color) Integer Red component (0-255) of a color GETGREEN(color) Integer Green component (0-255) of a color GETBLUE(color) Integer Blue component (0-255) of a color
Dim c As Long
c = RGB(255, 128, 0) ' bright orange, &H0080FF
Print GetRed(c); GetGreen(c); GetBlue(c) ' 255 128 0
Me.BackColor = RGB(0, 0, 128) ' dark blue background
Me.ForeColor = QBColor(14) ' yellow
Conversion Functions
Function Returns Description -------- ------- ----------- CBOOL(n) Boolean Returns True (-1) if n is nonzero or a non-empty string; False (0) otherwise CBYTE(n) Byte Converts n to Byte with the rounding CINT uses; error 6 (Overflow) outside 0 to 255 CCUR(n) Currency Converts n to Currency, rounded to four decimal places (halves to even); error 6 (Overflow) outside the Currency range CDATE(v) Date Converts a number (days since 12/30/1899) or date text to a Date; error 13 for text that is not a date, error 6 outside 1/1/100 to 12/31/9999 CDBL(n) Double Converts n to Double (a Date gives its number of days) CINT(n) Integer Converts n to Integer; halves round to the nearest even number (2.5 -> 2, 3.5 -> 4); error 6 (Overflow) outside -32768 to 32767 CLNG(n) Long Converts n to Long with the same rounding; error 6 (Overflow) outside the Long range CSNG(n) Single Converts n to Single CSTR(n) String Converts n to its String representation (a Date gives m/d/yyyy h:mm:ss AM) CVAR(v) Variant Returns v unchanged as a Variant
Passing Null to any of these except CVAR raises error 94 (Invalid use of Null). Text passed to one that gives a number must hold a number (CINT("12") is 12); other text raises error 13 (Type mismatch). VARTYPE, TYPENAME, and the IS functions (ISNULL, ISEMPTY, ISNUMERIC, ...) are described under Variants.
Date and Time Functions
These functions work with Date values (see Data Types). Wherever a date is expected, a Date, a number of days since 12/30/1899, or a string holding a date may be given; text that is not a date raises error 13 (Type mismatch).
Function Returns Description -------- ------- ----------- NOW Date The current date and time DATE Date Today's date (at midnight) TIME Date The current time of day (on 12/30/1899) DATESERIAL(year, month, day) Date The date with those parts TIMESERIAL(hour, minute, second) Date The time with those parts DATEVALUE(d) Date The date part of d (its time dropped) TIMEVALUE(d) Date The time part of d (on 12/30/1899) YEAR(d) Integer Year (100-9999) MONTH(d) Integer Month (1-12) DAY(d) Integer Day of the month (1-31) WEEKDAY(d [, firstDay]) Integer Day of the week, 1 for firstDay (default vbSunday) HOUR(d) Integer Hour (0-23) MINUTE(d) Integer Minute (0-59) SECOND(d) Integer Second (0-59) DATEADD(interval$, n, d) Date d moved by n intervals (n may be negative) DATEDIFF(interval$, d1, d2 [, firstDay [, firstWeek]]) Long Intervals from d1 to d2 (negative when d2 is earlier) DATEPART(interval$, d [, firstDay [, firstWeek]]) Integer One part of d ISDATE(v) Boolean True when v is a Date or a string holding a valid date MONTHNAME(month [, abbreviate]) String January ... December (Jan ... Dec when abbreviate is True) WEEKDAYNAME(n [, abbreviate [, firstDay]]) String Name of day n of the week counted from firstDay (default vbSunday)
NOW, DATE, and TIME need no parentheses. DATE$ and TIME$ still return the date and time as text.
Intervals
DATEADD, DATEDIFF, and DATEPART take the interval as a string, in upper or lower case:
Interval Meaning -------- ------- "yyyy" Year "q" Quarter "m" Month "y" Day of the year "d" Day "w" Weekday "ww" Week "h" Hour "n" Minute "s" Second
Any other interval raises error 5 (Illegal function call).
DATESERIAL and TIMESERIAL
A year from 0 to 29 means 2000 to 2029 and one from 30 to 99 means 1930 to 1999. Months, days, hours, minutes, and seconds outside their usual range carry into the next or previous unit, so DATESERIAL(2000, 13, 1) is 1/1/2001, DATESERIAL(2000, 3, 0) is the last day of February 2000, and TIMESERIAL(0, 90, 0) is 1:30 AM. Fractional arguments are rounded. A result before 1/1/100 or after 12/31/9999 raises error 5.
Dim lastDay As Date
lastDay = DateSerial(Year(Date), Month(Date) + 1, 0) ' last day of this month
Print Day(lastDay)
DATEADD
Adding months, quarters, or years keeps the day of the month where it can; when the new month is shorter the day becomes its last day, so DATEADD("m", 1, #1/31/2000#) is 2/29/2000. The time of day is kept. "y", "d", and "w" all add days and "ww" adds weeks. A fractional n drops its fraction. A result outside the Date range raises error 5.
Print DateAdd("m", 1, #1/31/2001#) ' 2/28/2001
Print DateAdd("h", -2, #1/1/2000#) ' 12/31/1999 10:00:00 PM
DATEDIFF
DATEDIFF counts how many interval boundaries lie between d1 and d2: DATEDIFF("yyyy", #12/31/1999#, #1/1/2000#) is 1 although the dates are a day apart, and DATEDIFF("d", ...) counts midnights, whatever the times of day. "w" counts whole weeks (7 days) and "ww" counts calendar weeks -- the number of firstDay days (Sundays by default) after d1 up to and including d2. A result too large for a Long raises error 6 (Overflow).
Print DateDiff("d", #1/1/2000#, #3/1/2000#) ' 60
Print DateDiff("ww", #1/1/2000#, #1/2/2000#) ' 1 (1/2/2000 is a Sunday)
Print DateDiff("w", #1/1/2000#, #1/2/2000#) ' 0
DATEPART and Weeks of the Year
DATEPART returns the part of d the interval names: "w" is the day of the week (1 for firstDay) and "ww" the week of the year. firstDay is one of vbSunday (1, the default) to vbSaturday (7), or vbUseSystemDayOfWeek (0, meaning Sunday). firstWeek chooses which week is week 1:
Constant Value Week 1 is -------- ----- --------- vbUseSystem 0 Same as vbFirstJan1 vbFirstJan1 1 The week holding January 1 (the default) vbFirstFourDays 2 The first week with at least four days in the new year vbFirstFullWeek 3 The first full week of the year
Days before week 1 belong to the last week of the previous year. A firstDay or firstWeek outside these values raises error 5.
Print DatePart("q", #5/15/2000#) ' 2
Print DatePart("ww", #1/1/2000#, vbSunday, vbFirstFourDays) ' 52
ISDATE, MONTHNAME, and WEEKDAYNAME
ISDATE is True for a Date and for a string that holds a valid date or time; numbers are not dates to ISDATE. MONTHNAME with a month outside 1 to 12, and WEEKDAYNAME with n outside 1 to 7, raise error 5.
If IsDate(Text1.Text) Then due = CDate(Text1.Text)
Print MonthName(Month(Date)); " "; WeekdayName(Weekday(Date), True)
File I/O Functions
Function Returns Description -------- ------- ----------- EOF(channel) Boolean True if the file pointer is at end of file FREEFILE Integer Next available file channel number (1..16) INPUT$(n, #channel) String Reads exactly n characters from the file LOC(channel) Long BINARY: position of the last byte read or written (0 at the start); RANDOM: number of the last record read or written; sequential: number of 128-byte blocks read or written LOF(channel) Long Length of the file in bytes FILEATTR(channel, 1) Long How the file is open: 1 INPUT, 2 OUTPUT, 4 RANDOM, 8 APPEND, 32 BINARY (QBasic's numbers) FILEATTR(channel, 2) Long The operating system's handle for the open file SEEK(channel) Long RANDOM: number of the next record; other modes: 1-based position of the next byte to be read or written FILELEN(path$) Long Length of the named file in bytes (no OPEN needed; error 53 when missing) GETATTR(path$) Integer File attribute bits (see vbReadOnly, vbHidden, etc.) CURDIR$ String Current working directory DIR$(pattern$) String First filename matching pattern, or "" DIR$ String Next match from the last DIR$(pattern$), or "" when done LBOUND(array [, dim]) Integer Lower bound of an array dimension (default dim = 1) UBOUND(array [, dim]) Integer Upper bound of an array dimension (default dim = 1)
The dim argument of LBOUND and UBOUND must be a constant from 1 to 8.
File sizes and positions are Longs, so they stop at 2 GB (2,147,483,647 bytes). A disk can hold bigger files (up to 4 GB on FAT32); LOF, FILELEN, LOC, and SEEK raise error 6 (Overflow) for a size or position past 2 GB rather than give a wrong number, and GET and PUT do the same for a record that would end past it. EOF stays right while the position is below 2 GB. FileOpsListSize and FileOpsTreeBytes (fileops.bas) give any file's size as a Double.
Miscellaneous Functions
Function Returns Description -------- ------- ----------- DATE$ String Current date as "MM-DD-YYYY" TIME$ String Current time as "HH:MM:SS" (24-hour) TIMER Double Seconds since midnight ENVIRON$(name$) String Value of the environment variable; empty string if not set COMMAND$ String The arguments the program was started with; empty string if none COMMAND$(n) String The nth argument (1 = first) without its quotes; empty string when there are fewer; COMMAND$(0) is the same as COMMAND$ ERR Long Current runtime error number (0 if no error) SHELL(cmd$) Integer Exit code of the command (also usable as a statement, discarding the code) IIF(cond, a, b) (any) a when cond is True, b when it is False CHOOSE(n, a, b, ...) (any) The nth value of the list (1 = a), or Null when there is no nth value; up to 31 values LOADPICTURE([file$ [, size [, colordepth [, x, y]]]]) Object A Picture object for a picture property (DragIcon, Picture), with Width, Height, and Type; no file (or "") gives an empty picture
DATE$ and TIME$ return text; the Date and Time Functions return Date values.
IIF and CHOOSE
IIF and CHOOSE evaluate every argument before picking one, as in Visual Basic, so a function called in an argument runs even when its value is not chosen. A fractional n in CHOOSE drops its fraction (2.9 picks the second value). An n below 1 or past the end of the list gives Null, as in Visual Basic (test it with ISNULL; see Variants).
Print IIf(score >= 50, "Pass", "Fail")
Print Choose(Weekday(Date), "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
LOADPICTURE
LOADPICTURE(file$) reads a picture file (BMP, PNG, JPEG, or GIF) -- or the icon of a DVX program, widget, or library file (.app, .wgt, .lib) -- and returns a Picture object (TypeName "Picture") that can be given to a picture property: a control's DragIcon, the Picture of an Image or ImageButton. Use SET, or a plain assignment, as with a file name; file names still work too. LOADPICTURE() or LOADPICTURE("") gives an empty picture, which clears the property. A missing file raises error 53 (File not found), a file that is not a picture error 481 (Invalid picture), so the check happens where the picture is loaded. Loading the same file again gives the same Picture object. Reading a picture property back gives a Picture object too (TypeName "Picture"), as in Visual Basic; used as a value (printed, joined to text, compared) it stands for its file name, so code that treats the property as a file name keeps working. The name of a program, widget, or library file works wherever a picture file's name does (a Picture property, DragIcon, PaintPicture, a Toolbar's AddButton) and shows its icon; such a file without an icon is not a picture (error 481).
The optional size and colordepth arguments are Visual Basic's: size is vbLPSmall (0), vbLPLarge (1), vbLPSmallShell (2), vbLPLargeShell (3), or vbLPCustom (4, with x and y giving the size), colordepth vbLPDefault (0), vbLPMonochrome (1), vbLPVGAColor (2), or vbLPColor (3). They choose among the images of an icon file; every DVX picture is a single bitmap, so, as for a bitmap in Visual Basic, they are checked and otherwise make no difference. A value outside these, or a negative x or y, raises error 5.
A Picture object has three properties, which can only be read:
Property Type Description -------- ---- ----------- Width Long The picture's width in HiMetric units (hundredths of a millimetre), as in Visual Basic; 0 for an empty picture. Height Long The picture's height in HiMetric units; 0 for an empty picture. Type Integer vbPicTypeBitmap (1) for a loaded picture, vbPicTypeNone (0) for an empty one.
The HiMetric sizes assume a standard display of 96 pixels an inch (the same as ScaleMode), so a pixel is about 26.46 HiMetric units. Convert them to pixels or another scale with a form's or PictureBox's ScaleX and ScaleY methods (see Drawing on Forms and PictureBoxes). Visual Basic's Handle property and Render method are not available: DVX pictures have no system handle.
Set Label1.DragIcon = LoadPicture("drag.bmp")
Image1.Picture = LoadPicture("logo.bmp")
Set Label1.DragIcon = LoadPicture() ' back to the dotted outline
Dim pic As Object
Set pic = LoadPicture("logo.bmp")
Print Me.ScaleX(pic.Width, vbHimetric, vbPixels); "x"; Me.ScaleY(pic.Height, vbHimetric, vbPixels)
COMMAND$
COMMAND$ returns the program's arguments exactly as they were given, as in Visual Basic (QBasic turned them into capitals; DVX keeps them as they are). Arguments are separated by blanks; an argument with blanks in it is written in double quotes. COMMAND$(n) picks out one argument with its quotes removed, so a program that opens the document it was started with only needs:
IF COMMAND$(1) <> "" THEN
OpenFile COMMAND$(1)
END IF
A program gets arguments when a document of a type associated with it is opened (the document is the argument), from a Program Manager item's Arguments, or from a command typed in a Run box. When the program runs in the IDE, COMMAND$ returns the Arguments set in Project Properties. A negative n is error 5 (Illegal function call).
Form and Control Statements
DVX BASIC supports Visual Basic-style forms and controls for building graphical user interfaces. A form is normally designed visually in the IDE and saved as a .frm file, but forms and their controls can also be built entirely in code.
Loading and Unloading Forms
LOAD FormName
UNLOAD FormName
UNLOAD Me
UNLOAD formVariable
LOAD creates the form and its controls in memory. It fires the form's Load event (Form1_Load for a form named Form1) when the form is first loaded. UNLOAD destroys the form, firing its QueryUnload event (which can cancel the close) and then its Unload event. The form name here is the literal name of the form as it appears in its .frm file; UNLOAD Me unloads the form whose event handler is running. UNLOAD also takes a Form variable, or anything else that gives a form (for example a form made with CreateForm and kept in an object's property), and unloads that form.
Showing and Hiding Forms
FormName.Show [mode]
FormName.Hide
Me.Show [mode]
Me.Hide
Pass vbModal (1) to Show for a modal dialog that blocks until the user closes it. Omit the mode for a modeless (non-blocking) window.
Form2.Show vbModal
Me.Hide
Property Access
Read and write control properties using dot notation:
ControlName.Property = value
value = ControlName.Property
Text1.Text = "Hello"
Label1.Caption = "Name: " & name$
x = Text1.Left
A control on another form is reached through that form's name, or through a variable holding a form reference: FormName.ControlName.Property works in both assignments and expressions, and so does FormName.ControlName.Method. Me.ControlName.Property does the same for the current form.
Using a property or method (or Show, Hide, Unload) on a variable that does not hold a form or control raises error 91 (Object variable not set). In the IDE, a property or method name that the control's type does not have is reported when the program is compiled.
Form2.Label1.Caption = "From Form1"
total = Form2.List1.ListCount
Form2.List1.Clear
Method Calls
ControlName.Method [args]
List1.AddItem "New entry"
List1.Clear
Me Keyword
Me refers to the current form -- that is, the form whose event handler is running. Use it to access the form's own properties, controls, and methods from within event handlers, especially when the form's name may change or when the same code runs from multiple forms. In the code of a class module, Me is instead the object whose procedure is running (see Objects and Class Modules).
Me.Caption = "Updated Title"
Me.Text1.Text = ""
Me.Hide
Nothing
Nothing is a null object reference. Assign it with SET to an object variable to indicate "no object", and test for it with Is. An object variable that has not been Set holds Nothing, and using a property or method of Nothing raises error 91 (Object variable not set).
Set myCtrl = Nothing
If myCtrl Is Nothing Then Print "No control chosen"
Setting the last variable that refers to a class instance or Collection to Nothing frees the object; see Objects and Class Modules.
Control Arrays
Multiple controls can share a name with unique indices. Access individual controls with parenthesized indices:
Option1(0).Value = True
Label1(idx).Caption = "Item " & Str$(idx)
Me.Label1(i).Visible = True
WITH
The WITH block provides a shorthand for setting several properties on the same control or for making several method calls. Inside the block, begin each property or method with a dot and omit the control name.
WITH ControlName
.Property1 = value1
.Property2 = value2
.Method arg1, arg2
END WITH
With Label1
.Caption = "Status: Ready"
.ForeColor = RGB(0, 128, 0)
.Visible = True
End With
Building Forms in Code
In addition to loading forms from .frm files, you can build a form entirely from code with CreateForm, CreateControl, SetEvent, and RemoveControl. The returned reference should be stored with SET.
CreateForm
SET formVar = CreateForm(name$, width%, height%)
Creates a new form with the given name and initial size. Returns a form reference. If a form of that name is already loaded, CreateForm returns that form; to open another copy of a form designed in a .frm file, use New FormName (see Several Copies of a Form in the Form topic).
CreateControl
SET ctrlVar = CreateControl(formRef, typeName$, ctrlName$ [, parentCtrlRef])
Creates a control on the given form. typeName$ is the control type (e.g. "CommandButton", "Label", "TextBox"). ctrlName$ is the unique name for the new control. If a parentCtrlRef is supplied, the new control is nested inside it (useful for placing controls inside containers like HBox or Frame); the parent must be a container on the same form, otherwise a runtime error (CreateControl: invalid parent) is raised.
SetEvent
SETEVENT ctrlRef, eventName$, handlerName$
Wires an arbitrary SUB as the handler for an event of a control, a form (Me or a form reference), or a menu item. The default event-handler convention (ControlName_EventName) is used automatically for controls defined in a .frm file; SetEvent is for controls you create dynamically or for reusing a single handler across multiple controls. A handler declared without parameters is called without the event's arguments.
RemoveControl
REMOVECONTROL formRef, ctrlName$
Removes a control from a form. A later reference to the removed control raises a runtime error (unknown control), as does a reference to a control of a form that has been unloaded.
LoadFormText
SET formVar = LOADFORMTEXT(frmText$)
Loads a form from the text of a .frm file -- one a program made, read from a file, or got from somewhere else -- and returns it, loaded but hidden like Load (its Load event runs; call Show). A form whose name is already loaded becomes another copy of its own, as with New. The form's controls fire events to procedures named after them in the running program (Gen1_Load, Go_Click), as for any form; a code section after the form's End is not compiled. Text that is not a form raises error 5 (Illegal function call). Form designers use it to show a .frm file being edited; Controls, CallByName, and the designer.bas functions then read and change it.
Dim f As Object
Dim n As Integer
Dim t As String
n = FreeFile
Open "PREVIEW.FRM" For Binary As #n
t = Input$(LOF(n), #n)
Close #n
Set f = LoadFormText(t)
f.Show
Print f.Controls.Count; f.Controls(1).Name
Dim frm As Form
Dim lbl As Control
Set frm = CreateForm("MyDlg", 300, 200)
frm.Caption = "Built in code"
Set lbl = CreateControl(frm, "Label", "StatusLabel")
StatusLabel.Caption = "Hello, world!"
Dim btn As Control
Set btn = CreateControl(frm, "CommandButton", "BtnClose")
BtnClose.Caption = "Close"
SetEvent btn, "Click", "OnCloseClick"
frm.Show
Sub OnCloseClick
Unload Me
End Sub
DoEvents
Yields control to DVX so the GUI can process pending events. Call this in long-running loops to keep the UI responsive (otherwise the window will appear frozen).
DOEVENTS
For i = 1 To 10000
' process data
If i Mod 100 = 0 Then DoEvents
Next
MsgBox
Displays a message-box dialog. Can be used as a statement (discards the result) or as a function (returns the button clicked).
MSGBOX message$ [, flags [, title$]]
result = MSGBOX(message$ [, flags [, title$]])
flags is the sum of a button-style constant and an icon constant. See Predefined Constants for the complete list. The message box plays the desktop sound chosen for its icon on the Control Panel's Sounds tab (the default beep without an icon).
MsgBox "Operation complete"
answer = MsgBox("Continue?", vbYesNo + vbQuestion, "Confirm")
If answer = vbYes Then
' proceed
End If
InputBox$
Displays an input dialog and returns the user's text entry. Returns an empty string if the user cancels.
result$ = INPUTBOX$(prompt$ [, title$ [, default$]])
name$ = InputBox$("Enter your name:", "Name Entry", "")
If name$ <> "" Then
Print "Hello, "; name$
End If
Event Handler Convention
Event handlers are named ControlName_EventName and defined as SUBs. A form's own events are named after the form: the Load event of a form named Form1 is Form1_Load. (Visual Basic's Form_Load spelling is not used: every procedure name is shared by the whole program, so each form's handlers need names of their own.)
Sub Command1_Click()
MsgBox "Button clicked!"
End Sub
Sub Form1_Load()
Me.Caption = "My App"
End Sub
Sub Text1_Change()
Label1.Caption = "You typed: " & Text1.Text
End Sub
Common Events
Event Description ----- ----------- Click Control was clicked DblClick Control was double-clicked Change Control value or text changed KeyPress Printable key was pressed (receives ASCII code) KeyDown Any key was pressed (receives key code and shift state) KeyUp Key was released MouseDown Mouse button pressed (receives button, X, Y) MouseUp Mouse button released MouseMove Mouse moved over the control Scroll Control was scrolled (mouse wheel or scrollbar) GotFocus Control received input focus LostFocus Control lost input focus Form1_Load Form is being loaded (fires once, after controls are created) Form1_QueryUnload Form is about to close; set Cancel = 1 to abort Form1_Unload Form is being unloaded Form1_Resize Form was resized Form1_Activate Form gained focus Form1_Deactivate Form lost focus Form1_NewArgs The running program was handed new arguments (COMMAND$ changed) Timer1_Timer Timer control interval elapsed (Timer control's default event)
Objects and Class Modules
A class module lets you define your own kind of object. The class describes what every object of that kind knows (its member variables and properties) and what it can do (its methods). Each object made from the class -- an instance -- has its own copy of the member variables, so you can have as many independent objects as you need, much as you can put several TextBoxes on a form.
A First Class
This class module, saved as Account.cls, describes a bank account:
VERSION DVX 1.00 CLASS
Attribute VB_Name = "Account"
Option Explicit
Public Owner As String
Private mBalance As Double
Public Sub Deposit(ByVal amount As Double)
mBalance = mBalance + amount
End Sub
Public Property Get Balance() As Double
Balance = mBalance
End Property
Private Sub Class_Initialize()
Print "Account created"
End Sub
Private Sub Class_Terminate()
Print "Account for "; Owner; " closed"
End Sub
Any other code in the project can then make and use Account objects:
Dim acct As Account
Set acct = New Account ' prints "Account created"
acct.Owner = "Scott"
acct.Deposit 100
acct.Deposit 50
Print acct.Owner & ": " & acct.Balance ' Scott: 150
Set acct = Nothing ' prints "Account for Scott closed"
Owner is Public, so code outside the class can read and change it. mBalance is Private: only the class's own code can touch it, and the outside world reads it through the Balance property, which has no way to change it.
Class Module Files (.cls)
Each class module is kept in its own .cls file, listed in the project's Classes group (see the IDE Guide). The file can begin with a few header lines, as a Visual Basic class file does; the IDE writes them for a new class module:
VERSION DVX 1.00 CLASS
Attribute VB_Name = "Account"
Option Explicit
The header is everything before the first line of code: blank lines, a VERSION line, a BEGIN ... END block and Attribute VB_... lines, in that order. All of it is optional, and a Visual Basic .cls file can be added as it is. The class's name comes from the Attribute VB_Name line; without one, the name is the file's name without .cls (ACCOUNT.CLS makes a class named ACCOUNT). The class name is the type name used in DIM ... AS, NEW, TypeOf, and TYPENAME.
What a Class Contains
Outside its procedures, a class module may hold only declarations: member variables, CONSTs, OPTION and DEFtype statements, and Attribute lines. Statements that do something belong in a procedure, usually Class_Initialize. The members are:
Member Written as Used from outside as ------ ---------- -------------------- Public member variable Public name As type obj.name (read and assign) Private member variable Private name As type, or Dim name As type not visible outside Method Sub or Function (Public unless Private) obj.Method args, x = obj.Func(args) Read property Property Get name(...) As type x = obj.name Write property Property Let name(..., value) obj.name = x Object property Property Set name(..., value) Set obj.name = someObject Friend procedure Friend Sub / Function / Property ... obj.name, by the program's own code
A Sub, Function, or Property is Public unless it is marked Private; Private procedures can be called only by the class's own code. A Friend one, as in Visual Basic, can be called by the rest of the program through a variable declared as the class, but it is not part of the object's late-bound interface: through As Object or As Variant it raises error 438, and an Implements interface does not include it. Inside the class's procedures the member variables are used by name, with no SHARED needed, and each call works with the variables of the object it was called for. The class's code can also use the program's DIM SHARED (or PUBLIC) module variables, procedures, and constants.
Properties
A property looks like a variable from outside, but reading or assigning it runs a procedure, so the class can check or calculate the value:
Private mC As Double
Public Property Get Celsius() As Double
Celsius = mC
End Property
Public Property Let Celsius(ByVal v As Double)
mC = v
End Property
Public Property Get Fahrenheit() As Double
Fahrenheit = mC * 9 / 5 + 32
End Property
Public Property Let Fahrenheit(ByVal v As Double)
mC = (v - 32) * 5 / 9
End Property
Dim t As New Thermometer
t.Celsius = 100
Print t.Fahrenheit ' 212
Property Get returns the value: assign it to the property name, as in a FUNCTION. Property Let receives the value being assigned as its last parameter; Property Set does the same for an object assigned with SET. A property can take arguments of its own before the value, such as Property Get Cell(r, c) and Property Let Cell(r, c, v), used as obj.Cell(1, 2) = 7. A property with only a Property Get is read-only: assigning it is a compile error through a variable of the class (error 438 through As Object). EXIT PROPERTY leaves a property procedure early.
Property procedures can also be written in an ordinary module, as in Visual Basic. The property is then used by its name like a variable, anywhere in the program: Size = 4 runs Property Let Size, Print Size runs Property Get Size, and Item(2) = "two" passes 2 and the value to Property Let Item.
Creating and Releasing Objects
An object variable starts as Nothing. NEW makes an object:
Dim a As Account ' Nothing so far
Set a = New Account ' now an Account
Dim b As New Account ' an Account is made right here, at the DIM
When an object is made, its Class_Initialize runs; when it is freed, its Class_Terminate runs. Both are Subs without parameters, usually Private, and both are optional.
An object stays alive as long as something refers to it: a variable, an array element, a TYPE field, a Collection, a Variant, or a member variable of another object. Each SET to another variable adds a reference; nothing is copied. When the last reference goes away -- SET to Nothing or to another object, a local variable at the end of its SUB or FUNCTION, removal from a Collection, ERASE -- the object is freed and its Class_Terminate runs right then, just after the statement that let go of it:
Dim a As New Account
Dim b As Account
Set b = a ' two references to one account
Set a = Nothing ' still alive: b refers to it
Set b = Nothing ' last reference gone: Class_Terminate runs now
Objects that refer to each other, directly or around a longer loop, keep each other alive even after the program's own variables let them go. DVX BASIC finds such groups while the program runs: after every thousand or so new objects (more when the program keeps many objects), and when the program's code runs to its end, it looks for objects that only the group itself still refers to. Each of those whose class has a Class_Terminate gets it called once, oldest object first, with every object of the group still intact; afterwards the group is freed. An object that a Class_Terminate stores somewhere (Set saved = Me) stays alive, and its Class_Terminate does not run again. Breaking the loop yourself (for example Set a.Partner = Nothing) still frees the objects at once instead of at the next check.
When the program ends normally -- its code runs to the end, and with forms the last form is unloaded -- the module variables are released as in Visual Basic, so the objects only they still hold get their Class_Terminate then. When the program ends with END (or is stopped), every object still alive is freed without running Class_Terminate, as with END in Visual Basic. Work that must always happen, such as saving a file, belongs in a method the program calls itself, not only in Class_Terminate.
Me
Inside a class's procedures, Me is the object the procedure was called for. Use it to pass the object to other code, or to go through the object's own properties (Me.Balance runs Property Get Balance):
Public Sub Move(ByVal dx As Double)
Me.X = Me.X + dx ' uses Property Get X and Property Let X
End Sub
Using Objects
When the compiler knows an object's class -- a variable, array, parameter, member variable, or FUNCTION declared As the class name or As Collection, New ClassName, or Me in a class module -- its members are found when the program is compiled (early binding), as in Visual Basic. A misspelled member, a Private one, a Sub used where a value is needed, a property used as a statement, assigning a property that has no Property Let or Set, reading one that has no Property Get, and the wrong number of arguments are then compile errors that point at the member. Calls made this way are also faster. A class's own members can be used anywhere in the class, also above the line that declares them, and through Me a class reaches its Private members as well (Me.mTotal).
Through a variable declared As Object or As Variant the member is found by name when the program runs (late binding): obj.Deposit 5 looks for a Public member named Deposit in whatever object obj holds at that moment. A misspelled or Private member then raises error 438 when the line runs, and the wrong number of arguments raises error 450. Using a member of Nothing raises error 91 either way. An object of another class that reaches a variable of a class anyway (for example through a ByRef As Object argument) has its members found by name, as late binding would.
An argument that can never fit an object parameter of a SUB, FUNCTION, or method is a compile error too: a number or text, or an object of another class than the parameter's (unless that class Implements it).
An object can be kept in a variable declared as its class name, As Object, or As Variant, in an array, in a TYPE field, in a Collection, or in another object's member variable. It can be passed to a SUB or FUNCTION and returned from a FUNCTION (assign the result with SET inside the function, Set MakeAccount = acct). TYPENAME gives an object's class name, and TypeOf obj Is ClassName tests it; Is tells whether two variables refer to the same object.
Arguments of a class's procedures follow the rules of SUB and FUNCTION calls when the object variable is declared as the class name: a parameter without BYVAL gets the caller's variable, so the procedure can change it, an omitted OPTIONAL argument gets its default, and a ParamArray collects the rest. When the object is reached through a variable declared As Object or As Variant, a variable or array element given as an argument is passed by reference too if the procedure's parameter is not BYVAL (Visual Basic's rule; other expressions go by value). What the procedure stores in such a parameter is converted to the type of the caller's variable (an Integer stays an Integer, as in Visual Basic). A TYPE field given as an argument (obj.Move rec.x, obj.Move pts(1).x) is passed by reference as well (a BYVAL parameter gets its value). The methods of forms and controls always take their arguments by value. An argument left out in the middle of a call (obj.Report a, , c) or at the end is an OPTIONAL parameter's default value, else its empty value: 0, "", and so on for a typed parameter, Missing for a Variant (ISMISSING is True). Leaving out a parameter that is not OPTIONAL raises error 449 (Argument not optional).
An object parameter is itself passed like any other parameter: without BYVAL, SET on the parameter changes the caller's variable (a SUB can make a new object for the caller); with BYVAL, the procedure gets its own reference to the same object, and SET on it does not affect the caller.
CALLBYNAME
result = CALLBYNAME(object, memberName$, callType [, arguments...])
CALLBYNAME object, memberName$, callType [, arguments...]
Uses the member of an object whose name the program has in a string, as if it had been written after a dot: a form, a control, a class instance, or a Collection. callType says what to do: vbGet reads a property (with arguments, a property that takes them), vbLet assigns it the last argument, vbSet assigns it an object (the last argument), vbMethod calls a method or function with the arguments. The function gives the property's value or the method's result (Empty for vbLet and vbSet); as a statement the result is dropped. Another callType raises error 5 (Illegal function call), as does vbLet or vbSet without a value; a member the object does not have raises the error the same member written after a dot would. A form designer's property window uses it to read and set the properties designer.bas lists for a control type.
Dim prop As String
prop = "Caption"
Print CallByName(Command1, prop, vbGet) ' Command1.Caption
CallByName Command1, prop, vbLet, "OK" ' Command1.Caption = "OK"
CallByName List1, "AddItem", vbMethod, "one" ' List1.AddItem "one"
Print CallByName(acct, "Balance", vbGet) ' a class instance's property
Default Member
A class can name one property or method as its default member, used when the object is written with parentheses directly after it. Put this line in the class, inside the procedure (as Visual Basic writes it) or on its own at the top of the class:
Attribute Item.VB_UserMemId = 0
With Item as the default member, obj(3) means obj.Item(3), and obj(3) = x calls Property Let Item. Collection's default member is Item, which is why c(1) and c("key") work. A call's result can be indexed directly, too: c(1)(0) is element 0 of the array item 1 holds, and Split(s, ",")(2) the third part.
Where a value is needed -- in arithmetic, a comparison, text, PRINT, a typed variable, or a typed parameter -- an object stands for its default member's value, as in Visual Basic: with Value as the default member, total = obj + 1 reads obj.Value, and obj = 9 (without SET) stores 9 through it. A member variable can be the default member too (Attribute Label.VB_UserMemId = 0). An object without a default member used as a value raises error 13. The object name on its own still means the object itself wherever an object is expected: SET, Is, passing it to an object parameter, adding it to a Collection. Controls have default properties as well (Text1 means Text1.Text); see the control overview.
WITH...END WITH
WITH names an object or a TYPE record once for a block of statements; inside it, a name starting with a dot (.Value, .Add 3) is that object's member or that record's field:
With acct
.Owner = "Ada"
.Deposit 100
Print .Balance
End With
With shapes(i).origin ' a TYPE record: its fields
.x = 5
.y = .x * 2
End With
The expression after WITH is worked out once, when the block starts: With New Account makes one object that lives until End With, and With list(i) keeps the element it named even if i changes inside. WITH blocks can be nested; a dot then means the innermost one. A word after a space that starts with a dot is a new argument of its own (Bump .Count passes the member to Bump). Me works too (With Me). Leaving the block by GOTO or EXIT is allowed.
Errors in a Class
A class reports a problem with ERROR, normally numbered from vbObjectError so that its numbers cannot be mistaken for the built-in ones. The error is handled by the ON ERROR handler of the code that called the class, as with any procedure:
' in the class
Public Sub Withdraw(ByVal amount As Double)
If amount > mBalance Then Error vbObjectError + 1
mBalance = mBalance - amount
End Sub
' in the program
On Error GoTo NoMoney
acct.Withdraw 500
Exit Sub
NoMoney:
If Err = vbObjectError + 1 Then MsgBox "Not enough money"
Resume Next
Rules and Limits
- A member's name cannot be a reserved word. Names such as Name, Show, Hide, Load, Print, Open, Close, Line, Input, Get, Put, Type, Error, and Timer are keywords in DVX BASIC and cannot be used; Text, Caption, Value, Count, Item, and Add are fine.
- SHARED and DIM SHARED cannot be used in a class module; a class's code reaches the program's module variables that are declared DIM SHARED or PUBLIC.
- A STATIC variable in a class procedure is kept per object, as in Visual Basic: each object has its own copy.
- A class has at most 65536 member variables, its procedures' STATIC variables included; more fails to compile with "Too many member variables in ClassName".
- Class_Initialize and Class_Terminate take no parameters.
- A class module cannot hold statements outside its procedures; the compiler reports "Only declarations are allowed outside procedures in a class module".
- NEW, PROPERTY, PUBLIC, PRIVATE, and TYPEOF are reserved words. VARIANT, OBJECT, COLLECTION, EMPTY, NULL, EACH, and IN are not.
See also: Interfaces (Implements)
Events
A class module can announce that something happened -- a download finished, a value changed, a step of a long job was done -- and let the code that uses its objects react, the way a form reacts to a button's Click. The class declares the event and raises it; the program declares a variable WithEvents and writes a SUB for each event it wants to handle.
Declaring and Raising an Event
In the class module, declare each event at the top with EVENT, then raise it from any of the class's procedures with RAISEEVENT:
' class module Downloader
Public Event Progress(ByVal percent As Integer, cancel As Boolean)
Public Event Done()
Public Sub Start()
Dim stopNow As Boolean
Dim p As Integer
For p = 10 To 100 Step 10
RaiseEvent Progress(p, stopNow)
If stopNow Then Exit Sub
Next
RaiseEvent Done
End Sub
An event may have parameters but no return value. Its parameters cannot be OPTIONAL or a PARAMARRAY, and an event is always public (Public Event and Event mean the same; Private Event is an error). RAISEEVENT can only be used inside the class's own procedures, with exactly the arguments the event declares. A parameter without BYVAL is passed by reference, so a handler can change the variable given for it -- the cancel flag above is how a handler asks the class to stop. This holds for a variable, an array element (a(i)), and a TYPE field (rec.f, arr(i).f), as in Visual Basic. When several handlers get the event they all get the same variable, so each sees what the one before it stored; every store is converted to the variable's or field's type at once, as an assignment would be. While the handlers run, the arrays whose elements (or elements' fields) were given are locked: REDIM of one of them (or ERASE of a dynamic one) raises error 10. An expression, or a variable in its own parentheses, given for a by-reference parameter is passed as one temporary value that every handler shares, as in Visual Basic: the variable itself does not change, but a handler sees what the one before it stored. An argument is converted to the type of a typed event parameter, with the rules of SUB calls (a variable of another type given by reference is a compile error).
Handling Events
In a module, a form, or another class module, declare a variable of the class WithEvents, outside any procedure, and write a SUB named variable_EventName with the same parameters as the event:
Dim WithEvents dl As Downloader
Sub StartDownload()
Set dl = New Downloader
dl.Start
End Sub
Sub dl_Progress(ByVal percent As Integer, cancel As Boolean)
Label1.Caption = percent & "%"
If percent >= 50 And StopWanted Then cancel = True
End Sub
Sub dl_Done()
MsgBox "Finished"
End Sub
The handler's parameters must match the event's: the same number, the same types, and the same BYVAL / BYREF choice, or the program does not compile. A handler is optional: an event nobody handles does nothing. RAISEEVENT calls the handler of every WithEvents variable that holds the raising object at that moment -- two variables holding the same object both get the event, in the order they were declared -- and waits for them to finish before the class's code continues. Setting the variable to another object (or to Nothing) moves the handlers to the new object at once.
A WithEvents variable in a form belongs to that form: its handlers run with the form's own variables and controls, even when the event is raised while another form's code is running, and a form that is loaded twice gets its own variable each time. Unloading the form stops its handlers.
Events in a Class Module
A class can handle the events of objects it holds. The variable is a member variable (usually Private) and the handlers are the class's own procedures; each object of the class handles the events of the object in its own variable, and the handler runs for that object (Me):
' Class module Logger
Private WithEvents source As Downloader
Private lines As Integer
Public Sub Watch(d As Downloader)
Set source = d
End Sub
Private Sub source_Progress(ByVal percent As Integer, cancel As Boolean)
lines = lines + 1
Print "log"; lines; percent
End Sub
Lifetime rule: listening never keeps an object alive. The object that raises an event does not hold a reference to its listeners; only the listener's own variable refers to the source. So:
- A listener lives as long as the program holds it, like any other object. When its last reference goes, its Class_Terminate runs and it hears nothing more -- an event is never delivered to an object that has ended.
- While a handler runs, the listener is held for the call, so a handler may drop the last outside reference to its own object (or Set its WithEvents variable to Nothing) safely; Class_Terminate then runs after the handler returns.
- A cycle -- the source holding a reference back to its listener, for example a Parent or Owner member -- is collected like any other cycle of objects: when nothing outside refers to them, both Class_Terminate procedures run (the older object first) and the objects are freed. An event raised during those Class_Terminate calls still reaches listeners that have not been freed yet.
- Several listeners may hold the same source: RAISEEVENT calls the module and form handlers first, then those of listening objects, the most recently created first.
Rules
- WITHEVENTS goes with DIM, PUBLIC, or PRIVATE at the top level of a module, form, or class module (not inside a procedure), for a single variable declared as a class module's name. It cannot be an array and cannot use New: Set it to an object in code.
- A WithEvents variable is visible to the procedures of its module or form without SHARED, as the handlers and the code that sets it need; in a class module it is a member variable of each object.
- Handlers are SUBs named variable_EventName in the same module, form, or class module as the variable.
- EVENT, RAISEEVENT, WITHEVENTS, and IMPLEMENTS are not reserved words: they only have their meaning where a name follows them in those places, so a program may still use them as its own names.
Interfaces (Implements)
An interface is a set of properties and methods that several classes promise to provide, so that code can work with any of them through one variable type. In DVX BASIC, as in Visual Basic, the interface is an ordinary class module -- its procedures are usually left empty -- and a class that provides it says IMPLEMENTS:
' class module IShape
Public Function Area() As Double
End Function
Public Sub Scale(ByVal factor As Double)
End Sub
' class module Circle
Implements IShape
Private r As Double
Private Function IShape_Area() As Double
IShape_Area = 3.14159 * r * r
End Function
Private Sub IShape_Scale(ByVal factor As Double)
r = r * factor
End Sub
For every public member of the interface, the implementing class writes a procedure named Interface_Member of the same kind (Sub, Function, Property Get / Let / Set) with the same number of parameters, ending in a ParamArray exactly when the interface's does; they are usually Private, so they are only reached through the interface. A call through the interface reaches the implementation directly (the compiler records which procedure implements each member), and an implementation's BYVAL parameter gets a value even where the interface's parameter is ByRef. A public variable of the interface is implemented as a Property Get and a Property Let (Property Set for an object variable). A class that leaves one out does not compile.
Code then uses the objects through a variable declared as the interface:
Dim s As IShape
Set s = New Circle
s.Scale 2
Print s.Area ' runs Circle's IShape_Area
Print TypeOf s Is IShape ' True
Print TypeName(s) ' Circle
Through a variable declared as the interface, a member always means the interface's member, even when the class has a public member of the same name of its own. Through the class's own type, As Object, or As Variant, the class's own public members are used. SET into a variable of the interface type accepts an object of any class that Implements it (and of the interface class itself); TypeOf obj Is Interface is True for them. A class can implement several interfaces with one IMPLEMENTS line for each. An argument left out of a call through the interface takes the default the interface's procedure declares, as in Visual Basic, even if the implementing procedure declares another. Friend procedures are not part of an interface.
Collection Object
A Collection is a ready-made object that holds a list of items of any kind -- numbers, text, objects -- in order. Items can be looked up by position or by a text key, added anywhere in the list, and removed, and FOR EACH walks through them. The list grows as needed; there is no size to declare.
Dim fruit As New Collection
fruit.Add "Apple", "a"
fruit.Add "Cherry", "c"
fruit.Add "Banana", "b", "c" ' before the item with key "c"
Print fruit.Count ' 3
Print fruit(1) ' Apple
Print fruit("B") ' Banana (keys ignore case)
Dim f As Variant
For Each f In fruit
Print f ' Apple, Banana, Cherry
Next
fruit.Remove "a"
fruit.Remove 1 ' removes Banana, now the first item
Members
Member Description
------ -----------
Add item [, key] [, before] [, after] Adds item at the end of the list, or before or after the item given by position or key. key, when given, is text that names the item.
Remove indexOrKey Removes the item at that position (1 = first) or with that key.
Item(indexOrKey) The item at that position or with that key. Item is the default member: c(2) is c.Item(2) and c("key") is c.Item("key").
Count The number of items.
Positions start at 1 and change as items are added and removed. A key must be text, is compared without regard to upper and lower case, and must be unique in the Collection. Give either before or after, not both; leave out the arguments you do not use with commas (c.Add item, , 1 puts item first). Items are stored as they are: an object added to a Collection stays alive while the Collection holds it.
Make a Collection with DIM c AS NEW COLLECTION or SET c = NEW COLLECTION. Like any object, it is freed when the last reference to it goes.
Through a variable declared As Collection the members are checked when the program is compiled: another member name, the wrong number of arguments, using Add or Remove as a value, Count or Item as a statement, and assigning any of them are compile errors. Keyed lookups compare a hash of the key first, so finding an item by key stays fast in large Collections.
Errors
Error When ----- ---- 5 A key that is not in the Collection (Item, Remove, before, or after), or Add given both before and after 9 A position that is not in the Collection 13 A key that is not text 438 A member other than Add, Remove, Item, and Count, or assigning one, through As Object or As Variant 450 The wrong number of arguments, through As Object or As Variant 457 Add with a key that is already in use
SQL Functions
DVX BASIC includes SQLite database support through the sql.bas include library. Add sql.bas to your project to gain access to these functions. All functions use database handles and result-set handles (Long integers) returned by SQLOpen and SQLQuery. Numeric parameters and results are Long, so row counts and integer column values keep their full 32-bit range.
Opening and Closing Databases
SQLOpen
Opens a SQLite database file and returns a database handle (> 0) or 0 on failure. The file is created if it does not exist. A file that cannot be changed (for example one marked read-only) still opens, but only for reading. When SQLOpen returns 0, SQLError$(0) tells you why.
db = SQLOpen(path$)
db = SQLOpen(App.Data & "\mydata.db")
SQLClose
Closes an open database.
SQLClose db
Executing SQL
SQLExec
Executes a SQL statement that does not return data (INSERT, UPDATE, DELETE, CREATE TABLE, etc.). Returns True on success.
ok = SQLExec(db, sql$)
SQLExec db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"
SQLExec db, "INSERT INTO users (name) VALUES ('Scott')"
ok = SQLExec(db, "DELETE FROM users WHERE id = 5")
Put text values in single quotes, as in the examples. Text in double quotes is read as a column name, so "Scott" in place of 'Scott' gives the error "no such column: Scott".
SQLAffected
Returns the number of rows affected by the last INSERT, UPDATE, or DELETE.
count = SQLAffected(db)
Querying Data
SQLQuery
Executes a SELECT query and returns a result-set cursor.
rs = SQLQuery(db, sql$)
rs = SQLQuery(db, "SELECT id, name FROM users ORDER BY name")
SQLNext
Advances the cursor to the next row. Returns True if a row is available, False when there are no more rows.
hasRow = SQLNext(rs)
SQLEof
Returns True if the cursor is past the last row.
done = SQLEof(rs)
Reading Fields
SQLField$ / SQLFieldText$
Returns a field value as a string. Use SQLField$ to look up by column name; use SQLFieldText$ to look up by 0-based column index.
value$ = SQLField$(rs, columnName$)
value$ = SQLFieldText$(rs, columnIndex)
name$ = SQLField$(rs, "name")
first$ = SQLFieldText$(rs, 0)
SQLFieldName$
Returns the column name for a given 0-based column index.
colName$ = SQLFieldName$(rs, columnIndex)
SQLFieldInt
Returns a field value as a Long integer.
value = SQLFIELDINT(rs, columnIndex)
SQLFieldDbl
Returns a field value as a double.
value# = SQLFIELDDBL(rs, columnIndex)
SQLFieldCount
Returns the number of columns in the result set.
count = SQLFieldCount(rs)
Result Set Cleanup
SQLFreeResult
Frees a result-set cursor. Always call this when finished iterating a query.
SQLFreeResult rs
Error Information
SQLError$
Returns the last error message for the database.
msg$ = SQLError$(db)
When a database file could not be opened, read, or written, the message ends with the reason in parentheses, for example "unable to open database file (No such file or directory)". If another program, or another handle in your own program, is changing the same database at that moment, the message is "database is locked": try again once the other change is finished, and free result sets with SQLFreeResult as soon as you are done with them, because an open result set stops other handles from saving changes.
Complete SQL Example
Dim db As Long
Dim rs As Long
db = SQLOpen(App.Data & "\contacts.db")
SQLExec db, "CREATE TABLE IF NOT EXISTS contacts (name TEXT, phone TEXT)"
SQLExec db, "INSERT INTO contacts VALUES ('Alice', '555-1234')"
rs = SQLQuery(db, "SELECT name, phone FROM contacts")
Do While SQLNext(rs)
Print SQLField$(rs, "name"); Tab(20); SQLField$(rs, "phone")
Loop
SQLFreeResult rs
SQLClose db
App Object
The App object provides read-only properties for the application's directory paths.
Property Returns Description -------- ------- ----------- App.Path String Directory containing the application's executable App.Config String Directory for application configuration files App.Data String Directory for application data files (databases, etc.)
configFile$ = App.Config & "\settings.ini"
dbPath$ = App.Data & "\myapp.db"
Print "Running from: " & App.Path
Clipboard Object
The Clipboard object shares text with other programs and with the copy and paste of text boxes, as in Visual Basic. The DVX clipboard holds text only, so the format arguments accept vbCFText (1): any other format is never present and GetText returns "" for it.
Method Returns Description ------ ------- ----------- Clipboard.Clear (none) Empty the clipboard. Clipboard.GetFormat(format) Boolean True when the clipboard holds data of that format (text for vbCFText). Clipboard.GetText([format]) String The clipboard text, or "" when it is empty. Clipboard.SetText text [, format] (none) Put text on the clipboard, replacing what was there. A format other than vbCFText is an error.
Clipboard.SetText Text1.Text
If Clipboard.GetFormat(vbCFText) Then Text2.Text = Clipboard.GetText()
A control or form of your own named Clipboard takes the name's place.
Screen Object
The Screen object is the DVX desktop, as in Visual Basic: its size, so a program can place its forms (for example in a corner of the screen), its colour depth, the mouse pointer, and the arrangement of the windows on it.
Property Type R/W Description -------------------- ------- --- ----------- Screen.Width Long R Width of the desktop in pixels Screen.Height Long R Height of the desktop in pixels Screen.BitsPerPixel Long R Colour depth of the display: 8, 15, 16, 24, or 32 bits per pixel Screen.MousePointer Integer R/W vbHourglass (11) for the busy pointer while a long job runs; another pointer (vbArrow, vbCrosshair, vbSizeNS, vbSizeWE, vbSizeNWSE, vbSizeNESW, vbNoDrop: see Mouse Constants) over every form of the program, whatever their controls' MousePointer; vbDefault (0) back to each control's own Screen.ActiveControl Control R The control of the program that has the keyboard focus, Nothing when none has it (another program's window has the focus) Screen.DebugLayout Boolean R/W True draws a coloured border around every layout container (VBox, HBox, Frame ...) in every window, to see how a form is laid out; False removes them
The sizes are in pixels, the unit of a form's Left, Top, Width, and Height in DVX (Visual Basic uses twips there).
' Put the form at the right edge, 40 pixels from the top
Me.Left = Screen.Width - Me.Width - 40
Me.Top = 40
Set the hourglass before a long job and put the arrow back after it. The pointer is the whole desktop's, so the program should not leave it busy; when the program ends, a busy pointer it set goes back to the arrow.
Screen.MousePointer = vbHourglass
CountWords
Screen.MousePointer = vbDefault
Arrange
Screen.Arrange arrangement puts every window on the desktop that is shown and not minimized (those of other programs too) in order, as the Window menu of a program does:
Constant Value Arrangement ---------------- ----- ----------- vbCascade 0 Overlapping, each one a little down and to the right of the one before vbTileHorizontal 1 Stacked from top to bottom, each the full width of the screen vbTileVertical 2 Side by side from left to right, each the full height of the screen vbTileGrid 4 In rows and columns
The values are those of Visual Basic's MDIForm.Arrange (vbTileGrid is DVX's own; 3, Visual Basic's vbArrangeIcons, is not used).
Sub mnuCascade_Click ()
Screen.Arrange vbCascade
End Sub
Any other property, method, or value stops the program with an error. The QBasic statement SCREEN and function SCREEN(row, column) are unaffected: Screen followed by a dot is the object. A control or form of your own named Screen takes the name's place.
INI Functions
DVX BASIC provides built-in functions for reading and writing standard INI configuration files (the same text format used by DVX system configuration).
IniRead
Reads a value from an INI file. Returns the default value if the file, section, or key is not found. All four arguments are required. Section and key names match without regard to case, and blanks around them are ignored ([ User ] is section User). If a name appears twice, the first one counts. Keys above the first section header belong to section "" (an empty section name). Lines starting with ; or # are comments.
value$ = IniRead(file$, section$, key$, default$)
name$ = IniRead(App.Config & "\app.ini", "User", "Name", "Unknown")
fontSize = Val(IniRead(App.Config & "\app.ini", "Display", "FontSize", "12"))
IniWrite
Writes a value to an INI file. Creates the file, section, or key if they do not exist. A new key goes after the last key of its section and a new section at the end of the file. Every other line -- comments, blank lines, other keys, lines of any length -- is kept exactly as it is. The changed key is written as key = value. Every line of the saved file ends in a carriage return and line feed (CR LF). The file is saved through a temporary file, so a failed write leaves the old file intact. If the file exists but cannot be read, or the new file cannot be written, IniWrite raises error 75 (Path/file access error) and leaves the file alone.
INIWRITE file$, section$, key$, value$
IniWrite App.Config & "\app.ini", "User", "Name", "Scott"
IniWrite App.Config & "\app.ini", "Display", "FontSize", Str$(fontSize)
Predefined Constants
The following constants are predefined by the compiler and available in all programs. No DIM or CONST declaration is required to use them.
MsgBox Button Style Flags
Constant Value Description -------- ----- ----------- vbOKOnly 0 OK button only (default) vbOKCancel 1 OK and Cancel buttons vbYesNo 2 Yes and No buttons vbYesNoCancel 3 Yes, No, and Cancel buttons vbRetryCancel 4 Retry and Cancel buttons
MsgBox Icon Flags
Add an icon flag to the button style (with + or OR) to display an icon in the message box.
Constant Value Description -------- ----- ----------- vbInformation &H10 Information icon (i) vbExclamation &H20 Warning icon (!) vbCritical &H30 Error/critical icon (X) vbQuestion &H40 Question icon (?)
MsgBox Return Values
Constant Value Description -------- ----- ----------- vbOK 1 User clicked OK vbCancel 2 User clicked Cancel vbYes 3 User clicked Yes vbNo 4 User clicked No vbRetry 5 User clicked Retry
Show Mode Flags
Constant Value Description -------- ----- ----------- vbModal 1 Show form as modal dialog
Window State, Arrangement, and Mouse Pointer
A form's WindowState and Screen.Arrange (see Form and Screen Object); the MousePointer values are under Mouse Constants.
Constant Value Description -------- ----- ----------- vbNormal 0 WindowState: a normal window vbMinimized 1 WindowState: shown only as an icon vbMaximized 2 WindowState: filling the screen vbCascade 0 Arrange: overlapping windows vbTileHorizontal 1 Arrange: windows stacked top to bottom vbTileVertical 2 Arrange: windows side by side vbTileGrid 4 Arrange: windows in rows and columns
File Attribute Flags
Bit values used with GETATTR and SETATTR.
Constant Value Description -------- ----- ----------- vbNormal 0 Normal file (no attributes) vbReadOnly 1 Read-only vbHidden 2 Hidden vbSystem 4 System file vbDirectory 16 Entry is a directory vbArchive 32 File has been modified since last backup
Compare Constants
The compare argument of FILTER, INSTRREV, REPLACE$, SPLIT, and STRCOMP.
Constant Value Description -------- ----- ----------- vbUseCompareOption -1 Follow OPTION COMPARE (the default) vbBinaryCompare 0 Exact match vbTextCompare 1 Ignore upper and lower case
Day and Week Constants
The firstDay and firstWeek arguments of the date functions, and WEEKDAY results.
Constant Value Description -------- ----- ----------- vbUseSystemDayOfWeek 0 Weeks start on Sunday vbSunday 1 Sunday vbMonday 2 Monday vbTuesday 3 Tuesday vbWednesday 4 Wednesday vbThursday 5 Thursday vbFriday 6 Friday vbSaturday 7 Saturday vbUseSystem 0 Week 1 holds January 1 vbFirstJan1 1 Week 1 holds January 1 vbFirstFourDays 2 Week 1 is the first with four days in the new year vbFirstFullWeek 3 Week 1 is the first full week
Drag and Drop Constants
The DragMode property, the Drag method, and the State argument of DragOver.
Constant Value Description -------- ----- ----------- vbManual 0 DragMode: the program starts drags with Drag vbAutomatic 1 DragMode: pressing the control starts a drag vbBeginDrag 1 Drag: start dragging (the default) vbEndDrag 2 Drag: drop where the mouse is (Drag 0 cancels) vbEnter 0 DragOver: the drag came onto the object vbLeave 1 DragOver: the drag went off the object vbOver 2 DragOver: the drag moved over the object
Clipboard Constants
Constant Value Description -------- ----- ----------- vbCFText 1 Text, the format the DVX clipboard holds
Mouse Constants
The Button argument of the MouseDown, MouseUp, and MouseMove events (several bits when several buttons are down), and the values of MousePointer (controls, forms, and Screen).
Constant Value Description -------- ----- ----------- vbLeftButton 1 The left button vbRightButton 2 The right button vbMiddleButton 4 The middle button vbDefault 0 MousePointer: the control's own pointer vbArrow 1 MousePointer: the arrow vbCrosshair 2 MousePointer: a cross, for placing things vbSizeNESW 6 MousePointer: two-way arrow, top right to bottom left vbSizeNS 7 MousePointer: two-way arrow, up and down vbSizeNWSE 8 MousePointer: two-way arrow, top left to bottom right vbSizeWE 9 MousePointer: two-way arrow, left and right vbHourglass 11 MousePointer: busy vbNoDrop 12 MousePointer: a circle with a line through it
Visual Basic's other pointers (vbIbeam, vbUpArrow, custom pointers) have no DVX shape; DVX has no MouseIcon.
Syntax Colour Constants
The kinds of text a TextArea colours: the index of SetSyntaxColor and GetSyntaxColor, and the digits of the Colors argument of the Colorize event ("0" is vbSyntaxText ... "6" vbSyntaxType).
Constant Value Description -------- ----- ----------- vbSyntaxText 0 Ordinary text (ForeColor) vbSyntaxKeyword 1 Keywords vbSyntaxString 2 Strings vbSyntaxComment 3 Comments vbSyntaxNumber 4 Numbers vbSyntaxOperator 5 Operators vbSyntaxType 6 Type names
CallByName Constants
Constant Value Description -------- ----- ----------- vbMethod 1 Call a method (or a function of a class) vbGet 2 Read a property vbLet 4 Assign a property a value vbSet 8 Assign a property an object (Set)
Key Code Constants
The KeyCode a KeyDown event gets, and the bits of its Shift argument. These are DVX's codes, so compare KeyCode with the constants rather than with numbers from Visual Basic. A character key gives its character's code (Asc("a") with no Shift), and Ctrl with a letter gives 1 to 26 (Ctrl+A is 1, Ctrl+C 3, Ctrl+V 22, Ctrl+X 24).
Constant Value Description -------- ----- ----------- vbKeyBack 8 Backspace vbKeyTab 9 Tab vbKeyReturn 13 Enter vbKeyEscape 27 Esc vbKeySpace 32 Space bar vbKeyPageUp, vbKeyPageDown &H149, &H151 Page Up, Page Down vbKeyEnd, vbKeyHome &H14F, &H147 End, Home vbKeyLeft, vbKeyUp &H14B, &H148 Arrow keys vbKeyRight, vbKeyDown &H14D, &H150 Arrow keys vbKeyInsert, vbKeyDelete &H152, &H153 Ins, Del vbKeyF1 to vbKeyF10 &H13B to &H144 Function keys F1 to F10 vbKeyF11, vbKeyF12 &H185, &H186 Function keys F11 and F12 vbShiftMask 3 Shift argument: a Shift key is down vbCtrlMask 4 Shift argument: Ctrl is down vbAltMask 8 Shift argument: Alt is down
Sub List1_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyDelete Then
DeleteSelected
ElseIf KeyCode = 1 And (Shift And vbCtrlMask) <> 0 Then
SelectEverything ' Ctrl+A
End If
End Sub
Scale and Picture Constants
The scale units of ScaleMode and of the ScaleX / ScaleY methods, and the values of a Picture object's Type (see Drawing on Forms and PictureBoxes, and LOADPICTURE).
Constant Value Description -------- ----- ----------- vbUser 0 The object's own scale (Scale, ScaleWidth ...) vbTwips 1 Twips (1440 an inch, 15 a pixel) vbPoints 2 Points (72 an inch) vbPixels 3 Pixels (the DVX default ScaleMode) vbCharacters 4 Characters (120 twips across, 240 down) vbInches 5 Inches vbMillimeters 6 Millimetres vbCentimeters 7 Centimetres vbHimetric 8 Hundredths of a millimetre (ScaleX / ScaleY only; a Picture's Width and Height) vbPicTypeNone 0 Picture.Type of an empty picture vbPicTypeBitmap 1 Picture.Type of a loaded picture
VarType Constants
The values VARTYPE returns (see Variants). For an array, VARTYPE adds vbArray to the type of its elements.
Constant Value Kind of value -------- ----- ------------- vbEmpty 0 Empty (nothing stored yet) vbNull 1 Null vbInteger 2 Integer vbLong 3 Long vbSingle 4 Single vbDouble 5 Double vbCurrency 6 Currency vbDate 7 Date vbString 8 String vbObject 9 An object or Nothing vbError 10 Error value (Visual Basic only; no DVX value has this type) vbBoolean 11 Boolean vbVariant 12 Variant (only in vbArray + vbVariant, an array of Variants) vbDataObject 13 Data object (Visual Basic only; no DVX value has this type) vbByte 17 Byte vbUserDefinedType 36 A TYPE variable vbArray 8192 Added to the element type for an array
Error Constants
Constant Value Description -------- ----- ----------- vbObjectError -2147221504 Base for your own error numbers, as in Visual Basic: a class module raises ERROR vbObjectError + n, and the handler recovers n with ERR - vbObjectError
Color Constants
Colors in the form RGB makes (&HBBGGRR, see Color Functions), as in Visual Basic.
Constant Value Color -------- ----- ----- vbBlack &H0 Black vbRed &HFF Red vbGreen &HFF00 Green vbYellow &HFFFF Yellow vbBlue &HFF0000 Blue vbMagenta &HFF00FF Magenta vbCyan &HFFFF00 Cyan vbWhite &HFFFFFF White
Boolean Constants
Constant Value Description -------- ----- ----------- True -1 Boolean true False 0 Boolean false
Include Libraries
DVX ships a set of BASIC include files (.bas) in the sdk/include/basic directory. Each file is a small library of DECLARE LIBRARY wrappers around native DVX runtime functions. Add an include file to your project (the IDE's Add File command, or editing the .dbp with another tool) and the declared functions become available to your code.
File Provides ---- -------- commdlg.bas File Open/Save dialogs (DlgFileOpenIn$ and DlgFileSaveIn$ start in a folder you name), input dialogs, choice dialogs, save-prompt dialogs, the color dialog, the font dialog (DlgFont), message boxes with your own buttons (DlgButtons) sql.bas SQLite database access (SQLOpen, SQLQuery, SQLNext, SQLField$, etc.) comm.bas Serial-port I/O (raw and packet) and optional encrypted links; port settings read back and changed, why a call failed, breaks, ring, and line errors (SerOpen, SerSet, SerLastError, CommOpen, CommHandshakeStep, ...) conn.bas Connections the way Terminal makes them: a serial port, a modem that dials, the secure link to the DVX host proxy, or Telnet, shown on a Terminal control, with capture, answerback, and file transfers (ConnCreate, ConnOpen, ConnPoll, ConnAttach, ConnSendFiles, ...) xfer.bas File transfers with XMODEM, XMODEM-1K, YMODEM, and ZMODEM, on a serial port or a connection, with their progress and questions (XferSendFile, XferStatus, XferBytes, XferAnswer, ...) prefs.bas Settings files (INI): load one, read and change its settings, list the keys of a section, save it; DVX's own settings file, the screen mode it starts in, the names its choices are stored as and its default choices, and choices stored by names of your own (PrefsLoad, PrefsGet$, PrefsSet, PrefsKeyName$, PrefsSaveAs, PrefsDvxIni$, PrefsVideoWidth, PrefsChoiceLabel$, PrefsChoiceDefault, PrefsGetChoiceFrom, ...) resource.bas Read and write DVX resource blocks (icons, text, binary data) in bundled files help.bas Compile help source files and see the compiler's messages, and open the DVX help viewer, at a topic if you like (HelpCompile, HelpCompileMessages$, HelpView, HelpViewTopic; HelpView "" opens the DVX user guide) hlpfile.bas Read compiled help files yourself, for a help viewer of your own: topics, table of contents, keyword index, full-text search, and each topic's headings, text, links, and pictures; show a topic in a HelpPane control (HlpOpen, HlpTocTitle$, HlpIndexKeyword$, HlpSearch, HlpLoadTopic, ...) net.bas Network connections: TCP (TcpConnect, TcpListen, TcpSend, TcpRecv$, TcpPeer$, ...), UDP messages and name lookups, any bytes; the network settings and status (NetConfigLoad, NetConfigSet, NetConfigStore, NetRestart, NetStatus$, ...) print.bas Printing: text with the page setup and pictures, with or without the Print dialog; a form; the Page Setup dialog; the printer and page settings; the printer ports (PrintModelLabel$, PrintSettingsStore, PrintPageSetting, PrintChoose, PrintForm, PrintPortStatus, ...) listhelp.bas Lists and icon views you draw yourself (a UserControl, a PictureBox) that behave like ListBox and Program Manager: selection with Shift and Ctrl, the arrow keys in a list or a grid, Ctrl+A, Space, type-ahead, where a dragged item lands, and Program Manager's icon view rules -- its cells, the grid with a scroll bar, names on two lines, dragging and dropping (ListSelCreate, ListSelClick, ListSelNavigate, ListTypeAhead, ListIconMetric, ListIconLabel$, ...) security.bas Security: random bytes for keys, a key exchange, a cipher, SHA-256 hashes, HMAC signatures, key making, and a safe compare (SecRandom$, SecDhCreate, SecCipherCrypt$, SecSha256$, SecHmac$, SecHkdf$, ...) game.bas Games: whether a key is being held down right now, forgetting held keys, frames FRAMEWAIT skipped, waiting for the screen's retrace (GameKeyDown, GameKeyClear, FrameMissed, GameWaitRetrace) joystick.bas Joysticks: how many there are, their names, where each stick points, and which buttons are pressed (JoyCount, JoyX, JoyY, JoyButton, ...); setting them up: calibration, dead zone, game port mode, keyboard joystick keys (JoySetCalibration, JoyCalibration, JoyRescan, JoyKeyIndex, JoySettingsStore, ...) sound.bas Sound: play WAV and VOC files, tones and noise, set the volume (PlayWave, PlayTone, StopWave, SoundInfo$, ...); sounds loaded once and played with volume, pan, and pitch, sound made by the program as it goes, the FM synthesizer chip (SoundLoad, SoundPlay, SoundSetPitch, SoundCreate, SoundStreamOpen, SoundStreamWrite, FmAlloc, FmWrite, ...); DVX's own event sounds (SoundPlayEvent), master volume, sound driver, and MIDI settings (SoundEventFile$, SoundSetMasterVolume, SoundSettingsStore, ...) music.bas Music: play MOD, S3M, XM, IT, and MIDI files, and play notes yourself (MusicLoad, MusicPlay, MusicStop, MusicNoteOn, MusicInfo$, ...); raw bytes to the MIDI port (MidiAcquire, MidiPut, MidiRelease, MidiPortInfo$) fileops.bas Files and folders: folder listings for a file list, paths and names, the file dialogs' filter rule, attributes and the time a file was changed, sizes, free space and disk sizes, drive kinds, whether a folder can be written, the folder for scratch files, making a folder path, and copying, moving, and deleting whole folders at once or as a job with progress, Cancel, and questions (FileOpsList, FileOpsListRows$, FileOpsFilterMatch, FileOpsSetModified, FileOpsDriveType, FileOpsIsWritable, FileOpsMakePath, FileOpsJobCreate, FileOpsJobRunFor, ...) system.bas What Control Panel changes, at once for the whole desktop: the screen mode, the system colors and themes, the wallpaper, text smoothing, and the mouse (with DVX's limits and defaults, MOUSE_* and SYS_VIDEO_DEFAULT_*), and storing them in DVX's settings; the system font's character pictures; the mouse pointer and the Shift, Ctrl, and Alt keys anywhere on the screen, moving the pointer, typing keys into the window in front, a picture of the whole screen, and the memory (SysSetVideoMode, SysSetColor, SysLoadTheme, SysSetWallpaper, SysSetTextSmoothing, SysSetMouse, SysColorsStore, SysFontGlyphRow, SysMouseX, SysSendKey, SysScreenshot, SysMemoryFreeKb, ...) font.bas Fonts: the font families DVX has, the sizes each comes in, whether one is fixed-pitch, and how wide text and how tall a line is in a font (FontFamilyCount, FontFamilyName$, FontFamilyFixed, FontSizeCount, FontSizeAt, FontNearestSize, FontTextWidth, FontLineHeight, FontAscent, FontRescan) richtext.bas RTF without a control: an RTF document as plain text and plain text as RTF (RtfToText$, TextToRtf$); a RichEdit control reads and writes RTF itself (TextRTF, SelRTF, LoadFile, SaveFile) layout.bas Control sizes in characters or in percent of the container instead of pixels (SizeChars, SizePercent) compiler.bas The BASIC compiler: compile a project or files you give it, find compile errors in their files, list a program's procedures and the language's keywords, build an .app or a control file; put a project's UserControls on a form designer's toolbox; read source text as the compiler does (tokens, code or string or comment, renaming a control in code, WithEvents variables, class Events), F1 help topics; open, change, and save project files and UserControl headers (CompSourceFromProject, CompSourceAdd, CompCompile, CompError$, CompMapFile$, CompProcList$, CompBuildApp, CompDesignControls, CompTokens$, CompRenameInCode$, CompHelpTopic$, ProjOpen, ProjSave, ProjCtlSetHeader$, ...) debug.bas Run a compiled program inside yours and debug it: breakpoints, stepping, its variables, expressions, Immediate-window lines, its PRINT output (RunCreate, RunStart, RunPoll, DbgSetBreakpoints, DbgStepOver, DbgLocals$, DbgEval$, DbgExec$, ...) designer.bas Form designers: the control types, whether each holds other controls and can be placed, the names of new controls, default events, toolbox tooltips and icons, and each type's properties (with their types and value names), methods, and events (with each event's parameters); the names in .frm and class module text; .frm files as data to read, edit, and write, controls copied and pasted as text, and a loaded form written back as .frm text (CtlTypeCount, CtlTypeName$, CtlTypeFlags, CtlPropCount, CtlPropName$, CtlPropType, CtlEventList$, CtlEventParams$, CtlTypeEventParams$, FrmFormName$, FrmControlNames$, ClsClassName$, FrmOpen, FrmText$, FrmCtlAdd, FrmCtlText$, FrmCtlPaste, FrmMenuAdd, FormText$, ...) shell.bas DVX itself, everything Program Manager and Task Manager do: start programs and open documents, command lines (split into their parts) and the Run dialog, the installed programs and their names, file types, the running programs (find one, bring it to the front, ask it to close, or end it), this program's priority, keys that work in every program, Task Manager, system information, Exit DVX, the desktop and the program started after it, this program's settings folder, the DVX log; Program Manager's program groups with their items, shortcut keys, and icons (ShellRun, ShellRunProgram, ShellProgramScan, ShellAssocCount, ShellRunningId, ShellRunningActivate, ShellRunningClose, ShellArg$, ShellSetPriority, HotkeyAdd, ShellExitDvx, ShellConfigFile$, ShellGroupsLoad, ShellItemGet$, ShellItemIcon$, ...)
Using an Include Library
' With commdlg.bas added to the project
Dim path As String
path = DlgFileOpen$("Open a file", "Text Files (*.txt)|All Files (*.*)")
If path <> "" Then
Print "Selected: " & path
End If
When a Library Is Not Installed
Some libraries are optional parts of DVX (the network, printing, sound, joysticks ...). Calling a function of a library that is not on the computer raises run-time error 453, "Library or function not installed", which ON ERROR can trap -- for example to show a message instead of a page of settings, as Control Panel does:
Dim status As String
On Error Resume Next
status = NetStatus$("message")
If Err.Number = 453 Then
lblNet.Caption = "The network is not installed."
Else
lblNet.Caption = status
End If
On Error GoTo 0
Message Boxes with Your Own Buttons
MsgBox offers fixed sets of buttons (OK, Yes and No, ...). With commdlg.bas in the project, DlgButtons(title, message, buttons, icon) shows a message box with the buttons you list, separated by |, with & before each button's access key. It returns the index of the button chosen: 0 for the first, which Enter presses. Esc and closing the box choose the last button, so make the last one the choice that changes nothing. icon is vbQuestion, vbInformation, vbExclamation, vbCritical, or 0. Up to six buttons; -1 is returned when the list is empty or longer.
Select Case DlgButtons("Copy", "notes.txt already exists. Replace it?", "&Yes|&No|Yes to &All|&Cancel", vbQuestion)
Case 0
' replace this one
Case 2
' replace this and every other one
Case Else
' keep it (No), or stop (Cancel)
End Select
Color Dialog
With commdlg.bas in the project, DlgColor(title, defaultColor) shows the color dialog: a palette of basic colors, Red, Green, and Blue sliders with number boxes, and a preview. It starts at defaultColor and returns the color chosen, in the form RGB makes and ForeColor and BackColor use, or -1 when the user cancels.
Dim c As Long
c = DlgColor("Text Color", Label1.ForeColor)
If c <> -1 Then
Label1.ForeColor = c
End If
Font Dialog
With commdlg.bas in the project, DlgFont(title, family, size, style, flags) shows the font dialog: the font families, the styles and sizes each comes in, Underline and Strikeout boxes, and a sample. It starts at the font named by family, size (in pixels), and style, and returns True when the user chooses OK. DlgFontName$(), DlgFontSize(), and DlgFontStyle() then give the font chosen; after Cancel they give the font the dialog started at. style adds up FONT_BOLD, FONT_ITALIC, FONT_UNDERLINE, and FONT_STRIKE, the constants of font.bas (add it to the project too, or use 1, 2, 4, and 8). With flags DLG_FONT_FIXED the dialog lists only the fixed-pitch families, for text that must line up in columns; with 0 it lists them all. The dialog's Licence button shows the chosen font's notice and licence. The answers fit a control's or a form's Font properties (see Fonts).
' With commdlg.bas and font.bas added to the project
Dim style As Long
If Label1.FontBold Then style = style + FONT_BOLD
If Label1.FontItalic Then style = style + FONT_ITALIC
If DlgFont("Label Font", Label1.FontName, Label1.FontSize, style, 0) Then
Label1.FontName = DlgFontName$()
Label1.FontSize = DlgFontSize()
Label1.FontBold = (DlgFontStyle() And FONT_BOLD) <> 0
Label1.FontItalic = (DlgFontStyle() And FONT_ITALIC) <> 0
Label1.FontUnderline = (DlgFontStyle() And FONT_UNDERLINE) <> 0
Label1.FontStrikethru = (DlgFontStyle() And FONT_STRIKE) <> 0
End If
Number Input Dialog
With commdlg.bas in the project, DlgIntInput(title, prompt, default, min, max) asks for a whole number with a spin box, and DlgIntInputStep(title, prompt, default, min, max, step) does the same with the arrows moving by step. Both return the number entered, or default when the user cancels; DlgInputCancelled() (True after a Cancel, False after OK) tells the two apart, as it does for DlgInputBox$.
n = DlgIntInputStep("Zoom", "Zoom in percent:", 100, 25, 400, 25)
If Not DlgInputCancelled() Then
SetZoom n
End If
Resources in Files
With resource.bas in the project, a program reads and writes the named resources DVX keeps at the end of program, widget, and library files: icons, text, and any bytes. ResOpen, ResCount, ResName$, ResType, and ResSize list them; ResGetText$ and ResAddText read and write text; ResAddFile and ResExtract copy a resource from or to a file; ResRemove takes one out. ResGetData$(file, name) gives every byte of a resource as a String (CHR$(0) included), and ResAddData file, name, type, bytes adds or replaces a resource of any bytes (type RES_TYPE_BINARY, RES_TYPE_TEXT, RES_TYPE_ICON, or RES_TYPE_FONT, a .DFN font the program carries; see Fonts). RES_NAME_ICON ("icon32") and RES_NAME_WIDGET_ICON ("icon24") are the names DVX gives a program's and a widget's icon; a name is at most RES_NAME_MAX - 1 characters.
Dim settings As String
settings = Chr$(1) + Chr$(0) + Chr$(255)
If ResAddData("mygame.app", "state", RES_TYPE_BINARY, settings) Then
Print Len(ResGetData$("mygame.app", "state")) ' 3
End If
Note: DVX BASIC does not have an $INCLUDE directive. Libraries are added to the project so that the compiler sees their DECLARE LIBRARY blocks.
Encrypted Serial Links
With comm.bas in the project, CommOpen opens a packet link and CommHandshake sets up encryption. Give both ends the same passphrase to authenticate the connection. Without a passphrase the link is still encrypted and tamper-checked, but it cannot tell who is on the other end. CommHandshake returns False if the passphrases differ, if only one side gives one, or if the link fails.
Dim link As Long
link = CommOpen(1, 115200, 8, "N", 1, SER_HANDSHAKE_NONE)
If CommHandshake(link, "shared secret") Then
CommSend link, "hello", 0, True
Else
Print "Handshake failed"
End If
After the handshake, data that arrives unencrypted is ignored unless that channel is opened for it with CommSetPlain link, channel, True.
CommHandshake waits until the key exchange is done. CommHandshakeStep(link, passphrase) does one step of it and returns at once, so a program can keep working: call it from a Timer until it returns something other than COMM_PENDING -- COMM_OK when the link is ready, else a COMM_ERR_ code. CommLastError(link) gives the COMM_ code of the last CommHandshake, CommHandshakeStep, CommSend, CommSetPlain, or CommPoll, which tells a wrong passphrase (COMM_ERR_AUTH) from a line that does not answer (COMM_ERR_TIMEOUT). CommDropped(link) counts arriving packets thrown away because they were tampered with or came unencrypted where that is refused. Channels are numbered 0 to COMM_MAX_CHANNEL.
Sub Timer1_Timer ()
Select Case CommHandshakeStep(link, txtPass.Text)
Case COMM_PENDING
lblState.Caption = "Connecting..."
Case COMM_OK
Timer1.Enabled = False
lblState.Caption = "Connected"
Case COMM_ERR_AUTH
Timer1.Enabled = False
lblState.Caption = "Wrong passphrase"
Case Else
Timer1.Enabled = False
lblState.Caption = "The other side does not answer"
End Select
End Sub
Serial Port Settings and Errors
With comm.bas, SerOpen returns False when a port cannot be opened; SerLastError(com) then says why, as an SER_ERR_ constant (SER_ERR_NO_UART: there is no port there, SER_ERR_IRQ_NOT_FOUND, SER_ERR_INVALID_BAUD, ...), or SER_OK when the last SerOpen or SerSet worked.
Function Description -------- ----------- SerSet(com, baud, dataBits, parity, stopBits, handshake) Change the settings of an open port (SerOpen's arguments). True when done. SerGetBaud(com), SerGetDataBits(com), SerGetParity$(com), SerGetStopBits(com), SerGetHandshake(com) The settings of an open port; parity is "N", "E", "O", "M", or "S". When the port is not open the numbers are an SER_ERR_ code and the parity "". SerTxPending(com), SerFlushTx com Bytes written but not sent yet; throw them away. SerSetBreak com, state Send a break: True holds the line in the break state, False releases it (hold it a quarter to half a second). SerGetRi(com) True while the other end rings. SerLineErrors(com) Errors seen on received data, added up: SER_LINE_OVERRUN (data came faster than it was read), SER_LINE_PARITY, SER_LINE_FRAMING, SER_LINE_BREAK; 0 for none.
Printing
With print.bas in the project, a program prints like the programs that come with DVX. PrintTextDialog shows the Print dialog (printer, copies, pages) and prints the text on pages laid out by the page setup: margins, a header, and a footer with the document name, date, and page number. PrintPageSetup shows the Page Setup dialog, which the user's choices are saved from for every program. PrintPictureDialog prints a picture file scaled to the page, on printer models that print pictures (PrintCanPrintPictures says whether the chosen one does). PrintText and PrintPicture print without asking first. Each returns True when it printed.
Sub mnuPrint_Click ()
PrintTextDialog txtNotes.Text, "Notes"
End Sub
Sub mnuPageSetup_Click ()
PrintPageSetup
End Sub
For printer output a line at a time, see LPRINT and OPEN "LPT1:".
A program that prints its own way has more:
Function Description
-------- -----------
PrintPageCount(text) How many pages text takes with the saved page setup and printer.
PrintTextPages(text, title, firstPage, lastPage, copies) Print text without asking: those pages (0 and 0 for all), copies times.
PrintChoose(title, pageCount) Show the Print dialog only (the page range when pageCount is above 0). True when the user chose Print; then PrintChoiceDest, PrintChoiceFile$, PrintChoiceCopies, PrintChoiceFirst, and PrintChoiceLast say what was chosen (pages 0 and 0: all).
PrintForm(formName) / PrintFormDialog(formName) Print what a loaded form shows, scaled to the page (a printer model that prints pictures); the second shows the Print dialog first.
PrintPortPresent(port), PrintPortStatus(port) Whether printer port LPTn (1 to 3) exists, and its state: PRINT_STATUS_ values added up (PRINT_STATUS_PAPER_OUT, PRINT_STATUS_SELECTED, PRINT_STATUS_NOT_BUSY, ...).
PrintStatusProblem$(status, afterWrite, where) What is wrong with a printer in that state, in words ("" when nothing is), for printer where such as "LPT1".
If PrintChoose("Report", 0) Then
If PrintDestIsFile(PrintChoiceDest()) Then
Open PrintChoiceFile$() For Output As #1
Else
Open "LPT" & (PrintChoiceDest() + 1) & ":" For Output As #1
End If
Print #1, "Report"
Close #1
End If
The page setup can be read and changed without the Page Setup dialog. Like the printer settings these take a handle from prefs.bas:
Function Description -------- ----------- PrintPageSetting(prefs, field) A number of the page setup: "Lines" (lines per page, 0 = the printer's usual length), "Columns", "MarginTop", "MarginBottom", "MarginLeft", "MarginRight", "TabWidth", or "Wrap" (True: long lines wrap at a space); -1 for an unknown field. PrintPageText$(prefs, field) "Header" or "Footer". In them &f is the document name, &p the page, &n the number of pages, &d the date, &t the time, && an ampersand; &l, &c, and &r put what follows left, centred, or right. PrintPageSetupProblem$(lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer) "" when this page setup can be used, else why not. PrintPageSettingsStore(prefs, lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer) Put a page setup into the settings (then PrefsSave). False for one PrintPageSetupProblem$ objects to. PrintPageLines(prefs, model) Lines per page the setup gives on printer model.
The limits are CONSTs: PRINT_LINES_MIN to PRINT_LINES_MAX, PRINT_COLUMNS_MIN to PRINT_COLUMNS_MAX, PRINT_TAB_MAX, PRINT_COPIES_MAX, with the defaults PRINT_COLUMNS_DEFAULT, PRINT_MARGIN_TB_DEFAULT, PRINT_MARGIN_LR_DEFAULT, and PRINT_TAB_DEFAULT.
A program can also change the printer settings the way the Control Panel's Printer page does. They are kept in DVX's settings file, so these calls take a handle from prefs.bas (add prefs.bas to the project too). Printer models and destinations are numbered from 0.
Function Description -------- ----------- PrintModelCount() / PrintModelLabel$(model) How many printer models there are, and the name of each. PrintDestCount() / PrintDestLabel$(dest) How many places the printer can be connected to (LPT1, LPT2, LPT3, a file), and the name of each. PrintDestIsFile(dest) True for the destination that prints into a file. PrintSettingModel(prefs) The printer model in the settings. PrintSettingDest(prefs) Where the printer is connected. PrintSettingFile$(prefs) The file printed into when the destination is a file. PrintSettingsProblem$(dest, filePath) "" when this choice can be used, else why not (printing to a file needs its name). PrintSettingsStore(prefs, model, dest, filePath) Put the choice into the settings, keeping the page setup as it is; then save with PrefsSave.
Dim prefs As Long
Sub Form1_Load ()
Dim i As Long
prefs = PrefsLoad(PrefsDvxIni$())
For i = 0 To PrintModelCount() - 1
cboModel.AddItem PrintModelLabel$(i)
Next i
cboModel.ListIndex = PrintSettingModel(prefs)
End Sub
Sub cmdOK_Click ()
If PrintSettingsStore(prefs, cboModel.ListIndex, PrintSettingDest(prefs), PrintSettingFile$(prefs)) Then
PrefsSave prefs
End If
PrefsClose prefs
End Sub
Files and Folders
With fileops.bas in the project, a program can show and change what is on the disks. FileOpsList(folder, filter) reads a folder once -- its folders and the files matching filter -- and returns a listing (a number; FileOpsListFree frees it). FileOpsListRows$ gives the whole listing as name, size, type, and date columns, ready for a ListView's SetRows, so even a folder of hundreds of files fills the list at once; FileOpsListSort re-sorts it by name, size, type, or date with folders first. FileOpsListName$, FileOpsListIsFolder, and FileOpsListSize read single entries, and FileOpsListFind finds one by name.
Paths: FileOpsPathNormalize$ (the full path of an existing folder, or ""), FileOpsPathParent$, FileOpsPathJoin$, FileOpsPathSame, FileOpsPathIsRoot, FileOpsPathIsAbsolute (True for a path that is complete by itself, such as "C:\DOCS", False for one that counts from the current folder), FileOpsPathIsInside(path, folder) (True when path is folder itself or anywhere inside it: check it before copying or moving a folder, so it is never copied into itself), FileOpsPathSep$ (the character between folder names, "\" on DOS), FileOpsPathNative$ (a path written with that character, for showing it: "C:/DOCS/A.TXT" becomes "C:\DOCS\A.TXT"), FileOpsBaseName$, and FileOpsExtension$. Every one of them takes / and \ alike and gives back \. FileOpsFilterMatch(itemName, filter) tells whether a name matches a filter the way the file dialogs match it: wildcards * and ?, several patterns separated by ; such as "*.bmp;*.png", case not mattering.
Files: FileOpsMakeDir and FileOpsRename (FileOpsError$ says why one failed), FileOpsTempPath$ and FileOpsReplaceFile (save safely: write the new file to FileOpsTempPath$(path$), then FileOpsReplaceFile swaps it in; the old file comes back if that fails), FileOpsValidName(folder, itemName) (whether a name can be used for a new file or folder in folder, by the rules of its disk; FileOpsError$ says why not), FileOpsValidLongName and FileOpsValidShortName (the long-name or the 8.3 rules by themselves, whatever the disk takes), FileOpsShortPath$ (the short 8.3 form of an existing path, for an old program that cannot take long names or blanks), FileOpsMakePath (a folder and every missing folder above it, which MKDIR does not make), FileOpsIsFolder, FileOpsGetAttr, and FileOpsSetAttr (the FILEOPS_ATTR_ bits), FileOpsModified$ and FileOpsSetModified (when a file was last changed, read and set in the form "YYYY-MM-DD HH:MM"; FileOpsSetModified returns False for a date that does not exist), FileOpsTreeSize with FileOpsTreeBytes, FileOpsTreeFiles, and FileOpsTreeFolders (the size of a whole folder), and FileOpsFormatSize$.
Drives: FileOpsDriveList$ (the drive letters), FileOpsDriveType (FILEOPS_DRIVE_FIXED for a hard disk, FILEOPS_DRIVE_REMOVABLE for a floppy disk drive, FILEOPS_DRIVE_REMOTE for a network drive or a CD-ROM; asking never makes a drive spin up), FileOpsBootDrive$ (the drive the computer started from), FileOpsFreeBytes, FileOpsFreeKb, and FileOpsTotalBytes (the size of the disk; right for disks past 2 GB too), FileOpsLongNames(path) (True when the disk holding path takes long file names: up to 255 characters, with spaces and several dots; otherwise names are 8.3). FileOpsIsWritable(folder) is False where files cannot be made (a CD-ROM, a write-protected floppy), and FileOpsReadOnlySystem() is True when DVX itself runs from such a disk, so settings cannot be saved in its folder. FileOpsTempDir$() is the folder for scratch files ("" when there is none).
FileOpsCopyTree, FileOpsMoveTree, and FileOpsDeleteTree do a whole folder at once. For a long copy, move, or delete the program stays usable when it runs the work as a job, a little at a time from a Timer: FileOpsJobCreate, then FileOpsJobAddSource for each file or folder, then FileOpsJobRunFor from the timer until it returns FILEOPS_STEP_DONE or FILEOPS_STEP_CANCELLED. When it returns FILEOPS_STEP_ASK (a file already exists, a file is read-only) or FILEOPS_STEP_ERROR, show FileOpsJobMessage$ and pass the user's choice to FileOpsJobAnswer. FileOpsJobCurrent$, FileOpsJobFilesDone, FileOpsJobFilesTotal, FileOpsJobPercent, FileOpsJobBytesDone, and FileOpsJobBytesTotal show the progress, and FileOpsJobCancel stops the job. Files copied from a CD-ROM are read-only; FileOpsJobClearAttr job, FILEOPS_ATTR_READONLY before the job runs makes every copy changeable (FILEOPS_ATTR_EDITABLE clears all four attribute bits). Whatever a program leaves open is freed when it ends.
Dim job As Long
Sub cmdCopy_Click ()
job = FileOpsJobCreate(FILEOPS_COPY, "C:\BACKUP")
If FileOpsJobAddSource(job, "C:\DOCS") Then Timer1.Enabled = True
End Sub
Sub Timer1_Timer ()
Select Case FileOpsJobRunFor(job, 50)
Case FILEOPS_STEP_ASK
If MsgBox(FileOpsJobMessage$(job), vbYesNo + vbQuestion) = vbYes Then
FileOpsJobAnswer job, FILEOPS_ANSWER_YES
Else
FileOpsJobAnswer job, FILEOPS_ANSWER_NO
End If
Case FILEOPS_STEP_ERROR
FileOpsJobAnswer job, FILEOPS_ANSWER_SKIP
Case FILEOPS_STEP_DONE, FILEOPS_STEP_CANCELLED
Timer1.Enabled = False
FileOpsJobFree job
Case Else
ProgressBar1.Value = FileOpsJobPercent(job)
End Select
End Sub
Working with DVX
With shell.bas in the project, a program works with DVX the way Program Manager does -- a whole desktop can be written in BASIC.
Starting programs. ShellRun program, args starts a program (args is what it gets as COMMAND$), or opens a document in the program set for its type; it returns True when it started. ShellRunProgram(program, args, folder) does the same the way a Program Manager item does: file names in args, and a document named without a folder, are looked for in folder first ("" for none), and it returns the new program's id (0 when it did not start; DVX has already said why). ShellMinimizeApp id runs that program minimized: its windows become icons, including the ones it opens while it starts. ShellRunCommand(commandLine) runs a whole command line (a program, in double quotes when its name has a blank, then its arguments) and returns the id too. ShellRunDialog$(history) shows DVX's Run dialog offering history (earlier commands, newest first, one per line) and returns what the user chose, "" for Cancel. ShellOpenDocument path opens a document in the program set for its type in the Control Panel.
Installed programs. ShellProgramScan(folder, skipPath) finds every program in folder and its subfolders ("" for the APPS folder) and returns a list: ShellProgramCount(list) programs, ShellProgramPath$(list, index) each one's path (index from 0), ShellProgramFree list when done (a list not freed is freed when the program ends). Give ShellAppPath$() as skipPath to leave this program out. ShellProgramName$(path) and ShellProgramDescription$(path) are the name and description a program shows, and ShellIsProgram(path) tells a program from a document. LOADPICTURE of a program gives its icon, so a PictureBox can show a grid of programs with PaintPicture. ShellProgramsFolder$() is the folder the installed programs are in (where ShellProgramScan("") looks), for example to start a Browse for a program there with DlgFileOpenIn$.
File types. ShellAssocProgram$(extension) tells which program opens files with that extension (with ",arguments" after it when it is given any), ShellAssocCommand$(document) just the program that opens a document. ShellAssocCount() file types are set; ShellAssocExt$(index) and ShellAssocValue$(index) read them (index from 0). ShellAssocSet(extension, program) sets one and ShellAssocRemove(extension) removes one, saved at once; ShellAssocNormalize$(extension) is the extension as stored (".txt" gives "TXT"; "" when it cannot be one).
Running programs. ShellRunningCount() says how many programs are running, and ShellRunningId(index) is the id of each one (index from 0; the same id ShellRunProgram returns). ShellAppId() is this program's own id. ShellRunningName$(id) and ShellRunningPath$(id) are the name a running program shows and the path of its .app ("" when it is not running), and ShellRunningFind(program) is the id of a running copy of a program, 0 when it is not running. ShellRunningActivate id brings a program to the front the way Task Manager's Switch To does (a minimized window is restored). ShellRunningClose id asks a program to close as if the user had closed its windows: it may ask to save its work and the user may cancel, and it is asked once your code has returned to DVX, so check from a Timer whether it is still running. ShellRunningKill id ends a program at once without asking it, the way Task Manager's End Task does: whatever it had not saved is lost, so keep it for a program that does not respond. ShellAppIsHung(id) is True when a program has been busy for 5 seconds or longer ("Not responding" in Task Manager). A program cannot end itself with ShellRunningKill (END does that). ShellAppMemory(id) is how many bytes of memory a program is using. ShellChangeCount() changes whenever a program starts or ends: check it from a Timer and update what you show when it is not the value you saw last. ShellTaskManager opens the Task Manager, ShellSystemInfo$() describes the computer (several lines), and ShellExitDvx exits DVX: every program is asked to close, each asking about unsaved work, and a program that stays open stops the exit (ask the user first).
Sub cmdNotepad_Click ()
Dim id As Long
id = ShellRunningFind("APPS\KPUNCH\NOTEPAD\NOTEPAD.APP")
If id = 0 Then
ShellRun "APPS\KPUNCH\NOTEPAD\NOTEPAD.APP", ""
Else
ShellRunningActivate id
End If
End Sub
Sub FillTaskList ()
Dim i As Long, id As Long
lstTasks.Clear
For i = 0 To ShellRunningCount() - 1
id = ShellRunningId(i)
If ShellAppIsHung(id) Then
lstTasks.AddItem ShellRunningName$(id) & " (not responding)"
Else
lstTasks.AddItem ShellRunningName$(id)
End If
Next i
End Sub
Command lines. ShellCommandProgram$(commandLine) and ShellCommandArgs$(commandLine) split a command line the way ShellRunCommand does: the program (the first word, or the text in double quotes when it starts with one) and the arguments after it. ShellArgCount(commandLine) and ShellArg$(commandLine, index) split any command line into its words (index from 0) by DVX's rule: words are separated by blanks, and a word with blanks in it is written in double quotes, which ShellArg$ leaves off ("" past the last word). ShellArgQuote$(text) writes one word that way: text in double quotes when it holds blanks, as it is otherwise, so a folder or file name with spaces in it reaches the program it is passed to whole (ShellRun "notepad.app", ShellArgQuote$(path$)). COMMAND$(n) gives this program's own arguments the same way.
Keys for every program. HotkeyAdd(keyText) makes a key such as "Ctrl+Alt+E" work whichever program is in front and returns its id (0 when it is not a key, needs Ctrl or Alt, or another program uses it). The program asks for the keys pressed from a Timer: HotkeyNext() returns the id of the oldest one not asked for yet, 0 when there is none. HotkeyRemove id stops one; the rest stop when the program ends. HotkeyText$(keyText) writes a key the way DVX does ("ctrl+alt+e" gives "Ctrl+Alt+E"; "" when it is not a key), handy for checking what the user typed. ShellItemKeyValid(keyText) is Program Manager's rule for an item's shortcut: Ctrl together with Alt or Shift, so it never takes a key a program uses itself ("" for no shortcut is valid too).
This program. ShellAppPath$ is this program's own path (ShellRun ShellAppPath$(), "" starts another copy), and ShellInstanceCount says how many copies are running, for example to place each new window a little lower and to the right. ShellSetPriority(priority) gives this program more or less of the computer's time while other programs run too: SHELL_PRIORITY_LOW, SHELL_PRIORITY_NORMAL (what every program starts with), SHELL_PRIORITY_HIGH, or any number between; ShellPriority() reads it. A program busy with long work leaves the others room at low priority. A program allows more than one copy when Project Properties has "Allow more than one copy to run at once" ticked. ShellConfigFile$(fileName) is the full path of fileName in this program's settings folder -- where DVX's own programs keep their settings, the same for every copy of the program -- and makes the folder when it is missing; keep a desktop's groups there with prefs.bas. ShellLog text writes a line to the DVX log. ShellSetDesktop program makes a program the desktop DVX starts with the next time ("" for Program Manager again); when the desktop ends, DVX ends. ShellSetWelcome program sets the program DVX starts after the desktop the next time it starts ("" for none).
Program groups. Program Manager keeps its groups in PROGMAN.INI; a desktop written in BASIC uses the same model and file format. ShellGroupsLoad(path) opens a groups file (a file not saved yet has the groups Main and Startup) and returns a handle, ShellGroupsSave(groups, path) writes it, and ShellGroupsFree frees it. Groups and items are numbered from 0; group 0 is Installed Programs, which ShellGroupsScan(groups, skipPath) fills with every installed program and which is never saved. ShellGroupsChanged tells cheaply whether a program was installed or removed since. ShellGroupCount, ShellGroupName$, ShellGroupFind, ShellGroupAdd, ShellGroupRename, ShellGroupDelete, and ShellGroupNameValid manage the groups. ShellItemCount and ShellItemAdd(groups, group, name, program) list and add items; ShellItemGet$ and ShellItemSet read and change an item's fields by name ("name", "program", "args", "folder", "icon", "key", "minimized", "col", "row", "description"); ShellItemDelete, ShellItemCopy, ShellItemMove, ShellItemReorder, and ShellItemPlace rearrange them, ShellGroupArrange and ShellGroupPlaceUnplaced lay out an icon grid, and ShellItemMatches is Program Manager's search. ShellGroupsGet and ShellGroupsSet read and set the options ("view": SHELL_VIEW_LARGE, SHELL_VIEW_SMALL, or SHELL_VIEW_LIST; "group", the group shown; "autoArrange", "minimizeOnRun", "restoreAlone"), and ShellGroupsHistory$ / ShellGroupsHistoryAdd keep the Run history (what ShellRunDialog$ takes). ShellItemIcon$(iconFile, program) is the file an item's icon comes from -- its icon file, else its program or picture, else the program that opens the document -- ready for LOADPICTURE.
Dim groups As Long, main As Long, i As Long
groups = ShellGroupsLoad(ShellConfigFile$("groups.ini"))
main = ShellGroupFind(groups, "Main")
i = ShellItemAdd(groups, main, "Notepad", "APPS\KPUNCH\NOTEPAD\NOTEPAD.APP")
If Not ShellItemSet(groups, main, i, "key", "Ctrl+Alt+N") Then MsgBox "Not a shortcut."
picIcon.Picture = LOADPICTURE(ShellItemIcon$(ShellItemGet$(groups, main, i, "icon"), ShellItemGet$(groups, main, i, "program")))
If Not ShellGroupsSave(groups, ShellConfigFile$("groups.ini")) Then MsgBox "Not saved."
ShellGroupsFree groups
Sub Form1_Load ()
Dim copies As Long
copies = ShellInstanceCount()
Me.Left = 40 + (copies - 1) * 20
Me.Top = 30 + (copies - 1) * 20
End Sub
A desktop's Timer keeps its running count current and starts the programs whose keys were pressed:
Sub Timer1_Timer ()
Dim id As Long
If ShellChangeCount() <> lastChange Then
lastChange = ShellChangeCount()
lblRunning.Caption = ShellRunningCount() & " programs running"
End If
id = HotkeyNext()
Do While id <> 0
StartItemWithKey id
id = HotkeyNext()
Loop
End Sub
Changing DVX's Settings
With system.bas in the project, a program changes what Control Panel changes, at once for the whole desktop. Add prefs.bas too: the settings are saved through a prefs.bas handle.
Screen mode: SysVideoModeCount() says how many modes the display offers (numbered from 0), and SysVideoModeWidth, SysVideoModeHeight, and SysVideoModeBpp (bits per pixel) describe each. SysSetVideoMode(wide, high, bpp) changes the mode and keeps every window; it returns False when the mode could not be set (the old one stays), also while a program has the whole screen to itself. A mode the monitor cannot show leaves the user unable to answer, so do as Control Panel does: ask "Keep this mode?" and go back to the old mode by itself after some seconds. Screen.Width, Screen.Height, and Screen.BitsPerPixel tell the mode in use, and SYS_VIDEO_DEFAULT_WIDTH, SYS_VIDEO_DEFAULT_HEIGHT, and SYS_VIDEO_DEFAULT_BPP the mode DVX starts in when its settings name none. SysScreenshot(path) saves the whole screen as a picture file (BMP when path ends in .bmp, PNG otherwise) and returns True when it was written.
Colors: the system colors are numbered from 0 to SysColorCount() - 1. SysColorLabel$(id) is the text to show for one ("Desktop", "Window Face" ...), SysColorName$(id) the name it is stored as, and SysColorId(colorName) the id of a name (-1 for none) -- to draw in the user's colors, for example SysGetColor(SysColorId("desktop")). SysGetColor and SysSetColor read and set a color as an RGB() value; each SysSetColor redraws the whole screen. SysResetColors goes back to DVX's own colors. SysLoadTheme(path) and SysSaveTheme(path) read and write a theme file (.THM, kept in CONFIG\THEMES); both return True when they worked.
Wallpaper: SysSetWallpaper(path) shows a BMP, JPG, PNG, or GIF image behind the desktop ("" for none) and returns False when the image could not be loaded (the old one stays). SysSetWallpaperMode mode says how it fills the screen, a choice of PREFS_CHOICE_WALLPAPER (Stretch, Tile, or Center; see Settings Files). SysWallpaper$() and SysWallpaperMode() tell what is in use.
Text: SysSetTextSmoothing True smooths the edges of text and SysSetTextSmoothing False stops it; SysTextSmoothing() tells which the user chose. Text is smoothed only when the screen shows more than 256 colors, and only in the fonts and sizes that come smoothed. Every window is drawn again when it changes.
Mouse: SysSetMouse wheelDir, dblClickMs, accel, mickeys, wheelStep applies a whole mouse setup at once: the scroll wheel's direction (1 or -1, the values of PREFS_CHOICE_WHEEL), the double-click time in milliseconds, the acceleration (a value of PREFS_CHOICE_ACCEL), how far the mouse moves for 8 pixels of pointer movement (8 is normal, higher is slower), and the lines scrolled per wheel notch (1 to 10). An accel or mickeys of 0 leaves that one as it is. SysMouseWheelDir, SysMouseDoubleClick, SysMouseAccel, SysMouseMickeys, and SysMouseWheelStep read the setup in effect. MOUSE_WHEEL_STEP_MIN and MOUSE_WHEEL_STEP_MAX are the limits of the wheel step, and MOUSE_WHEEL_STEP_DEFAULT, MOUSE_DBLCLICK_DEFAULT_MS, and MOUSE_SPEED_DEFAULT are DVX's defaults (what a Default button puts back).
The pointer and the keyboard: SysMouseX() and SysMouseY() are where the mouse pointer is on the screen, and SysMouseButtons() the buttons held (vbLeftButton, vbRightButton, and vbMiddleButton bits) -- also while the pointer is over another program's window, where your MouseMove events do not see it. SysSetMousePos x, y moves the pointer (it is kept on the screen). SysShiftState() is the Shift, Ctrl, and Alt keys held now (vbShiftMask, vbCtrlMask, and vbAltMask bits). SysSendKey(keyCode, shift) types a key into the window that has the focus as if the user pressed it, like SendKeys in other BASICs: keyCode as a KeyDown event gets it -- a character code such as ASC("a") or 13 for Enter, or a vbKey constant such as vbKeyF1 -- and shift the vbShiftMask, vbCtrlMask, and vbAltMask bits held with it (SysSendKey ASC("s"), vbCtrlMask is Ctrl+S). The keys arrive in order, one at a time, once your code has returned to DVX; up to 64 can wait, and SysSendKey returns False when that many are waiting. A program cannot press the mouse buttons: to "click" a control, call its event procedure or set its value.
Memory: SysMemoryTotalKb() is the computer's memory in KB and SysMemoryFreeKb() how much of it is free (-1 when it cannot be told).
System font: SysFontWidth() and SysFontHeight() are the size of every character of the system font in pixels, and SysFontFirstChar() to SysFontFirstChar() + SysFontCharCount() - 1 the character codes it has. SysFontGlyphRow(code, row) is one row of a character's picture: a bit per pixel, &H80 the leftmost -- for drawing text dot by dot, enlarged or in a font editor of your own.
Saving: SysColorsStore, SysWallpaperStore, SysFontsStore, and SysMouseStore write the colors, the wallpaper, text smoothing, and the mouse setup into a prefs.bas handle, in the sections DVX reads when it starts; PrefsSetVideoMode does the same for a screen mode. Store them in PrefsLoad(PrefsDvxIni$()) and save it, and DVX starts with them next time. SysColorsLoad, SysWallpaperLoad, SysFontsLoad, and SysMouseLoad apply what a handle holds. A PrefsCreate handle filled with the Store calls is a snapshot in memory: load it again to undo every change, the way Cancel works in Control Panel.
Dim snap As Long
Dim oldW As Long, oldH As Long, oldBpp As Long
Sub Form1_Load ()
snap = PrefsCreate()
SysColorsStore snap
SysWallpaperStore snap
SysFontsStore snap
SysMouseStore snap
oldW = Screen.Width
oldH = Screen.Height
oldBpp = Screen.BitsPerPixel
End Sub
Sub cmdOK_Click ()
Dim ini As Long
ini = PrefsLoad(PrefsDvxIni$())
SysColorsStore ini
SysWallpaperStore ini
SysFontsStore ini
SysMouseStore ini
PrefsSetVideoMode ini, Screen.Width, Screen.Height, Screen.BitsPerPixel
If Not PrefsSave(ini) Then MsgBox "The settings could not be saved."
PrefsClose ini
Unload Me
End Sub
Sub cmdCancel_Click ()
SysColorsLoad snap
SysWallpaperLoad snap
SysFontsLoad snap
SysMouseLoad snap
If Screen.Width <> oldW Or Screen.Height <> oldH Or Screen.BitsPerPixel <> oldBpp Then
SysSetVideoMode oldW, oldH, oldBpp
End If
Unload Me
End Sub
Fonts
Besides the System font (the 8x16 font DVX draws everything with unless told otherwise), DVX comes with font families in its FONTS folder: families for text (sans-serif, serif, and fixed-pitch), display and handwriting families for headings, and pixel fonts. Which of them a computer has depends on how DVX was installed: Setup puts in none, the base set (DejaVu Sans, Sans Mono, and Serif), or all of them. Text in a family that is not installed is drawn in the System font, so a program that cares can look for the family first (FontFamilyCount and FontFamilyName$). With font.bas in the project a program can list them and measure text in them.
A family comes in up to four styles -- regular, bold, italic, and bold italic -- and in the pixel sizes it was made at. A font is always drawn at one of those sizes, never scaled: asking for another size gives the nearest one (FontNearestSize says which), and asking for a style a family has no face for makes it from the nearest face, bold by thickening and italic by slanting. Name a font by its family, size, and style on every call; a family name DVX does not have means the System font. Style is 0 for regular or FONT_BOLD + FONT_ITALIC; FONT_UNDERLINE and FONT_STRIKE are lines drawn with the text.
' With font.bas added to the project: every family and its sizes
Dim i As Long
Dim j As Long
Dim sizes As String
For i = 0 To FontFamilyCount() - 1
sizes = ""
For j = 0 To FontSizeCount(FontFamilyName$(i), 0) - 1
sizes = sizes & " " & Str$(FontSizeAt(FontFamilyName$(i), 0, j))
Next j
Print FontFamilyName$(i); ":"; sizes
Next i
' How wide a heading is in 24-pixel bold DejaVu Sans
Print FontTextWidth("DejaVu Sans", 24, FONT_BOLD, "Chapter One")
FontFamilyFixed tells a fixed-pitch family (every character as wide as every other, for columns and code) from a proportional one. FontLineHeight is the height of one line of text and FontAscent the distance from the top of a line to the baseline the letters sit on. FontRescan reads the FONTS folder again after fonts were added or removed. FontLicence$(family, size, style) gives the notice and licence that go with a font: where it came from, its copyright notices, and its licence text (empty for the System font).
A program can carry fonts of its own, so its text looks the same whatever fonts were installed. List the .DFN files in the project's Fonts (Project > Properties in the IDE): Make builds them into the program, and they are its fonts whenever it runs, from the IDE too. FontAddFile(path), FontAddData(label, bytes), and FontAddResources(file) add more while it runs. A program's fonts are its own -- other programs never see them -- and they go when it ends. Where a font it carries is of a family that is installed too, its sizes and styles come first and the installed family fills in the rest: a program that carries DejaVu Sans Bold at 36 pixels draws that size from its own font and every other size and style of DejaVu Sans from the FONTS folder. Fonts are made from TrueType, Type 1, and bitmap fonts with the SDK's mkfont, or copied from the FONTS folder; each .DFN carries its own licence.
' With font.bas added to the project: a title in a font the program carries
If FontAddFile(App.Path + "\TITLE.DFN") Then
lblTitle.FontName = "DejaVu Sans"
lblTitle.FontSize = 36
lblTitle.FontBold = True
End If
Every control and every form can have a font of its own, set with the properties FontName, FontSize (in pixels), FontBold, FontItalic, FontUnderline, and FontStrikethru, as in Visual Basic; they need no include file. A control that is given no font uses the font of the control it is in (a Frame, for example), else its form's, so setting a form's font changes every control on it that has none. Reading one of the properties gives the font the object draws with: a FontSize the family does not come in reads back as the nearest size it has, and a FontName DVX does not have reads back as "System". Setting one changes only that part of the font. Controls that edit text in columns (TextBox, TextArea, ComboBox, SpinButton, and Terminal) take only fixed-pitch families and show the System font in place of a proportional one. Print, TextWidth, TextHeight, PrintAccel, and TextWidthAccel on forms and picture boxes use the object's font. In the form designer, the Font row of the Properties window sets the six properties at once, and they are saved in the .frm file; DlgFont (see Font Dialog) lets the user choose a font while the program runs.
Sub Form1_Load ()
Me.FontName = "DejaVu Sans" ' every control without a font of its own
Me.FontSize = 14
lblTitle.FontSize = 24 ' the title larger, in DejaVu Sans too
lblTitle.FontBold = True
txtNotes.FontName = "DejaVu Sans Mono"
End Sub
Two controls mix fonts in one control. A RichLabel shows text built by calls -- SetFont, SetStyle, and SetColor choose how the text added next looks, AddText adds it -- in paragraphs aligned left, centered, or right. A RichEdit is a text box the user writes such text in, like a small word processor: the Sel properties (SelBold, SelFontName, SelColor, SelAlignment, ...) read and change the selection's look, TextRTF holds the whole text with its looks as RTF, and LoadFile and SaveFile read and write files. richtext.bas turns RTF into plain text and back without a control. See the controls' own pages for their properties.
Sub cmdBold_Click ()
RichEdit1.SelBold = Not RichEdit1.SelBold
End Sub
Held Keys for Games
With game.bas in the project, GameKeyDown(keyCode) tells whether a key is being held down right now, so a game can move something for as long as a key is held instead of waiting for key presses. It returns True only while one of the program's own windows is active. Name the key with one of the GAMEKEY_ constants (the arrow keys, Shift, Ctrl, Alt, Space, Enter, Esc, and a few letters) or with its PC keyboard scan code (add &H80 for the grey keys between the typing keys and the number pad). A Timer polls it:
Sub Timer1_Timer ()
If GameKeyDown(GAMEKEY_LEFT) Then shipX = shipX - 2
If GameKeyDown(GAMEKEY_RIGHT) Then shipX = shipX + 2
End Sub
GameKeyClear forgets the keys held now, so each reads as up until it is pressed again (after a pause screen, say); like GameKeyDown it only works while one of the program's windows is active. FrameMissed() counts the frames FRAMEWAIT skipped because the program was too slow to draw them in time, since it began waiting at its present rate (0 before the first FRAMEWAIT), so a game can draw less when it falls behind. GameWaitRetrace waits until the screen starts drawing its next picture from the top; a full-screen game that draws right after it does not tear.
Do
MoveEverything
If FrameMissed() > lastMissed Then
lastMissed = FrameMissed()
detail = detail - 1
End If
DrawEverything
FRAMEWAIT 60
Loop Until quitting
Joysticks
With joystick.bas in the project, a program reads joysticks and game pads. JoyCount() says how many there are; they are numbered from 1, and so are their axes, buttons, and hats. JoyX(joy) and JoyY(joy) say where the stick points, from -32768 (all the way left or up) to 32767 (all the way right or down), 0 in the middle; JoyAxis(joy, axis) reads any axis (3 and 4 are a throttle or rudder). JoyButton(joy, button) is True while the button is held down, and also once after a quick press that happened since the program last asked about that button, so a Timer never misses a shot. JoyHat(joy, hat) gives the direction a hat switch is pushed in degrees (0 is up, 90 right) or JOY_HAT_CENTERED. JoyName$(joy), JoyAxes(joy), and JoyButtons(joy) describe the joystick.
Sub Timer1_Timer ()
If JoyCount() = 0 Then Exit Sub
shipX = shipX + JoyX(1) \ 8192
If JoyButton(1, 1) Then FireShot
End Sub
When no joystick is connected the keyboard stands in for one (the arrow keys, with Left Ctrl, Space, Left Shift, and Z as buttons), and it only answers while one of the program's windows is active. Joysticks are calibrated, and the keyboard joystick's keys chosen, on the Control Panel's Game Controllers tab. QBasic's STICK and STRIG work too, without joystick.bas.
joystick.bas can also set joysticks up, as the Control Panel does. JoyHats(joy) counts the hats, JoyAxisPresent(joy, axis) is True when an axis answers, JoyConnected(joy) is False once a joystick stops answering, and JoyCanCalibrate(joy) is True for one with analog axes. For calibrating, JoyRawAxis(joy, axis) gives an axis's raw reading and JoyRawButtons(joy) the buttons held (1 for button 1, 2 for button 2, 4 for button 3, ... added up); JoySetCalibration(joy, axis, minV, centerV, maxV) takes the readings at one end, the middle, and the other end, and JoyClearCalibration(joy, axis) lets the axis find its own range again. JoyDeadZone(joy) and JoySetDeadZone(joy, percent) are the dead zone round the middle (0 to 50 percent). JoyChoiceCount, JoyChoiceLabel$, JoyChoice, and JoySetChoice list and change the game port's mode (JOY_CHOICE_GAMEPORT) and when the keyboard joystick stands in (JOY_CHOICE_FALLBACK); JoyKeyCount and JoyKeyName$(index) list the keys the keyboard joystick can use, and JoyKbdSlot(slot) and JoySetKbdSlot(slot, keyIndex) its keys for up, down, left, right, and buttons 1 to 4 (slots 0 to 7). These lists are numbered from 0, so they fill a DropDown in order. JoyKeyIndex(keyName) gives a key's number by its name (-1 when unknown), JoyKeyCode(index) its raw key code, which GameKeyDown in game.bas takes, and JoyKeyNameOf$(rawCode) the name of a raw key code. JoyIsKeyboard(joy) is True for the keyboard standing in for a joystick, and JoyCalibration(joy, axis, part) reads back an axis's calibration: part JOY_CAL_MIN, JOY_CAL_CENTER, or JOY_CAL_MAX of the readings set with JoySetCalibration, or of the range the axis found by itself (0 for a joystick that cannot be calibrated). JoySetting$(key, default) and JoySetSetting key, value reach any other joystick setting; JOY_KEY_POLL_HZ is the key of how often joysticks are read, JOY_POLL_HZ_MIN to JOY_POLL_HZ_MAX times a second (JOY_POLL_HZ_DEFAULT normally), and dead zones go up to JOY_DEADZONE_MAX percent (JOY_DEADZONE_DEFAULT normally). After a new game port mode, JoyRescan() looks for joysticks again; it returns the new count, or -1 while another program is using a joystick. Every change applies at once; JoySettingsStore handle puts them all into a settings handle from prefs.bas for the program to save, and JoySettingsLoad handle takes them back from one, to undo.
Sub cmdCenter_Click ()
' The stick rests in the middle: remember the readings
centerX = JoyRawAxis(1, 1)
End Sub
Sub cmdFinish_Click ()
If Not JoySetCalibration(1, 1, lowX, centerX, highX) Then
MsgBox "Move the stick all the way to each side."
End If
End Sub
Sound
With sound.bas in the project, a program plays sound files and tones through whatever sound hardware the computer has: a Sound Blaster, the PC speaker (tones only, unless sampled sound on the speaker is switched on), or nothing at all, in which case the program works the same but is silent. Several programs can play at once, and everything a program plays stops when it ends.
PlayWave(fileName) plays a WAV or VOC file once and PlayWaveLoop(fileName) over and over; PlayTone(frequency, milliseconds, volume) plays a tone of frequency hertz (20 to 20000) and PlayNoise(frequency, milliseconds, volume) noise, which sounds brighter the higher the frequency; a length of 0 plays until stopped. Each returns a voice number, or SOUND_FAILED (0) when the sound could not be played, and then SoundError$() says why. StopWave(voice) stops one sound, StopAllSounds() all of the program's sounds, and SoundPlaying(voice) is True while a sound is still playing. SoundVolume(volume) sets the volume of everything the program plays. Volumes are percent (0 to 100). SoundInfo$() describes the sound device, like "Sound Blaster 16 (DSP 4.05) at 220h, 22050 Hz, 16-bit stereo".
Dim Shared boom As Long
Sub cmdFire_Click ()
boom = PlayWave("BOOM.WAV")
If boom = SOUND_FAILED Then
lblStatus.Caption = SoundError$()
End If
End Sub
WAV files must be uncompressed (8 or 16 bits, mono or stereo); VOC files may be 8- or 16-bit. QBasic's BEEP, SOUND, and PLAY work too, without sound.bas.
PlayTriangle(frequency, milliseconds, volume) plays a triangle tone, softer than PlayTone's square tone. SoundGetVolume() gives the volume of everything the program plays. SoundCaps(field) tells what the sound device can do, by name in any case: "dac" (it plays recorded sound), "synth" (it has a music synthesizer), "tone" (it plays tones on a tone generator), and "running" are True or False; "voices" (how many sounds play at once), "rate" (hertz), "bits", and "channels" are numbers. SoundPlayEvent eventIdx, wait plays one of DVX's own event sounds (SOUND_EVENT_DEFAULT, SOUND_EVENT_ERROR, SOUND_EVENT_WARNING, ...), the one the settings choose; with wait True it returns once the sound has played.
A sound played often is best loaded once. SoundLoad(fileName) loads a WAV or VOC file and returns a sound number, or 0 (SoundError$() says why); SoundCreate(data, rate, bits, channels) makes a sound from raw sound data in a string: 8-bit samples (0 to 255, 128 is silence) or 16-bit ones (two bytes each, the low byte first), mono or stereo (left, right, left, ...), rate times a second (SOUND_RATE_MIN to SOUND_RATE_MAX). SoundPlay(sound, volume, pan, loopIt) plays it on a new voice and returns the voice number; pan goes from SOUND_PAN_LEFT (-100) through SOUND_PAN_CENTER (0) to SOUND_PAN_RIGHT (100). SoundLength(sound) is its length in milliseconds and SoundFree sound stops its voices and forgets it. While a voice plays, SoundSetVolume voice, volume, SoundSetPan voice, pan, and SoundSetPitch voice, percent change it (pitch is a percent of the recorded speed: SOUND_PITCH_NORMAL is 100, 200 is an octave higher, from SOUND_PITCH_MIN 25 to SOUND_PITCH_MAX 400), and SoundSetFrequency voice, hertz changes a tone's frequency. A program's sounds are freed when it ends.
Dim Shared laser As Long
Sub Form1_Load ()
laser = SoundLoad("LASER.WAV")
End Sub
Sub Fire (x As Integer)
Dim v As Long
v = SoundPlay(laser, 80, (x - 160) * 100 \ 160, False)
SoundSetPitch v, 90 + Int(Rnd * 20)
End Sub
A program can also make its sound as it goes, for speech or music of its own. SoundStreamOpen(rate, bits, channels, bufferMs) opens a stream that starts playing at once (silence until data arrives) and holds bufferMs milliseconds of sound (0 for the usual SOUND_STREAM_MS_DEFAULT; SOUND_STREAM_MS_MIN to SOUND_STREAM_MS_MAX). SoundStreamWrite(stream, data) adds raw sound data in the stream's format and returns how many bytes it took; keep the rest and write it again later. SoundStreamSpace(stream) is the room left and SoundStreamQueued(stream) what is waiting to play, both in samples (a stereo sample counts once). SoundStreamVoice(stream) is the stream's voice, for SoundSetVolume, SoundSetPan, and SoundSetPitch, and SoundStreamClose stream stops it. A Timer keeps a stream fed:
Sub Timer1_Timer ()
Dim taken As Long
taken = SoundStreamWrite(stream, pending)
pending = Mid$(pending, taken + 1)
If Len(pending) = 0 Then pending = MakeMoreSound()
End Sub
On a computer with an FM synthesizer chip (AdLib and most Sound Blasters), a program can play instruments of its own. FmInfo$(field) describes the chip: "description" (like "OPL3 FM synthesizer at 388h", "" when there is none), "voices", "free" (voices no program has), "stereo", and "owned" (one program has the whole chip). FmAlloc() takes one of its two-operator voices (0 when none is free), FmWrite(voice, register, value) sets one of the voice's registers (the FM_ constants: FM_MOD_CHAR, FM_CAR_LEVEL, FM_FNUM_LOW, FM_KEY_BLOCK with FM_KEY_ON, ...; True when it worked), and FmFree voice gives it back. A player of chip music files can take the whole chip with FmRawAcquire() (False while another program has it), set any register with FmRawWrite(register, value) (bank * FM_BANK_REGS + register; the second bank only on an OPL3) and give it back with FmRawRelease. A frequency number for FM_FNUM_LOW and FM_KEY_BLOCK is hertz * 2 ^ 20 / (FM_CHIP_HZ * 2 ^ block).
sound.bas also changes DVX's own sound settings, as the Control Panel's Sounds page does. DVX plays a sound for each of SoundEventCount() events, numbered from 0: SoundEventLabel$(eventIdx) names one ("Start DVX", "Error", ...) and SoundEventKey$(eventIdx) gives its key in DVX.INI. With a settings handle from prefs.bas (PrefsLoad(PrefsDvxIni$())), SoundEventFile$(handle, eventIdx) is the event's sound file ("" for none) and SoundSetEventFile handle, eventIdx, fileName changes it; after the program saves the file, SoundEventsReload makes DVX play the new sounds. SoundTestFile(fileName) plays a file the way DVX plays its event sounds (0 when it cannot, and SoundError$() says why), and SoundFolder$() is the folder of DVX's own sounds. SoundMasterVolume() and SoundSetMasterVolume percent are the volume of all sound from every program. SoundDriverCount() reads the list of sound drivers and SoundDriverName$(index) names each; SoundDriverAuto$() is the setting that means "find the best one". SoundMidiCount() and SoundMidiLabel$(index) list where MIDI music can play. SoundSettingDriver$(handle), SoundSettingMidi(handle), and SoundSettingVolume(handle) read the sound driver, MIDI choice, and master volume from the settings, and SoundSettingsStore handle, driver, midi, volume stores them (a new driver or MIDI choice takes effect when DVX next starts).
Dim ini As Long
Dim i As Long
ini = PrefsLoad(PrefsDvxIni$())
For i = 0 To SoundEventCount() - 1
lstEvents.AddItem SoundEventLabel$(i) & ": " & SoundEventFile$(ini, i)
Next i
Music
With music.bas in the project, a program plays music files: tracker modules (MOD, S3M, XM, and IT files, made of recorded instrument sounds) wherever the computer can play sampled sound, and MIDI files (.MID, types 0 and 1) on the computer's music synthesizer, or on a built-in synthesizer that sounds much the same when there is none. Several programs can play music at once, and everything a program plays stops when it ends.
MusicLoad(fileName) loads a music file and returns its music number, or MUSIC_FAILED (0) when it could not be loaded; MusicError$() then says why. MusicPlay(music, loopIt) plays it from where it is (the start after loading or stopping); with loopIt True it starts over at the end until it is stopped. MusicStop(music) stops and goes back to the start, MusicPause(music) and MusicResume(music) pause and go on, and MusicPlaying(music) is True while it plays. MusicVolume(music, volume) sets its volume in percent (0 to 100). MusicPosition(music) and MusicLength(music) give the place and the length in milliseconds, and MusicSeek(music, milliseconds) goes to a place (a module goes to the start of the part that holds it). MusicFree(music) stops a music and forgets it.
Dim Shared tune As Long
Sub Form1_Load ()
tune = MusicLoad("THEME.MOD")
If tune = MUSIC_FAILED Then
lblStatus.Caption = MusicError$()
Else
MusicPlay tune, True
End If
End Sub
A program can also play notes itself, like a keyboard. MusicMidiOpen() returns a live music that is already playing. MusicNoteOn(music, channel, note, velocity) starts a note and MusicNoteOff(music, channel, note) ends it; MusicProgram(music, channel, program) chooses a channel's instrument, MusicControl(music, channel, controller, value) sets a controller (7 volume, 10 pan, 11 expression, 64 the sustain pedal, 123 all notes off), and MusicPitchBend(music, channel, bend) bends the channel's notes (-8192 to 8191, two semitones either way at the ends). Channels are 1 to 16, and channel 10 (MUSIC_DRUMS) plays drums, one per note. Notes, velocities, instruments, and controller values are 0 to 127; middle C is note 60, and instruments are numbered as in General MIDI (0 piano, 24 guitar, 40 violin, 56 trumpet, 73 flute). The same calls work on a MIDI file that is playing.
Dim Shared keys As Long
Sub Form1_Load ()
keys = MusicMidiOpen()
MusicProgram keys, 1, 73
End Sub
Sub cmdC_Click ()
MusicNoteOn keys, 1, 60, 100
tmrNoteOff.Enabled = True
End Sub
Sub tmrNoteOff_Timer ()
MusicNoteOff keys, 1, 60
tmrNoteOff.Enabled = False
End Sub
MusicGetVolume(music) gives a music's volume in percent. MusicInfo$(music, field) tells about it, by name in any case: "title" (a module's own title), "output" (where it plays, for people), "kind" ("module", "midi", or "live"), "where" ("stream", "mpu401", "fm", or "software"; "" before it first plays), and "paused" and "looping" (True or False as text; VAL gives the value).
A program that sends MIDI itself (system exclusive messages, a sequencer of its own) can have the MIDI port, one program at a time, when the settings send MIDI music to the MIDI port. MidiPortInfo$(field) describes it: "description" ("" when there is none), "present", and "owned" (a program has it). MidiAcquire() takes the port (False when another program has it or there is none), MidiPut(byteValue) sends one byte (False when the port could not take it yet: send it again), and MidiRelease gives the port back.
Network Connections
With net.bas in the project, a program talks to other computers over the network set up in the Control Panel. TcpConnect returns a handle (0 when it failed; NetError$ says why), TcpSend sends text, and TcpRecv$ returns whatever has arrived without waiting, so a Timer keeps the program responsive:
Dim conn As Long
Sub Form1_Load ()
conn = TcpConnect("bbs.example.org", 23)
If conn = 0 Then
Print "Could not connect: " & NetError$()
End If
End Sub
Sub Timer1_Timer ()
If TcpPending(conn) > 0 Then Print TcpRecv$(conn)
If TcpConnected(conn) = 0 Then Timer1.Enabled = False
End Sub
Function Description
-------- -----------
HostLookup$(hostName) The address of a name, such as "192.0.2.10", or "" when it is not found.
HostName$(address) The name of an address; the address itself when no name is known for it.
NetAddress$() This computer's address ("" while it has none).
NetError$() Why the last call failed, in plain words.
TcpConnect(host, port) Connect; a handle, or 0. Waits up to 20 seconds; other programs keep running.
TcpListen(port) Wait for connections on port; a listening handle, or 0.
TcpAccept(handle) A connection that arrived on a listening handle, or 0 when none is waiting.
TcpSend(handle, bytes) Send text or any other bytes; the bytes sent, or -1.
TcpPending(handle) Bytes waiting to be read (-1 for a bad handle).
TcpRecv$(handle) What has arrived (up to 4096 bytes), or "".
TcpConnected(handle) 1 while the connection is open, 0 once the other side closed it.
TcpShutdown(handle) Tell the other side nothing more will be sent; what it sends can still be read.
TcpPeer$(handle) "address:port" of the other side ("" when not connected).
TcpLocalPort(handle) The port a handle uses on this computer (the one TcpListen(0) picked).
TcpSetNoDelay(handle, enable) True: send small pieces at once instead of gathering them (typing, games).
TcpSetKeepAlive(handle, enable) True: check now and then that the other side of an idle connection is still there.
TcpClose handle Close a connection or a listening handle.
UdpOpen(port) A UDP handle on port (0 = any free port), or 0.
UdpSend(handle, host, port, text) Send one message; the bytes sent, or -1. "255.255.255.255" reaches every computer nearby.
UdpRecv$(handle) The next message that arrived, or "".
UdpFrom$() "address:port" of the sender of the last UdpRecv$ message.
UdpClose handle Close a UDP handle.
The strings sent and received may hold any bytes, CHR$(0) too, so binary protocols work as well as text. Every connection a program opens is closed for it when the program ends.
A program can also edit the network settings the way the Control Panel's Network page does, and show the network's state. The settings are kept in DVX's settings file, so NetConfigLoad and NetConfigStore take a handle from prefs.bas (add prefs.bas to the project too). NetConfigLoad gives a settings handle (0 when none is free); read and change its fields by name, check them, store them, and restart the network only when they changed:
Dim prefs As Long
Dim cfg As Long
Sub Form1_Load ()
prefs = PrefsLoad(PrefsDvxIni$())
cfg = NetConfigLoad(prefs)
txtName.Text = NetConfigGet$(cfg, "hostname")
lblState.Caption = NetStatus$("message")
End Sub
Sub cmdOK_Click ()
NetConfigSet cfg, "hostname", txtName.Text
If NetConfigProblem$(cfg) <> "" Then
MsgBox NetConfigProblem$(cfg)
Exit Sub
End If
NetConfigStore cfg, prefs
PrefsSave prefs
If NetConfigChanged(cfg) Then NetRestart
NetConfigFree cfg
PrefsClose prefs
End Sub
Function Description
-------- -----------
NetConfigLoad(prefs) A settings handle holding the network settings of prefs (0 for the usual settings), or 0 when none is free.
NetConfigGet$(cfg, field) One setting as text: "dhcp" ("-1" when the address comes from the network automatically, "0" when it is fixed), "address", "netmask", "gateway", "dns1", "dns2", "hostname", "vector" (the packet driver's interrupt, 0 = find it).
NetConfigSet(cfg, field, value) Change one setting (same names; "dhcp" and "vector" take numbers). True when the field exists.
NetConfigProblem$(cfg) "" when the settings can be used, else why not in plain words.
NetConfigChanged(cfg) True when the settings differ from what NetConfigLoad read.
NetConfigStore(cfg, prefs) Put the settings into prefs (then save it with PrefsSave). True when done.
NetConfigFree cfg Free the settings handle.
NetVectorCount() / NetVector(index) The packet driver interrupts to choose from: index 0 is 0 (find it), then each one the settings accept.
NetRestart() Stop the network and start it with the saved settings. True when it is running.
NetStop Stop the network (every program's connections are closed) until NetRestart.
NetStatus$(field) The network's state as text: "message" (in plain words), "up" and "link" ("-1" / "0"), "dhcp", "address", "netmask", "gateway", "dns1", "dns2", "hostname", "driver" (the network card's driver), "mac" (its hardware address), "vector", "sockets", "rxpackets", "rxbytes", "rxdropped", "txpackets", "txbytes". "" for an unknown field.
Settings handles a program leaves open are freed for it when it ends.
CommRecvEncrypted(handle, channel) tells whether the data the last CommRecv$ on that channel returned can be trusted. It returns True when every byte of it arrived encrypted and authenticated, and also when that CommRecv$ returned nothing or CommRecv$ has not been called on the channel yet. It returns False when any byte arrived unencrypted, which can only happen before CommHandshake or on a channel opened with CommSetPlain, and for an invalid handle or a channel outside 0-127.
Dim msg As String
msg = CommRecv$(link, 1)
If msg <> "" And Not CommRecvEncrypted(link, 1) Then
Print "Unencrypted: " & msg
End If
Connections and Terminals
With conn.bas in the project, a program talks to another computer the way the Terminal program does, whatever is in between: a serial port, a modem (it sends its setup command, dials, dials again when the line is busy, and notices when the carrier is lost), the secure link to the DVX host proxy (encrypted, and authenticated with a passphrase), or Telnet over the network. ConnCreate makes a connection (a number); ConnSetSerial, ConnSetModem, ConnSetHost, and ConnSetPassphrase set it up, and ConnOpen(conn, type) starts it. Opening only starts: dialing and the key exchange go on while the program runs, so call ConnPoll often from a Timer. ConnState says where it is (CONN_CLOSED, CONN_OPENING, CONN_OPEN) and ConnStatus$ what is happening or why it ended; ConnStatusSerial changes whenever ConnStatus$ does.
ConnAttach conn, "Term1" puts what arrives on the Terminal control Term1 (ConnPoll moves it there) and sends what is typed in it. ConnPoll then also appends what arrives to a capture file (ConnCapture), answers the other side's ENQ with an answerback (ConnSetAnswerback), and notices a ZMODEM sender starting a download: ConnZmodemRequest is True once, and the program answers with ConnReceiveFiles in the same Timer event. ConnSetEcho shows what is typed on the Terminal too. Without a Terminal, read with ConnRead$ and send with ConnWrite (any bytes, CHR$(0) too). ConnOpen returns CONN_OK or a CONN_ERR_ code; ConnErrorText$ puts the code in words. ConnSetXferWindow conn, bytes limits how much a ZMODEM sender may send before it waits for an answer (0 = no limit, up to XFER_WINDOW_MAX; -1, the default, picks by the line). ConnSendBreak holds a serial line in the break state for a moment; ConnClose hangs up and ConnFree forgets the connection.
Dim Shared conn As Long
Dim Shared download As Long
Sub Form1_Load ()
conn = ConnCreate()
ConnSetSerial conn, 2, 115200, 8, "N", 1, CONN_FLOW_RTSCTS
ConnAttach conn, "Term1"
ConnOpen conn, CONN_SERIAL
End Sub
Sub Timer1_Timer ()
ConnPoll conn
If ConnZmodemRequest(conn) Then download = ConnReceiveFiles(conn, XFER_ZMODEM, "DOWNLOAD", XFER_EXIST_RENAME, False)
End Sub
File Transfers
With xfer.bas in the project, a program sends and receives files with XMODEM, XMODEM-1K, YMODEM, or ZMODEM. A transfer is a number: XferSendFile and XferReceive start one on a serial port opened with SerOpen (comm.bas), and ConnSendFiles and ConnReceiveFiles start one on a connection (conn.bas), with the options Terminal has: escaping every control character, asking the receiver to continue interrupted files, and what to do with a file that is already there. On a serial port XferPoll moves the transfer along; on a connection ConnPoll does. XferStatus tells where it is (XFER_RUNNING until it ends with XFER_DONE, XFER_FAILED, or XFER_CANCELLED), and XferBytes, XferSize, XferFile$, XferFileIndex, XferFileCount, XferBatchBytes, XferBatchSize, XferResumedAt, and XferErrors show its progress; XferMessage$ says how it ended. A receive with XFER_EXIST_ASK stops in XFER_ASKING when a file already exists: XferAskSize, XferAskRemoteSize (the size the sender offers, -1 when it did not say), and XferCanResume describe it, and XferAnswer continues with an XFER_EXIST_ choice. XferCancel stops a transfer and XferClose frees it.
XferCrc16(bytes) and XferCrc32(bytes) give the check values the protocols use (CRC-16 as XMODEM computes it, 0 to 65535; the CRC-32 of ZMODEM and ZIP files as a Long, negative when its top bit is set) for any bytes. XferSafeName$(name, folder) gives the name a receive into folder stores a sender's file name as: no folder or device name; a long name, its case kept, where folder's disk takes long names (a character a name cannot hold becomes _), else an 8.3 name of characters every disk accepts.
Lists You Draw Yourself
With listhelp.bas in the project, a list or icon view a program draws itself -- in a UserControl or a PictureBox -- follows the same rules as ListBox and ListView. ListSelCreate(multi, count) makes a selection for count items (several at a time when multi is True) and returns a handle; it remembers which items are selected and which one is current (the one the keyboard moves). Items are numbered from 0. Give it the clicks and keys, and ask it what to draw:
Function Description -------- ----------- ListSelClick sel, index, shift A click on an item, with the MouseDown event's Shift: alone it selects only that item, with Ctrl it turns the item on or off, with Shift it selects the items from the last click up to it. ListSelKey(sel, keyCode, shift) Ctrl+A selects every item (returns LISTSEL_KEY_ALL), Space turns the current item on or off (LISTSEL_KEY_TOGGLE); LISTSEL_KEY_NONE for other keys. ListSelNavigate(sel, keyCode, shift, columns, pageRows) The arrow keys, Home, End, PgUp, and PgDn, in a list (columns 1) or a grid with columns items per row, pageRows rows a page; with Shift they select from the last click. The new current item, or -1 when nothing moved. ListSelMove sel, index, shift Make an item current as the keyboard does (after type-ahead). ListSelCursor(sel), ListSelSetCursor sel, index The current item (-1: none); make one item current and the only one selected. ListSelIsSelected(sel, index), ListSelSelectedCount(sel) Whether an item is selected; how many are. ListSelSetItem sel, index, selected; ListSelSetAll sel, selected Select or clear one item, or all. ListSelInsert sel, index; ListSelRemove sel, index An item was added or removed: the others keep their selection. ListSelReorder sel, fromIndex, dest An item was dragged to dest: its selection goes with it. ListReorderDest(fromIndex, toSlot, count) Where an item dragged from fromIndex lands when dropped in front of slot toSlot (count: after the last item); -1 when the drop changes nothing. ListSelReset sel, count; ListSelCount(sel); ListSelFree sel The list now holds count new items; how many it holds; free the handle. ListNavigateIndex(keyCode, current, count, pageSize), ListGridNavigate(keyCode, current, count, columns, pageRows) The same keys without a selection handle. ListTypeAhead(ch, items, current) The next item after current whose text starts with the letter ch, any case (going round to the first); items holds the texts separated by CHR$(10). -1 when none does. ListTypeAheadMatch(ch, text) True when text starts with the letter ch.
Sub UserControl_KeyDown (KeyCode As Integer, Shift As Integer)
If ListSelKey(sel, KeyCode, Shift) <> LISTSEL_KEY_NONE Then UserControl.Refresh
If ListSelNavigate(sel, KeyCode, Shift, columns, rowsShown) >= 0 Then UserControl.Refresh
End Sub
Sub UserControl_KeyPress (KeyAscii As Integer)
Dim i As Long
i = ListTypeAhead(Chr$(KeyAscii), itemTexts, ListSelCursor(sel))
If i >= 0 Then
ListSelMove sel, i, 0
UserControl.Refresh
End If
End Sub
Selections a program leaves open are freed when it ends.
An icon view drawn with listhelp.bas looks and behaves like Program Manager's: the same cells for large icons, small icons, and the list, the same frame and scroll bar, a large icon's name on two lines, and the same rules for the keys, dragging and dropping. The grid functions take the view mode (LISTICON_VIEW_LARGE, LISTICON_VIEW_SMALL, or LISTICON_VIEW_LIST), the size of a character (TextWidth("W") and TextHeight("W")), the size of the whole view, how many items it shows, and rowsTaken: LISTICON_BY_ORDER when the items fill the grid in order, else how many rows items placed in cells of their own take. Draw the view's frame with DrawBevel (LISTICON_FRAME wide; with the face -1 after the items, so a row cut off at the bottom stops at the frame) and its scroll bar with DrawScrollBar as a control's bar (its control argument True); the grid leaves room for it.
Function Description
-------- -----------
ListIconMetric(mode, metric, cw, ch, w, h, count, rowsTaken) One value of the grid: a cell's size (LISTICON_CELL_W, LISTICON_CELL_H), where its picture goes and how big it is (LISTICON_PICTURE_X, LISTICON_PICTURE_Y, LISTICON_PICTURE_SIZE), where its name goes and how many characters it shows (LISTICON_TEXT_X, LISTICON_TEXT_Y, LISTICON_NAME_CHARS; a large icon's two lines are each centred in the cell), the list's detail column (LISTICON_DETAIL_X, 0 in the other views); the inside of the frame less the scroll bar (LISTICON_INNER_X, LISTICON_INNER_Y, LISTICON_INNER_W, LISTICON_INNER_H), the columns, the rows shown whole and in all (LISTICON_COLUMNS, LISTICON_ROWS_SHOWN, LISTICON_ROWS), the largest top row (LISTICON_TOP_ROW_MAX), True when the scroll bar is needed (LISTICON_SCROLLBAR), where the text of an empty view goes (LISTICON_EMPTY_X, LISTICON_EMPTY_Y), the frame's width (LISTICON_FRAME), and the rows a wheel notch scrolls (LISTICON_WHEEL_ROWS).
ListIconCellAt(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) The cell under x, y with the view scrolled to topRow, numbered row * columns + column; -1 outside the grid.
ListIconLabel$(name, maxChars, lineNo) Line 1 or 2 of a large icon's name, maxChars characters a line (LISTICON_NAME_CHARS): the first line ends at the last blank that fits, else it is cut; the second is the rest, cut. "" for a line the name does not need.
ListIconNavigate(keyCode, current, cells, columns, pageRows) The item the arrow keys, Home, End, PgUp, or PgDn move to when the items stand in cells of their own, gaps allowed: cells holds each item's column and row, all separated by commas ("0,0,2,0,1,1"). -1 for another key or when no item is that way.
ListIconScrollPress(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) A left press on the scroll bar: the top row after it (an arrow moves a row, the trough a page, the thumb nothing); -1 when x, y is not on the scroll bar.
ListIconScrollGrab(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) How far below the top of the thumb a press is; -1 when it is not on the thumb.
ListIconScrollDrag(mode, cw, ch, w, h, count, rowsTaken, grab, y) The top row with the thumb, held grab pixels below its top, dragged to y. While the button is down the control gets the mouse even outside it.
ListIconDragStarted(dx, dy) True when a press has moved far enough to start a drag.
ListIconDragScroll(mode, cw, ch, w, h, count, rowsTaken, topRow, y) While an item is dragged: the top row after the view scrolls a row because the mouse is above or below the grid.
ListIconDropCell(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) The cell a dragged item would be dropped on (above or below the grid: the nearest row); -1 for none.
ListIconDropDest(fromIndex, cell, count) Items in order: the place an item dragged from fromIndex takes when dropped on a cell -- that cell's, the last place for a cell past the end; -1 when the drop changes nothing.
Sub UserControl_Paint ()
Dim cw As Long, ch As Long
cw = UserControl.TextWidth("W")
ch = UserControl.TextHeight("W")
UserControl.DrawBevel 0, 0, UserControl.Width, UserControl.Height, True, vbWhite, ListIconMetric(LISTICON_VIEW_LARGE, LISTICON_FRAME, cw, ch, UserControl.Width, UserControl.Height, itemCount, LISTICON_BY_ORDER)
' ... each item in its cell, its name in two lines:
UserControl.Print ListIconLabel$(itemName, ListIconMetric(LISTICON_VIEW_LARGE, LISTICON_NAME_CHARS, cw, ch, UserControl.Width, UserControl.Height, itemCount, LISTICON_BY_ORDER), 1);
End Sub
Security
With security.bas in the project, a program has the building blocks of DVX's encrypted serial link for its own protocols and files. All of them take and give strings that may hold any bytes, CHR$(0) too.
Function Description -------- ----------- SecRandom$(count) count random bytes, good for keys and salts (RND is not). SecAddEntropy bytes stirs something unpredictable (key timings, mouse positions) into the generator. SecDhCreate() A key exchange with fresh keys (a few seconds on a slow computer); a handle, or 0. SecDhPublicKey$(dh) This side's public key (SEC_DH_KEY_SIZE bytes), to send to the other side. SecDhComputeSecret(dh, otherPublicKey) Combine with the other side's public key: SEC_OK, or SEC_ERR_PARAM for a key that is not acceptable. SecDhSecret$(dh) The secret both sides now share, which nobody listening can work out. Make keys from it with SecHkdf$; never use it as a key itself. SecDhFree dh frees the handle. SecCipherCreate(key) A cipher with a SEC_XTEA_KEY_SIZE-byte key; 0 for a key of another length. SecCipherCrypt$(cipher, bytes) Encrypt -- or decrypt, the same call -- bytes. SecCipherSetCounter cipher, n sets the position in the key stream; the same key and position must never encrypt two different messages. SecCipherFree cipher frees it. SecSha256$(bytes) The SHA-256 hash (SEC_SHA256_SIZE bytes). For data in pieces: SecSha256Open, SecSha256Add h, piece, then SecSha256End$(h), which also frees the handle. SecHmac$(key, bytes) The HMAC-SHA256 signature of bytes with key; SecHmacOpen(key), SecHmacAdd, and SecHmacEnd$ for pieces. SecHkdf$(salt, secret, info, length) length bytes (up to SEC_HKDF_MAX_OUT) of keys made from secret; a different info gives different keys. SecEqual(a, b) True when a and b hold the same bytes, compared in the same time wherever they differ (for signature checks).
' Both sides: make keys, swap public keys (here over a TCP connection), agree a cipher key
Dim dh As Long, cipher As Long, key As String
dh = SecDhCreate()
TcpSend conn, SecDhPublicKey$(dh)
' ... receive the other side's key into theirs ...
If SecDhComputeSecret(dh, theirs) = SEC_OK Then
key = SecHkdf$("", SecDhSecret$(dh), "chat cipher", SEC_XTEA_KEY_SIZE)
cipher = SecCipherCreate(key)
End If
SecDhFree dh
Handles a program leaves open are freed, and their keys wiped, when it ends.
Settings Files
With prefs.bas in the project, a program keeps its settings in an INI file the way DVX's own programs do. PrefsLoad(path) reads the file once (a file that is not there reads as empty); PrefsGet$, PrefsGetInt, and PrefsGetBool read a key of a section (each with the value to use when it is missing), and PrefsSet, PrefsSetInt, PrefsSetBool, and PrefsRemove change it in memory. PrefsSave writes it back, keeping comments and every line it did not change. To write a whole new file -- dropping sections that are no longer wanted -- fill a PrefsCreate handle and save it with PrefsSaveAs: the old file is replaced only once the new one is completely written. PrefsClose frees a handle. IniRead and IniWrite (built in) do one value at a time and read or write the whole file each time; prefs.bas is the faster choice for many settings.
Dim ini As Long
ini = PrefsLoad(ShellConfigFile$("MYAPP.INI"))
userName = PrefsGet$(ini, "User", "Name", "Unknown")
PrefsSetInt ini, "Window", "Left", Me.Left
If Not PrefsSave(ini) Then MsgBox "The settings could not be saved."
PrefsClose ini
PrefsDvxIni$() is the path of DVX's own settings file, DVX.INI, which Control Panel changes; other libraries (system.bas, joystick.bas, net.bas, print.bas, and sound.bas) read and store their part of it through a prefs.bas handle. PrefsSetVideoMode handle, wide, high, bpp stores the screen mode DVX starts in, and PrefsVideoWidth(handle), PrefsVideoHeight(handle), and PrefsVideoBpp(handle) read it (DVX's own start-up mode, SYS_VIDEO_DEFAULT_* in system.bas, for a value that is not there). PrefsKeyName$(handle, section, index) is the name of key number index of a section (from 0, in the order of the file; "" past the last key), to go through every key of a section.
Some settings are stored by name, from a fixed list of choices: the mouse acceleration (PREFS_CHOICE_ACCEL), the wallpaper mode (PREFS_CHOICE_WALLPAPER), and the scroll wheel direction (PREFS_CHOICE_WHEEL). For each list, PrefsChoiceCount(table) says how many choices it has (numbered from 0), PrefsChoiceLabel$(table, i) is the text to show, PrefsChoiceName$(table, i) the name stored in the file, and PrefsChoiceValue(table, i) the value it stands for (what SysSetMouse takes). PrefsChoiceIndex(table, choiceName) and PrefsChoiceIndexOfValue(table, value) find a choice (-1 when there is none), and PrefsGetChoice(handle, section, key, table) reads one from a file, giving DVX's default choice when the key is missing or unknown. PrefsChoiceDefault(table) is that default choice, what a Defaults button selects. For a list of your own, PrefsGetChoiceFrom(handle, section, key, choiceNames, defaultName) does the same with the names separated by "|" ("large|small|list" stores choice 1 as "small"), giving defaultName's choice for a missing or unknown key.
For i = 0 To PrefsChoiceCount(PREFS_CHOICE_ACCEL) - 1
cboAccel.AddItem PrefsChoiceLabel$(PREFS_CHOICE_ACCEL, i)
Next i
cboAccel.ListIndex = PrefsChoiceIndexOfValue(PREFS_CHOICE_ACCEL, SysMouseAccel())
Help Files
With help.bas in the project, HelpCompile(source, output) compiles a help source file (.dhs) into a help file (.hlp), True when it worked; HelpCompileMessages$() gives the compiler's errors and warnings, one per line, each with the file and line ("error: NOTES.DHS:1: directive .h1 outside of a topic"). HelpView file opens a help file in the DVX Help Viewer (a file named without a folder is looked for beside the program; "" opens the DVX user guide). HelpViewTopic file, topicId opens it at a topic, the way F1 does. A Help Viewer that is already open shows the file in its window instead of a second copy starting.
With hlpfile.bas, a program reads help files itself -- everything a help viewer needs. HlpOpen(path) opens a help file and returns a handle (0 when it is not one); HlpClose closes it (a program's open files are closed when it ends). Topics are numbered from 0: HlpTopicCount, HlpTopicTitle$ and HlpTopicId$ (the name links and HelpTopic use), HlpFindTopic(handle, topicId) (-1 when there is none), and HlpStartTopic(handle, topicId), the topic to show first (topicId when the file has it, else the file's own start page). HlpTocCount, HlpTocTitle$, HlpTocDepth, HlpTocTopic, and HlpTocExpanded give the table of contents in order -- a depth of 0 is the top level, a topic of -1 a heading that only groups others -- ready to fill a TreeView. HlpIndexCount, HlpIndexKeyword$, and HlpIndexTopic give the keyword index (sorted), for a list or DlgChoice. HlpSearch(handle, words) searches the titles, text, and keywords as the Help Viewer does and returns how many topics have every word; HlpSearchTopic(handle, n) gives them.
To show a topic, put a HelpPane control on the form and call HelpPane1.ShowTopic handle, topic: headings, paragraphs, lists, notes, code, tables, pictures, and links look as they do in the Help Viewer, and the text wraps to the control's width. A click on a link, or Enter on one reached with Tab, fires the HelpPane's LinkClick event with the ID of the topic the link names; the program decides what to do, usually show that topic and remember it for a Back button. A program that draws topics itself reads them with HlpLoadTopic(handle, topic) and HlpRecordType, HlpRecordFlags, HlpRecordText$, and HlpRecordLink$ (the HLP_REC_ constants), saves a picture for LOADPICTURE with HlpRecordImageSave, and wraps text with HlpWrap$.
Dim hlp As Long
Dim history(63) As Long, histPos As Long
Sub Form1_Load ()
hlp = HlpOpen(App.Path + "\MYAPP.HLP")
For i = 0 To HlpTocCount(hlp) - 1
If HlpTocDepth(hlp, i) = 0 Then
Toc.AddItem HlpTocTitle$(hlp, i)
Else
Toc.AddChildItem lastTop, HlpTocTitle$(hlp, i)
End If
If HlpTocDepth(hlp, i) = 0 Then lastTop = Toc.ItemCount() - 1
Toc.SetItemTag Toc.ItemCount() - 1, Str$(HlpTocTopic(hlp, i))
Next i
GoTopic HlpStartTopic(hlp, "")
End Sub
Sub GoTopic (ByVal topic As Long)
histPos = histPos + 1
history(histPos) = topic
HelpPane1.ShowTopic hlp, topic
Me.Caption = HlpTopicTitle$(hlp, topic) + " - My Help"
End Sub
Sub HelpPane1_LinkClick (Topic As String)
GoTopic HlpFindTopic(hlp, Topic)
End Sub
Sub Toc_Change ()
If Val(Toc.GetItemTag(Toc.ListIndex)) >= 0 Then GoTopic Val(Toc.GetItemTag(Toc.ListIndex))
End Sub
A help viewer written in BASIC can also take the place of DVX's own: installed as APPS\KPUNCH\DVXHELP\DVXHELP.APP with a NewArgs event procedure, it is handed "helpfile topic" when F1 is pressed while it is open (see New Arguments in the Form topic).
Sizes in Characters
Control sizes (Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight) are pixels. With layout.bas in the project, SizeChars(n) is a size of n characters of the system font and SizePercent(n) one of n percent of the container (0 to 100); give them to a size property and the layout works out the pixels each time it arranges the form, so a box for an address keeps its width whatever the font. Reading the property back gives the pixels it stands for at that moment.
txtAddress.MinWidth = SizeChars(16)
lstFiles.MaxHeight = SizePercent(50)
Compiling and Debugging Programs
With compiler.bas in the project, a program compiles BASIC programs -- what a development tool written in BASIC needs. A source set holds the files of one program: CompSourceFromProject(src, dbpPath) reads a whole project (.dbp) from disk, or CompSourceAdd(src, path, text, kind) adds files one at a time, in the order a project compiles: modules, then class modules, then UserControls, then forms (COMP_KIND_AUTO tells them apart by the file name; a form's whole .frm text is added, and the code after its layout is compiled; a UserControl's whole .ctl text, its header and its code). CompCompile(src, optionExplicit, validate) gives a compiled module, or 0 with CompError$() saying what is wrong and CompErrorFile$() / CompErrorFileLine() where. With validate True, Label1.Captoin and other misspelled control members, and controls of an unknown type, are found while compiling instead of when the line runs (the set's own UserControls are known types).
Line numbers of compiled programs (errors, breakpoints, the line a program is paused on) count through all the files of the source set. CompMapFile$(src, line) and CompMapLine(src, line) give the file and the line in it; CompConcatLine(src, path, fileLine) goes the other way. CompSourceFiles$ lists the files in compile order. CompBuildApp(module, src, outPath, release) builds an .app the way the IDE's Make Executable does, with the project's name, icon, and help file, its forms, and its UserControls. For a control project (its Type setting is "Control") it builds a control file instead (outPath NAME.wgt), the way the IDE's Make Control does: the project's UserControls with their toolbox pictures and names, there for every program once the file is copied into DVX's WIDGETS folder (DVX loads it when it starts). CompProcList$(text) lists the SUB and FUNCTION blocks of source text with their first and last lines (also for code that does not compile yet), CompIsIdent checks a name and CompKeyword$ lists the language's keywords, CompKeywordClass the kind of each (COMP_KW_OTHER, COMP_KW_TYPE, or COMP_KW_LITERAL), and CompKeywordReserved whether a program may also use it as a name of its own.
Project files: ProjOpen(dbpPath) opens a project file (.dbp) and ProjNew(name, folder) starts a new one; ProjSave writes it and ProjClose frees it. ProjFileCount, ProjFile$ (the name as the project lists it), ProjFilePath$ (the full path), ProjFileKind (COMP_KIND_MODULE, COMP_KIND_FORM, COMP_KIND_CLASS, or COMP_KIND_CONTROL), and ProjFileFind list its files; ProjAddFile, ProjRemoveFile, and ProjRenameFile (on disk and in the project together, saved at once) change the list. ProjGet$ and ProjSet read and set its settings by their names in the file: Name, Type ("Control" for a control project, empty for a program), Author, Publisher, Version, Copyright, Description, Icon, HelpFile, CommandArgs, StartupForm, OptionExplicit, and MultiInstance (the last two "true" or "false"). This is the project model the DVX BASIC IDE uses.
A UserControl's header (the Begin UserControl block at the top of its .ctl file) is read and changed the way the IDE's Properties window does it, the rest of the text left as it is: ProjCtlKeys$() lists the keys in the window's order with the kind of value each takes (Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, DefaultEvent; the kinds Name, Integer, Boolean, Color, and Text), ProjCtlHeader$(text, key) reads one (its default when the header leaves it out), ProjCtlSetHeader$(text, key, value) gives the text with one set (an empty value removes it; Name renames the control), and ProjCtlNew$(name) the text of a new UserControl, as File > New UserControl makes it.
With debug.bas too, a program runs a compiled module as a child program inside itself: RunCreate(module, src, appDir, args) prepares it, RunStart(run, startupForm, breakOnStart) loads its forms, and RunPoll(run, ms), called from a Timer, runs it for at most ms milliseconds at a time and returns what it is doing: RUN_RUNNING, RUN_EVENTS (waiting for events on its forms), RUN_PAUSED, RUN_ENDED, or RUN_ERROR (RunError$ and RunErrorLine tell why and where). RunOutput$ collects what it PRINTs; INPUT asks with an input box. RunStop ends it and RunFree forgets it.
DbgSetBreakpoints run, "12, 40" sets breakpoints (line numbers of the source set). While the program is paused, DbgLine and DbgProc$ say where, DbgLocals$ and DbgCallStack$ give the variables and the calls as rows for a ListView's SetRows, DbgEval$(run, expr) shows a variable or expression as the IDE's Watch window does, DbgExec$(run, line) runs an Immediate-window line (x = 5 changes a variable, anything else is printed), and DbgSetVar changes a variable; DbgStepInto, DbgStepOver, DbgStepOut, DbgRunToLine, and DbgContinue go on, DbgPause stops it at the next line. The program's form captions show [break] while it is paused.
A program paused in its module-level code waits until you step or continue it. One paused inside one of its event handlers -- or in its forms' Load code, which runs inside RunStart -- waits right there while your Timer and menus keep working; RunPoll called meanwhile returns at once, and RunStart returns only after such a pause.
' compiler.bas and debug.bas in the project; tmrRun is a Timer
Dim Shared src As Long, mdl As Long, kid As Long
Sub StartDebugging
src = CompSourceCreate()
If Not CompSourceFromProject(src, "C:\PROJ\HELLO.DBP") Then MsgBox CompError$(): Exit Sub
mdl = CompCompile(src, True, True)
If mdl = 0 Then MsgBox CompErrorFile$() & " line" & Str$(CompErrorFileLine()) & ": " & CompError$(): Exit Sub
kid = RunCreate(mdl, src, "", "")
DbgSetBreakpoints kid, Str$(CompConcatLine(src, "C:\PROJ\MAIN.BAS", 12))
If RunStart(kid, "", False) Then tmrRun.Enabled = True
End Sub
Sub tmrRun_Timer
Select Case RunPoll(kid, 40)
Case RUN_PAUSED
lstLocals.SetRows DbgLocals$(kid)
lblWhere.Caption = CompMapFile$(src, DbgLine(kid)) & ":" & Str$(CompMapLine(src, DbgLine(kid)))
Case RUN_ENDED, RUN_ERROR
tmrRun.Enabled = False
txtOutput.Text = txtOutput.Text & RunOutput$(kid) & RunError$(kid)
End Select
End Sub
Form Designers and Code Editors
A form designer or a code editor written in BASIC uses the same pieces as the DVX BASIC IDE. designer.bas describes the control types, which CtlTypeName$(0 to CtlTypeCount - 1) gives in alphabetical order: the toolbox lists the types CtlTypeFlags marks CTL_FLAG_DESIGNABLE (with CtlTypeTooltip$ and the icon CtlTypeIconFile writes), new controls are named CtlTypeNamePrefix$ plus a number, and the properties window lists CtlPropName$ for 0 to CtlPropCount - 1 with CtlPropType (CTL_PROP_STRING ... CTL_PROP_ENUM, plus CTL_PROP_READONLY) and CtlPropEnum$ for an enumerated property's choices. "Form" and "Menu" stand for the form itself and a menu item. The event list is CtlEventList$, and CtlEventParams$ writes an event procedure's parameter list; CtlTypeEventParams$(typeName, eventName) does it for one type, whose events may have parameters of their own (a UserControl's events), and "UserControl" stands for the object in a UserControl's own code (UserControl_MouseDown has Button, Shift, X, and Y). For a project's files, FrmFormName$ and FrmControlNames$ read a .frm file's form and control names (the Object list), FrmCode$ its code, and ClsClassName$ a class module's name.
The form being designed is a real form: CreateControl and RemoveControl add and remove controls, CallByName reads and sets a property by its name, Me.Controls lists the controls, Container (Set ctl.Container = Frame1) and MoveBefore move them, and LoadFormText shows a .frm file. Its DesignMode (see the Form control) stops the controls from working and reports the mouse and keys in DesignMouse and DesignKey, with the selection handles drawn for DesignSelection.
A project's own UserControls go on the toolbox too, as in the IDE. Compile the project -- its forms may be left out -- and give the module and the source set to CompDesignControls(module, src, appDir) from compiler.bas: the UserControls are then control types of this program alone, listed by CtlTypeName$ with their Description as the tooltip and their ToolboxIcon as the icon, with their properties, methods, and events, and known to CreateControl and LoadFormText. On the forms they run their code -- not the project's module-level code -- with UserControl.UserMode False on a form in DesignMode; App.Path is appDir, or the project's folder for "". When a control's code calls UserControl.PropertyChanged, the form raises DesignPropertyChanged (CtrlName, PropertyName), so the properties window can read them again. Call CompDesignControls again after a UserControl changed (the ones before are let go first) and CompDesignRelease when the project closes; the end of the program lets them go too. The module stays in use while they are there, even after CompModuleFree, and must be one this program compiled.
' compiler.bas and designer.bas in the project; Canvas is the form
' being designed
Sub LoadProjectControls(dbpPath As String)
Dim src As Long
Dim mdl As Long
Dim made As Long
src = CompSourceCreate()
If CompSourceFromProject(src, dbpPath) Then mdl = CompCompile(src, True, False)
If mdl = 0 Then
MsgBox CompError$()
Else
made = CompDesignControls(mdl, src, "")
If CompError$() <> "" Then MsgBox CompError$()
CompModuleFree mdl
End If
CompSourceFree src
End Sub
Sub Canvas_DesignPropertyChanged(CtrlName As String, PropertyName As String)
If CtrlName = Canvas.DesignSelection Then RefreshPropertyList
End Sub
A .frm file can also be worked on as data, the way the DVX BASIC IDE does it, without showing it. FrmOpen(text$), FrmOpenFile(path$), or FrmNew(name$) gives a handle to a form model (0 when the text is not a form); FrmText$ writes it back as .frm text in the form the IDE saves and FrmSave writes it to a file; FrmFree frees it (a program's open handles are freed when it ends). FrmGet$ and FrmSet read and change the form's properties (Caption, Width, AutoSize, Layout, Icon, Tag ...; Name renames the form), FrmGetCode$ and FrmSetCode its code. The controls are numbered from 0 in file order, each one followed by the controls inside it: FrmCtlCount, FrmCtlFind, FrmCtlName$, FrmCtlType$, FrmCtlIndex (its control array index, -1 when it is not in an array), and FrmCtlParent (the container, -1 for the form) describe them; FrmCtlGet$ and FrmCtlSet read and set any property as text (Name renames the control and its whole control array, the controls inside it follow; Index makes it an array member; an OptionButton set to True clears the others in its container), FrmCtlRemoveProp takes a set one out, and FrmCtlPropCount / FrmCtlPropName$ list the ones that were set. FrmCtlAdd adds a control (its name "" gives the next free one, Command1, Label2 ...) as the last control of a container (-1: the form), FrmCtlRemove removes one with what is inside it, FrmCtlMove puts one before another of the same container, and FrmCtlReparent into another container. FrmCtlText$ gives a control with what is inside it as .frm text, what a designer puts on the Clipboard to copy it, and FrmCtlPaste puts such text into a container: a name that is already taken is replaced by a free one. The menu items are numbered from 0 in menu order with a Level (0 for a menu on the menu bar or a popup menu, 1 for its items, 2 and more for submenus): FrmMenuCount, FrmMenuGet$ / FrmMenuSet (Name, Caption, Shortcut, ShortcutText, Checked, RadioCheck, Enabled, Visible, Level), FrmMenuAdd, FrmMenuRemove (with the items under it), FrmMenuMove (past the previous or next item of its level), and FrmMenuIndent (one level in or out, with the items under it). Only what was set is written: a property a control has by default is left out of the file.
FormText$(formName$) writes a loaded form as it is now as .frm text -- one shown with LoadFormText, made with CreateForm and CreateControl, or one of the program's own: its properties, its controls in their containers with each property that differs from a new control of that type, and its menus (a form loaded this way has no code). A designer that keeps its form live saves it with FormText$, and FrmOpen(FormText$(...)) reads it as a model.
compiler.bas reads source text as the compiler does, for a code editor: CompTokens$(source) lists its tokens (line, column, length, kind, and text, the kind one of Keyword, Identifier, String, Number, Date, Symbol, Comment, and Error); CompInCode(source, position) tells whether a character is code rather than inside a string or a comment; CompRenameInCode$(source, oldName, newName, codeOnly) renames a form, control, or menu item where code refers to it (Command1.Caption, Command1_Click), leaving strings and comments alone with codeOnly; CompWithEvents$ lists the WithEvents variables a file declares and CompClassEvents$ the Events of a class module, for the Object and Event lists; CompHelpTopic$(word) and CompHelpCtrlTopic$(typeName) are the help topics F1 opens (HelpViewTopic on dvxbasic.hlp beside the IDE).
A code editor is a TextArea: SetSyntaxMode "bas" colours BASIC as the IDE does (SetSyntaxColor changes the colours), SetLineMarker marks breakpoints and the current line, GutterClick reports clicks on the line numbers, SelStart / SelLength / SelText and Cut, Copy, Paste, SelectAll, and Undo give the Edit menu, FindNext / ReplaceSelection / ReplaceAll find and replace, and LineCount / GetLine / CursorLine / CursorCol read the text (see TextArea).
' designer.bas in the project: put a copy of the first control of a
' .frm file into a Frame of the same form and save it
Sub CopyIntoFrame(path As String)
Dim h As Long
Dim box As Long
h = FrmOpenFile(path)
If h = 0 Then Exit Sub
box = FrmCtlAdd(h, "Frame", "", -1)
If FrmCtlSet(h, box, "Caption", "Copies") And FrmCtlPaste(h, FrmCtlText$(h, 0), box) >= 0 Then
If Not FrmSave(h, path) Then MsgBox "Could not save " & path
End If
FrmFree h
End Sub
' designer.bas in the project: fill a properties list for a control
Sub ShowProperties(ctl As Object, typeName As String)
Dim i As Integer
Dim v As String
lstProps.Clear
For i = 0 To CtlPropCount(typeName) - 1
If (CtlPropType(typeName, i) And CTL_PROP_TYPEMASK) <> CTL_PROP_OBJECT Then
v = CallByName(ctl, CtlPropName$(typeName, i), vbGet)
lstProps.AddItem CtlPropName$(typeName, i) & " = " & v
End If
Next
End Sub
Common Properties, Events, and Methods
Every control in DVX BASIC inherits a set of common properties, events, and methods. These are handled by the form runtime before dispatching to widget-specific interface descriptors.
Common Properties
Property Type R/W Description
---------- ------- --- -------------------------------------------
Name String R The control's name (e.g. "Command1"). Read-only at runtime.
Left Integer R/W X position in pixels relative to the parent container.
Top Integer R/W Y position in pixels relative to the parent container.
Width Integer R/W Current width in pixels. Setting this changes the minimum width constraint.
Height Integer R/W Current height in pixels. Setting this changes the minimum height constraint.
MinWidth Integer R/W Minimum width for layout. Setting it works like setting Width; reading it returns the minimum that was set.
MinHeight Integer R/W Minimum height for layout. Setting it works like setting Height; reading it returns the minimum that was set.
MaxWidth Integer R/W Maximum width cap (0 = no limit, stretch to fill). It wins over MinWidth and over the width the control's contents ask for.
MaxHeight Integer R/W Maximum height cap (0 = no limit, stretch to fill). It wins over MinHeight and over the height the control's contents ask for.
Weight Integer R/W Layout weight. 0 = fixed size, >0 = share extra space proportionally.
Visible Boolean R/W Whether the control is visible.
Enabled Boolean R/W Whether the control accepts user input. Disabling a container (Frame, VBox, HBox...) also blocks mouse and keyboard input to every control inside it.
BackColor Long R/W Background color as a 24-bit RGB value packed in a Long (use the RGB function to construct).
ForeColor Long R/W Foreground (text) color as a 24-bit RGB value packed in a Long (use the RGB function to construct).
FontName String R/W The family of the font the control draws its text in, such as "DejaVu Sans" ("System" is DVX's own 8 x 16 font). A family DVX does not have gives the System font. See Fonts below.
FontSize Integer R/W The size of the font in pixels. A size the family does not come in becomes the nearest one it has.
FontBold Boolean R/W True for the bold style of the font.
FontItalic Boolean R/W True for the italic style of the font.
FontUnderline Boolean R/W True to underline the text.
FontStrikethru Boolean R/W True to draw a line through the text.
TabIndex Integer R Accepted for VB compatibility but ignored. DVX has no tab order.
ToolTipText String R/W Tooltip text shown after the mouse hovers over the control. Empty string or unset = no tooltip.
HelpTopic String R/W Help topic opened by F1 while the control has the focus. Empty = the form's HelpTopic. F1 uses the project's help file.
DragMode Integer R/W vbManual (0, the default): the program starts a drag with the Drag method. vbAutomatic (1): pressing the left mouse button on the control starts a drag. See Drag and Drop below.
DragIcon Picture R/W Picture shown under the mouse while the control is dragged: a picture file name (BMP, PNG, JPEG, or GIF) or a Picture object from LOADPICTURE (Set works too). Reading it gives a Picture object, which stands for the file name where a value is needed. Empty = a dotted outline the size of the control.
Parent Form R The form the control is on (Text1.Parent.Caption).
Tag String R/W Any text the program wants to keep with the control (DVX does nothing with it), as in Visual Basic. Also in the .frm file.
MousePointer Integer R/W The mouse pointer shown over the control (and the controls inside it that have none of their own): vbDefault (0, the control's own pointer), vbArrow (1), vbCrosshair (2), vbSizeNESW (6), vbSizeNS (7), vbSizeNWSE (8), vbSizeWE (9), vbHourglass (11), or vbNoDrop (12). A form's MousePointer is used where no control has one, and Screen.MousePointer over every form of the program. Change it in MouseMove to show different pointers over parts of a control. Also in the .frm file; another value is an error.
Container Object R/W The container control the control is in (a Frame, VBox, TabPage ...), or its form. Set Text1.Container = Frame1 moves the control into Frame1, after the controls there; Set Text1.Container = Me moves it onto the form itself. The new container must be a container control on the same form, not the control itself or one inside it.
Size properties (Width, Height, and the Min/Max sizes) are limited to 0..32767.
Fonts
The six Font properties are one font. A control that has not been given one draws in the font of the control it is in (a Frame, for example), or else in its form's; the properties read the font the control draws with. Setting one of them gives the control a font of its own with that one part changed, keeping the others: Label1.FontBold = True makes the label's text bold in whatever family and size it had. Controls that edit text in columns (TextBox, TextArea, ComboBox, SpinButton, and Terminal) take only fixed-pitch families, such as "DejaVu Sans Mono", and show the System font in place of any other. The fonts DVX has, and the sizes each comes in, are listed by font.bas; DlgFont in commdlg.bas lets the user choose one. In the form designer the Font row of the Properties window sets all six, and they are saved in the .frm file.
Default Properties
As in Visual Basic, a control's name used where a value is needed means its default property: Text1 = "hi" sets Text1.Text, Print Label1 prints Label1.Caption, and If Check1 Then reads Check1.Value. Where an object is expected -- SET, Is, an object parameter, Collection.Add, TypeOf -- the name still means the control itself.
Control Default property ------- ---------------- TextBox, TextArea, ComboBox, DropDown, ListBox, DBGrid Text Label, Frame, Data Caption CheckBox, OptionButton, CommandButton Value HScrollBar, SpinButton, ProgressBar Value Image, ImageButton, PictureBox Picture Timer Enabled
Other controls, forms, and menu items have no default property: using one as a value raises error 13.
Common Events
These events are wired on every control, whether it is loaded from a .frm file or created at runtime with CreateControl (whose handlers SetEvent names).
Event Parameters Description --------- ------------------------------------------- ------------------------------------------- Click (none) Fires when the control is clicked. DblClick (none) Fires when the control is double-clicked. Change (none) Fires when the control's value or text changes. GotFocus (none) Fires when the control receives keyboard focus. LostFocus (none) Fires when the control loses keyboard focus. KeyPress KeyAscii As Integer Fires when a printable key is pressed. KeyAscii is the ASCII code. KeyDown KeyCode As Integer, Shift As Integer Fires when any key is pressed down, after the control has handled it. KeyCode is the key (vbKeyDelete, vbKeyReturn, ... or a character's code; Ctrl+A to Ctrl+Z are 1 to 26); Shift holds vbShiftMask, vbCtrlMask, and vbAltMask bits (see Key Code Constants in the language reference). KeyUp KeyCode As Integer, Shift As Integer Fires when a key is released. MouseDown Button As Integer, X As Integer, Y As Integer Fires when a mouse button is pressed over the control. MouseUp Button As Integer, X As Integer, Y As Integer Fires when a mouse button is released over the control. MouseMove Button As Integer, X As Integer, Y As Integer Fires when the mouse moves over the control. Scroll Delta As Integer Fires when the control is scrolled (mouse wheel or scrollbar). DragDrop Source As Control, X As Integer, Y As Integer Fires when a dragged control is dropped on this control. Source is the dragged control; X and Y are where, in this control. DragOver Source As Control, X As Integer, Y As Integer, State As Integer Fires while a dragged control moves over this control. State is vbEnter (0) when it comes onto the control, vbOver (2) as it moves across, vbLeave (1) when it goes off.
Common Methods
Method Parameters Description -------- ---------- ------------------------------------------- SetFocus (none) Gives keyboard focus to this control. Refresh (none) Forces the control to repaint. Drag [Action] Starts or ends dragging this control: vbBeginDrag (1, the default) starts, vbEndDrag (2) drops it where the mouse is, 0 cancels the drag without a drop. MoveBefore [Control] Moves the control just before another control (into that control's container when it is in another one), or with none to the end of its own container. The layout places controls in this order, and Controls lists them in it. ScrollToControl Control Of a ScrollPane: scrolls so that a control inside it (the control, or its name as a string) is in view, also one that cannot take the focus (a Label). On another control, or with a control that is not inside it, it is an error.
Drag and Drop
Any control can be dragged onto another control or onto a form, as in Visual Basic. While a control is dragged, a dotted outline of it (or its DragIcon picture) follows the mouse, and the controls and forms it passes over get DragOver events. Releasing the mouse button drops it: the control or form under the mouse gets a DragDrop event, with the dragged control as Source. Pressing Esc cancels the drag.
The mouse pointer shows where a drop can land: over a control or form that has a DragDrop event procedure it is the usual arrow; over one without, and outside the program's forms, it is the "no drop" sign (a circle with a line through it). The outline or DragIcon picture still follows the mouse either way.
With DragMode set to vbAutomatic, pressing the left mouse button on the control starts the drag, and the control gets no Click, MouseDown, or MouseUp from the left button. With DragMode vbManual, start the drag yourself, usually in MouseDown. A drag started while the mouse button is up ends at the next click. During a drag no mouse events other than DragOver and DragDrop are raised.
Drop targets only react: nothing is moved or copied for you. Use Source in DragDrop to decide what to do, for example read its Name or Caption, or check what kind of control it is with TypeOf Source Is TextBox (see Operators).
Note that vbCancel is the MsgBox Cancel value (2), which as a Drag action means vbEndDrag; cancel a drag with Drag 0.
DragIcon takes a file name or a picture loaded with LOADPICTURE: Set Label1.DragIcon = LoadPicture("drag.bmp"). A missing file or one that is not a picture raises its error at LOADPICTURE; a file name that cannot be read when the drag starts shows the outline instead.
' Label1 has DragMode = 1 (vbAutomatic) in the form designer.
Sub List1_DragOver(Source As Control, X As Integer, Y As Integer, State As Integer)
If State = vbEnter Then Label2.Caption = "Drop to add " & Source.Caption
End Sub
Sub List1_DragDrop(Source As Control, X As Integer, Y As Integer)
List1.AddItem Source.Caption
End Sub
' DragMode vbManual: the button starts its own drag, left button only.
Sub Command1_MouseDown(Button As Integer, X As Integer, Y As Integer)
If Button = 1 Then Command1.Drag vbBeginDrag
End Sub
Drawing on Forms and PictureBoxes
Forms and PictureBoxes have Visual Basic's drawing methods. They draw with the same code as the QBasic graphics statements (see Console and Graphics in the language reference), in pixels unless ScaleMode or Scale says otherwise, with colours as Visual Basic RGB values, &HBBGGRR (RGB(255, 0, 0) or &HFF is red). A PictureBox draws on its picture; a form gets a drawing area the size of its window the first time it draws, placed with its controls like a PictureBox (so draw on a form that has no controls, or use a PictureBox beside them).
Drawings stay until they are drawn over or cleared. In a form's own code, write Me.Line, Me.Circle, and so on: Line, Circle, and PSet without an object are the QBasic graphics statements (they draw on the program's screen), and Print without an object is the PRINT statement.
Drawing Methods
Method Description ------ ----------- Cls Clears the drawing to BackColor; CurrentX and CurrentY go to 0. PSet [Step](x, y) [, color] Draws a point (DrawWidth wide). Line [[Step](x1, y1)]-[Step](x2, y2) [, color] [, B | BF] Draws a line, a box (B), or a filled box (BF). A box without F is filled with FillColor when FillStyle is 0. Circle [Step](x, y), radius [, color [, start [, end [, aspect]]]] Draws a circle, ellipse, or arc, filled with FillColor when FillStyle is 0 (full circles). DrawFocusRect x, y, width, height [, color] Draws the dotted rectangle DVX shows around the control that has the keyboard focus (every other pixel of its edge), for things a program draws itself, such as the focused link of a help topic. A negative width or height counts from the other edge. Point(x, y) The colour of a pixel, -1 outside the picture. With fewer than 16 million colours on the screen it is the colour the screen shows: black, white, and full colours such as RGB(255, 0, 0) read back exactly, others may differ slightly. Print items Writes text at CurrentX, CurrentY in ForeColor, in the object's font FontScale times as large, with PRINT's separators (; and ,), SPC, and TAB (counted in the font's character width); ends by moving to the next line unless the last item is followed by ; or ,. PaintPicture picture, x, y [, width, height [, srcX, srcY, srcWidth, srcHeight [, opcode]]] Copies a picture, stretched to width x height: an image file (BMP, PNG, JPEG, or GIF), a picture from LOADPICTURE, or a PictureBox given by its name as a string or by a Control variable. opcode combines each pixel with the one already there (see Raster Operations). Scale [(x1, y1)-(x2, y2)] Gives the drawing coordinates of its own: (x1, y1) is the top-left corner and (x2, y2) the bottom-right one (y2 below y1 makes y go up). Scale alone goes back to pixels. ScaleX(width [, fromScale [, toScale]]) A width in fromScale converted to toScale (see Scales). ScaleY(height [, fromScale [, toScale]]) A height in fromScale converted to toScale. TextWidth(text) Width of the text in the object's font, in the scale's units, at the FontScale size. TextHeight(text) Height of a line of text in the object's font, in the scale's units, at the FontScale size.
Drawing the DVX Look
These methods draw the parts DVX's own controls are made of, in the colours of the current theme, for controls a program draws itself (a PictureBox that acts as a list or a toolbar). Coordinates and lengths are in the scale's units; colours are RGB values.
Method Description
------ -----------
DrawBevel x, y, width, height [, sunken [, face [, border]]] A raised box like a window or button edge, or with sunken True a sunken one like a text box's. face is the fill colour (the theme's face colour when left out; -1 leaves the inside as it is, to frame what is already drawn); border is the edge width in pixels (2).
DrawButtonFace x, y, width, height [, pressed [, face]] A button face, up or with pressed True pushed down.
DrawArrow x, y, size, direction [, color] A solid triangle centred on x, y, size pixel rows tall, pointing dvxArrowUp (0), dvxArrowDown (1), dvxArrowLeft (2), or dvxArrowRight (3), in ForeColor unless a colour is given.
DrawScrollBar x, y, length, position, visible, total [, horizontal [, control]] A scroll bar with its arrows and its thumb, for a list of total rows of which visible are shown from row position, with its top-left corner at x, y. It stands upright (16 pixels wide, length tall, arrows at the top and bottom) unless horizontal is True: then it lies across (length wide, 16 pixels tall, arrows at the left and right ends). With control True it is the narrower bar of a control such as ListBox (14 pixels).
DrawScrollArrow x, y, size, direction One arrow button of a scroll bar, as at the ends of DrawScrollBar's bars: a square button size wide and tall (size in the scale's horizontal units) with its top-left corner at x, y and an arrow pointing dvxArrowUp, dvxArrowDown, dvxArrowLeft, or dvxArrowRight, in the theme's scroll bar colours.
ScrollBarClick(length, offset, position, visible, total [, horizontal [, control]]) The new position after a click on a scroll bar DrawScrollBar drew with the same length, rows, direction, and kind: offset is how far the click is from the top of the bar, or from its left end when horizontal is True. The arrows move one row, the trough a page, the thumb leaves it as it is. Needs no drawing.
ScrollBarsNeeded(contentWidth, contentHeight, areaWidth, areaHeight) Which scroll bars content that big needs in an area that big, as DVX's controls decide it: vbSBNone (0), vbHorizontal (1), vbVertical (2), or vbBoth (3). A vertical bar narrows the area and may make a horizontal one needed, and the other way round. The sizes are in the object's scale. Needs no drawing.
PrintAccel text [, enabled] Prints a caption in the object's font at CurrentX, CurrentY with the letter after & underlined (&& prints one &); enabled False gives the greyed, embossed look of a disabled control. CurrentX moves past the text.
TextWidthAccel(text) Width of a caption without its & signs in the object's font, in the scale's units.
AccelKey(text) The access key of a caption in lower case ("f" for "&File"), "" when it has none.
Sub Picture1_Paint()
Picture1.DrawBevel 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, True, vbWhite
Picture1.DrawButtonFace 4, 4, 60, 20, pressed
Picture1.CurrentX = 10: Picture1.CurrentY = 8
Picture1.PrintAccel "&Go", Picture1.Enabled
Picture1.DrawScrollBar Picture1.ScaleWidth - 18, 2, Picture1.ScaleHeight - 22, topRow, 8, 40
Picture1.DrawScrollBar 2, Picture1.ScaleHeight - 18, Picture1.ScaleWidth - 22, leftCol, 10, 60, True
End Sub
Sub Picture1_MouseDown(Button As Integer, X As Integer, Y As Integer)
' A click on the bar along the bottom scrolls sideways
If Y >= Picture1.ScaleHeight - 18 And X < Picture1.ScaleWidth - 20 Then
leftCol = Picture1.ScrollBarClick(Picture1.ScaleWidth - 22, X - 2, leftCol, 10, 60, True)
Picture1.Refresh
End If
End Sub
Without a colour, drawing uses ForeColor. Step makes a point relative to CurrentX, CurrentY, which every drawing method leaves at its last point. Coordinates, radii, CurrentX and CurrentY, and PaintPicture's rectangles are in the scale's units (a radius in its horizontal units); DrawWidth is always in pixels. A colour or angle out of range stops the program with an error.
Raster Operations
PaintPicture's opcode says how each copied pixel is combined with the pixel already there (the destination) and with the FillColor (the pattern), as in Visual Basic. Leaving it out copies the picture (vbSrcCopy).
Constant Value Result -------- ----- ------ vbSrcCopy &HCC0020 The picture vbSrcPaint &HEE0086 Picture OR destination vbSrcAnd &H8800C6 Picture AND destination vbSrcInvert &H660046 Picture XOR destination vbSrcErase &H440328 Picture AND NOT destination vbNotSrcCopy &H330008 NOT picture vbNotSrcErase &H1100A6 NOT (picture OR destination) vbMergeCopy &HC000CA Picture AND pattern vbMergePaint &HBB0226 NOT picture OR destination vbPatCopy &HF00021 The pattern vbPatPaint &HFB0A09 Pattern OR NOT picture OR destination vbPatInvert &H5A0049 Pattern XOR destination vbDstInvert &H550009 NOT destination vbGrayCopy &H1000000 The picture greyed, as DVX shows a disabled picture (DVX only) vbTransparentCopy &H2000000 Every pixel of the picture but the key colour: a colour given after the opcode, else the colour of the picture's top-left pixel (DVX only)
vbGrayCopy and vbTransparentCopy copy the picture at its own size (width and height are not used): Picture1.PaintPicture "ship.bmp", x, y, 32, 32, 0, 0, 32, 32, vbTransparentCopy, vbMagenta draws a sprite.
Any of the 256 raster operations of Windows can be given by its number; the bits work on the pixel values, so with fewer than 32 bits a pixel the results follow the display's colour format.
Picture1.PaintPicture Picture2, 0, 0, 32, 32, 0, 0, 32, 32, vbSrcInvert ' XOR the picture in
Picture1.PaintPicture Picture2, 0, 0, 32, 32, 0, 0, 32, 32, vbSrcInvert ' and out again
Scales
ScaleMode picks the units: 0 user (set by Scale or the Scale properties), 1 twips (1440 an inch, 15 a pixel), 2 points (72 an inch), 3 pixels, 4 characters (120 twips across, 240 down), 5 inches, 6 millimetres, 7 centimetres. Unlike Visual Basic, whose default is twips, DVX starts every form and PictureBox in pixels (ScaleMode 3), and Scale without arguments goes back to pixels. Setting ScaleLeft, ScaleTop, ScaleWidth, or ScaleHeight makes a user scale from the current one; ScaleWidth and ScaleHeight may not be 0. With a user scale the units stay fixed to the picture's edges when it is resized; other scale modes keep their unit size.
The constants vbUser (0), vbTwips (1), vbPoints (2), vbPixels (3), vbCharacters (4), vbInches (5), vbMillimeters (6), and vbCentimeters (7) name the scale modes. The units assume a standard display of 96 pixels an inch (15 twips a pixel), whatever the screen really is, so an inch is always 96 pixels.
ScaleX and ScaleY convert a width or a height from one scale to another, as in Visual Basic: fromScale is the scale the value is in, vbHimetric (8, hundredths of a millimetre, the unit of a Picture's Width and Height) when it is left out; toScale the scale wanted, the object's ScaleMode when left out. vbUser means the object's current scale. Visual Basic's vbContainerPosition and vbContainerSize are not available; any other scale number stops the program with an error.
Picture1.Scale (0, 100)-(100, 0) ' 0..100 across, y going up
Picture1.Line (0, 0)-(100, 100), RGB(255, 0, 0)
Picture1.ScaleMode = 7 ' centimetres
Picture1.Circle (2, 2), 1
Dim pic As Object
Set pic = LoadPicture("logo.bmp")
w = Me.ScaleX(pic.Width, vbHimetric, vbPixels) ' the picture's width in pixels
AutoRedraw and the Paint Event
A form or PictureBox fires its Paint event when it is shown, when the form is resized, when a PictureBox changes size (its Width or Height set to a new value, or its Resize method) while its form is showing, and when the program calls its Refresh method, so a program can draw everything in Paint as in Visual Basic. Unlike Visual Basic, DVX keeps what was drawn even with AutoRedraw False (the default), so drawing outside Paint also stays on the screen. With AutoRedraw = True, Paint does not fire. A program that sets AutoRedraw = False itself gets Visual Basic's behaviour exactly: the drawing is cleared to BackColor before each Paint.
Sub Picture1_Paint ()
Picture1.Circle (50, 50), 40, RGB(0, 128, 0)
End Sub
Drawing Properties
Property Type Default Description -------- ---- ------- ----------- ForeColor Long &H000000 Colour of drawing and printing. BackColor Long &HFFFFFF Colour Cls clears to. Setting it clears the drawing to the new colour. CurrentX Single 0 Where the next drawing or printing starts. CurrentY Single 0 DrawWidth Integer 1 Pen width in pixels. FillStyle Integer 1 How boxes (B) and circles are filled with FillColor: 0 solid, 1 (the default) not at all, 2 horizontal lines, 3 vertical lines, 4 upward diagonals, 5 downward diagonals, 6 a cross hatch, 7 a diagonal cross hatch (the lines 8 pixels apart, the gaps left as they are). Other values are an error. BF fills solid in the line's colour. FillColor Long &H000000 Colour boxes and circles are filled with. FontScale Integer 1 How many times as large Print writes its text (1 to 8): each dot of the font becomes a FontScale x FontScale block, for titles and banners. TextWidth and TextHeight follow it; PrintAccel does not. AutoRedraw Boolean False True: no Paint events (see above). ScaleMode Integer 3 Units of the coordinates (see Scales). ScaleLeft Single 0 Coordinate of the left edge. ScaleTop Single 0 Coordinate of the top edge. ScaleWidth Single (size) Width of the drawing in the scale's units. ScaleHeight Single (size) Height of the drawing in the scale's units.
Example
Sub Command1_Click ()
Picture1.Resize 200, 150
Picture1.BackColor = RGB(255, 255, 255)
Picture1.DrawWidth = 2
Picture1.Line (10, 10)-(190, 140), RGB(0, 0, 255), B
Picture1.FillStyle = 0
Picture1.FillColor = RGB(255, 255, 0)
Picture1.Circle (100, 75), 40, RGB(255, 0, 0)
Picture1.CurrentX = 70
Picture1.CurrentY = 120
Picture1.Print "Hello!"
End Sub
Data Binding
DVX BASIC provides VB3-style data binding through three properties that can be set on most controls:
Property Set On Description ---------- ----------- ------------------------------------------- DataSource Any control Name of the Data control to bind to (e.g. "Data1"). DataField Any control Column name from the Data control's recordset to display.
How It Works
- Place a Data control on the form and set its DatabaseName and RecordSource properties.
- Place one or more display/edit controls (TextBox, Label, etc.) and set their DataSource to the Data control's name and DataField to a column name.
- When the form loads, the Data control auto-refreshes: it opens the database, runs the query, and navigates to the first record.
- Bound controls are updated automatically each time the Data control repositions (the Reposition event fires, and the runtime pushes the current record's field values into all bound controls).
- When a bound control loses focus (LostFocus), its current text is written back to the Data control's record cache, and Update is called automatically to persist changes.
Master-Detail Binding
For hierarchical data (e.g. orders and order items), use two Data controls:
- A master Data control bound to the parent table.
- A detail Data control with its MasterSource set to the master's name, MasterField set to the key column in the master, and DetailField set to the foreign key column in the detail table.
When the master record changes, the detail Data control automatically re-queries using the master's current value for filtering. All controls bound to the detail are refreshed.
DBGrid Binding
Set the DBGrid's DataSource to a Data control name. The grid auto-populates columns from the query results and refreshes whenever the Data control refreshes.
Example
VERSION DVX 1.00
Begin Form frmData
Caption = "Data Binding Example"
AutoSize = False
Width = 400
Height = 280
Begin Data Data1
DatabaseName = "myapp.db"
RecordSource = "customers"
End
Begin Label lblName
Caption = "Name:"
End
Begin TextBox txtName
DataSource = "Data1"
DataField = "name"
End
Begin Label lblEmail
Caption = "Email:"
End
Begin TextBox txtEmail
DataSource = "Data1"
DataField = "email"
End
End
Sub Data1_Reposition ()
Print "Current record changed"
End Sub
Sub Data1_Validate (Cancel As Integer)
If txtName.Text = "" Then
MsgBox "Name cannot be empty!"
Cancel = 1
End If
End Sub
Control Arrays
DVX BASIC supports VB-style control arrays. Multiple controls can share the same name, differentiated by an Index property. When an event fires on a control array element, the element's index is passed as the first parameter.
Defining Control Arrays in FRM
Begin CommandButton Command1
Caption = "Button A"
Index = 0
End
Begin CommandButton Command1
Caption = "Button B"
Index = 1
End
Begin CommandButton Command1
Caption = "Button C"
Index = 2
End
Event Handler Convention
When a control has an Index property (>= 0), the event handler receives Index As Integer as the first parameter, before any event-specific parameters.
Sub Command1_Click (Index As Integer)
Select Case Index
Case 0
MsgBox "Button A clicked"
Case 1
MsgBox "Button B clicked"
Case 2
MsgBox "Button C clicked"
End Select
End Sub
Accessing Array Elements in Code
Use the indexed form ControlName(Index) to access a specific element:
Command1(0).Caption = "New Text"
Command1(1).Enabled = False
Note: Control array elements share the same event handler Sub. The runtime prepends the Index argument automatically. If you define parameters on the Sub, Index comes first, followed by the event's own parameters (e.g. KeyPress would be Sub Ctrl1_KeyPress (Index As Integer, KeyAscii As Integer)).
FRM File Format
The .frm file is a text file that describes a form's layout, controls, menus, and code. It follows a format compatible with VB3 .frm files, with DVX-specific extensions.
Structure
VERSION DVX 1.00
Begin Form FormName
form-level properties...
Begin Menu mnuFile
Caption = "&File"
Begin Menu mnuOpen
Caption = "&Open"
End
End
Begin TypeName ControlName
property = value
...
End
Begin Frame Frame1
Caption = "Group"
Begin TypeName ChildName
...
End
End
End
BASIC code follows...
Sub FormName_Load ()
...
End Sub
Rules
- The VERSION line is optional. VERSION DVX 1.00 marks a native DVX form. VB forms with version <= 2.0 are accepted for import.
- The form block begins with Begin Form Name and ends with End.
- Controls are nested with Begin TypeName Name / End.
- Container controls (Frame, VBox, HBox, Toolbar, TabStrip, ScrollPane, Splitter, WrapBox) can have child controls nested inside them.
- Properties are assigned as Key = Value. String values are optionally quoted.
- A Key = Value line belongs to the innermost block that is still open, also when it comes after the blocks nested inside it. DVX writes a block's properties before the blocks inside it.
- Everything after the form's closing End is BASIC source code.
- Comments in the form section use ' (single quote).
- Blank lines are ignored in the form section.
Common FRM Properties
Property Applies To Description ----------------------- --------------- ------------------------------------------- Caption Form, controls Display text or window title. Text TextBox, ComboBox Initial text content. MinWidth / Width Controls Minimum width. Both names are accepted. MinHeight / Height Controls Minimum height. Both names are accepted. MaxWidth Controls Maximum width (0 = no cap). MaxHeight Controls Maximum height (0 = no cap). Weight Controls Layout weight for flexible sizing. Left Form, controls X position (used by Form when Centered=False; informational for controls). Top Form, controls Y position. Index Controls Control array index (-1 or absent = not in array). Visible Controls Initial visibility. Enabled Controls Initial enabled state. Layout Form "VBox" or "HBox". AutoSize Form Auto-fit window to content. Resizable Form Allow runtime resizing. Centered Form Center window on screen. DatabaseName Data SQLite database file path. RecordSource Data Table name or SQL query. DataSource Bound controls Name of the Data control. DataField Bound controls Column name in the recordset.
Form
VB Equivalent: Form -- DVX Widget: Window + VBox/HBox root
The Form is the top-level container representing a DVX window. It is declared in the .frm file with Begin Form FormName. All controls are children of the form's content box, which uses either VBox (default) or HBox layout.
Form Properties
Property Type Default Description
---------- ------- -------------- -------------------------------------------
Name String "Form1" The form's name, used for event dispatch and Load statement.
Caption String (same as Name) Window title bar text.
Width Integer 400 Window width in pixels. In the .frm file it is ignored when AutoSize is True.
Height Integer 300 Window height in pixels. In the .frm file it is ignored when AutoSize is True.
Left Integer 0 Initial X position. Used when Centered is False.
Top Integer 0 Initial Y position. Used when Centered is False.
Layout String "VBox" Content box layout: "VBox" (vertical) or "HBox" (horizontal).
AutoSize Boolean False When True, the window shrink-wraps to fit its content.
Resizable Boolean True Whether the user can resize the window at runtime.
Centered Boolean True When True, the window is centered on screen. When False, Left/Top are used.
Visible Boolean False True shows the form and False hides it, exactly like Show and Hide (the first show fires Activate).
ContextMenu String "" Name of a popup menu shown when the form is right-clicked.
HelpTopic String "" Help topic opened by F1 while the form is active and the focused control has no HelpTopic of its own. Empty opens the project's help file at its default page.
Padding Integer 4 Space in pixels between the window's edges and the controls (the content box's Padding); 0 is none.
Spacing Integer 4 Space in pixels between one control and the next (the content box's Spacing); 0 is none.
WindowState Integer 0 vbNormal (0), vbMinimized (1, shown only as an icon at the bottom of the screen), or vbMaximized (2, filling the screen). Set on a hidden form (or in the .frm file), it takes effect when the form is shown; reading it then gives the state the form will have.
Controls Collection (none) The form's controls (read-only), in the order they were made: Me.Controls.Count, Me.Controls(1) (the first), Me.Controls("Text1"), For Each c In Me.Controls. Elements of a control array are in it too but have no name key. Each use makes a new Collection; changing it does not change the form.
DesignMode Boolean False True puts the form in design mode, for a form designer: see Designing Forms below. Not in .frm files.
Icon Picture (DVX's) The picture shown for the window while it is minimized: a picture file name (BMP, PNG, JPEG, or GIF) or a picture from LOADPICTURE (Set works too); "" puts DVX's back. Reading it gives a Picture object. Also in the .frm file. A file that is not a picture is an error.
Tag String "" Any text the program wants to keep with the form, as for controls.
MousePointer Integer 0 The mouse pointer over the form where its controls have none (see MousePointer under Common Properties).
FontName String "System" The form's font: the font of every control on it that has none of its own, and of the form's Print and TextWidth. FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru go with it, as for controls (see Fonts under Common Properties). Also in the .frm file.
ActiveControl Control (none) The control of the form that has the keyboard focus (read-only), Nothing when none of them has it: Me.ActiveControl.SelText for an Edit menu's Copy. Screen.ActiveControl is the focused control of the whole program. Not in .frm files.
DesignSelection String "" The name of the control (Text1, or Text1(2) in a control array) whose selection handles are drawn while the form is in design mode; "" for none. Not in .frm files.
Maximizing a window, or restoring a maximized one, changes its size, so Resize fires. Minimizing does not change the size and fires no Resize. A minimized window keeps its maximized state: set WindowState = vbMaximized to bring it back filling the screen, vbNormal to bring it back at its normal size.
frmTerminal.Show
frmTerminal.WindowState = vbMinimized ' starts as an icon
Form Events
Event Parameters Description ----------- --------------------- ------------------------------------------- Load (none) Fires after the form and all controls are created. This is the default event. Unload (none) Fires when the form is being closed or unloaded. QueryUnload Cancel As Integer Fires before Unload. Set Cancel = 1 to abort the close. Resize (none) Fires when the window changes size: resized by the user, maximized, or restored (by the user or WindowState). Activate (none) Fires when the window gains focus. Deactivate (none) Fires when the window loses focus. Paint (none) Fires when the form is shown or resized and on Refresh, unless AutoRedraw is True (see Drawing on Forms and PictureBoxes). DragDrop Source As Control, X As Integer, Y As Integer Fires when a dragged control is dropped on an empty part of the form. X and Y are where, in the form. DragOver Source As Control, X As Integer, Y As Integer, State As Integer Fires while a dragged control moves over an empty part of the form. State is vbEnter, vbLeave, or vbOver. NewArgs (none) Fires when the running program is handed new arguments: COMMAND$ has changed (see New Arguments below). DesignMouse Action As Integer, Button As Integer, X As Integer, Y As Integer, CtrlName As String, Handle As Integer In design mode: the mouse was pressed, moved, released, or double-clicked in the form. See Designing Forms below. DesignKey KeyCode As Integer, Shift As Integer In design mode: a key was pressed in the form (KeyCode and Shift as in KeyDown). DesignPropertyChanged CtrlName As String, PropertyName As String In design mode: the code of a UserControl on the form called PropertyChanged. See Designing Forms below.
See Drag and Drop under Common Properties, Events, and Methods. While a control is dragged over a form with no DragDrop event procedure, the mouse pointer shows the "no drop" sign.
New Arguments
A program with a NewArgs event procedure on any of its forms can be handed new arguments while it runs, instead of DVX starting another copy of it. COMMAND$ (and COMMAND$(1), COMMAND$(2), ...) then give the new arguments, and NewArgs fires on every loaded form that has the procedure. When the arguments arrive while one of the program's event procedures is still running -- for example while it shows a message box or a dialog, or a form with vbModal -- they are held, and NewArgs fires once that procedure has finished; if several arrive meanwhile, only the last is given.
DVX hands new arguments to the help viewer: F1 and the Help menus of programs (and HelpView in BASIC) send "helpfile topic" to the program installed as APPS\KPUNCH\DVXHELP\DVXHELP.APP when it is already running. A help viewer written in BASIC and installed there gets them in NewArgs. Starting a program that is already running -- from Program Manager, the Run dialog, a document opened in it, or ShellRun -- gives it the new arguments when it allows only one copy and has a NewArgs procedure: its window comes to the front and NewArgs fires. Without a NewArgs procedure DVX says it is already running; a program that allows more than one copy (Project Properties) starts another copy instead.
Sub Form1_NewArgs ()
' F1 in another program while this help viewer is open
If COMMAND$(1) <> openFile Then OpenHelpFile COMMAND$(1)
GoTopic HlpStartTopic(hlp, COMMAND$(2))
End Sub
Form Methods
Statement Description ------------------ ------------------------------------------- Load FormName Load the form (creates the window and controls, fires Load event). Unload FormName Unload the form (fires Unload, destroys window). FormName.Show Make the form visible. FormName.Show 1 Show as a modal dialog (vbModal): the statement returns when the form is hidden or unloaded, so the code after it can read what the user chose. FormName.Hide Hide the form without unloading it. FormName.FitWidth Make the window just wide enough for its controls, keeping its height (AutoSize fits both). FormName.FitHeight Make the window just tall enough for its controls, keeping its width. FormName.SaveScreenshot file$ Save a picture of what the window shows inside its frame to a file: BMP when the name ends in .bmp, PNG otherwise. An error when the form has no window or the file cannot be written.
Several Copies of a Form
A form can be open more than once, the way a program opens a second document window. New FormName makes another copy of the form: its own window, controls, menus, and form-level variables, built from the same .frm. Its Load event runs, and it starts hidden like any loaded form, so call Show. Keep the copy in a variable declared As the form's name (or As Form, As Object):
Dim f As Form1 ' a Form1 copy, or the form itself
Set f = New Form1 ' a new copy
f.Caption = "Second window"
f.Text1.Text = "Hello" ' this copy's Text1
f.Show
Dim g As New Form1 ' declares and makes a copy at once
The code of the form serves every copy. In it, Me, the controls named on their own (Text1), and the form-level variables are those of the copy whose event is running, so each window keeps its own state. Unload f closes that copy only.
The form's name (Form1.Caption, Form1.Show, Load Form1) always means the first copy loaded -- the one the program starts with -- as in Visual Basic. If that copy was unloaded, the name means the next one still open.
Designing Forms
A form designer written in BASIC shows the form being designed as a real form and turns on its design mode. While DesignMode is True, the controls on the form take no mouse clicks or keys and fire no events (Timers too are quiet); the form instead tells the designer what the user does:
- DesignMouse (Action, Button, X, Y, CtrlName, Handle): Action is DSGN_MOUSE_DOWN (1, a button was pressed), DSGN_MOUSE_MOVE (2), DSGN_MOUSE_UP (3), or DSGN_MOUSE_DBLCLICK (4, a double click: open the code, say). Button is the button pressed or released (1 left, 2 right, 4 middle; for a move, the buttons held). X and Y are where, in the form. CtrlName is the control there -- the innermost one when containers hold others -- or "" for the form itself. Handle is the selection handle of the selected control under the mouse: DSGN_HANDLE_E (3, the right edge: drag to change the width), DSGN_HANDLE_SE (4, the corner), or DSGN_HANDLE_S (5, the bottom edge), or DSGN_HANDLE_NONE (-1).
- DesignKey (KeyCode, Shift): a key, for Delete, the arrow keys, and so on.
- DesignPropertyChanged (CtrlName, PropertyName): the code of a UserControl on the form -- one of the project's own, put on the designer's toolbox with CompDesignControls (compiler.bas) -- called UserControl.PropertyChanged PropertyName: read its properties again. Such controls run their code on the form with UserControl.UserMode False.
DesignSelection names the selected control; the form draws its eight selection handles over the controls (the three that resize in black) and shows the resize mouse pointers over them. The constants are in designer.bas, which also lists the control types, their properties, and events (see Include Libraries in the language reference). Together with CreateControl, RemoveControl, CallByName, Controls, Container, MoveBefore, LoadFormText, and the .frm model of designer.bas (FrmOpen, FrmText$, FrmCtlText$ / FrmCtlPaste to copy and paste controls, FormText$ to write the live form as .frm text), a designer has what the DVX BASIC IDE's own designer uses.
Sub Canvas_DesignMouse(Action As Integer, Button As Integer, X As Integer, Y As Integer, CtrlName As String, Handle As Integer)
If Action = DSGN_MOUSE_DOWN Then
If Handle <> DSGN_HANDLE_NONE Then
resizing = True ' drag the selected control's size
Else
Canvas.DesignSelection = CtrlName
ShowProperties CtrlName
End If
ElseIf Action = DSGN_MOUSE_UP Then
resizing = False
End If
End Sub
Drawing
Forms have Visual Basic's drawing methods and properties (Cls, PSet, Line, Circle, Point, Print, PaintPicture, DrawFocusRect, DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, ScrollBarClick, ScrollBarsNeeded, PrintAccel, Scale, CurrentX, CurrentY, ForeColor, BackColor, DrawWidth, FillColor, FillStyle, FontScale, AutoRedraw, ScaleMode, ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight): see Drawing on Forms and PictureBoxes. In the form's own code use Me.Line, Me.Circle, and so on.
Example
Sub Form1_Load ()
Form1.Caption = "Hello World"
Print "Form loaded!"
End Sub
Sub Form1_QueryUnload (Cancel As Integer)
If MsgBox("Really quit?", 4) <> 6 Then
Cancel = 1
End If
End Sub
Sub Form1_Resize ()
Print "Window resized"
End Sub
Common Properties, Events, and Methods
UserControls: Your Own Controls
A UserControl is a control you write in DVX BASIC. Once written, it is used exactly like the controls DVX comes with: you put it on a form in the designer (or in a .frm file with Begin), set its properties, call its methods, and write handlers for its events. Programs written in C can create and use it too.
A UserControl lives in a .ctl file. In the IDE, File > New UserControl makes one (see UserControls in the IDE). The file starts with a header that describes the control, then holds the control's code. The code is a class: every control on a form is one object of the class, with its own copies of the class's variables.
VERSION DVX 1.00
Begin UserControl Gauge
Width = 120 ' natural (smallest) size in pixels
Height = 30
CanGetFocus = True
BackColor = &H00FFFFFF&
ToolboxIcon = "gauge.bmp" ' 24 x 24 picture for the toolbox
Description = "A gauge" ' the toolbox tooltip
DefaultEvent = "Changed"
End
Option Explicit
Public Event Changed(ByVal Level As Integer)
Private mLevel As Integer
Public Property Get Level() As Integer
Level = mLevel
End Property
Public Property Let Level(ByVal Value As Integer)
mLevel = Value
RaiseEvent Changed(mLevel)
UserControl.Refresh
End Property
Private Sub UserControl_Paint()
UserControl.Cls
UserControl.Line (0, 0)-(mLevel * UserControl.Width \ 100, UserControl.Height - 1), RGB(0, 128, 0), BF
End Sub
A form uses it like any control:
Begin Form Form1
Begin Gauge Gauge1
Level = 40
End
End
Sub Gauge1_Changed (ByVal Level As Integer)
lblLevel.Caption = Str$(Level)
End Sub
The Header
The header is a Begin UserControl block: the control's name (the type name forms use) and its settings. Every setting is optional. In the IDE, the Properties window shows and changes them while the .ctl file is the active file.
Setting Meaning ------- ------- Width, Height The natural size in pixels: the smallest size the form's layout gives it (64 x 64 when not given). The code changes it with UserControl.MinWidth and MinHeight. CanGetFocus True (the default): a click or Tab gives it the keyboard focus. False: it never has the focus and gets no keys. BackColor The colour Cls clears it to (white when not given). ToolboxIcon A 24 x 24 picture file in the project folder, shown in the designer's toolbox. Description The toolbox tooltip (its name when not given). DefaultEvent The event the designer opens when the control is double-clicked.
Properties, Methods, and Events
The control's public members are what programs use:
- Public Property Get / Let (or Set) pairs and Public variables are its properties. A property with only Property Get is read-only, with only Property Let write-only. A property whose type is Integer, Long, or Byte is a whole number, Boolean is True / False, Single and Double (and Currency) are numbers, String, Date, and Variant are text; a property of an object type, an array, or a TYPE cannot be a property of a control.
- A property declared As an ENUM is a choice of its members, as a built-in control's Alignment is: the designer's Properties window shows the member's name and a double-click steps to the next member, the form file stores the name (Look = Circle), and code sets it with a member, its number, or its name as text (Shape1.Look = Circle, Shape1.Look = 1, Shape1.Look = "Circle"). This needs the members numbered 0, 1, 2, and so on, each number once, as an ENUM numbers them when no values are given; an ENUM with other values (Small = 10) makes a whole-number property.
- Public Subs and Functions are its methods. They take any arguments; a Function's value is the method's result.
- Public Events are its events. RaiseEvent sends one to the program that has the control: its handler CtrlName_EventName runs with the arguments, and a ByRef parameter the handler changes (a number, a Boolean, or a String) comes back changed.
Names that every control already has (Name, Left, Top, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, Weight, Visible, Enabled, ReadOnly, BackColor, ForeColor, Caption, Text, ToolTipText, ContextMenu, HelpTopic, DataSource, DataField, Tag, MousePointer ...) belong to the form: a control's own property of such a name is not used. Give your properties other names (Title instead of Caption, Value or Level instead of Text).
A property's default is the value it has after UserControl_Initialize ran. The form's .frm sets only the properties the designer user changed, through your Property Let, after Initialize.
The UserControl Object
Inside the control's code, UserControl is the control itself: the surface it draws on and the side the program sees.
Member Description
------ -----------
Width, Height Its current size in pixels (read-only; the form's layout decides it).
MinWidth, MinHeight Its natural size: the form lays it out again when you change them.
Enabled False while the control, or a container it is in, is disabled (read-only).
HasFocus True while it has the keyboard focus (read-only).
CanGetFocus The header's CanGetFocus (read-only).
UserMode False while it sits in a form designer (the IDE's, or a form with Form.DesignMode = True), True in a running program. In the designer the code draws it, but the user's clicks and keys go to the designer.
Name The name the program gave the control ("" when a C program gave none).
SetFocus Gives it the keyboard focus.
Refresh Runs UserControl_Paint now and shows the result.
Invalidate Asks for UserControl_Paint at the next chance, once however often it is called before then: change many things (add a hundred items) and the control is drawn once.
PropertyChanged name Tells a designer that a property changed, so its property sheet shows the new value.
Cls, Line, PSet, Circle, Print, Point, PaintPicture, DrawFocusRect, TextWidth, TextHeight, Scale, ScaleX, ScaleY Drawing, as on a PictureBox.
DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, ScrollBarClick, ScrollBarsNeeded, PrintAccel, TextWidthAccel, AccelKey Drawing in DVX's own look (bevels, button faces, arrows, upright and horizontal scroll bars and their arrow buttons, captions with an underlined access key), as on a PictureBox.
CurrentX, CurrentY, ForeColor, BackColor, FillColor, FillStyle, FontScale, DrawWidth, AutoRedraw, ScaleMode, ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight Drawing properties, as on a PictureBox.
The drawing members work exactly as on a PictureBox (see Drawing on Forms and PictureBoxes). Write them with UserControl in front -- UserControl.Line, UserControl.Print -- as a form's code writes Me.Line: a drawing statement on its own (Line, Cls, Print) draws on the program's screen, not on the control.
The drawing stays until you draw again. UserControl_Paint runs when the control first appears, after its size changed, when you call Refresh, and after you call Invalidate; draw everything there and call UserControl.Invalidate (or UserControl.Refresh, to see the result at once) whenever what it shows changes.
The Control's Events
The program runs these procedures of the control's code when they exist. Each is a Private Sub with exactly these parameters:
Procedure When --------- ---- UserControl_Initialize () The control was made (before the form's property values are set). UserControl_Terminate () The control is going away (its form unloads). It cannot draw any more. UserControl_Resize () Its size changed (and once when it first appears). UserControl_Paint () It must be drawn (after Resize, after Refresh). UserControl_Click () The left button was pressed and let go over it. UserControl_DblClick () A second quick left press. UserControl_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single) A button went down over it. UserControl_MouseMove (Button As Integer, Shift As Integer, X As Single, Y As Single) The mouse moved over it (Button 0), or with the left button held after a press on it, anywhere. UserControl_MouseUp (Button As Integer, Shift As Integer, X As Single, Y As Single) A button went up. UserControl_MouseWheel (Delta As Integer) The wheel turned over it (Delta is positive downwards). UserControl_KeyDown (KeyCode As Integer, Shift As Integer) A key went down while it has the focus. UserControl_KeyPress (KeyAscii As Integer) A character key was typed. UserControl_KeyUp (KeyCode As Integer, Shift As Integer) A key went up. UserControl_GotFocus () It got the keyboard focus. UserControl_LostFocus () It lost the keyboard focus.
Button is vbLeftButton, vbRightButton, or vbMiddleButton (in MouseMove, the buttons held). Shift holds vbShiftMask, vbCtrlMask, and vbAltMask. X and Y are pixels from the control's top-left corner. The compiler reports a handler with other parameters.
These events are the control's own: the program does not see them. The program sees only the events the control raises. To let a program react to clicks, raise your own Click (Public Event Click, then RaiseEvent Click in UserControl_Click); an event named Click, DblClick, Change, KeyPress, KeyDown, KeyUp, MouseDown, MouseUp, MouseMove, or Scroll reaches the program's CtrlName_Click (and so on) handler as for any control. Give those events Visual Basic's parameters: Button, Shift, X, Y for MouseDown, MouseUp, and MouseMove; KeyCode, Shift for KeyDown and KeyUp; KeyAscii for KeyPress; Delta for Scroll. GotFocus and LostFocus reach the program by themselves.
Using Controls in a Program
In the IDE, File > New UserControl (or File > Add File with a .ctl file) adds a control to the open project; while the project is open the control is in the Toolbox, ready to be placed on the project's forms. In the project file, the .ctl file is listed in the [Controls] section of the project's .dbp:
[Controls]
File0 = gauge.ctl
Count = 1
When the program starts, its controls are registered before any form is loaded, so its forms can use them; they go away when the program ends. They belong to the program: other programs (and other copies of the same program) can have controls of the same name, and each program uses its own. The control's code runs in the program, with the program's rules: an error the code does not handle ends the program, and DoEvents works as anywhere.
Installing a Control for Every Program
A control project holds only controls (no forms): its .dbp says
[Project]
Name = GAUGE
Type = Control
[Controls]
File0 = gauge.ctl
Count = 1
Building it -- in the IDE, File > Make Control (set the project's Type to Control in File > Project Properties first); or with the BASIC compiler, bascomp gauge.dbp (on DVX, SYSTEM\BASCOMP.EXE) -- makes gauge.wgt, a control file. Copied into DVX's WIDGETS folder (in a folder of its own, like the controls DVX comes with), it is loaded when DVX starts: every program can use its controls, the designer's toolbox shows them with their ToolboxIcon and Description, and C programs create them by name. A control project may hold several controls and the modules they need; its help file goes beside the control file as for any widget.
All the programs using an installed control file share one copy of its code and its module variables: module-level code of the control project runs once, when the first of its controls is made. A control's own variables (its class members) are separate for each control. Because the code is shared:
- DoEvents does nothing in an installed control's code, and its code never lets another program run in between.
- While its code runs for one program, another program that needs it (to paint or for an event) is turned away: the control keeps its old picture and the event is dropped (DVX.LOG notes it once).
- An error its code does not handle is shown, and the control carries on; it does not end the program that was using it.
Limits
- A control cannot hold other controls; it draws what it shows on its UserControl surface.
- Properties are numbers, Booleans, text, and ENUM choices; a property of an ENUM whose members are not numbered 0, 1, 2 ... is a whole number in the Properties window.
- CanGetFocus is fixed by the header (read-only in code).
- UserControl_Terminate cannot draw: the surface is already gone.
- A program cannot have a control of the same name as one installed in WIDGETS (the program stops with "Control cannot be registered").
See also: UserControls in the IDE
Terminal
DVX Extension -- DVX Widget: ansiterm
A VT100/ANSI terminal emulator with a text-mode cell buffer, 16-color CGA palette, and scrollback history. Processes standard ANSI escape sequences (cursor control, erase, scroll, SGR colors, DEC private modes) typically used by DOS BBS software. Default size is 80 columns by 25 rows. Supports text selection with the mouse, Ctrl+C to copy, and Ctrl+V to send clipboard text as input. Shift+PgUp and Shift+PgDn page through the scrollback history and the mouse wheel scrolls it; any other key is sent to the host and returns the view to the live screen.
Its font must be fixed-pitch, every character the same width; given a proportional font it draws in the system font instead. A fixed-pitch font of any size changes the size of every cell.
Type-Specific Properties
Property Type Description ---------- ------- ------------------------------------------- Cols Integer Number of character columns (1-512). Setting Cols or Rows gives the terminal a new size, starting as a new terminal: empty screen and scrollback, modes the host set reset (its colors and connection stay); set the form's AutoSize to True afterwards to fit the window to it. Rows Integer Number of character rows (1-512). Scrollback Integer Maximum number of scrollback lines. Older lines are dropped (default 500). Setting it discards the existing history.
Type-Specific Methods
Method Description ------ ----------- Clear Clear the terminal screen and reset the cursor to the home position. Copy() Copy the selected text to the clipboard and clear the selection. Returns False when nothing is selected. Paste Send the clipboard's text to the attached connection as if it were typed (new lines as Enter), as Ctrl+V does. Poll Poll the attached communication channel for incoming bytes and feed them into the ANSI parser. ResetColors Back to the standard 16 colors. SetColor index%, color& The color (RGB(), as ForeColor takes it) of text attribute index (0-15, in CGA order: black, blue, green, cyan, red, magenta, brown, light gray, then their bright forms). An index outside 0-15 is ignored. Write text$ Write a string to the terminal with full ANSI escape processing.
No default event. Uses common events only.
Serial Communication
The conn.bas library connects a Terminal the way the Terminal program does -- a serial port, a modem, the secure link, or Telnet -- with capture, answerback, and file transfers (ConnAttach; see Include Libraries). For a packet link of your own, use the comm.bas library:
Dim link As Long
link = CommOpen(1, 115200, 8, "N", 1, SER_HANDSHAKE_NONE)
If link <> 0 Then
If CommHandshake(link, "secret") Then
CommAttach link, "Terminal1", 0, True
Else
MsgBox "Handshake failed"
End If
End If
The passphrase is optional. Both ends must give the same passphrase, or both give none; otherwise CommHandshake returns False. See comm.bas for the full communications API.
Example
Begin Terminal Terminal1
MinWidth = 640
MinHeight = 400
End
Sub Form1_Load ()
Terminal1.Write "Welcome!" & Chr$(13) & Chr$(10)
End Sub
Frame
VB Equivalent: Frame -- DVX Widget: frame (titled container)
A container with a titled border. Child controls are placed inside the frame using VBox layout. In the .frm file, nest Begin/End blocks inside the Frame block.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Caption String The title displayed in the frame border.
Property Type Description -------- ------- ------------------------------------------- Alignment Enum Where the children sit along the frame from top to bottom when they do not fill it: Left (the top, the default), Center, or Right (the bottom). Padding Integer Space in pixels between the border and the children: 4 unless set; 0 is none. Spacing Integer Space in pixels between one child and the next: none unless set.
No type-specific methods or events.
Setting Enabled = False on a Frame disables every control inside it for mouse and keyboard input.
Container: Yes
Default Event: Click
Example
Begin Frame Frame1
Caption = "Options"
Begin CheckBox Check1
Caption = "Option A"
End
Begin CheckBox Check2
Caption = "Option B"
End
End
HBox
DVX Extension -- DVX Widget: hbox (horizontal layout container)
A container that arranges its children horizontally, left to right. No visible border. Use Weight on children to distribute extra space proportionally.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Alignment Enum Where the children sit along the row when they do not fill it: Left (the left, the default), Center, or Right (the right). Padding Integer Space in pixels between the edge of the box and the children: 4 unless set; 0 is none. Spacing Integer Space in pixels between one child and the next: 4 unless set; 0 is none.
Alignment = Right puts a row of buttons at the right, as dialog boxes have them.
No type-specific methods or events.
Container: Yes
Default Event: Click
Example
Begin HBox ButtonRow
Spacing = 4
Begin CommandButton Command1
Caption = "OK"
End
Begin CommandButton Command2
Caption = "Cancel"
End
End
VBox
DVX Extension -- DVX Widget: vbox (vertical layout container)
A container that arranges its children vertically, top to bottom. No title or border. Use Weight on children to distribute extra space proportionally.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Alignment Enum Where the children sit along the column when they do not fill it: Left (the top, the default), Center, or Right (the bottom). Padding Integer Space in pixels between the edge of the box and the children: 4 unless set; 0 is none. Spacing Integer Space in pixels between one child and the next: 4 unless set; 0 is none.
No type-specific methods or events.
Container: Yes
Default Event: Click
Example
Begin VBox Column1
Spacing = 4
Begin Label Label1
Caption = "Name:"
End
Begin TextBox Text1
End
End
CommandButton
VB Equivalent: CommandButton -- DVX Widget: button | Name Prefix: Command
A push button that triggers an action when clicked. The Caption may include a '&' before a character to mark an accelerator key (e.g. "&OK" underlines 'O', and Alt+O activates the button).
Type-Specific Properties
Property Type Description -------- ------ ------------------------------------------- Caption String The text displayed on the button. Use '&' for an accelerator key. Default Boolean True: Enter presses this button, wherever the focus is on its form (unless the focus is on another button, which Enter presses instead). Cancel Boolean True: Escape presses this button, wherever the focus is on its form.
Default and Cancel make a form work like a dialog box: give the OK button Default = True and the Cancel button Cancel = True. If several buttons on a form have the property, the first one (in the order they were made) that is visible and enabled is pressed.
No type-specific methods or events.
Default Event: Click
Example
Begin Form Form1
Caption = "Button Demo"
Begin CommandButton Command1
Caption = "&Click Me!"
End
End
Sub Command1_Click ()
MsgBox "Button was clicked!"
End Sub
PictureBox
VB Equivalent: PictureBox -- DVX Widget: canvas | Name Prefix: Picture
A drawing surface (canvas). Supports drawing lines, rectangles, circles, text, and individual pixels. Can load BMP, PNG, JPEG, and GIF images and save BMP or PNG images. The default canvas size is 64x64 pixels.
Colors are Visual Basic RGB values, &HBBGGRR, as RGB() makes them (RGB(255, 0, 0) or &HFF is red).
Type-Specific Properties
Property Type Description -------- ---- ----------- BorderStyle Enum 0 - None: no frame, the control is exactly the size of the drawing. 1 - FixedSingle (the default): a sunken frame 2 pixels wide around the drawing. Mouse coordinates and drawing positions start inside the frame either way. ImageHeight Integer Height of the drawing in pixels (read-only). After Load it is the loaded image's height. ImageWidth Integer Width of the drawing in pixels (read-only). After Load it is the loaded image's width.
Load and Save return 0 on success and -1 on failure (missing or unreadable file, unsupported format, path that cannot be written). Call them as functions to check the result, or as statements to ignore it. A failed Load leaves the drawing unchanged.
Type-Specific Methods
Method Description ------ ----------- Clear color% Fill the entire canvas with the specified color. DrawLine x0%, y0%, x1%, y1%, color% Draw a line between two points. DrawRect x%, y%, w%, h%, color% Draw a rectangle outline. DrawText x%, y%, text$ Draw text at the given position. FillCircle cx%, cy%, radius%, color% Draw a filled circle. FillRect x%, y%, w%, h%, color% Draw a filled rectangle. GetPixel(x%, y%) Returns the color at a pixel. With fewer than 16 million colors on the screen it is the color the screen shows: black, white, and full colors such as RGB(255, 0, 0) read back exactly, others may differ slightly. Load(path$) Load a BMP, PNG, JPEG, or GIF image; the canvas takes the image's size. Returns 0 on success, -1 on failure. Resize w%, h% Resize the canvas to new dimensions. The drawing is cleared to white. Save(path$) Save the canvas: BMP when path$ ends in .bmp, PNG otherwise. Returns 0 on success, -1 on failure. SetPenColor color% Set the drawing color for subsequent operations. SetPenSize size% Set the pen/brush size in pixels (1 to 255; larger values use 255). SetPixel x%, y%, color% Set the color of a single pixel.
Default Event: Click
Example
Picture1.Resize 200, 150
Picture1.Clear &HFFFFFF
Picture1.SetPenSize 2
Picture1.DrawRect 10, 10, 180, 130, &H000000
Picture1.FillCircle 100, 75, 40, RGB(255, 0, 0)
Picture1.DrawText 60, 130, "Hello!"
If Picture1.Save("output.bmp") <> 0 Then
MsgBox "Could not save output.bmp"
End If
A PictureBox also has Visual Basic's drawing methods and properties (Cls, PSet, Line, Circle, Point, Print, PaintPicture, DrawFocusRect, DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, ScrollBarClick, ScrollBarsNeeded, PrintAccel, Scale, CurrentX, CurrentY, ForeColor, BackColor, DrawWidth, FillColor, FillStyle, FontScale, AutoRedraw, ScaleMode, ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight) and a Paint event: see Drawing on Forms and PictureBoxes.
CheckBox
VB Equivalent: CheckBox -- DVX Widget: checkbox
A toggle control with a label. The checked state is exposed as a Boolean. Supports accelerator keys (put '&' before a character in the Caption). Press Space or Enter to toggle when focused.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Caption String The text displayed next to the checkbox. Value Boolean True if checked, False if unchecked.
No type-specific methods or events.
Default Event: Click
Example
Begin CheckBox Check1
Caption = "Enable feature"
End
Sub Check1_Click ()
If Check1.Value Then
Label1.Caption = "Feature ON"
Else
Label1.Caption = "Feature OFF"
End If
End Sub
ComboBox
VB Equivalent: ComboBox -- DVX Widget: combobox (editable text field + drop-down list, default maxLen 256)
A combination of a single-line text input and a drop-down list. The user can either type a value or pick one from the list. Selecting a list item copies its text into the edit field. Supports the same AddItem / RemoveItem / Clear / List methods as ListBox.
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type Description --------- ------- ------------------------------------------- Text String The text in the editable field. ListIndex Integer Index of the currently selected list item (-1 = user typed something not in the list). MaxLength Integer Most characters the edit field holds (0 = the default, 256). Longer text is cut when it is set.
Type-Specific Methods
Method Description ------ ----------- AddItem text$ Append an item to the drop-down list. Clear Remove all items. List(index%) Return the text of the item at the given index. ListCount() Return the number of items in the list. RemoveItem index% Remove the item at the given index. The selection stays on the same item; removing the selected item leaves no selection (the text is kept).
Default Event: Click
Example
Begin ComboBox Combo1
End
Sub Form1_Load ()
Combo1.AddItem "Small"
Combo1.AddItem "Medium"
Combo1.AddItem "Large"
End Sub
Sub Combo1_Change ()
Label1.Caption = "Selected: " & Combo1.Text
End Sub
Data
VB Equivalent: Data -- DVX Widget: data | Name Prefix: Data
A data access control that connects to a SQLite database and provides record navigation. Other controls bind to a Data control via their DataSource and DataField properties. The Data control cache is populated by calling Refresh. Move operations and Refresh auto-save the current row if dirty; a move is refused while the save fails, and Refresh discards an edit that cannot be saved. Updates find the record by the key value it was loaded with, so the key column itself can be edited; a key stored as a number in a column without a declared type, or a missing (NULL) key, is still found as long as it identifies exactly one record. Writing a field back with the value it already has is not an edit and does not fire Validate. While a Validate handler runs, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, Refresh, and Update on the same Data control are ignored (they would save, and validate, the same record again); Delete is allowed. A RecordSource that is a SELECT query is read-only, and edits made to its records are dropped when the current record changes.
Type-Specific Properties
Property Type Description ------------ ------- ------------------------------------------- DatabaseName String Path to the SQLite database file. RecordSource String Table name or SQL SELECT query for the recordset. KeyColumn String Primary key column name (used for UPDATE/DELETE operations). Caption String Text displayed on the navigator bar. BOF Boolean True if the current position is the first record or there are no records (read-only). EOF Boolean True if the current position is past the last record or there are no records (read-only). MasterSource String Name of a master Data control (for master-detail binding). MasterField String Column in the master recordset to read for the filter value. DetailField String Column in this recordset that matches the master field. RecordCount Long The number of records (read-only). FieldCount Integer The number of columns (read-only). AbsolutePosition Long The current record, 0 for the first (-1 when there are none). Setting it moves there (saving pending changes first, as the Move methods do) and fires Reposition; a value out of range does nothing.
Type-Specific Methods
Method Parameters Description
------------ ---------- -------------------------------------------
MoveFirst (none) Navigate to the first record.
MoveLast (none) Navigate to the last record.
MoveNext (none) Navigate to the next record.
MovePrevious (none) Navigate to the previous record.
AddNew (none) Append a blank record and move to it.
Delete (none) Delete the current record.
Refresh (none) Re-query the database and reload records.
Update (none) Write pending changes to the database.
GetField(col$) The current record's value of a column, as text, without a bound control ("" for an unknown column).
SetField col$, value$ Change a column of the current record; written to the database by Update (or when the record changes).
FieldName(index%) The name of a column, 0 for the first ("" past the last).
GetCell(row&, col%) The text of any cell (both from 0) without moving the current record ("" outside).
Type-Specific Events
Event Parameters Description ---------- ----------------- ------------------------------------------- Reposition (none) Fires after the current record changes. Default event. Validate Cancel As Integer Fires before writing. Set Cancel = 1 to abort.
Default Event: Reposition
Example
Begin Data Data1
DatabaseName = "books.db"
RecordSource = "titles"
KeyColumn = "id"
End
Begin TextBox Text1
DataSource = "Data1"
DataField = "title"
End
Sub Form1_Load ()
Data1.Refresh
End Sub
DBGrid
VB Equivalent: DBGrid -- DVX Widget: dbgrid | Name Prefix: DBGrid
A data-bound grid that displays records from a Data control in a tabular format. Columns auto-populate from the query results. Bind the grid using the DataSource property. Click a column header to sort; drag a column border to resize. The highlighted row is always the bound Data control's current record: selecting a row moves the Data control's cursor, and navigating the Data control moves the highlight. Re-binding keeps the sort order and column settings, and a column width set by dragging a border is kept. Text wider than its column is clipped. The mouse wheel scrolls the rows without changing the current record.
Type-Specific Properties
Property Type Description ---------- ------- ------------------------------------------- DataSource String Name of the Data control that supplies records. GridLines Boolean Show or hide grid lines between cells (default: True). SelectedRow Integer Index of the current record's row (-1 = no records). Setting it moves the Data control's cursor; negative values are ignored.
Type-Specific Methods
Method Parameters Description ---------------- ------------------------------- ------------------------------------------- Refresh (none) Reload and redraw the grid from the Data control. SetColumnHeader Field As String, Header As String Set the displayed header text for a column. SetColumnVisible Field As String, Visible As Boolean Show or hide a column by field name. SetColumnWidth Field As String, Width As Integer Set the pixel width of a column by field name.
Type-Specific Events
Event Parameters Description -------- ---------- ------------------------------------------- Click (none) Fires when a row is clicked. DblClick (none) Fires when a row is double-clicked. Default event.
Default Event: DblClick
Example
Begin Data Data1
DatabaseName = "books.db"
RecordSource = "titles"
End
Begin DBGrid DBGrid1
DataSource = "Data1"
GridLines = True
End
Sub Form1_Load ()
Data1.Refresh
End Sub
Sub DBGrid1_DblClick ()
MsgBox "Opening row " & Str$(Data1.BOF)
End Sub
DropDown
DVX Extension -- DVX Widget: dropdown (non-editable drop-down list)
A read-only drop-down list. Unlike ComboBox, the user cannot type free text; they can only select from the provided items. Supports AddItem / RemoveItem / Clear / List. Use the Up and Down keys to change the choice. F4, Alt+Down, Space, or Enter opens the list; in the open list use the arrow keys to move and Enter (or F4, Alt+Down) to pick. Esc closes the list without changing the choice.
Type-Specific Properties
Property Type Description --------- ------- ------------------------------------------- ListIndex Integer Index of the currently selected item (-1 = none).
Type-Specific Methods
Method Description ------ ----------- AddItem text$ Append an item to the drop-down list. Clear Remove all items. List(index%) Return the text of the item at the given index. ListCount() Return the number of items in the list. RemoveItem index% Remove the item at the given index. The selection stays on the same item; removing the selected item leaves no selection.
Default Event: Change (fires when the user picks a new item; Click fires only when the arrow opens the popup)
Example
Begin DropDown DropDown1
End
Sub Form1_Load ()
DropDown1.AddItem "Small"
DropDown1.AddItem "Medium"
DropDown1.AddItem "Large"
DropDown1.ListIndex = 1
End Sub
Sub DropDown1_Change ()
Label1.Caption = "Picked: " & DropDown1.List(DropDown1.ListIndex)
End Sub
HelpPane
VB Equivalent: (none) -- DVX Widget: helpview
Shows one topic of a help file the way the DVX Help Viewer does: headings, paragraphs, and lists wrapped to the control's width, notes, code, tables, pictures, rules, and links, with a scroll bar when the topic is longer than the control. Open the help file with hlpfile.bas (HlpOpen) and give its handle to ShowTopic. With a TreeView for the table of contents and a few buttons, a HelpPane makes a complete help viewer (see Help Files under Include Libraries).
A link is followed with a click, or with Enter or Space after Tab has moved to it: the HelpPane fires LinkClick with the ID of the topic the link names and remembers it in LinkTopic. It does not change topic by itself -- the LinkClick event procedure usually shows the topic and adds it to its Back list. Everything a topic shows is copied when it is shown, so closing the help file afterwards does not change what the HelpPane shows.
Type-Specific Properties
Property Type R/W Description
--------- ------- --- -------------------------------------------
Topic Integer R The topic shown (numbered from 0, as hlpfile.bas numbers them); -1 when nothing is shown.
LinkTopic String R The topic ID of the last link followed ("" before any).
Type-Specific Methods
Method Description ------ ----------- ShowTopic hlpFile&, topic& Show a topic of an open help file (an HlpOpen handle) from the top. A topic that is not in the file, or a closed handle, shows nothing. Clear Show nothing.
Type-Specific Events
Event Parameters Description --------- ----------------- ------------------------------------------- LinkClick Topic As String A link was followed (clicked, or Enter or Space on it). Topic is the ID of the topic it names; HlpFindTopic gives its number.
Default Event: LinkClick
Example
' hlpfile.bas in the project
Dim hlp As Long
Sub Form1_Load ()
hlp = HlpOpen(App.Path + "\MYAPP.HLP")
HelpPane1.ShowTopic hlp, HlpStartTopic(hlp, "")
End Sub
Sub HelpPane1_LinkClick (Topic As String)
HelpPane1.ShowTopic hlp, HlpFindTopic(hlp, Topic)
Me.Caption = HlpTopicTitle$(hlp, HelpPane1.Topic)
End Sub
Sub Form1_Unload ()
HlpClose hlp
End Sub
ImageButton
DVX Extension -- DVX Widget: imagebutton
A button that displays an image instead of text. Has the same press/release behavior as CommandButton, but shows a bitmap. Typically used inside a Toolbar.
Type-Specific Properties
Property Type Description ----------- ------- ------------------------------------------- Picture Picture Path to an image file to load (or a Picture object from LOADPICTURE); a program, widget, or library file gives its icon. Reads back as a Picture object for the file the image came from, which stands for the file name where a value is needed. Setting it to "" clears the image. A file that cannot be loaded leaves the image and Picture unchanged. ImageWidth Integer Width of the loaded image in pixels (read-only). ImageHeight Integer Height of the loaded image in pixels (read-only).
No type-specific methods or events.
Default Event: Click
Example
Begin Toolbar Toolbar1
Begin ImageButton ImageButton1
Picture = "icons/save.bmp"
End
Begin ImageButton ImageButton2
Picture = "icons/open.bmp"
End
End
Sub ImageButton1_Click ()
MsgBox "Save clicked"
End Sub
Image
VB Equivalent: Image -- DVX Widget: image
A static image display control. Loads bitmap images from file. Set the Picture property at design-time (in the .frm file) or at runtime to load a file.
Type-Specific Properties
Property Type Description ----------- ------- ------------------------------------------- Picture Picture Path to an image file to load (or a Picture object from LOADPICTURE); a program, widget, or library file gives its icon. Reads back as a Picture object for the file the image came from, which stands for the file name where a value is needed. Setting it to "" clears the image. A file that cannot be loaded leaves the image and Picture unchanged. A disabled Image is drawn grayed (its transparent color stays transparent). Stretch Boolean When True, an image larger than the control is scaled down to fit, keeping its proportions; a smaller image stays at natural size. When False, the image is shown at natural size. ImageWidth Integer Width of the loaded image in pixels (read-only). ImageHeight Integer Height of the loaded image in pixels (read-only). MaskColor Long The transparent colour (an RGB value): with UseMaskColor True, pixels of this colour are not drawn, so the form shows through. UseMaskColor Boolean True leaves out the pixels of MaskColor (default False).
No type-specific methods or events.
Default Event: Click
Example
Begin Image Image1
Picture = "assets/logo.bmp"
Stretch = True
End
Sub Image1_Click ()
MsgBox "Size: " & Str$(Image1.ImageWidth) & "x" & Str$(Image1.ImageHeight)
End Sub
Label
VB Equivalent: Label -- DVX Widget: label
A static text label. Supports left, center, and right alignment. Put '&' before a character in the Caption to define an accelerator that moves focus to the next focusable control.
Type-Specific Properties
Property Type Description --------- ---- ------------------------------------------- Caption String The text displayed by the label. Alignment Enum Text alignment: Left (default), Center, or Right.
No type-specific methods or events.
Default Event: Click
Example
Begin Label Label1
Caption = "Hello, World!"
Alignment = "Center"
End
ListBox
VB Equivalent: ListBox -- DVX Widget: listbox
A scrollable list of selectable items. Items are managed via methods (AddItem, RemoveItem, Clear). Supports single and multi-select modes, drag-to-reorder, and keyboard navigation with type-ahead search. When the box is narrower than its longest item (its width limited by MaxWidth or by the form), a horizontal scroll bar appears; it and the Left and Right keys scroll the items sideways a character at a time.
Type-Specific Properties
Property Type Description --------- ------- ------------------------------------------- ListIndex Integer Index of the currently selected item (-1 = no selection).
Type-Specific Methods
Method Parameters Description
--------------- --------------------------------------- -------------------------------------------
AddItem Text As String Add an item to the end of the list.
Clear (none) Remove all items from the list.
ClearSelection (none) Deselect all items.
IsItemSelected Index As Integer Returns True if the item at Index is selected.
List Index As Integer Return the text of the item at the given index.
ListCount (none) Return the number of items in the list.
RemoveItem Index As Integer Remove the item at the given index. Items after it keep their selection; removing the selected item leaves no selection.
SelectAll (none) Select all items (multi-select mode).
SetItemSelected Index As Integer, Selected As Boolean Select or deselect a specific item.
SetItems Items As String Bulk-load items from a pipe-delimited string (e.g. "Red|Green|Blue"). Replaces existing items. Every "|" separates two items ("a|" is "a" and an empty item); "" empties the list.
SetMultiSelect Multi As Boolean Enable or disable multi-select mode.
SetReorderable Reorderable As Boolean Enable or disable drag-to-reorder.
Default Event: Click
Example
Sub Form1_Load ()
List1.AddItem "Apple"
List1.AddItem "Banana"
List1.AddItem "Cherry"
End Sub
Sub List1_Click ()
Dim idx As Integer
idx = List1.ListIndex
If idx >= 0 Then
Label1.Caption = "Selected: " & List1.List(idx)
End If
End Sub
ListView
VB Equivalent: ListView -- DVX Widget: listview
A multi-column list with column headers. Supports sorting, multi-select, and drag-to-reorder.
Type-Specific Properties
Property Type R/W Description --------- ------- --- ------------------------------------------- ListIndex Integer R/W Index of the currently selected row (-1 = none).
Type-Specific Methods
Method Description ------ ----------- AddItem text$ Add a row (sets first column text). Clear Remove all rows. ClearSelection Deselect all rows. GetCell$(row%, col%) Returns the text of a cell. IsItemSelected(index%) Returns True if the row is selected. ListCount() Returns the number of rows (the same as RowCount; also read as the ListCount property). RemoveItem index% Remove a row by index. Rows after it keep their selection; removing the selected row leaves no selection. RowCount() Returns the number of rows. SelectAll Select all rows. SelectedRows$() Returns the numbers of the selected rows (as IsItemSelected numbers them) separated by spaces, "" when none: one call instead of IsItemSelected for every row of a long list. SetCell row%, col%, text$ Set the text of a cell. SetColumns spec$ Define columns. Pipe-delimited "Name,Width,Align|Name,Width": the width in characters (missing or 0 = as wide as the contents), then Left (the default), Center, or Right for the column's text. SetItemSelected index%, selected Select or deselect a row. SetMultiSelect multi Enable or disable multi-select. SetReorderable reorderable Enable or disable row reordering. SetRows text$ Replace all the rows at once: one row per line (separated by CHR$(10)), cells separated by CHR$(9) (Tab); missing cells are blank and extra ones are dropped. Much faster than AddItem and SetCell for long lists. The selection is cleared and the cursor goes to the first row. SetSort col%, dir% Sort by column. dir: 0=none, 1=ascending, 2=descending. An invalid column or direction clears the sort. Has no effect while reordering is enabled.
Type-Specific Events
Event Parameters Description ----------- ---------------------- ------------------------------------------- ColumnClick Column As Integer A column header was clicked (Column 0 is the first). Without a handler the list sorts itself by that column's text (the same column again reverses); a program that orders the rows itself calls SetSort -1, 0 in the handler and fills the list in its own order.
Default Event: Click
Example
ListView1.SetColumns "Name,20|Size,10|Type,15"
ListView1.AddItem "readme.txt"
ListView1.SetCell 0, 1, "1024"
ListView1.SetCell 0, 2, "Text"
ListView1.SetSort 0, 1
' Many rows at once; the size column right-aligned
ListView2.SetColumns "Name,14|Size,10,Right"
ListView2.SetRows "a.txt" & Chr$(9) & "120" & Chr$(10) & "b.txt" & Chr$(9) & "4,096"
ProgressBar
VB Equivalent: ProgressBar -- DVX Widget: progressbar
A progress indicator bar, lying flat (the default) or standing upright.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Value Integer Current progress value (0-100). Vertical Boolean True: the bar stands upright and fills from the bottom up. False (the default): it lies flat and fills from left to right. Can be set in the designer or at run time.
No type-specific events or methods. No default event.
OptionButton
VB Equivalent: OptionButton -- DVX Widget: radio (radio group + radio button) | Name Prefix: Option
A radio button for mutually exclusive choices. OptionButtons placed in the same container form a group: selecting one clears the others. The Value property is True for the selected button.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Caption String The text displayed next to the radio button. Value Boolean True when this button is selected. Setting it to True selects this button and clears the others in its container; setting it to False deselects it. Setting Value does not raise Click. OptionIndex Integer The index of this button among the OptionButtons of its container (read-only).
Type-Specific Methods
Method Parameters Description ----------- ----------------- ------------------------------------------- SetSelected Index As Integer Select the OptionButton at the given index within this button's container.
Default Event: Click
RichEdit
VB Equivalent: RichTextBox -- DVX Widget: richedit
A text box for text in many fonts, sizes, and colors, bold, italic, underlined, or struck through, in paragraphs aligned left, centered, or right -- a small word processor's page. The user types, selects, and edits as in any text box; a program reads and changes the selection's look with the Sel properties, and the whole text, with its looks, as RTF (Rich Text Format, the format word processors save), so it can be saved to a file and loaded again.
Text starts in the control's own font (FontName, FontSize, and the other Font properties, or the form's) and in its ForeColor; text left in them follows the control when they change. The Sel properties read the first character of the selection; setting one changes the selection, or with nothing selected, the text typed next. SelStart counts characters from 0, and the break between two paragraphs is one character.
Type-Specific Properties
Property Type Description
-------- ---- -----------
TextRTF String The whole text with its fonts, styles, colors, and alignment, as RTF. Setting it replaces the text (plain text is taken as it is).
SelRTF String The selection as RTF. Setting it replaces the selection.
SelStart Integer Where the selection starts (the caret when nothing is selected).
SelLength Integer How many characters are selected.
SelText String The selection's text. Setting it replaces the selection with plain text.
SelBold Boolean Bold.
SelItalic Boolean Italic.
SelUnderline Boolean Underlined.
SelStrikethru Boolean Struck through.
SelColor Long The color, as RGB() makes it; -1 for the control's ForeColor.
SelFontName String The font family ("" for the control's own).
SelFontSize Integer The size in pixels (the nearest one the family comes in).
SelAlignment Integer The alignment of the selection's paragraphs: 0 left, 1 centered, 2 right.
CanUndo Boolean An edit can be undone (read-only).
CanRedo Boolean An undone edit can be done again (read-only).
Text (a common property) is the plain text, a line per paragraph; setting it replaces everything with plain text. ReadOnly lets the user select and copy but not change the text.
Type-Specific Methods
Method Description ------ ----------- Undo Undo the last edit (typing a run of characters is one edit). Up to 100 edits are kept. Redo Do an undone edit again. Cut, Copy, Paste The clipboard. Text copied from a RichEdit pastes back into a RichEdit with its looks; text from elsewhere takes the look of the text typed next. SelectAll Select all the text. Find(text) Select the next place text is (any case), from the selection on and around past the end; returns where it starts, or -1. LoadFile(path) Load a file: RTF when it is RTF, else plain text. Returns 0, or -1 when it cannot be read. SaveFile(path) Save the text: as RTF when path ends in .rtf, else as plain text. Returns 0, or -1 when it cannot be written.
Type-Specific Events
Event Parameters Description ----- ---------- ----------- SelChange (none) The selection or the caret moved.
Change fires after every edit. Default Event: Change
Keys
Key Action --- ------ Ctrl+B, Ctrl+I, Ctrl+U Bold, italic, or underline on or off. Ctrl+L, Ctrl+E, Ctrl+R Align the paragraph left, centered, or right. Ctrl+Z, Ctrl+Y Undo, Redo. Ctrl+X, Ctrl+C, Ctrl+V Cut, Copy, Paste. Ctrl+A Select all. Ctrl+Left, Ctrl+Right A word back or on. Ctrl+Home, Ctrl+End The start or the end of the text. Shift with a moving key Extend the selection.
A double click selects a word, a triple click the paragraph.
Example
Sub Form_Load ()
RichEdit1.Text = "Dear friend,"
RichEdit1.SelStart = 0
RichEdit1.SelLength = 4
RichEdit1.SelBold = True
RichEdit1.SelColor = RGB(0, 0, 160)
End Sub
Sub cmdItalic_Click ()
RichEdit1.SelItalic = Not RichEdit1.SelItalic
End Sub
Sub cmdSave_Click ()
If RichEdit1.SaveFile("LETTER.RTF") <> 0 Then MsgBox "The letter could not be saved."
End Sub
RichLabel
DVX Widget: richlabel
Text that mixes fonts, sizes, bold and italic, underlining, and colors, in paragraphs that are each aligned left, centered, or right. Build the text with calls: SetFont, SetStyle, and SetColor choose how the text you add next looks; AddText adds it; AddLineBreak starts a new line; and AddParagraph starts a new paragraph. Clear empties the control so you can build it again.
Text starts in the control's own font (FontName, FontSize, and the other Font properties, or the form's). Set Width to have the text wrap at that width; otherwise each paragraph stays on one line and the control is as wide as its longest line.
Methods
Method Description ------ ----------- Clear Remove all the text. The font, style, and color go back to the control's own. SetFont family, size The font family (for example "DejaVu Serif") and size in pixels of the text added next. "" and 0 go back to the control's own font and size. SetStyle style FONT_BOLD, FONT_ITALIC, FONT_UNDERLINE, and FONT_STRIKE added together (from font.bas; 0 for plain) for the text added next. SetColor color The color of the text added next, as RGB() makes it; -1 for the control's ForeColor. AddText text Add text in the font, style, and color chosen. AddLineBreak Start a new line in the same paragraph. AddParagraph alignment Start a new paragraph: 0 left, 1 centered, 2 right (as a Label's Alignment).
Changing the font, style, or color affects only the text added afterwards. A line is as tall as the largest font on it.
No type-specific properties or events.
Default Event: Click
Example
Sub Form_Load ()
RichLabel1.Clear
RichLabel1.SetFont "DejaVu Serif", 18
RichLabel1.SetStyle FONT_BOLD
RichLabel1.AddText "Welcome"
RichLabel1.AddParagraph 0
RichLabel1.SetFont "", 0
RichLabel1.SetStyle 0
RichLabel1.AddText "Text in "
RichLabel1.SetColor RGB(160, 0, 0)
RichLabel1.AddText "red"
RichLabel1.SetColor -1
RichLabel1.AddText " and "
RichLabel1.SetStyle FONT_ITALIC + FONT_UNDERLINE
RichLabel1.AddText "italic underlined"
RichLabel1.SetStyle 0
RichLabel1.AddText " words."
End Sub
ScrollPane
DVX Extension -- DVX Widget: scrollpane | Name Prefix: Scroll
A scrollable container. Place child controls inside and the ScrollPane automatically provides scrollbars when the content exceeds the visible area. Clicking on content that does not respond to the mouse (a Label, an Image, or empty space) gives the ScrollPane the focus, so the arrow and page keys scroll it.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- NoBorder Boolean When True, suppresses the border around the scroll pane. Padding Integer Space in pixels between the edge and the controls inside (0, the default, is none). Spacing Integer Space in pixels between the controls inside, which are stacked top to bottom (0, the default, is none). ScrollTop Integer How far the content is scrolled down, in pixels (run time). Values past the end stop at the end. ScrollLeft Integer How far the content is scrolled to the right, in pixels (run time). Values past the end stop at the end.
Type-Specific Methods
Method Description ------ ----------- ScrollToTop Scroll back to the top-left corner of the content. ScrollToControl ctrl Scroll so that a control inside the pane (the control, or its name) is in view, also one that cannot take the focus.
Events
Event Description ----- ----------- Change The user scrolled the content (the scrollbars, dragging a scroll box, the arrow, page, Home, and End keys); read ScrollTop and ScrollLeft for the new position. Setting ScrollTop or ScrollLeft, or ScrollToTop, does not fire it.
Scrolling a Picture Bigger Than the Window
A ScrollPane with NoBorder = True filling a form, holding a PictureBox larger than the window, gives the form scrollbars of its own: the user scrolls the picture, Change says where it is, and ScrollTop / ScrollLeft move it from code. To draw only the part in view (for a picture too large to keep whole), keep the PictureBox the size of the view, and draw from Change with ScrollTop and ScrollLeft as the offset.
Sub Form1_Load ()
picBig.Resize 1200, 900
picBig.Line (0, 0)-(1199, 899), RGB(0, 0, 255), B
End Sub
Sub spView_Change ()
lblWhere.Caption = "Top " & spView.ScrollTop & ", left " & spView.ScrollLeft
End Sub
Container: Yes
No default event.
Line
VB Equivalent: Line -- DVX Widget: separator
A visual separator line. The underlying widget supports both horizontal and vertical orientations. A Line placed in a horizontal container (HBox, Toolbar) is vertical; anywhere else it is horizontal.
No type-specific properties, events, or methods.
HScrollBar
VB Equivalent: HScrollBar -- DVX Widget: slider | Name Prefix: HScroll
A horizontal slider/scrollbar control. The value ranges between Min and Max (default 0 to 100). Arrow keys move the value by 1, Page Up/Page Down by a tenth of the range, Home/End to the limits. The Change event fires only when the value actually changes.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Value Integer The current slider position (clamped to the Min..Max range). Min Integer Lowest value (default 0). Setting Min above Max raises Max to match. Max Integer Highest value (default 100). Setting Max below Min lowers Min to match.
Default Event: Change
Example
Begin HScrollBar HScroll1
MinWidth = 200
End
Sub HScroll1_Change ()
Label1.Caption = "Value: " & Str$(HScroll1.Value)
End Sub
Spacer
DVX Extension -- DVX Widget: spacer
An invisible layout spacer. Takes up space in the layout without rendering anything. Useful for pushing controls apart. Give it a Weight to absorb extra space.
No type-specific properties, events, or methods.
SpinButton
DVX Extension -- DVX Widget: spinner | Name Prefix: Spin
A numeric input with up/down buttons. Supports integer mode (default) and real-number mode with configurable decimal places. Up/Down arrows step by the step size, Page Up/Page Down by ten steps. Typed or pasted text is committed on Enter or when the control loses focus; Esc cancels the edit. Ctrl+Z undoes the last edit of the typed text; once the text is committed or reformatted there is nothing to undo. Reading Value while the user is typing returns the typed number without interrupting the edit. Change also fires as each character is typed; stepping at a limit, or committing text that parses to the unchanged value, does not fire it.
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Value Integer Current value (in real mode, the value truncated to an integer). RealMode Boolean True to use floating-point mode; False for integer mode. Decimals Integer Number of decimal places shown in real mode. RealValue Double The value in real mode, with its fraction (Value is it cut to a whole number). Setting it keeps it inside the range.
Type-Specific Methods
Method Parameters Description -------- ------------------------------ ------------------------------------------- SetRange Min As Integer, Max As Integer Set the allowed value range (used in both integer and real mode; default 0 to 100). SetStep Step As Integer Set the increment per button click. SetRealRange Min As Double, Max As Double The range in real mode, with fractions (SetRange takes whole numbers). SetRealStep Step As Double The increment per click in real mode (0 or less gives 0.01).
Default Event: Change
Splitter
DVX Extension -- DVX Widget: splitter
A resizable split pane. Holds exactly two child widgets separated by a draggable divider. By default the divider is vertical and the panes sit side by side; set Vertical to False for a horizontal divider with one pane above the other.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Position Integer Position of the divider in pixels from the left (or, when Vertical is False, from the top). Vertical Boolean True (the default): panes side by side. False: one pane above the other. Can be set in the designer or at run time.
Container: Yes
No default event.
StatusBar
VB Equivalent: StatusBar -- DVX Widget: statusbar
A horizontal container styled as a status bar, typically placed at the bottom of a form. Put Labels (or other controls) inside it as its panes: they sit side by side, each drawn with a sunken-panel border for the classic segmented status-bar look. Give the pane that should take the spare room Weight = 100. Panes can be nested in the designer or the .frm, or added at run time with CreateControl(form, "Label", name$, StatusBar1).
No type-specific properties, events, or methods.
Container: Yes
Example
Begin StatusBar Status1
Begin Label lblStatus
Caption = "Ready"
Weight = 100
End
Begin Label lblPos
Caption = "Line 1, Col 1"
End
End
TabStrip
VB Equivalent: TabStrip -- DVX Widget: tabcontrol
A tabbed container. Each tab page is a separate container that holds child controls. Switching tabs shows one page and hides others.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- TabIndex Integer Index of the active tab (0-based). Note: this property name collides with the common VB-compatibility TabIndex property, which shadows it at runtime. Use the SetActive method instead to switch tabs.
Type-Specific Methods
Method Description ------ ----------- AddPage title$ Add a new tab page with the given title. GetActive() Returns the index of the active tab. SetActive index% Switch to the tab at the given index. This is the recommended way to change tabs at runtime (the TabIndex property is shadowed by the common property handler). A negative index selects the first tab and an index past the last tab selects the last tab. Switching closes a drop-down list opened from the page being hidden and moves the focus to the tab strip if it was on a control of the page being hidden. The Click event is not raised for a switch made in code.
Container: Yes
Default Event: Click
Warning: The TabIndex property is shadowed by the common property handler at runtime. Use the SetActive method to change tabs programmatically.
TextBox
VB Equivalent: TextBox -- DVX Widget: textbox (single-line text input, 256 chars unless MaxLength says otherwise)
A single-line text input field. Supports data binding via DataSource and DataField properties.
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type Description ------------ ------ ------------------------------------------- Text String The text content of the input field. Setting it clears the undo history. MaxLength Integer The most characters the box holds (0 = the default, 256; at most 32767). A longer text is cut, and typing stops at the limit. PasswordChar String "" for a plain box; a character makes it a password box that shows that character for each one typed and does not copy or cut its text. Mask String "" for a plain box; an input mask makes it a masked box: # takes a digit, A a letter, * any character, and every other character of the mask is fixed text the cursor skips (Mask = "(###) ###-####" for a phone number). The text keeps the mask's shape (empty places show _), and what the box held fills its places in order. Mask = "" makes it a plain box again holding the formatted text, with its MaxLength from before. A masked box is not a password box. DataSource String Name of a Data control for data binding. DataField String Column name for data binding. SelStart Integer Where the selection starts, or the cursor when nothing is selected (0 = before the first character). Setting it moves the cursor there and selects nothing. SelLength Integer The number of characters selected (0 = none). Setting it selects that many characters from SelStart. SelText String The selected text. Setting it replaces the selection (or inserts at the cursor), leaves the cursor after the new text, and fires Change; in a masked box it fills the places from the selection's start, as pasting does.
Type-Specific Methods
Method Description ------ ----------- Copy Copy the selection to the clipboard (Ctrl+C); a password box copies nothing. Cut Copy the selection to the clipboard and remove it (Ctrl+X); fires Change. A password box does nothing. Paste Put the clipboard's text in place of the selection (Ctrl+V; line breaks are left out); fires Change. SelectAll Select the whole text (Ctrl+A). Undo Take back the last change (Ctrl+Z); a second Undo puts it back.
The methods do what the keys do, so Cut, Paste, and Undo do nothing in a read-only box. An Edit menu works on whichever box has the focus with Me.ActiveControl (see Form).
Default Event: Change
Example
Begin TextBox Text1
Text = "Enter text here"
End
Sub Text1_Change ()
Label1.Caption = "You typed: " & Text1.Text
End Sub
TextArea
VB Equivalent: TextArea (DVX extension) -- DVX Widget: textarea (multi-line text input, default max 65536 chars)
A multi-line text editing area. This is a DVX extension with no direct VB3 equivalent (VB uses a TextBox with MultiLine=True). Supports line numbers, auto-indent, find/replace, and tab configuration.
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type R/W Description ---------- ------- --- ------------------------------------------- Text String R/W The full text content. Setting it clears the undo history. CursorLine Integer R Current cursor line number (1-based). CursorCol Integer R Current cursor column (1 = the start of the line; a tab counts as one). LineCount Integer R The number of lines (an empty text has one). SelStart Integer R/W Where the selection starts, or the cursor when nothing is selected (0 = before the first character). Setting it moves the cursor there and selects nothing. SelLength Integer R/W The number of characters selected (0 = none). Setting it selects that many characters from SelStart. SelText String R/W The selected text. Setting it replaces the selection (or inserts at the cursor when nothing is selected), leaves the cursor after the new text, and fires Change; Undo takes it back. MaxLength Integer R/W The most characters the area holds (0 = the default, 65536; at most 262144). A smaller value cuts the text.
Type-Specific Methods
Method Description ------ ----------- AppendText text$ Append text to the end of the buffer and invalidate the widget. ClearLineMarkers Remove every line marker. Copy Copy the selection to the clipboard (Ctrl+C). CursorToEnd Put the cursor after the last character, nothing selected. Cut Copy the selection to the clipboard and remove it (Ctrl+X); fires Change. FindNext needle$, caseSensitive, forward Search from the cursor and select the match. A forward search finds a match starting at the cursor; repeating it moves on to the next match. Returns True if found. GetLine(line%) Returns the text of a line (1 = the first) without its line end; "" past the last line. GetSyntaxColor(index%) Returns the colour the syntax colouring draws that kind of text in (index as for SetSyntaxColor); -1 for the area's ForeColor. GetWordAtCursor() Returns the word under the cursor. GoToLine line% Scroll to the given line and select it. Paste Put the clipboard's text in place of the selection (Ctrl+V); fires Change. ReplaceAll needle$, replacement$, caseSensitive Replace all occurrences. Returns count of replacements. ReplaceSelection(needle$, replacement$, caseSensitive, forward) When the selection is needle$, replace it (Change fires); then find the next needle$ in that direction, as FindNext does. Returns True when it replaced or found something (a Replace button). SelectAll Select the whole text (Ctrl+A). SetAutoIndent enabled Enable or disable automatic indentation. SetCaptureTabs enabled When True, Tab key inserts a tab/spaces (firing Change) instead of moving focus. SetCursor line%, col% Move the cursor to a line and column (both start at 1) without selecting anything. Values past the end clamp to the text. SetLineMarker line%, gutterColor&, backColor& Mark a line (1 = the first): a dot of gutterColor in the line-number gutter (a breakpoint) and / or the line's background in backColor (the current line). -1 leaves that part out; -1 for both removes the marker. Markers stay on their line numbers when the text changes. SetShowLineNumbers show Show or hide the line number gutter. SetSyntaxColor index%, color& The colour of one kind of text: 0 plain text, 1 keywords, 2 strings, 3 comments, 4 numbers, 5 operators, 6 types (and True, False, Nothing); -1 restores the default. SetSyntaxMode mode$ Activate syntax highlighting. Modes: "dhs" (help source), "bas" (BASIC, coloured as the DVX BASIC IDE colours it), "custom" (the program colours each line in the Colorize event). Pass "" to disable. SetTabWidth width% Set the tab stop width in characters. SetUseTabChar useChar When True, Tab inserts a tab character; when False, inserts spaces. Undo Take back the last change (Ctrl+Z); a second Undo puts it back.
Cut, Paste, and Undo do nothing in a read-only area.
Type-Specific Events
Event Parameters Description ----------- ----------------------- ------------------------------------------- GutterClick LineNumber As Integer The line-number gutter (shown by SetShowLineNumbers) was clicked beside a line: toggle a breakpoint there. A click in the gutter does not move the cursor. Colorize LineText As String, Colors As String With SetSyntaxMode "custom", for every line as it is drawn: Colors comes with one "0" for each character of LineText; set each to the digit of a syntax colour (vbSyntaxText 0 to vbSyntaxType 6, colours as SetSyntaxColor sets them). Characters it leaves out stay plain. It runs whenever lines are drawn, so it must be quick and must not change the area.
TextArea1.SetSyntaxMode "custom"
Sub TextArea1_Colorize (LineText As String, Colors As String)
Dim i As Integer
For i = 1 To Len(LineText)
If Mid$(LineText, i, 1) = "#" Then Mid$(Colors, i, 1) = CStr(vbSyntaxComment)
Next
End Sub
Default Event: Change
Example
TextArea1.SetShowLineNumbers True
TextArea1.SetAutoIndent True
TextArea1.SetTabWidth 4
TextArea1.SetCaptureTabs True
' Search for text
If TextArea1.FindNext("TODO", False, True) Then
Print "Found at line"; TextArea1.CursorLine
End If
'' Replace all occurrences
Dim count As Integer
count = TextArea1.ReplaceAll("old", "new", False)
Print "Replaced"; count; "occurrences"
' A code editor: BASIC colours, a breakpoint dot on the clicked line
TextArea1.SetSyntaxMode "bas"
Sub TextArea1_GutterClick (LineNumber As Integer)
TextArea1.SetLineMarker LineNumber, RGB(200, 0, 0), -1
End Sub
Timer
VB Equivalent: Timer -- DVX Widget: timer (non-visual)
A non-visual control that fires its event at a regular interval. The Timer widget is invisible at runtime -- it has no on-screen representation.
Type-Specific Properties
Property Type Description -------- ------- ------------------------------------------- Enabled Boolean True to start the timer, False to stop it. Interval Integer Timer interval in milliseconds. Setting it restarts the countdown. An Interval of 0 (or less) disables the Timer event.
Type-Specific Methods
Method Parameters Description ------ ---------- ------------------------------------------- Start (none) Start the timer. Stop (none) Stop the timer.
Type-Specific Events
Event Parameters Description ----- ---------- ------------------------------------------- Timer (none) Fires each time the interval elapses. This is the default event. A one-shot timer is already stopped when its event runs, so the handler can call Start to run it again.
Note: The Timer control fires the Timer event instead of Change. The onChange callback on the underlying widget is remapped automatically.
Example
Begin Timer Timer1
Interval = 1000
Enabled = True
End
Dim Shared counter As Integer
Sub Timer1_Timer ()
counter = counter + 1
Label1.Caption = "Ticks: " & Str$(counter)
End Sub
Toolbar
VB Equivalent: Toolbar -- DVX Widget: toolbar
A horizontal container styled as a toolbar, with compact padding and spacing. Place buttons, labels, or other controls inside.
Container: Yes
No type-specific properties, events, or methods.
TreeView
VB Equivalent: TreeView -- DVX Widget: treeview
A hierarchical tree of expandable/collapsible nodes. Supports multi-select and drag-to-reorder. Items are accessed by depth-first index (0-based): an item's children follow it, before its next sibling. Adding children under an item that has none puts them right after it (index + 1, index + 2, ...), and moves the items after them along.
Type-Specific Properties
Property Type R/W Description --------- ------- --- ------------------------------------------- ListIndex Integer R/W Index of the current (selected) node, -1 when none. Setting it selects that node as a click would: its parents are expanded, it is scrolled into view, and the Change event fires.
Type-Specific Methods
Method Description
------ -----------
AddChildItem parentIndex%, text$ Add a child node under the node at the given index.
AddItem text$ Add a root-level node.
Clear Remove all nodes.
GetItemTag$(index%) Returns the text SetItemTag stored on the node ("" when none).
GetItemText$(index%) Returns the text of the node at the given index.
HasChildren(index%) Returns True if the node has nodes under it.
IsExpanded(index%) Returns True if the node at the given index is expanded.
IsItemSelected(index%) Returns True if the node at the given index is selected.
ItemCount() Returns the total number of nodes (all levels).
SetExpanded index%, expanded Expand or collapse the node at the given index. Collapsing a node that hides the current item moves the current item to that node.
SetItemSelected index%, selected Select or deselect the node at the given index. Without multi-select this sets (or clears) the single selected node.
SetItemTag index%, tag$ Keep any text with the node (like VB's Node.Tag): a file path, a key. "" removes it.
SetMultiSelect multi Enable or disable multi-select mode. Turning it off clears the multi-selection.
SetReorderable reorderable Enable or disable node reordering.
The Change event fires when the current node changes (a click, the arrow keys, or ListIndex set by the program).
Default Event: Click
Example
TreeView1.AddItem "Animals"
TreeView1.AddChildItem 0, "Cat"
TreeView1.AddChildItem 0, "Dog"
TreeView1.AddItem "Plants"
TreeView1.AddChildItem 3, "Oak"
TreeView1.SetExpanded 0, True
Print "Items:"; TreeView1.ItemCount()
WrapBox
DVX Extension -- DVX Widget: wrapbox | Name Prefix: WrapBox
A container that arranges children in a flowing layout, wrapping to the next row when the available width is exceeded. Similar to CSS flexbox with flex-wrap: wrap.
Type-Specific Properties
Property Type Description --------- ---- ------------------------------------------- Alignment Enum Horizontal alignment of items: Left, Center, or Right.
Container: Yes
Default Event: Click