Skip to content

Data Binding & Events

tpecholt edited this page Dec 25, 2025 · 6 revisions

Table of Contents


Introduction

To make the generated code dynamic ImRAD implements data binding and events. While other ImGui tools traditionally offered little to no support for this important functionality ImRAD was designed to support it since the beginning.

Data binding - a way to dynamically change widget state or to report widget state back to the program

Event - member function which will be called upon something happens (button click, text input character filter, window appearance...)


Data Binding

One-way binding is changing a widget state by assigning an expression to one of its properties. For example Button.size_x can be bound to a member variable so it can be changed programmatically.

Two-way binding requires an l-value expression (such as variable reference). It allows for both programmatic widget state change and user interaction where the entered widget value is reported back to the program. An example of this is the Input widget value.

Flexible binding allows for both types of expressions. ImRAD will try to determine if the entered expression is a reference or not and output the appropriate code. This can be useful for certain widgets which support both types of binding.

The expression becomes a part of the generated Draw() method. That means it will be continuously evaluated during the widget drawing phase. It's up to the programmer to provide early calculation and caching if the performance is a concern.

The format of the binding expression depends on the property type:

  • other than string property - the expression is a regular C++ expression. It may contain literals, variables, operators, function calls etc. For example numRows + 1 is a valid int expression.

  • string property - follows std::format rules but the argument expressions are placed directly within the {} replacement fields. For example List of all {showError ? "errors" : "notes"} is a valid format expression. Width and precision substitution (e.g. {:{}}) is not supported.

    To make the substitution obvious replacement fields are visualized in the designer:

    image

Note: ImRAD performs only basic format string checking. Incorrect binding expressions may cause the generated code to stop compiling.


One-way binding

This allows to modify widget state but not letting the user to change it (apart from hovered/focused/activated state). Examples are Text.text, Selectable.label, Button.size_x, tooltip, disabled and visible state.

Some properties which don't have sensible default values will let you enter the expression directly:

image

And the binding button on right will turn orange. Either way you can use the binding button to type the binding expression using the Binding dialog:

image

This will show you a list of all available fields and lets you to create new as well.


Two-way binding

This lets you to change widget state programmatically and still allow user to interact with the widget as well. Examples are Input.value, Checkbox.checked, MenuItem.checked etc.

Two-way binding properties are found inside the Bindings section:

image

Here the combo lets you perform common tasks (Rename, New variable) as well as quickly selecting one of the existing field with compatible type. If you want to enter the binding expression manually use the binding button.


Flexible binding

Some ImGui controls come in two overloads. One used for two-way binding accepts a pointer to store the value:

Selectable(label, &value)

and one used for one-way binding:

Selectable(label, true)

ImRAD supports both. Upon entering the expression ImRAD will try to detect if the expression is a reference and if so it uses the two-way binding variant. One-way binding variant is used otherwise. Other examples are RadioButton.checked and special forceFocus property.

If you entered a variable reference but you still want to use one-way binding (so the user can't change the widget state) use parenthesis like this:

(checkedVar)

Not all widgets support flexible data binding. You will be warned if one-way binding expression is entered but only two-way binding is supported.

Note: Reference detection may not always work as detecting it requires a full C++ compiler. Use parenthesis to disambiguate.


Binding into Arrays

Some widgets allow to bind into arrays and show a list of items. These are Table which presents items in rows and Child which may present them in columns.

  1. You start by binding the parent item/rowCount property:

    image

    Here ImRad already offers a list of containers available as fields. This currently supports std::vector, std::array, std::span container types and their smart / raw pointers.

    Successful array binding is signaled in the Hierarchy tree:

    image

    It advises you to use special $item and $index variables for data binding

  2. Next place child widgets to visualize the item. You only need to place widgets for one item. The generated code will use a for cycle to show all of them.

    For accessing the current item you may use myArray[i] because the default name for the index is i.

    image

    But the for loop variable may change. To prevent that your binding expression can refer to a special $item and $index variables. So following binding is preferred:

    image

  3. In some cases you want to persist the current item index. For example to access it from an event handler. In that case use row/itemIndex property:

    image

    Note: In some cases ImGui may provide you with the current item index without creating a new field. Look into ImGui::TableGetRowIndex() and similar (imgui_internal.h).


Class Wizard

Class wizard lets you manage class fields, rename the window class or create a new inner struct. It is accessible from the toolbar:

image


Events

To assign a new event double click the event property or choose New Method from the popup menu. Existing events can be assigned by picking it from the list:

image

You can inspect the code to see what happened (but not code preview as that only shows the content of the Draw() method).

Clone this wiki locally