Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The Show method can be used to add a Forms to the necessary managers so that it is drawn and can respond to input events. This method should not be called on Forms instances which have been added through the Gum tool - it is only used on Forms which are added in code, and which do not have a parent.
The following code can be used to add a Button to a Screen in code:
Note that controls which are added in code through Show should be removed manually in the Screen's CustomDestroy.
CheckBox is a component which can be used to represent a bool value (true or false) at runtime.
The CheckBox control can optionally include a Text instance named TextInstance. Setting the CheckBox control’s Text property changes the TextInstance’s displayed string.
A nullable bool indicating whether the CheckBox is checked. Note that the value is a bool? , but the current implementation of FlatRedBall.Forms does not support the *indeterminate *value (a value of null ). IsChecked can be read from the control or can be explicitly set on the control as shown in the following example:
CheckBox provides events for when it is checked and unchecked as shown in the following example:
CheckBox also exposes the more general Click event which can be used to handle any click regardless of IsChecked state, as shown in the following example:
The FrameworkElement class serves as the base class for all FlatRedBall.Forms Controls. A FrameworkElement has the following characteristics:
A Visual object of type GraphicalUiElement (Gum runtime object)
X and Y values, using the Visual's unit types
ActualX and ActualY, returning the pixel coordinates of the left and top of the control, respectively
HandleTab can be used to tab to the previous or next item which can receive focus. This is a manual way to force tabbing rather than relying on the built-in functionality. For information about how to execute automatic tabbing with gamepads, see the Forms and Xbox30GamePad tutorial.
If a button has focus, it can pass focus to the next button. The following code shows how to do this using the keyboard:
If your game has multiple elements which can be focused you can handle tabbing regardless of which element has focus as shown in the following code:
The Visual property is the underlying Gum object used to render the Framework Element. The Visual object is of type GraphicalUiElement.
The Forms object can be thought of as a wrapper to the underlying Visual object. The Visual object provides some unique functionality that is not available directly to the Forms object. The following is a brief summary of what each offers: The Forms object:
Provides a standardized interface for performing common operations such as setting a Button's Text, adding items to a ListBox, and highlighting text in a TextBox.
Provides basic controls for positioning and sizing through X, Y, Width, and Height
Provides standard access to state properties such as IsEnabled, IsMouseOver, and parent/children relationships
By contrast, the Visual object provides:
Full control over positioning using all Gum properties such as X, Y, XUnits, YUnits, XOrigin, YOrigin, and ChildrenLayout
Full control over sizing using all Gum properties such as Width, Height, WidthUnits, and HeightUnits
Full access to all custom properties (such as custom states) if converted to its specific type
Animation support
State interpolation support
Property assignment through strings
Most cases require interacting only with the main Forms object, but if additional flexibility is needed then the Visual object can be accessed and modified. Note that some properties (such as a Button's state) are controlled by the Forms object, so modifying these values may result in the Forms object overriding the changes in response to runtime logic (such as the mouse hovering over a Button).
Forms and their Visuals have a two-way relationship. If you have access to one, you can get access to the other. For example, consider the following code:
DefaultFormsComponents is a static property in the FrameworkElement class which associates a FlatRedBall.Forms control to a default Gum component type. DefaultFormsComponent is used whenever code instantiates a FlatRedball.Forms object, allowing the internal engine to automatically create visuals for it. In other words, this association defines the default appearance of a FlatRedBall.Forms object by associating it with a Gum type. For example, the following code shows how a simple button can be created. Notice that this code does not require instantiating a Gum object - the Button automatically has a Visual object created internally.
Internally, the Button class looks at the DefaultFormsComponents dictionary. It searches for an entry for its type ( typeof(FlatRedBall.Forms.Controls.Button)
), and if it finds a match, it uses that as its visual.
If your project has FlatRedBall.Forms added (default if you used the wizard), then FRB automatically populate the DefaultFormsComponents dictionary according to behaviors assigned to components in the Gum project. Unmodified projects which include Forms components will have a standard component for each Forms control. The code for this is added to the GumIdb.Generated.cs
file. For example, the code may look like this:
Keep in mind this association can be overridden. For example, you may add the following to your custom code:
The DefaultFormsComponents can be assigned and re-assigned any number of times, allowing a project to have per-screen or even per-function default behavior, rather than a global association.
Note that you can also add additional entries for any custom Forms type - you are not limited to Forms types which ship with the FlatRedBall.Forms library. Also, if you have custom .Forms controls that should have default implementations, you should consider doing so before any Screen runs, such as in Game1.cs. This guarantees that any loading code
The ComboBox control (also often referred to as a drop-down control) allows the user to select a value from a list of options. It expands and collapses in response to user activity.
The ComboBox control requires:
An Text named TextInstance
An object named ListBoxInstance which implements ListBoxBehavior (is a ListBox)
The ListBoxInstance is the part of the combo box which appears when the user clicks on the main body. The ListBoxInstance hides when the user selects an item in the list box, clicks on the combo box main body, or clicks outside of the combo box. The combo box gum component can have the list box default to visible, but the ComboBox controll will control its visibility at runtime. The ListBoxInstance is typically positioned outside of the bounds of the ComboBox. The ComboBox control does not control the size of the list box when it is visible (when the ComboBox is expanded) - this is controlled by the Gum component.
The ComboBox class provides a similar interface to the ListBox class. This includes the following properties:
Items
SelectedIndex
SelectedItem
ListBoxItemGumType
ListBoxItemFormsType
Furthermore, the ComboBox class provides the following event:
SelectionChanged
For more information on working with these properties and events, see the ListBox page: http://flatredball.com/documentation/api/flatredball-forms/controls/listbox/
The ListBoxItemGumType property is used to automatically instantiate ListBoxItem instances as needed whenever new objects are added to the Items property. This property is not needed if adding ListBoxItem instances to Items. The ListBoxItemGumType should be a Gum runtime type which implements the ListBoxItem behavior. The following example shows how to use the ListBoxItemType. property:
For more information on using the ListBoxItemType, see the ListBox and ListBoxItem pages.
The ComboBox's Items collection exposes the Items collection of the Listbox which appears when the ComboBox expands. For information on working with Items, see the ListBox page.
Button is a standard clickable object with states for enabled (default), hover, pressed, and disabled.
The Button control has no requirements - an empty container is sufficient.
The Button control can optionally include a Text instance named TextInstance. Setting the Button control's Text property changes the TextInstance's displayed string.
Buttons provide events for Click and Push events. The following code shows how to handle these events on a button obtained from a gum runtime object named ButtonInstance:
The DialogBox control is used to display dialog to the screen. It provides a number of common dialog functionality including:
Typewriter (letter by letter) display of the text
Multi-page display using an IEnumerable
Task (async) support for logic after
Force display of entire page and page advance input
Input support using keyboard, mouse, and gamepads
Like other FlatRedBall.Forms controls, the easiest way to create a DialogBox is to add a DialogBox instance into your screen. By default dialog boxes are visible, but you may want to mark yours as invisible in your Gum screen so it doesn't display in game until you need it to display. For most games only a single DialogBox instance is needed unless you intend to have multiple dialog boxes displayed at the same time.
To display a dialog box, use one of the Show methods. The simplest is to call Show with a string, as shown in the following code:
Alternatively, multiple pages can be displayed using an IEnumerable such as a string array as shown in the following code snippet:
DialogBox can display multiple pages through the Show and ShowAsync methods. Each string is treated as an entire page if it fits. A single string will be be broken up into multiple pages if it is too large.
The following code results in multiple pages automatically being set.
Automatic paging only applies if the number of lines is limited on the backing Text object. The default implementation of the DialogBox should automatically limit the number of lines.
The default implementation's Text property has the following relevant properties:
Note that if the Text property can extend indefinitely - either by allowing it through a Text Overflow Vertical Mode of Spill or by having a Height Units of Relative to Children.
The ShowAsync method returns a task which can be used to await for all pages to be shown and for the final page to be dismissed. A common usage of ShowAsync is in a scripted sequence. For example, a scripted sequence may combine dialog and player movement. Since the player can choose when to advance text, the amount of time that a DialogBox is displayed must be awaited. The following shows how code might be used to implement a scripted sequence which combines dialog being displayed and player movement.
Note that if your game requires advancing the dialog with the Keyboard or Xbox360GamePad, then the DialogBox must have its IsFocused property set to true. See the section on IsFocused for more information.
The DialogBox control provides a few approaches for showing multiple pages. As shown above, the Show method can take an array of string
s. Alternatively, the ShowAsync
method can be used to show one page at a time.
This approach is useful if your DialogBox implementation has additional properties for each page of dialog. For example, a DialogBox can be modified in Gum to have a Text instance displaying the name of the person speaking.
Since the Show method exists on the standard DialogBox, it does not have a way to specify the speaker. We can access the visual on the DialogBox to modify the SpeakerTextInstance directly through the Visual property.
Note that to access the SpeakerTextInstance, the Visual must be used, which is a reference to the Gum object. The dialogBox
is an instance of the standard DialogBox
forms object, so it ony provides methods and properties common to every DialogBox
. For more information about Forms vs Gum objects, see the Forms vs Gum in Code Tutorial.
DialogBox text fully support styling, including pages. The following code results in styled text.
More information on styled text can be found in the Gum documentation: https://docs.flatredball.com/gum/gum-elements/text/text#using-bbcode-for-inline-styling
DialogBox responds to input and can respond to two types of input: confirm input and cancel input.
Confirm input performs the following actions:
If dialog is printing out character-by-character, the entire page is immediately displayed
If the entire page is displayed and the DialogBox has more pages to display, the page is cleared and the next page begins displaying
If the entire page is displayed and the DialogBox has no more pages to display, the dialog box is dismissed
Cancel input performs the following actions:
If dialog is printing out character-by-character, the entire page is immediately displayed
If the entire page is displayed and the DialogBox has more pages to display, the page is cleared and the next page is displayed in its entirety
If the entire page is displayed and the DialogBox has no more pages to display, the dialog box is dismissed
In other words, confirm and cancel input behave the same except that cancel immediately prints out the next page, giving players the choice to skip letter-by-letter display.
Dialog can be advanced with Mouse, Keyboard, Xbox360GamePad, and a custom Func
predicate named AdvancePageInputPredicate
. Note that if a DialogBox has a non-null AdvancePageInputPredicate
, then all other forms of input are ignored. This allows games to fully customize a DialogBox's page advance logic.
The Mouse can only perform confirm input. If a dialog is clicked, then its confirm action is executed.
The Keyboard's IInputDevice implementation is used for confirm and cancel actions:
Space (DefaultPrimaryActionInput) is used to confirm
Escape (DefaultCancelInput) is used to cancel
Note that the keyboard actions will only apply if the DialogBox has focus. For example, the following code shows how to give a DialogBox focus:
For more information on IsFocused and DialogBoxes, see the section below.
Xbox360GamePad Input
Xbox360GamePads can be used to advance dialog. A DialogBox must have focus for the Xbox360GamePads to advance dialog, just like the Keyboard. Furthermore, the desired Xbox360GamePads must be added to the GuiManager's GamePadsForUiControl as shown in the following code:
For more information, see the GuiManager.GamePadsForUiControl page.
Once gamepads are added, the dialog box can be shown and focused just like in the example above for Keyboard input.
To customize advance behavior, the AdvancePageInputPredicate delegate can be used to control DialogBox advancement. This method can be used to advance dialog box behavior using custom input or other conditions such as the completion of a tutorial. DialogBoxes must have focus or their AdvancePageInputPredicate will not apply.
The following code shows how to advance the page on a secondary click. Note that this code does not perform any additional logic, such as whether the cursor is over the DialogBox. This means that right-clicking anywhere on the screen advances the dialog.
As mentioned above, assigning AdvancePageInputPredicate prevents all other default page advance logic, so the user will not be able to advance the dialog with the keyboard, gamepads, or with a left-click on the DialogBox.
As mentioned in the section on DialogBox Input, Xbox360Gamepad and Keyboard input will only advance and dismiss the dialog box if IsFocused is set to true. This must be set explicitly to give the dialog box focus. Note that mouse clicks will advance the dialog box automatically even if the DialogBox's IsFocused is not set to true.
Typically IsFocused is set to true whenever dialog is displayed. When a DialogBox is dismissed, it is hidden and its IsFocused is set to false. This means that if multiple DialogBox pages are displayed one-after-another using ShowAsync
, IsFocused
must be set to true before each ShowAsync
call is performed, as shown in the following code:
The dismiss method can be used to visually remove a DialogBox and raise events as if it has been advanced. The Dismiss method performs the following logic:
Makes the DialogBox invisible (sets IsVisible to false)
Raises the PageAdvanced and FinishedShowing events
Clears all pages so that the dialog box can be used "fresh"
Removes focus (sets IsFocused to false)
The SettingsView control provides common settings for controlling your game's audio and full screen status.
Specifically, the SettingsView can be used to control:
Song (music) volume
Sound effect volume
Full screen/windowed
Note that future versions of SettingsView may expand to include more controls and settings in the future.
The first step in using the SettingsView is to add an instance of the view to your Gum screen. If your project has included FlatRedBall.Forms then you have a default SettingsView in Gum.
This behavior can be disabled by setting IsAutoApplyingChangesToEngine to false.
By default the IsFullscreen property does not automatically apply a full screen/windowed status to the engine. At the time of this writing full screen status is typically controlled through generated code, so the SettingsView does not have access to the methods necessary to set full screen/windowed.
To toggle full screen you can either bind a ViewModel to the IsFullscreen property or you can subscribe to the FullscreenSet property.
The following code shows how to subscribe to the FullscreenSet property:
The RadioButton control can be used to select from a number of options. RadioButton instances are grouped according to their parent and a group name. Only one RadioButton in a parent/group combination can be selected at once. Selecting another RadioButton in the same group will deselect other RadioButton instances.
The RadioButton control can optionally include a Text instance named TextInstance. Setting the RadioButton control’s Text property changes the TextInstance’s displayed string.
The default Group for RadioButton instances is an empty string. Therefore, all grouping is done by parent. This is sufficient for most cases as groups of radio buttons may need to be visually separated from one another, and this is often done by assigning different parents in Gum.
The RadioButton class provides events for whenever it is checked, unchecked, or clicked (similar to the CheckBox class). The Checked and Unchecked events can be handled to respond to the specific state changing, as shown in the following code example:
RadioButton also exposes the more general Click event which can be used to handle any click regardless of IsChecked state, as shown in the following example:
The OnScreenKeyboard, also referred to as a "software keyboard", can be used to enter text in a TextBox using a GamePad. Console games and games which use a controller as a primary input device will usually include some form of OnScreenKeyboard for text entry. Although the OnScreenKeyboard is primarily designed to be used with a GamePad, the mouse can also be used to click on the individual keys. The OnScreenKeyboard must always be paired with a TextBox. Creating an OnScreenKeyboard without pairing it to a TextBox will result in runtime exceptions when the user attempts to click on one of the keys.
Strictly speaking, the OnScreenKeyboard has no layout requirements - it can have any controls. However, to be functional, it must contain at least one object implementing ButtonBehavior. A typical keyboard will have many ButtonBehavior-implementing instances - one for each key. Buttons can be either buttons with special functionality or regular keys. A button is designated as being special by its name. The following names provide special functionality:
KeyBackspace - deletes the character before the caret
KeyReturn - currently not functional as of February 19, 2021, but will provide functionality in future versions
KeyLeft - moves the caret to the left one character
KeyRight - moves the caret to the right one character
KeySpace - inserts the space character at the current caret index
Any button which does not have one of the names listed above will insert the same character as its text into the text box. This behavior allows keyboards to be fully customizable - they can provide as many or as few characters as desired.
Note that although the diagram above displays buttons as direct children of the OnScreenKeyboard, buttons can be added as children of containers, and the hierarchy can be of any depth. All buttons will be recursively found and used by the keyboard.
The OnScreenKeyboard must be paired with a TextBox instance. Usually this is done by placing an instance of an OnScreenKeyboard in the same Screen or Component as a TextBox, and adding the following code in the initialization of the Forms object:
The code above assumes that the OnScreenKeyboard and TextBox are named KeyboardInstance and TextBoxInstance, respectively. Once this association is made, clicking on a key will modify the TextBox. Note that by default, the TextBox will lose focus when a button on the OnScreenKeyboard is clicked. This can be solved by setting the TextBox instance's LosesFocusWhenClickedOff to false.
As mentioned above, the OnScreenKeyboard requires an associated TextBox. The easiest approach for implementing an OnScreenKeyboard is to place both controls in a Gum page.
This implementation will create a matching Forms class. For example, if above is in GameScreenGum, then your project would have a class called GameScreenGumForms. This Forms class will contain both the Keyboard and TextBox so the setup can be performed as shown in the following code:
Alternatively, initialization can happen in the FlatRedBall Screen using the Forms object as shown in the following code snippet:
For this example, the following Gum layout will be used:
Note that the button will not be used except to show that tabbing to a different control is possible when the keyboard is not active. The following code enables full control of the text box with the gamepad:
The code above results in a fully functional keyboard controlled by the gamepad, as shown in the following animation:
The ListBox is a scrollable view which displays multiple ListBoxItem instances. When one ListBoxItem is selected, the previously-selected ListBoxItem becomes deselected.
The ListBox control requires:
An object named VerticalScrollBarInstance which implements ScrollBarBehavior (is a ScrollBar)
An object named InnerPanelInstance of any type (typically a Container)
An object named ClipContainerInstance of any type (typically a Container with ClipsChildren set to true)
The ListBox control typically handles the creation and positioning of ListBoxItem instances. The InnerPanelInstance in the list box Gum component will typically use a Children Layout value of TopToBottomStack.
Alternatively the InnerPanelInstance can use a Children Layout value of LeftToWriteStack with the Wraps Children value set to true.
Items represents the data that the ListBox is managing. Items can either contain regular types (such as strings, ints, or classes representing data in your game like data for a car in a racing game), or instances of ListBoxItems. Whenever an object is added to the Items collection the list box will automatically update its visuals to display the newly-added object. If the newly-added object is a regular type, then the ListBox will internally construct a new ListBoxItem.
The most common types of items added to a ListBox are
Strings - these display in the ListBox. Usually strings are used in ListBoxes when first learning how to use ListBoxes, to diagnose problems related to displaying items in a ListBox, or to add debug information to a game
ViewModel - for final implementations, using ViewModels is recommended when populating ListBoxes.
Any object type can be added to a ListBox. The following code shows how to add strings to the Items property, resulting in the list box displaying a single entry for each item.
The selected item can be controlled using a number of properties.
The SelectedItem property gets and sets the selected item. Setting this value will select the first matching instance found in the Items property. The following code example adds three strings, then selects the second one:
Items can be deselected by setting the SelectedItem to null;
SelectedIndex The SelectedIndex property gets and sets the index of the currently selected item. A value of -1 indicates no selection. The following code example shows how to deselect the selected item in a ListBox:
The SelectionChanged event is raised whenever a selection changes due to a mouse click or code change of the SelectedItem or SelectedIndex. The event provides a list of newly-selected items and deselected items. Note that at the time of this writing only a single item can be selected at a time, but future versions of FlatRedBall.Forms may add multi-selection support. The following code example shows how to react to the selection changing on a ListBox:
The event handling the selection changing can also use the SelectedItem property, as shown in the following code:
The ScrollIntoView method scrolls the ListBox so that the argument item is in view. The argument object should be one of the items in the ListBox's Items property.
The following code shows how to bring an item into view, assuming ItemToBringIntoView is a valid instance inside the ListBox's Items list.
Scrollbar is a common control used when scrolling is needed to display information. ScrollBars are included in other standard FlatRedBall.Forms controls such as ListBox and ComboBox, but they can be used in custom controls as well.
The ScrollBar control requires:
An object named UpButtonInstance which implements the ButtonBehavior (is a Button)
An object named DownButtonInstance which implements the ButtonBehavior (is a Button)
An object named ThumbInstance which implements the ButtonBehavior (is a Button)
Although not required, the following is strongly recommended:
An object of any name (typically TrackInstance) which contains the ThumbInstance
The thumb in a Slider visually represents the Value variable. FlatRedBall.Forms will automatically adjust the height of the thumb according to the ViewportSize, Minimum, and Maximum variables, therefore the thumb should use a visual element which does not distort when scaled vertically (such as a NineSlice or ColoredRectangle). The thumb will always be contained vertically within the track, and the thumb can move and extend to the edges of the track. Therefore, usually the track should not overlap the up and down buttons.
Although the thumb, up button, and down button are required, they do not need to be visible. Certain styles of scroll bars do not include up and down buttons - especially of the game is not controlled with a mouse. In this case the up and down buttons can be set to invisible. If the buttons are invisible a track is not necessary - the thumb can be a direct child of the component. Unlike the Slider control, the track will always be fully contained within its parent object, so a track is only necessary to add padding between the track and the edge of the Slider control.
The four values which are used to control the scrollbar behavior are:
Minimum - The smallest number for Value
Maximum - The largest number for Value
ViewportSize - The size of the thumb, where the ViewportSize plus the value range equals the entire range (see below for an example)
Value - The current value, with inclusive minimum and maximum values of Minimum and Maximum
For example, the four values can be set as shown in the following code:
This can be visualized as shown in the following image:
Keep in mind that these values do not change the height of the ScrollBar. This is controlled by the Height property just like every other FlatRedBall.Forms control, and it is subject to sizing variables just like all other Gum objects.
SmallChange and LargeChange control how the ScrollBar's Value changes in response to UI events. SmallChange controls the change in Value when the up or down buttons are clicked. LargeChange controls the change in Value when the mouse is clicked on the track (the area between the up/down buttons and the thumb. The following example code shows how to assign the SmallChange and LargeChange variables:
This code produces the behavior shown in the following animation:
ListBoxItem is a selectable control used in the ListBox control or controls which contain a ListBox (such as ComboBox). ListBoxItem instances can be manually instantiated just like any control, or may be instantiated internally by the ListBox control.
The ListBoxItem control has no requirements – an empty container is sufficient.
The ListBoxItem control can optionally include a Text instance named TextInstance. Setting the ListBoxItem control’s Text property changes the TextInstance’s displayed string.
By default ListBoxItem instances have a single Text object to display information. Some games may require each ListBoxItem to display more than just text. For example, a list box used to display items that a player can buy may display the item name, item icon, and item price. The easiest way to display custom data in a ListBoxItem is to use data binding. Customizing controls doesn't require the creation of any new classes or inheritance - the binding can be performed in the Gum runtime's custom code. For this example, we will use a Gum item which has a Text object for the item description and another for the item cost, as shown in the following image:
Unlike our normal ListBoxItem, this item contains two text objects.
Also, note that this Gum item should the ListBoxItemBehavior added as well, as shown in the following image:
Now that we have this set up, we need to do the following things:
Assign the ListBox.ListBoxItemGumType to use this ListBoxItem
Add our data to the ListBox for the store
Bind the visuals to the items in the StoreListBoxItemRuntime.cs file
First we need to assign the ListBoxGumType to the ListBox. This tells the ListBox which type of Gum visual to use for each item. Assuming you already have access to your ListBox forms object, your code should look similar to the following snippet:
Next we add items to our ListBox. The simplest way is to add items to the ListBox.Items property. These items can be any class because in the next step we'll write code to use that class to update the visuals on the ListBoxItem. For example, if your game defines inventory in a CSV file, you may have a dictionary of inventory items. Similarly, if your game defines states in an entity, then each state is also available in a dictionary. We'll assume that the data is stored in a CSV called GlobalContent.StoreData.
We can access the values in the CSV in a foreach loop to fill the listBox.
At this point the ListBox should fill up with the data from the CSV, but it will not display any information about the store items - we'll set that up next.
Next we can bind our visuals to display information from our StoreData. Binding is performed using the SetBinding function which exists on every Gum and Forms object. In this case we will bind the Text property on our two Text objects to the Name and Cost properties on our StoreData items. To do this, we will add the following code to our StoreListBoxItemRuntime.cs (the custom code file created for our Gum object).
Now our UI displays the values in the CSV.
Above we covered how to use data binding to update the visuals on a ListBoxItem. Another option is to create a derived ListBoxItem and handling the visuals manually. This approach is useful if you are not comfortable with using binding. For this example consider a racing game with a list of cars. We can create a CarListItem in Gum to display information about cars in a player's garage.
Notice that this list item does not have a single Text but instead four - for displaying the year, make and model, horsepower, and weight. This shows that list items can contain anything to display the necessary information or decoration.
For this example we'll use a data class called CarData, defined as shown in the following code:
We need to create logic to assign the values from CarData instances to the text values in the CarListItem Gum component. For this we can create a new class which inherits from ListBoxItem, as shown in the following code:
Finally, we can get our ListBox instance and assign the desired Gum and Forms types, as shown in the following code:
Now if we add any objects that are not ListBoxItems, the ListBox will internally use the CarListBoxItem control and assign a CarListItemRuntime Gum runtime for its visual. For example, the following code could be used to populate the list box:
This creates a list of our cars.
HeightUnits of RelativeToContainer - the height unit depends on the container, so the height does not increase as more lines of Text are added.
Text Overflow Vertical Mode of Truncate Line - this prevents text from spilling over the bounds.
Once the SettingsView has been created and added to a Screen. By default the music slider modifies and the sound effect slider modifies .
This code assumes that you are using the generated CameraSetup code provided by the .
The RadioButton control has no requirements – an empty container is sufficient.
ListBox inherits from .
The requirements for the ListBox are identical to the requirements for the ScrollViewer control. For more information on requirements, see the ScrollViewer page:
ListBoxItems can be customized using the VisualTemplate to customize how items in a ListBox are displayed. For more information on how to use Visual Template, see the page.
ListBoxItem instances are typically created and added to a ListBox. For more information and examples on working with ListBoxItem instances in a ListBox, see the page.
The StackPanel control is a container which can be used to hold children elements which can be stacked either horizontally or vertically.
Unlike other FlatRedBall.Forms controls, the StackPanel does not use a Gum runtime object from the existing project. Rather, the StackLayout is itself an invisible control which can contain other objects. Note that even though the StackLayout doesn't use a component defined in your Gum project, it still has a backing Visual object so it can be added to any other FlatRedBall.Forms object and it has full support for all Gum variables such as position and size.
The following code creates a StackLayout, sets it to be drawn (meaning its children will be drawn), then uses AddChild to add four buttons.
Note that the above code creates Button instances using the Button constructor. In a normal project this would instantiate the Buttons using the default Gum visual object as specified on the DefaultFormsComponents static property.
The Orientation property sets whether children stack horizontally or vertically. The default value is Orientation.Vertical. The following code shows how to change the orientation to Horizontal.
The TextBox control can be used to let the user enter text input such as a profile name.
The TextBox control requires:
An Text named TextInstance
An object named CaretInstance of any type
Although not required, the TextInstance should be contained within an object that has its **Clips Children **set to True. This can be a container within the TextBox, of the TextBox component can have its Clips Children value set to true.
Optionally, the TextBox can contain:
An object named SelectionInstance
The TextChanged event is raised whenever the Text on the TextBox changes. Keep in mind this occurs on every character changed, so if the user types multiple characters, the event will be raised for every character, as shown in the following code example:
By default the LosesFocusWhenClickedOff property is set to true. If this property is true, the TextBox instance will lose the focus when the user clicks elsewhere. This functionality matches functionality in other UI systems such as WPF and Xamarin.Forms. If a TextBox is to be associated with an OnScreenKeyboard, you may want to set this property to false so the caret doesn't become invisible when clicking on the OnScreenKeyboard buttons.
The Slider control can be used to set a value between a minimum and maximum value. For example, Sliders can be used to set color values in a game tool.
The Slider control requires:
An object named ThumbInstance which implements the ButtonBehavior (is a Button)
Note that the current implementation of FlatRedBall.Forms only supports a horizontal slider, so the Gum component should be laid out such that the thumb can move left and right.
The thumb represents the Slider's Value. It can be moved by clicking + dragging the thumb or by clicking on the track (the thumb's parent). The thumb's parent may be the component itself, or it may be a Container instance within the component. If an explicit track is used, we recommend naming it TrackInstance. While this is not required by the Slider control, Sliders may not behave correctly if not naming the track TrackInstance.
A track parent of the thumb can be used to control the visual bounds of the thumb by placing a padding between the min/max values and the edges of the component.
The thumb's minimum and maximum X position will be defined by its parent's bounds. For example, the following diagram shows the minimum and maximum position of the thumb inside a Slider with no explicit track container:
Notice that the thumb's width is not considered when setting the mins and maxes. The min and max positions can be made symmetric by changing the Thumb's X Origin to Center. Furthermore, the edges of the thumb can be kept within the bounds of the component by adding a track. Otherwise, parts of the thumb may fall outside of the component where the clicks are ignored. The following image shows the minimum and maximum position of a thumb using X Origin of Center and an explicit Track container:
Keep in mind that the track need not be centered inside the component. It can be offset to account for an asymmetric thumb, or to allow for additional visuals besides the track.
As shown above, the thumb can be added as a child of a container to control its minimum and maximum X position. Doing so, along with setting the X Origin to Center is a common way to create a Slider, but this does introduce a subtle problem. By default, a cursor must overlap a visual element for its children to also be tested. If a child is not fully contained inside of its parent, then part of it may not receive cursor events. For example, consider the image above. Notice that the thumb is is not fully contained in its parent's bounds. Part of the thumb hangs over the edge of its parent as shown in green.
The user may expect to be able to click+drag the thumb by grabbing it when it hangs over the container parent, but this will not work. To solve this problem, the parent's RaiseChildrenEventsOutsideOfBounds can be set to true in the gum runtime's custom code. For example, if your slider Gum object is called SliderRuntime , and if the thumb's parent is called ContainerInstance , the following code can be used to enable grabbing the thumb even if it hangs over the edge of its parent:
The Value variable represents the current value in the scroll bar, which falls between the Minimum and Maximum values inclusively. By default there is no visual representation of the value, so it can be printed out to screen using the FlatRedBall Debugger. The following code example shows how to set a a Slider to display values between 0 and 100, and to output them in real time.
The ValueChanged event is raised whenever the Value property is changed on a slider. The ValueChanged event enables writing logic to respond to the changed slider only when the value changes. The FlatRedBall Debugger.CommandLineWrite method is used to display the value when it changes.
TicksFrequency can be used to snap values rather than allowing every value between Minimum and Maximum. TicksFrequency is a numeric value specifying the snapping value. It only applies if IsSnapToTickEnabled is set to true. The following code can be added to the example above to snap the values to whole numbers:
If IsMoveToPointEnabled is set to true then clicking on the track will move the thumb to the clicked point, rather than moving the thumb up or down the Slider by the LargeChange value.
The SmallChange property controls the change in value when pressing left or right when the Slider is focused and is controlled by a GamePad. For example, the following code produces the behavior shown in the animation:
The TreeView control is a scrollable view which can contain a hierarchy of TreeViewItems. The TreeView is conceptually similar to the ListBox control, but allows items to be embedded within other items.
The TreeView control requires:
An object named VerticalScrollBarInstance which implements ScrollBarBehavior (is a ScrollBar)
An object named InnerPanelInstance of any type (typically a Container)
An object named ClipContainerInstance of any type (typically a Container with ClipsChildren set to true)
The Items property represents the top-level data displayed in the TreeView. Items can contain regular types (such as strings, ints, or classes representing data in your game such as car data in a racing game) or TreeViewItem instances. When an object is added to the Items collection the TreeView will automatically update its visuals to display the newly-added object. If the newly-added object is a regular (non TreeViewItem) type, then the TreeView will construct a new TreeViewItem internally. If the newly-added object is already a TreeViewItem, then the TreeView will display the TreeViewItem as-is. Note that at the time of this writing, adding TreeViewItems is required to create a hierarchy.
The following code shows how to create a hierarchy of objects. Notice that the code constructs a TreeViewItem, then uses the reference to add additional objects as sub-items of the TreeViewItem.
Note that the TreeView's Items property only contains the top-level item. In the example above, the treeView.Items property only contains a single entry - Monster. The specific monster types are contained in the Items property of the parent TreeViewItem.
For information on creating a custom TreeViewItem, see the TreeViewItem documentation.
Returns the selected object, or null if nothing is selected. This returns the object as it was originally added to the TreeView or its parent TreeViewItem.
SelectedItem returns the selected TreeViewItem, or null if one isn't selected. This property may return a nested item. Note that even if a non-TreeViewItem object is added to a TreeView, the SelectedItem for that added object will still be a TreeView. SelectNextVisible/SelectPreviousVisible Selects the next or previous visible object sequentially. This may select an object that above or below the hierarchy. This can be used to implement selecting items using a keyboard (up or down arrows) or game pad (up or down on dpad or analog stick). If no object is selected, these methods will throw an InvalidOperationException. The following example code could be used to move the selection using the arrow keys:
The ScrollViewer control is a general-purpose control which can be used to display a scrollable area. The ScrollViewer exposes a control called InnerPanel which is the host for all content within the ScrollViewer. The InnerPanel is a Gum GraphialUiElement so its sizing and positioning rules are flexible and can be modified either in code or Gum.
The ScrollViewer control requires:
An object named VerticalScrollBarInstance which implements ScrollBarBehavior (is a ScrollBar)
An object named InnerPanelInstance of any type (typically a Container)
An object named ClipContainerInstance of any type (typically a Container with ClipsChildren set to true)
The clip container is a container which will hold the InnerPanelInstance. The hierarchy of controls should be:
ScrollViewerComponent
VerticalScrollBarInstance
ClipContainerInstance
InnerPanelInstance
Contents of the ScrollViewer - added at runtime through code
This hierarchy allows each control to serve a separate function. The clip container's purpose is to clip the drawing of all contained objects. The clip container prevents the inner panel (and all controls within the inner panel) from overlapping the vertical scroll bar and from spilling out of the scroll viewer. The clip container should be of standard type Container because this control is typically used for clipping children. The clip container should have its ClipsChildren variable set to true. For information on this property, see the Gum documentation on ClipsChildren: http://vchelaru.github.io/Gum/generalproperties/Clips%20Children.html The clip container will typically fill the most of the scroll viewer component, leaving room for the vertical scroll bar and any desired border. This can be accomplished by setting WidthUnits and HeightUnits to RelativeToContainer.
ScrollViewer instances support hiding or showing the VerticalScrollBarInstance. This can impact the horizontal size of the ClipContainerInstance. In the case of a hidden VerticalScrollBarInstance, the ClipContainerInstance should extend to occupy space that would otherwise be used by the VerticalScrollBarInstance. To respond to the VerticalScrollBarInstance being hidden and shown, the ScrollViewer Gum object should have a state category titled ScrollBarVisibility with two states:
NoScrollBar
VerticalScrollVisible
These two states should adjust the visibility of the VerticalScrollBarInstance and the width of the ClipContainerInstance as shown in the following diagram:
Keep in mind that this category applies to any control which inherits from ScrollBar, such as ListBox. Also, these values should not be directly assigned in code, but rather are controlled through the ScrollBar's VerticalScrollBarVisibility property (see below).
The inner panel is used to provide the following behavior:
Scrolls all contents in response to the vertical scroll bar events
Provides size information to the vertical scroll bar to resize the thumb (see more information below)
Provides a common parent to all custom content for scrolling
Currently the ScrollViewer control only supports vertical scrolling, so the inner panel should be configured to grow and shrink only on the Y axis. This can be accomplished by setting the following values on the InnerPanelInstance:
X = 0
X Units = PixelsFromLeft
Y = 0
Y Units = PixelsFromTop
Width = 0
Width Units = RelativeToContainer (makes the panel stretch horizontally to the width of the ClipContainerInstance)
Height = 0
Height Units = RelativeToChildren (makes the panel initially have no height, but makes it stretch vertically according to its contents)
Setting HeightUnits to RelativeToChildren results in the inner panel enables automatic resizing according to the inner panel's children (which are added at runtime).
As additional controls are added to inner panel, it will expand vertically. Controls which fall outside of the clip container will not be rendered. The following diagram shows this, but has controls outside of the clip container dimmed for illustrative purposes:
The inner panel can be used to hold controls which are positioned in absolute pixel values or which are automatically stacked (such as a list box). If the controls within the inner panel are to be positioned in absolute terms, the Children Layout value should be set to Regular. If the controls within the inner panel are to be stacked automatically, the inner panel Children Layout value should be set to TopToBottomStack.
The VerticalScrollBarVisibility property controls the behavior of the scrollbar in response to the number of items in the ScrollViewer. The three available values are:
Auto - the vertical scrollbar will display only if the ScrollViewer requires scrolling to display all items. This is the default functionality
Hidden - the vertical scrollbar will never show, but the ScrollViewer still supports scrolling with the mouse wheel or swiping on the touchscreen
Visibile - the vertical scrollbar will always display
When working with a ScrollViewer we need to consider two sizes:
Width and Height of the ScrollViewer itself, which can be explicitly in code or which can be set on the Gum runtime object in the Gum tool
The InnerPanel width and height, which also can be explicitly set in code or which can be set on the Gum runtime object in the Gum tool
The following code shows how a manually sized ScrollViewer will behave:
When viewing the code above, keep in mind:
At the time of this writing the ScrollViewer object only supports scrolling vertically (up and down).
We set the scrollViewer.Visual's WidthUnits, Width, HeightUnits, and Height for the sake of making a clear example. This is not necessary (and often not desirable) in a real world example where layout is controlled by the Gum tool.
The code above adjusts the ScrollViewer such that the view displays 40% (400 / 1000) of the available height of the InnerPanel. The scroll bar can be used to to scroll through the container.
Objects can be added to ScrollViewer instances in Gum, but it requires manually entering the suffix ".InnerPanelInstance" to the parent name of the child. For example, consider a page with a ScrollViewer in Gum:
To add an instance to the ScrollViewerInstance:
Add an object as a child to the ScrollViewerInstance, by either drag+dropping the item in the tree view, or by manually setting the Parent of the child to ScrollViewerInstance.
Select the child
Scroll to find the Parent property
Append .InnerPanelInstance to the parent name
Notice the dotted line shows the expansion of the InnerPanelInstance. Also, the reason we append .InnerPanelInstance is because we want to attach to the InnerPanelInstance inside the ScrollViewerInstance, and InnerPanelInstance is the name of the panel, as is shown if the ScrollViewer is selected in Gum.
Controls can be added to the InnerPanel in code. The following code shows how to create and add 20 ColoredRectangle of random color to an Inner Panel.
The code above will produce the following ScrollViewer:
TreeViewItem is a selectable control used in the TreeView. TreeViewItem instances can be manually instantiated just like any control, or may be instantiated internally by the TreeView control.
The TreeViewItem requires:
An object named **InnerPanelInstance **of any type (typically a Container)
Typically the InnerPanelInstance has the following characteristics:
Height units depending on children
Stacks children vertically
TreeViewItem controls can optionally include:
An object named ToggleButtonInstance which implements the Toggle behavior
An object named ListBoxItemInstance which implements the ListBoxItem behavior
If available, the ListBoxItemInstance is used to display selection, hover, and the backing data. If available, the ToggleButtonInstance is used to display whether the tree view item is expanded, and to allow the user to expand/collapse the TreeViewItem.
By default FlatRedBall.Forms will have a standard implementation for a TreeViewItem (this is added to Gum projects through the Gum plugin in Glue). FlatRedBall.Forms provides a number of ways to customize TreeViewItems.
Of course, the easiest way to customize TreeViewItems is to open the Gum project and modify the existing TreeViewItem, which is located in the DefaultForms folder.
The standard TreeViewItem implementation includes all of the necessary components and characteristics to be used in FlatRedBall.Forms.
Game projects can contain custom TreeViewItems, and can even contain different types of TreeViewItems for use in different situations. As shown above, custom TreeViewItems only need an InnerPanelInstance, but can include a toggle button to control collapsing/expanding and a ListBoxItemInstance for selection. For example, the following Component called QuestCategory is used to contain related quests.
The Gum plugin will attempt to generate code for this type, but you can force the association between your custom type and the TreeViewItem in code. For example, the following code associates the QuestCategory control with the TreeViewItem forms control: Once a new TreeViewItem control has been created it can be used in code in a number of ways (see the next section).
The TreeViewItem added to a TreeView or added as a child of an existing TreeViewItem can be specified in a project-wide or very specific way.
The most direct way to add a TreeViewItem to a TreeView or as a child of an existing TreeViewItem is to construct it and add it directly, as shown in the following code:
The previous section shows how to add a TreeViewItem directly to a TreeView or another TreeView Item. If an object which is not a TreeViewItem is added to the Items list, the TreeView or TreeViewItem will attempt to construct a TreeViewItem internally. The order for determining which type of Gum object to construct is as follows:
Does the TreeView or TreeViewItem have its TreeViewItemGumType property set? If so, use that type.
Does the FrameworkElement.DefaultFormsComponents contain an entry for TreeViewItem? If so, use that type.
If none of the conditions above are true, and the TreeViewItem is being added as a child of another TreeViewItem, use the parent's type.
By default the visual will displayed by a FlatRedBall.Forms.Controls.TreeViewItem instance. This can be changed by setting the TreeView or parent TreeViewItem's TreeViewItemFormsType.
The requirements for the TreeView are identical to the requirements for the ScrollViewer and ListBox controls.