LogoLogo
  • Gum Tool
    • Introduction
    • Setup
      • Running from Source
    • Showcase
    • Tutorials and Examples
      • Intro Tutorials
        • 1 - The Basics
        • 2 - Variables Tab
        • 3 - Files
        • 4 - Components
        • 5 - Exposing Variables
        • 6 - Parent
        • 7 - States
        • 8 - State Categories
      • Animation Tutorials
        • 1 - Introduction to Animation
        • 2 - Creating an Animation
        • 3 - Playing Animations inside other Animations
        • 4 - Combining Multiple Categories
      • Examples
        • Bottom-Up Stack
        • Centering
        • Custom NineSlice
        • Health Bar
        • Padding
    • Gum Elements
      • General Properties
        • Alpha
        • Base Type (Inheritance)
        • Blend
        • Clips Children
        • Has Events
        • Height Units
        • Ignored By Parent Size
        • Locked
        • Max Height
        • Max Width
        • Min Height
        • Min Width
        • Order
        • Parent
        • Rotation
        • Visible
        • Variable References
        • Width Units
        • X
        • X Origin
        • X Units
        • Y
        • Y Origin
        • Y Units
      • Behaviors
        • Default Implementation
      • Circle
      • ColoredRectangle
      • Component
        • Default Child Container
      • Container
        • Children Layout
        • Contained Type
        • Is Render Target
        • Stack Spacing
        • Wraps Children
      • NineSlice
        • Blend
        • Custom Frame Texture Coordinate Width
        • Texture Address
        • Texture Left
        • Texture Top
      • Polygon
        • Points
      • Skia Standard Elements
        • General Properties
          • Has Dropshadow
          • Is Filled
          • Use Gradient
        • Arc
          • Start Angle
          • Sweep Angle
          • Thickness
        • Canvas
        • ColoredCircle
        • LottieAnimation
        • RoundedRectangle
          • Corner Radius
        • Svg
      • Sprite
        • Color
        • Source File
        • Texture Address
        • Wrap
      • States
        • Categories
      • Text
        • Blue
        • Color
        • Font
        • Font Scale
        • Font Size
        • Green
        • Is Bold
        • Is Italic
        • MaxLettersToShow
        • Outline Thickness
        • Red
        • Text
        • Text Overflow Horizontal Mode
        • Text Overflow Vertical Mode
        • Use Custom Font
        • Use Font Smoothing
    • Project Tab
    • Code Tab
      • Runtime Generation Details
      • Generation Scope
      • Inheritance Location
      • Is CodeGen Plugin Enabled
      • Show CodeGen Preview
    • Localization
    • Menu
      • Content
      • Project Properties
    • Plugins
      • AddAndRemoveVariablesForType
      • AddMenuItem
      • Export
      • Setting Up Post Build Events
    • Variables
      • Add Variable
    • Bitmap font generator (.fnt)
    • Upgrading
      • Migrating 2025 April 27 to Preview
      • Breaking Changes
        • Removal of Variable Spaces
  • Code
    • Gum Code Reference
      • AnimationRuntime
      • BitmapCharacterInfo
      • BitmapFont
      • Camera
      • CircleRuntime
      • ColoredRectangleRuntime
      • Component Runtimes
      • ContainerRuntime
      • Cursor
      • Gum Class Overview
      • DataUiGrid
        • Reflection
        • Categories
      • ElementSave
        • ToGraphicalUiElement
      • GraphicalUiElement
        • Absolute Values
        • AddToManagers
        • AnimateSelf
        • ApplyState
        • BlendState
        • CanvasHeight
        • CanvasWidth
        • Children
        • ContainedElements
        • Font Values
        • GetAbsoluteHeight
        • GetAbsoluteWidth
        • GetChildByNameRecursively
        • GetFrameworkElementByName
        • IsAllLayoutSuspended
        • Parent
        • RemoveFromManagers
        • UpdateLayout
      • GumProjectSave
      • IDataUi
      • InstanceMember
      • InstanceSave
      • InteractiveGue
        • CurrentInputReceiver
        • RaiseChildrenEventsOutsideOfBounds
        • RollOverBubbling
      • IPositionedSizedObject
      • Layer
      • NineSliceRuntime
      • RectangleRuntime
      • Renderer
        • SinglePixelTexture
      • SelectedState
      • SpriteRenderer
        • LastFrameDrawStates
      • SpriteRuntime
        • TextureAddress
        • TextureHeight
        • TextureLeft
        • TextureTop
        • TextureWidth
      • TextRuntime
        • Color
        • FontScale
        • FontSize
        • HorizontalAlignment
        • Text Wrapping
        • VerticalAlignment
      • VariableSave
    • FNA
    • Kni
    • Meadow
    • MonoGame
      • Setup
        • Linking Game Project to Source (Optional)
      • Tutorials
        • Gum Project Forms Tutorial
          • Setup
          • Gum Screens
          • Common Component Types
          • Styling
          • Multiple Screens
        • Code-Only Gum Forms Tutorial
          • Setup
          • Forms Controls
          • ListBox Items
          • Input in Forms
        • [Deprecated] Gum Project (.gumx) Tutorial
          • Setup
          • Gum Screens
          • Gum Forms
          • Customizing Forms
          • Strongly Typed Components Using Code Generation
          • Multiple Screens
      • Setup for GumBatch (Optional)
      • Loading a Gum Project (Optional)
      • async Programming
      • Custom Runtimes
      • GumBatch
      • Gum Forms
        • Binding (ViewModels)
        • Controls
          • Button
          • CheckBox
          • FrameworkElement
            • BindingContext
            • IsEnabled
            • IsFocused
            • ModalRoot and PopupRoot
            • SetBinding
          • ListBox
          • ListBoxItem
          • MenuItem
          • PasswordBox
          • RadioButton
          • ScrollViewer
            • VerticalScrollBarValue
          • StackPanel
          • Slider
          • TextBox
        • Control Customization In Code
          • MenuItem
        • Control Customization in Gum Tool
        • Gamepad Support
        • Keyboard Support
        • Troubleshooting
      • File Loading
      • Resizing the Game Window
      • Known Issues
      • Samples
        • MonoGameGumFormsSample
    • Nez
    • Silk.NET
    • SkiaGum
      • Setup
        • WPF
    • .NET MAUI
      • Setup
Powered by GitBook
On this page
  • Introduction
  • Built-in ViewModel Class
  • Binding Concepts
  • ViewModel-Inheritance
  • BindingContext
  • SetBinding Method
  • DependsOn

Was this helpful?

Edit on GitHub
Export as PDF
  1. Code
  2. MonoGame
  3. Gum Forms

Binding (ViewModels)

Introduction

Gum Forms supports binding of FrameworkElement properties to properties on another object such as a view model. If the view model implements INotifyPropertyChanged then the FrameworkElement subscribes to property changes and automatically updates itself in response to changes.

Binding can be performed on the following types of properties:

  • Properties of primitive types, such as a TextBox's Text property

  • List properties such as a ListBox's Items property

  • Properties on Visuals such as a TextRuntime's Color property

Built-in ViewModel Class

Any class which implements INotifyPropertyChanged can be used for binding, so if you have a preferred implementation that you'd like to use, you can use it with Gum. If you have no preference, or if you are new to binding, you can use Gum's built-in ViewModel class. For simplicity the rest of this document uses the built-in ViewModel class.

Binding Concepts

The approach that Gum uses for binding is common in other C# front end frameworks such as WPF, Maui, and Avalonia. This pattern is addresses a few common considerations when developing UI.

  • Creating a centralized object to hold data

  • Keeping UI in sync with the central data

  • Managing dependencies between properties

  • Separating UI to keep code easier to refactor and test

Typically the class that contains the data is called a view model. View models can also contain logic to respond to actions such as adding health to a player, or subtracting money when an item is purchased. The term view model appears in the pattern Model-View-ViewModel (MVVM), although this document focuses primarily on the binding capabilities in Gum and not the entire MVVM pattern.

ViewModel-Inheritance

The view model class is responsible for storing information that is displayed by the UI, and similarly which can be set by the UI through user interaction. The view model often is specific to a particular page or component. For example, if your game includes an OptionsScreen, then you might also have an OptionsScreenViewModel.

This class inherits from the ViewModel class and contains properties which can be viewed or edited by the OptionsScreen or its contained UI.

Binding to FrameworkElement properties is usually two-way. This means that changes to the UI also result in changes to the view model's property. Similarly changes to the view model in code update the UI. For example, a view model may contain a property named PlayerName which is bound to a TextBox's Text property. If the user changes the text in the TextBox, then the value is also changed on the view model. Similarly, if the code changes the view model's PlayerName property, this change is pushed to the TextBox.

FrameworkElements like TextBox automatically push their changes to their bound view model, so the only code needed to support UI->view model changes is the initial binding.

The view model must broadcast its changes to push changes to the FrameworkElement. The easiest way to do this is to inherit from the Gum ViewModel class and use the Get and Set methods in the property getters and setters.

The following class is an example of an OptionsScreenViewModel which has values for common options in a game.

public class OptionsScreenViewModel : ViewModel
{
    public bool IsFullscreen
    {
        get => Get<bool>();
        set => Set(value);
    }
    
    public float MusicVolume
    {
        get => Get<float>();
        set => Set(value);
    }
    
    public float SfxVolume
    {
        get => Get<float>();
        set => Set(value);
    }
    
}

BindingContext

The BindingContext property exists on FrameworkElement types as well as all visuals which inherit from BindableGue such as TextRuntime and ContainerRuntime.

This property is used to assign the view model that a control should use. The assignment of BindingContext cascades from parent to all children recursively, so typically the BindingContext is only assigned at the top level, such as on a screen or StackPanel.

Once BindingContext is assigned, individual UI properties can be bound to properties on the view model. To bind to a property, a reference to a UI control is needed. This reference can be obtained by using properties in generated code or by getting an instance of a FrameworkElement by calling GetFrameworkElementByName.

The following code shows how to assign an instance of an OptionsScreenViewModel to a code generated OptionsScreen:

var optionsScreen = new OptionsScreen();
optionsScreen.AddToRoot();

var viewModel = new OptionsScreenViewModel();
optionsScreen.BindingContext = viewModel;
// All items within OptionsScreenViewModel inherit the binding context

BindingContext can also be assigned on Forms controls created in code, as shown in the following block:

var stackPanel = new StackPanel();
stackPanel.AddToRoot();

var viewModel = new OptionsScreenViewModel();
stackPanel.BindingContext = viewModel;
// all items added to stackPanel inherit the binding context. 
// For example, the following button would inherit binding context:
var button = new Button();
stackPanel.AddChild(button);

BindingContext can be assigned on FrameworkElements obrained from non-code-generated screens as well. For example, the following code shows how to obtain a StackPanel that was added to a Gum screen and assign its BindingContext.

var stackPanel = myScreen.GetFrameworkElementByName<StackPanel>("StackPanelInstance");
stackPanel.BindingContext = viewModel;
// all children of stackPanel inherit the binding context

SetBinding Method

Once a FrameworkElement is assigned a BindingContext, either directly or indirectly through its parent, it can bind any of its properties to a property on the view model. Usually binding is performed using the nameof keyword for compile time safety.

For example, the following code shows how to bind a Button instance's Text property to a view model's ButtonText property.

buttonInstance.SetBinding(
    nameof(buttonInstance.Text), 
    nameof(viewModel.ButtonText));

Binding can be performed on properties which might change during normal interaction, such as a ListBox instance's SelectedObject. In this case if the SelectedObject changes, the view model's property is automatically updated.

listBoxInstance.SetBinding(
    nameof(listBoxInstance.SelectedObject),
    nameof(viewModel.SelectedObject));

DependsOn

Gum's ViewModel supports using the DependsOn attribute to define dependencies between properties. By using this attribute, changes in one property can result in changes to other properties. The same property can be used as a dependency for multiple properties, allowing changes to one property resulting in many pieces of UI being updated.

The following code shows how a single variable can be used to update multiple UI properties. First we can declare a ViewModel using DependsOn to make IsBrokeTextVisible and MoneyDisplay depend on Money.

class PlayerViewModel : ViewModel
{
    public int Money
    {
        get => Get<int>();
        set => Set(value);
    }

    [DependsOn(nameof(Money))]
    public bool IsBrokeTextVisible => Money <= 0;

    [DependsOn(nameof(Money))]
    public string MoneyDisplay => $"${Money:N0}";
}

We can use the PlayerViewModel to update the UI in response to Money changing. The following block of code shows how to do so in a code-only project.

var viewModel = new PlayerViewModel();

var mainPanel = new StackPanel();
mainPanel.AddToRoot();
mainPanel.BindingContext = viewModel;

var addMoneyButton = new Button();
mainPanel.AddChild(addMoneyButton);
addMoneyButton.Text = "+";
addMoneyButton.Click += (_, _) =>
    viewModel.Money += 100;

var subtractMoneyButton = new Button();
mainPanel.AddChild(subtractMoneyButton);
subtractMoneyButton.Text = "-";
subtractMoneyButton.Click += (_, _) =>
    viewModel.Money -= 100;

var moneyLabel = new Label();
mainPanel.AddChild(moneyLabel);
moneyLabel.SetBinding(
    nameof(moneyLabel.Text),
    nameof(viewModel.MoneyDisplay));

var isBrokeLabel = new Label();
mainPanel.AddChild(isBrokeLabel);
isBrokeLabel.Text = "No more money!";
isBrokeLabel.SetBinding(
    nameof(isBrokeLabel.IsVisible),
    nameof(viewModel.IsBrokeTextVisible));

If this code were in a Screen defined in Gum with code generation, the code might look like the following block:

void CustomInitialize()
{
    var viewModel = new PlayerViewModel();
    // Can be added to the entire screen:
    this.BindingContext = viewModel;
    
    AddMoneyButton.Click += (_, _) =>
        viewModel.Money += 100;
        
    SubtractMoneyButton.Click += (_, _) =>
        viewModel.Money -= 100;
        
    MoneyLabel.SetBinding(
        nameof(MoneyLabel.Text),
        nameof(viewModel.MoneyDisplay));
        
    IsBrokeLabel.SetBinding(
        nameof(IsBrokeLabel.IsVisible),
        nameof(viewModel.IsBrokeTextVisible));
}

PreviousGum FormsNextControls

Last updated 20 days ago

Was this helpful?

Two buttons updating money