Data binding is the process of controlling the appearance and behavior of UI through a separate object usually called a view model. The data binding pattern used by FlatRedBall.Forms is referred to as Model View ViewModel, abbreviated as MVVM. Developers familiar with data binding in WPF, Xamarin.Forms, or Knockout JS will find data binding in FlatRedBall.Forms familiar.
Binding can be performed on FlatRedBall Forms objects similar to binding on other UI frameworks such as WPF and Xamarin.Forms. This tutorial provides a binding example using Button, TextBox, and ListBox instances.
This tutorial uses a Glue screen which contains the following:
TextBoxInstance (TextBox)
AddButton (Button)
ListBoxInstance (ListBox)
RemoveButton (Button)
These instances are all default FlatRedBall.Forms objects which are available in all FlatRedBall projects created with the wizard.
Like the previous tutorial, this tutorial uses a ViewModel for binding. The ViewModel will need properties to bind to the following UI properties:
The string displayed in the text box.
Whether the Add button is enabled. It is enabled only if there is a string in the text box.
The list of objects in the list box.
The selected item in the list box.
Whether the Remove button is enabled. It is only enabled if there is a selection in the ListBox.
Not only will we be binding to FlatRedBall.Forms objects, but the binding will be two way binding. This means that the UI objects can make changes to the view model. For example, when the user clicks on an item in the list box, the SelectedItem is updated automatically.
The ViewModel includes a number of properties which will be controlled by the UI elements:
TextBoxText
SelectedItem
Notice that although these are controlled by the UI rather than code, they are written identical to other ViewModel properties. Also, they can be used as dependency properties for properties like IsAddButtonEnabled and IsRemoveButtonEnabled. In other words, the binding properties and dependency attributes will result in the add and remove buttons automatically enabling and disabling themselves according to properties that they depend on.
The ObservableCollection is used as the type for the ListBoxItems. This enables the ListBox to react to any items being added or removed. Whenever the ListBoxItem's Add or Remove methods are called, the ListBox will create ListBoxItems automatically. Notice that the ListBoxItems property has a private setter - there is no reason to make this public since the ObservableCollection will never change after it has been created.
The ViewModel can be created in the FlatRedBall screen class, just like in the previous tutorial. Binding to FlatRedBall.Forms object properties is done the same way as binding to Gum objects - by using the SetBinding method.
Like the previous tutorial, the add and remove buttons do not access any properties on the UI elements - only properties on the ViewModels are accessed.
Notice that the BindingContext is assigned on the Forms object. The Forms.BindingContext property is the same as the GumScreen.BindingContext property, so either can be assigned.
The BindingContext property is used for all data binding. This tutorial introduces the BindingContext property and creates a simple example using BindingContext to control the UI.
For this tutorial we will create a Gum screen which contains the following:
Points Display
Health Bar
Button for awarding points
Button for taking damage
Button for healing
Normally the actions performed by the buttons listed above would occur through regular game logic (such as collision) but we will use buttons for the sake of simplicity. The specifics of the visuals do not matter, so your screen may look like the following:
A few details in the screen above are important:
The three buttons are FlatRedBall.Forms Button objects. In other words, the three buttons are created in Gum using components which implement the Button behavior. The most common of these is ButtonStandard.
The HealthBar (ColoredRectangle) is contained within a HealthContainer (Container) using the Percent Width Units
Each object in the screen is descriptively named. This will make data binding easier.
The screen is called GameScreenGum. This is the default Gum Screen name for a FlatRedBall Screen called GameScreen. If you have created a game with a GameScreen using the wizard, you will also have a GameScreenGum
The first step in binding to your UI is to create a ViewModel object. ViewModel objects contain all of the properties which you would want to control on your UI programmatically. For the screen above, we will control the following properties:
The string displayed by the PointsTextInstance
The string displayed by the HealthTextInstance
The width of the HealthBar
The ViewModel class should implement the INotifyPropertyChanged interface. Fortunately, FlatRedBall provides a convenient class for this. View models are plain C# classes that are created in Visual Studio. We recommend creating a ViewModels folder in your project, but you can organize them however you like. The following code is an example of what the ViewModel class might look like:
The base ViewModel class provides common functionality for MVVM implementation - specifically the Get and Set functions and the DependsOn attribute. Using the ViewModel base class is not required, and any INotifyPropertyChanged implementation will work.
The Get and Set functions provide a quick way to implement notification to the UI. When the Set function is called (such as when Score is assigned), internally the Score value will be stored, and any object watching for changes to the Score variable will be notified. Furthermore, any object watching properties which depend on Score will also be notified.
The DependsOn attribute creates a dependency relationship between the property which has the attribute (such as ScoreDisplay) and the property which it depends on (such as Score). Once this dependency is established, changing the Score property will also notify the UI that ScoreDisplay has changed. Notice that a single property can depend on multiple properties, as is the case of HealthDisplay depending on both CurrentHealth and MaxHealth.
The ViewModel establishes which properties can be assigned, and the dependency between properties. Once a ViewModel is created, it can be applied to a Gum object. Note that ViewModels can be applied to Gum objects (GraphicalUiElements) or Forms objects (such as Button). BindingContext assignments cascade - you only need to assign the BindingContext at the top level and all children will recursively receive the same BindingContext.
Typically the BindingContext is done at the root level. We recommend assigning the Forms object which is available if you are using Forms in your game. Once the BindingContext is assigned, each individual UI property needs to be bound to the corresponding ViewModel property. The following code shows how this type of binding would be done:
Note that the above code assigns the binding on Gum objects through GumScreen. Although we're not doing it in this tutorial, we could also call SetBinding on Forms objects. For example, if the ViewModel had a property which was to be used as the Text on one of the buttons, the following code could be used:
This topic is covered in more detail in the next tutorial.
The ViewModel object is defined at GameScreen class scope. This is required so that the game can make modifications to the ViewModel after the screen is initialized. FlatRedBall recommends that the ViewModel property is named ViewModel to standardize code across different pages and views.
Once the ViewModel is created, we assign the BindingContext. This assignment tells the GumScreen and everything inside of the GumScreen to use this as its BindingContext. This assignment will also assign the BindingContext on any Forms objects in your screen, so you only need to do this assignment on the GumScreen. Once the BindingContext is assigned, the code establishes individual bindings between UI properties and ViewModel properties. This binding creates an automatic connection between the ViewModel property to the UI property, so that any changes to the ViewModel will automatically update the UI (assuming the Get and Set functions and DependsOn are written correctly in the ViewModel). Notice that the code uses the nameof keyword in C#. The code above could also be written as shown in the following snippet:
While this is less verbose, it produces code which is easier to break. Using nameof provides compile-time checks against referenced properties.
Once the binding is set up, assigning properties on the ViewModel will automatically update the UI. For example, the values above initialize the score and health as shown in the following screenshot:
The ViewModel properties can be updated at any time. Doing so automatically updates the bound UI. For example, the following code can be used to modify the properties in response to button clicks:
Notice that the click events on the buttons do not directly access any UI elements - only the properties on the ViewModel. This makes it much easier to maintain the UI and to continue to add dependencies.
Often times games include custom reusable Forms components. These components may have dedicated ViewModel classes which can also be reused across multiple screens. This walkthrough shows how to create a reusable component, a reusable ViewModel, and how to assign the binding context appropriately.
For this view we will have a simple component which could be used for a game's settings. Note that if you actually need to create a view for settings, you should review the control as a starting point. We'll be creating our own for this tutorial to keep it shorter.
Keep in mind the contents of this control are purely to serve as an example. The actual contents of your control could vary. For this example, consider the following control:
This control has two sliders and a checkbox, so our ViewModel should be built to support these controls. The following is an example ViewModel for this control:
To make the CustomSettingsView forms control reusable, we can set the binding on each of the controls in its custom code. To do this:
Open your project in Visual Studio
Open the CustomSettingsViewRuntime.cs file, which is located in the GumRuntimes folder. Note that if you added the control to a subfolder, you will find the runtime code in a subfolder as well.
Add the following code to CustomInitialize:
Note that this does not actually instantiate the CustomSettingsViewModel - it simply sets the binding so that the contained Forms components (sliders and checkbox) will be bound once the BindingContext is assigned. It is up to the parent container or screen to instantiate and assign the ViewModel.
Now our CustomSettingsViewRuntime is set up to bind its contained object's properties to a ViewModel's properties. Next we need to assign the view model.
This assignment can happen either directly by setting the BindingContext, or it can also be done by binding the BindingContext property. We'll cover both scenarios below and discuss why you might want to use one approach or another.
The simplest approach is to directly assign the BindingContext to an instance of the CustomSettingsViewModel.
For this example we'll consider a FlatRedball Screen called MainMenu which has an instance of the CustomSettingsView in its Gum screen. Even though we won't create it in this tutorial, the MainMenu may also have a corresponding MainMenuViewModel which would be assigned to Forms.BindingContext. We can override the BindingContext on the contained CustomSettingsViewInstance as shown in the following code:
This approach will correctly assign the BindingContext to the CustomSettingsView, but it does require explicit assignment of view models at the Screen level. The next section shows how to set up binding in a way that does not require multiple assignments at the screen level.
The BindingContext property is itself bindable. For simple situations this is not used very frequently, but if you have more advanced UI you may benefit from having binding context. For example, consider a situation where the CustomSettingsView is a child of another view, such as a PauseMenu.
In this situation, the PauseMenu may be part of the GameScreen and have its own binding context. Of course, it would be possible to access the internals of the PauseMenu in the GameScreen as shown in the following code:
This code begins to create a maintenance problem - if the Screen must explicitly assign all children BindingContext properties, then the children (PauseMenu) reusability goes down. Rather, we would want the PauseMenu to be responsible for assigning all of its children's binding context.
Therefore, we could modify our code to support this.
The first step is to modify the PauseMenuViewModel (the ViewModel of the parent component) to contain a CustomSettingsViewModel as shown in the following code:
The next step is to modify the PauseMenuRuntime's code to set the CustomSettingsView's BindingContext. As mentioned above, when CustomInitialize is called the view will not yet have a BindingContext assigned. Therefore, we cannot directly assign the CustomSettingViewInstance's BindingContext, but must bind to it:
Now, the GumScreen does not need to directly assign the CustomSettingsViewInstance's BindingContext. The code has become simpler:
By assigning the PauseMenu's BindingContext, the internal CustomSettingsViewInstance looks for a property on the PauseMenuViewModel with the name "CustomSettingsViewModel. Since the PauseMenuViewModel has this property, this will automatically be bound.
For this particular example it may seem like a pointless tradeoff - the binding context assignment was removed from the GameScreen, but additional code was added to the PauseMenuRuntime and PauseMenuViewModel. This extra code becomes more valuable if the PauseMenu (or any other container) were to be reused across multiple screens.
Notice that the code above has reduced the explicit assignments from three to two. We could further reduce this code by moving the PauseMenuViewMode inside the GameScreenViewModel and similarly binding the PauseMenu's BindingContext inside of the GameScreenGumRuntime. The amount of embedded ViewModel assignment you perform depends on the level of reusability your game needs.
The ListBox type includes the ability to customize individual items using templates. Two properties exist on the ListBox for customization:
VisualTemplate - this allows the usage of any Gum object as an item in a list box
FrameworkTemplate - this allows the usage of a FrameworkElement as an item in a list box. Note that the type of FrameworkElement must inherit from ListBoxItem
The following screenshot shows an example of a customized ListBoxItem. It contains a list box which displays levels with images and a check box.
Although the appearance of each item in the ListBox is different from the default appearance, the ListBox itself is still a standard ListBox with a custom VisualTemplate. VisualTemplates can be used for any type of modification to the ListBoxItem. Common examples include:
Adding additional Text instances for more information, such as the name and price of an item as separate Text instances
Adding images or icons to an object, such as a preview image in a level selection list box
Adding additional controls to a list box, such as a check box for multi-selection
Most of the time games only need a VisualTemplate and do not need to use a FrameworkTemplate. Therefore, this tutorial convers the usage of VisualTemplate which should cover almost all cases. FrameworkTemplate can be used for rare situations where the same visual is used for list box items which have different functionality. Most of the time this different behavior can be achieved with a VisualTemplate and data binding.
By default the ListBox creates one ListBoxItem for every instance in its Items property. The ListBoxItem is defined in the Gum project under the Controls folder as shown in the following screenshot:
The first step in replacing this ListBoxItem in our ListBox is to create a new component in Gum. The easiest way to do this is to copy/paste the existing ListBoxItem as a starting point. You can create a copy by using the CTRL+C, CTRL+V hotkeys or by right-clicking on the component and selecting the Duplicate option.
This component can be structured however you want. There are no requirements for what it must contain, how it must be named, or what states it must contain. By copying the existing ListBoxItem, we bring over the ability for the ListBox to be selected. Even this is optional, so if you do not want your ListBoxItem to respond to selections visually, you can remove the states or modify them to have no impact on appearance.
For this tutorial we will be creating a new ListBoxItem which has two Text instances:
InventoryNameTextInstance
InventoryCountTextInstance
Note that by default ListBoxItems attempt to assign a Text object by the name of TextInstance. By changing our Text names, our new ListBoxItem no longer supports default Text display. If you are creating a new ListBoxItem which is intended to be used as a default ListBoxItem throughout your entire game, you should consider keeping a Text instance with th ename TextInstance. In this case we are creating a ListBoxItem to be used in a specific case so this name isn't required.
To use the newly-created ListBoxItem, first you need a screen with a ListBox. For example, the following screenshot shows a single ListBox in the MainMenuGum screen.
Once this ListBox is added, the following code can be used to populate the ListBoxInstance, including using the new InventoryListBoxItem as the template:
Running the game shows something similar to the following screenshot:
The ListBox contains ten (10) instances of the InventoryListBoxItemRuntime Gum component - one for each integer added to listBox.Items. The VisualTemplate assignment tells the ListBox that each Item should result in a new InventoryListBoxItemRuntime being created.
The code above shows how to create instances of the InventoryListBoxItemRuntime component based on the contents of the ListBox.Items collection. This approach is good for testing how a custom ListBoxItem appears in your ListBox, but it won't work for a full game.
Typically each item in a ListBox needs more information than an integer. In the case of inventory, each item in the list box needs at least a name and count.
We'll create a new class which contains this information. We also want to create a class which will notify the ListBoxItem any time a property changes. To make this simpler, FlatRedBall provides a ViewModel
class which can be used as the base for your custom classes. By using ViewModel, and the Get/Set methods, any changes to properties are automatically broadcasted to UI. In other words, this enables changing a property on the ViewModel-inheriting class, and that updating the UI immediately.
The term "ViewModel" comes from the MVVM pattern which is a common way to display and manage data in C# front ends such as WPF, .NET MAUI, and Avalonia. Classes which inherit from ViewModel are often referred to generally as "view models", and should have the "ViewModel" or "VM" suffix for clarity.
We can create a new ViewModel for our new custom list box items as shown in the following code:
Each InventoryItemViewModel will correspond to an individual InventoryListBoxItemRuntime. We also need to create a ViewModel for the entire screen. This ViewModel contains a list of InventoryItemViewModels in an ObservableCollection. Our ViewModel for the entire screen should match the name of the screen, so we will create a class called MainScreenViewModel.
Finally we can modify our MainMenu.cs class to use the MainScreenViewModel for its items, as shown in the following code:
If we run our game now, the ListBox shows four items (one for each InventoryItemViewModel instance). Notice that we do not directly add items to the ListBox.Items. Instead, we bind the listBox.Items to the viewModel.ListBoxItems. This results in the ListBox automatically keeping in sync with the ViewModel.
We can observe this behavior by adding code to the MainMenu's CustomActivith to add a new item whenever the space bar is pressed.
Note that removing items from the view model also results in removal of the matching item in the ListBox.
Next we'll update our binding so that the Text instances in our InventoryListBoxItem display the name and count properties from our InventoryItemViewModel. To do this, open InventoryListBoxItemRuntime.cs and modify the contents as shown in the following code.
Now if we run our application, each item displays the information from each InventoryItemView.
Next we'll keep track of the selected item. We can do this by creating a new property on the MainScreenViewModel which has
We can bind to the SelectedItem in MainMenu.CustomInitialize. Modify the code to add the "New" code shown here:
Just like all other properties, the SelectedObject/SelectedItem properties stay synced, so we can use the ViewModel's SelectedItem in code. For example, we can modify CustomActivity to increase inventory on the selected item when the Enter key is pressed:
If we run the code, select an item, and press Enter, the inventory count increases.
This tutorial shows how to bind a ListBox and ListBoxItems to view models. Interacting with the view models (adding or removing items, modifying properites on the view models) automatically update the view.
For more information on the specific syntax of ViewModels (such as Get/Set and DependsOn), see the page.