For the complete documentation index, see llms.txt. This page is also available as Markdown.

Advanced Binding Options

Introduction

Gum provides multiple options to control how binding behaves using the Binding type.

Using the Binding Class

The fastest way to set up binding is to use the name of the properties being bound, as shown in the following code example:

// Initialize
label.SetBinding(
    // UI property:
    nameof(label.Text),

    // ViewModel property:
    nameof(MyViewModel.LabelText));

The SetBinding call shown above is a shortcut for creating a Binding object. The following code is functionally identical:

// Initialize
// Binding is created using the ViewModel property
Binding binding = new(nameof(MyViewModel.LabelText));
label.SetBinding(
    // UI property:
    nameof(label.Text),
    binding);

The Binding instance can be modified by setting its properties on initializers. For example the following code shows how to update the binding to be one-way from the source (ViewModel) to the target (Label):

Lambda Binding

Binding can also be created using lambda expressions, which provide compile-time checking and refactoring support. Lambda expressions are extension methods available on FrameworkElement.

A typed lambda specifies the ViewModel type explicitly:

A parameterless lambda captures the ViewModel from a local variable:

Both approaches extract the property path from the expression at call time. The resulting binding behaves identically to a string-based binding.

Nested Property Paths

The Path on a Binding supports dotted paths to bind through multiple levels of properties. For example, if a ViewModel has a CurrentPlayer property which itself has a Name property, the binding can reach through both:

If any intermediate property in the path changes, the binding automatically re-evaluates. For example, if CurrentPlayer is replaced with a different object, the label updates to show the new player's name.

Lambda expressions also support nested paths:

Index-Based Binding

Binding paths support integer indexer access using bracket notation. This is useful for binding to a specific item in a list or array.

Indexes can appear at any position in a path:

Lambda expressions also support indexing:

If the collection implements INotifyCollectionChanged (such as ObservableCollection), the binding automatically re-evaluates when items are added, removed, replaced, or cleared. If the bound index is out of range, the binding uses the FallbackValue if one is set.

The following fiddle demonstrates index-based binding with a team of players. The TextBox is two-way bound to Players[0].Name, and three Labels display each player by index. Typing in the TextBox updates the first player's label, and the Replace button demonstrates collection change notification.

Try on XnaFiddle.NET

Available Binding Properties

The following properties are available on the Binding class. Note that the term "source" typically refers to the ViewModel:

  • Path - the property path on the source. This can be a simple name like LabelText, a dotted path like Player.Name, or an indexed path like Items[0].Text.

  • Mode - the direction of data flow in binding. This can be used to create one-way binding.

  • UpdateSourceTrigger - controls when binding is applied

  • FallbackValue - the value to apply when binding cannot retrieve a value from the source

  • TargetNullValue - the value to use when the source property value is null

  • Converter - a converter to transform values between the source and target

  • ConverterParameter - an optional parameter to pass to the Converter

  • StringFormat - a format string to apply to the value

Converter Example

The Converter property accepts an IValueConverter implementation that transforms values as they flow between the source and target. The following example converts a boolean to a display string:

The converter is assigned through the Binding object:

When IsActive is true, the label displays "Yes". If the binding is two-way and the label text is set to "No", the converter's ConvertBack method sets IsActive to false.

StringFormat Example

The StringFormat property applies a format string when displaying source values. This is useful for formatting numbers, dates, or adding prefix text:

When StringFormat is set, the binding effectively becomes one-way for display purposes. Setting the target value does not attempt to parse the formatted string back to the source.

FallbackValue Example

The FallbackValue is used when the binding path cannot be resolved, such as when an intermediate property is null or a collection index is out of range:

Implicit Type Conversion

When the source and target of a binding have different types, Gum automatically attempts to convert values using System.Convert.ChangeType in both directions. This means a string-typed control property like TextBox.Text can be bound to a numeric ViewModel property without writing a converter.

The following ViewModel exposes an int property:

The TextBox can be bound directly to Score:

In the example above, setting vm.Score = 12 updates textBox.Text to "12", and typing "42" into the textbox updates vm.Score to 42. The standard numeric types are supported: byte, int, float, double, and decimal.

Conversion Failures

If Convert.ChangeType cannot convert a value — for example, the user types "hello" into a TextBox bound to int — the binding silently leaves the destination unchanged. No exception is thrown.

This applies in both directions and to edge cases such as empty strings. Clearing the text in a TextBox bound to int does not reset the source to 0 — it leaves the source at its previous value.

The same rule applies to Nullable<T> source properties. An empty string does not convert to null, so the source is left unchanged. To support clearing a numeric field to null, supply a Converter with explicit ConvertBack logic.

Culture Sensitivity

Implicit conversion uses the current culture. On a system where the decimal separator is ,, the string "3.14" will not parse as a decimal number; "3,14" will. If your application needs culture-invariant parsing regardless of system settings, supply a Converter rather than relying on implicit conversion.

Last updated

Was this helpful?