---
description: Code your own UI/UIX Library for Microsoft XNA's Monogame
---

# Page 7. Microsoft XNA: Custom UI/UIX Panels

## **Prerequisites**

Before we dive into implementing mouse interactivity in your MonoGame project, let's ensure that you have the necessary foundation in place. If you're new to Microsoft XNA and MonoGame or need a refresher, it's highly recommended to start with our introductory project. You can follow the step-by-step walkthrough provided on [**Page 1: Microsoft XNA's Hello World w/ MonoGame**](https://joshuaparsonscreativity.gitbook.io/joshuaparsonscreativity/group-1/page-1.-microsoft-xnas-hello-world-w-monogame) to get acquainted with the basics.

**List of Prerequisites:**

1. **Visual Studio**: Make sure you have a working installation of Visual Studio, which is the development environment we'll use for this project.
2. **MonoGame Framework**: Ensure that you have the MonoGame framework installed. This framework provides the essential tools and libraries for game development.
3. **.xnb Font File**: To display text in your MonoGame project, you'll need a .xnb file of your desired font. If you haven't created one yet, you can refer back to **Page 1** for guidance on how to generate it.
4. **Assembled Classes**: Make sure you have all the necessary classes. If any are missing please refer to [_HERE_](https://drive.google.com/drive/folders/1V4k4Pn97yQjtj02gJDG3gZdPJfwBvlfR?usp=sharing) _(_[_https://drive.google.com/drive/folders/1V4k4Pn97yQjtj02gJDG3gZdPJfwBvlfR?usp=sharing_](https://drive.google.com/drive/folders/1V4k4Pn97yQjtj02gJDG3gZdPJfwBvlfR?usp=sharing)_)_ or alternatively refer to the previous pages

## Introduction to

The crucial aspects of designing a User Interface (UI) and User Experience (UX) for applications developed using Microsoft XNA and MonoGame. Our goal is to provide a solid understanding of UI design principles and how to implement them effectively.

#### Key Objectives

1. **Introduce UI Elements**: We will introduce various UI elements that are essential for creating interactive and user-friendly applications, such as `TextElements`, `PanelElements`, `ButtonElements`, `TextboxElements`, `DropdownElements`, and `MenubarElements`.
2. **Customization**: Emphasize the importance of customization in UI design. Teach readers how to adjust parameters and functions to tailor the UI to their specific needs.
3. **UI Hierarchy**: Explain the concept of hierarchical UI structure using  `PanelElement` and demonstrate how it can be used to create organized and visually pleasing layouts.
4. **Event Handling**: Illustrate how to handle user interactions with UI elements through event handling, allowing readers to define actions for button clicks, text input, and more.
5. **Styling and Theming**: Introduce UI styling and theming, empowering readers to maintain visual consistency and adapt their UI to different contexts.
6. **Documentation and Guidance**: Provide comprehensive documentation and guidance for each UI element, ensuring that readers have clear instructions on how to use parameters, event handling, and styling options effectively.

Let's clarify the difference between UI (User Interface) and UIX (User Interface eXperience), without any room for ambiguity.

### **User Interface (UI):**

UI, or User Interface, refers to the visual and interactive components of a software application that users interact with. It encompasses all the elements, such as buttons, text fields, menus, icons, and screens, that enable users to interact with the software. UI design focuses on the layout, appearance, and functionality of these elements to ensure they are user-friendly, aesthetically pleasing, and efficient.

Key aspects of UI include:

1. **Visual Design**: Designing the appearance of UI elements, including colors, typography, icons, and overall layout.
2. **Interactivity**: Implementing functionality for user interactions, such as clicking buttons, entering text, and navigating menus.
3. **Consistency**: Maintaining a consistent visual style and interaction patterns throughout the application.
4. **Usability**: Ensuring that the UI elements are intuitive and easy for users to understand and navigate.

In summary, UI is primarily concerned with the design and functionality of the visual components that users directly interact with within an application.

### **User Interface eXperience (UIX):**

UIX, or User Interface eXperience, extends beyond the surface-level design and functionality of UI. It encompasses the entire user's journey and the emotions and perceptions they have while interacting with the software. UIX considers the holistic experience of using the application, including how it makes users feel, how efficiently they can achieve their goals, and whether it leaves a positive or negative impression.

Key aspects of UIX include:

1. **Emotional Impact**: Evaluating how the application's design and interactions make users feel. A positive UIX aims to create a delightful and emotionally satisfying experience.
2. **Efficiency and Effectiveness**: Focusing on how well users can accomplish their tasks within the application. A good UIX ensures that users can achieve their goals efficiently and without frustration.
3. **Consistency Across Touchpoints**: Considering the user's experience across different devices and platforms to maintain a consistent and seamless interaction.
4. **Feedback and Improvement**: Gathering user feedback and continuously improving the UI and UX based on user insights.

In essence, UIX encompasses UI but goes a step further by considering the holistic experience, emotions, and efficiency of the user throughout their interaction with the software. It aims to create a positive and memorable experience that goes beyond the visual design and functional aspects of the UI.

## Introducing the UI Elements

#### PanelElement (New)

The `PanelElement` serves as a container for organizing other UI elements. You can customize its size, and color, and add borders to it. It provides structure to your UI, helping you group related elements together and create visually appealing layouts.

#### TextElement (Enhanced)

The `TextElement` is more than just text display; it's the foundation of our UI system. We're enhancing it to handle various _sizes and alignments_. You can now customize properties like _font size, text color, and alignment_, allowing you to create a wide range of text styles to suit your application's aesthetic.

#### ButtonElement (New) - coming soon (Page 8)

Buttons are essential for user interaction, and the `ButtonElement` is highly versatile. You can define its size, color, and text, and specify what action should occur when it's clicked. This element empowers you to create interactive features and user-friendly interfaces.

#### TextboxElement (New) - coming soon (Page 9)

The `TextboxElement` allows users to input text. You can control its size, color, and apply validation rules to ensure data integrity. It's a valuable addition for forms and user input scenarios, enabling smooth data entry.

#### DropdownElement (New) - coming soon (Page 10)

Dropdown menus are handy for presenting selectable options, and the `DropdownElement` is here for that purpose. You can customize its appearance, populate it with choices, and determine how it behaves when users make selections. It simplifies user choices and enhances usability.

#### MenubarElement (New) - coming soon (Page 11)

The `MenubarElement` acts as the main menu for your application, providing navigation and access to essential functions. You can customize its appearance, add buttons or dropdowns, and define the actions associated with each menu item. It serves as the central control hub for your app.

## PanelElement.cs

At the core of this part of the code is the creation of a new class, `PanelElement`. This class will serve as the blueprint for creating various panel elements within your game interface. Each `PanelElement` instance will have properties like `Position`, `Size`, `BackgroundColor`, `BorderColor`, and a few others, which will dictate how each panel will look and where it will be placed on the screen.

The constructor of `PanelElement` is designed to accept various parameters with default values, making it easy for a developer to create a new panel with minimal information. If certain details are not provided, the constructor will use default values. For example, if no position is provided, it defaults to `Vector2.Zero`, which represents the coordinates (0,0). Similarly, if no size is provided, it defaults to the current window size.

In your project, create a new class named `PanelElement.cs` under your namespace. This class will serve as the foundation for creating panels in your UI.

```csharp
using Microsoft.Xna.Framework;
using System.Collections.Generic;

namespace HelloWorldXNA
{
	public class PanelElement
	{
		public Vector2 Position { get; set; }
		public Vector2 Size { get; set; } 
		public Color BackgroundColor { get; set; }
		public bool HasBorder { get; set; }
		public Color BorderColor { get; set; }
		public int Layer { get; set; }
		public bool IsVisible { get; set; }
		public List<TextElement> TextElements { get; } = new List<TextElement>();


		public PanelElement(
			Color backgroundColor,
			Vector2 position = default,
			Vector2 size = default,
			bool hasBorder = false,
			Color borderColor = default,
			int layer = 0,
			bool isVisible = true)
		{
			Position = position == default ? Vector2.Zero : position; // Default to (0, 0) if no position provided
			Size = size == default ? new Vector2(GraphicsDeviceManager.DefaultBackBufferWidth, GraphicsDeviceManager.DefaultBackBufferHeight) : size; // Default to the current window size if no size provided
			BackgroundColor = backgroundColor;
			HasBorder = hasBorder;
			BorderColor = borderColor != default ? borderColor : Color.Black; // Default to Black if no border color provided
			Layer = layer;
			IsVisible = isVisible;
		}
		public void AddTextElement(TextElement textElement)
		{
			TextElements.Add(textElement);
		}
	}
}
```

This code defines the `PanelElement` class, which is used to create panels in your game. Here's a breakdown of each part:

* **Properties**: These properties define various characteristics of the panel, such as its Vector2 position, Vector2 size, background color, whether it has a border, border color, rendering layer, and visibility.

As a reminder in the context of game development and computer graphics, a `Vector2` is a common data structure used to represent a two-dimensional vector. It's part of the XNA and MonoGame frameworks, which are used for developing games in C#.

A `Vector2` typically consists of two components:

* **X Component**: Represents the value along the horizontal axis (usually the X-axis)
* **Y Component**: Represents the value along the vertical axis (usually the Y-axis)



* **Constructor**: The constructor allows you to create a `PanelElement` instance with specific properties. It takes several parameters:
  * `backgroundColor`: The background color of the panel
  * `position`: The position of the panel, defaulting to (0, 0) if not provided
  * `size`: The size of the panel, defaulting to the current window size if not provided
  * `hasBorder`: A boolean indicating whether the panel should have a border
  * `borderColor`: The color of the border, defaulting to black if not provided
  * `layer`: The rendering layer of the panel, where 0 is at the bottom (used for layering multiple panels)
  * `isVisible`: A boolean indicating whether the panel should be visible

This class provides the necessary structure to create and manage panels in your game, allowing you to customize their appearance, position, and behavior.

The first line in the constructor, `Position = position == default ? Vector2.Zero : position;`, is responsible for setting the position of the panel. Here's a breakdown:

* `Position =`: This part starts the assignment of the `Position` property.
* `position == default`: Here, you are checking whether the `position` parameter provided to the constructor is equal to the default value for `Vector2`. In C#, the `default` keyword represents the default value for a data type. For `Vector2`, the default value is `(0, 0)`.
* `?`: This is called the conditional operator, also known as the ternary operator. It's used for inline conditional expressions. It has the form `condition ? trueValue : falseValue`. If the condition is true, it returns `trueValue`, otherwise, it returns `falseValue`.
* `Vector2.Zero`: If the `position` parameter is not the default value (i.e., it's not `(0, 0)`), then this part of the line assigns the value of `position` to the `Position` property.

So, in plain English, this line of code is saying: "If the `position` parameter is not the default `Vector2` value `(0, 0)`, then set the `Position` property to the value of `position`. Otherwise, set it to `Vector2.Zero` (which is `(0, 0)`)."

**Setting the Size**

The second line you mentioned is similar:

```csharp
Size = size == default ? new Vector2(GraphicsDeviceManager.DefaultBackBufferWidth, GraphicsDeviceManager.DefaultBackBufferHeight) : size;
```

Here, you are setting the `Size` property of the `PanelElement` object:

* You check whether the `size` parameter is the default value. If it's not, you assign its value to the `Size` property.
* If `size` is the default value, you create a new `Vector2` object with dimensions obtained from `GraphicsDeviceManager.DefaultBackBufferWidth` and `GraphicsDeviceManager.DefaultBackBufferHeight`. This is done to provide a default size based on the current window dimensions.

In essence, these two lines ensure that the `Position` and `Size` properties of the panel are set either to default values or to the values provided during panel creation, making it convenient for developers to work with panels in their games.

## PanelElementRenderer and UIManager

Next, we will craft the`PanelElementRenderer` and `UIManager`. These classes are pivotal in organizing the rendering logic and managing the UI elements respectively. `PanelElementRenderer` will contain methods to draw a panel, and `UIManager` holds a list of panels and orchestrates when and how they are drawn. This separation aligns with the Single Responsibility Principle, making your code modular, organized, and easier to maintain or expand in the future.

The `UIManager` class also has a method `Window_ClientSizeChanged` to handle window resizing events. When the window size changes, this method updates the size of all managed panels accordingly, which is a neat way to keep the UI responsive to window size changes.

### Step 1: Encapsulation and Single Responsibility Principle (SRP)

#### PanelElementsRenderer.cs

The Single Responsibility Principle articulates that a class should have one, and only one, reason to change. Applying this principle, it's advantageous to encapsulate the logic related to `PanelElement` operations within a dedicated class, say `PanelElementRenderer`, instead of within the `Game1.cs`. This promotes a cleaner separation of concerns, making the code more modular and easier to manage.

```csharp
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace HelloWorldXNA {
	public class PanelElementRenderer
	{
		private GraphicsDevice graphicsDevice { get; }
		private Texture2D pixelTexture { get; }


		public PanelElementRenderer(GraphicsDevice gd)
		{
			graphicsDevice = gd;
			pixelTexture = new Texture2D(graphicsDevice, 1, 1);
			pixelTexture.SetData(new[] { Color.White });
		}

		public void DrawPanel(SpriteBatch spriteBatch, PanelElement panel)
		{
			if (panel.IsVisible)
			{
				Rectangle panelRectangle = new Rectangle((int)panel.Position.X, (int)panel.Position.Y, (int)panel.Size.X, (int)panel.Size.Y);

				if (panel.HasBorder)
				{
					DrawFilledRectangle(spriteBatch, panelRectangle, panel.BorderColor);
					panelRectangle.Inflate(-1, -1);
				}

				DrawFilledRectangle(spriteBatch, panelRectangle, panel.BackgroundColor);
			}
		}

		private void DrawFilledRectangle(SpriteBatch spriteBatch, Rectangle rect, Color color)
		{
			spriteBatch.Draw(pixelTexture, rect, color);
		}
	}
}
```

This allows for `Game1.cs` to instantiate and utilize `PanelElementRenderer` for rendering panel elements using&#x20;

* The `DrawPanel` method by taking a `SpriteBatch` and a `PanelElement` as arguments It then checks whether the panel is visible using `panel.IsVisible`.
* A `Rectangle` object representing the panel's dimensions is created using the `panel`'s position and size properties.
* If the panel should have a border (`panel.HasBorder` is true), it draws the border using the `DrawFilledRectangle` method, then calls `Inflate` on `panelRectangle` to shrink it by 1 pixel on all sides.
* Finally, it draws the panel's background using the `DrawFilledRectangle` method.

#### UIManager.cs

Let's refactor `Window_ClientSizeChanged` to the `UIManager` class to handle window resizing events. When the window size changes, this method updates the size of all managed panels accordingly, which is a neat way to keep the UI responsive to window size changes.

```csharp
using System;
using System.Collections.Generic;
using HelloWorldXNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public class UIManager
{
	private List<PanelElement> _panels = new List<PanelElement>();
	private PanelElementRenderer _renderer;
	private GraphicsDevice _graphicsDevice;

	public UIManager(GraphicsDevice graphicsDevice)
	{
		_renderer = new PanelElementRenderer(graphicsDevice);
		_graphicsDevice = graphicsDevice;
	}

	public void AddPanel(PanelElement panel)
	{
		_panels.Add(panel);
		_panels.Sort((panel1, panel2) => panel1.Layer.CompareTo(panel2.Layer));
	}

	public void Draw(SpriteBatch spriteBatch)
	{
		foreach (var panel in _panels)
		{
			_renderer.DrawPanel(spriteBatch, panel);
		}
	}

	public void Window_ClientSizeChanged(object sender, EventArgs e)
	{
		Vector2 newSize = new Vector2(_graphicsDevice.Viewport.Width, _graphicsDevice.Viewport.Height);
		foreach (var panel in _panels)
		{
			panel.Size = newSize;
		}
	}
}
```

In this refactoring:

1. `UIManager` now has a `Window_ClientSizeChanged` method which iterates through all `PanelElement` objects and updates their sizes based on the window size.
2. In `Game1`'s `Initialize` method, `this.Window.ClientSizeChanged` is now subscribed to `uiManager.Window_ClientSizeChanged` instead of `Window_ClientSizeChanged` method in `Game1`.
3. The `Window_ClientSizeChanged` method has been removed from `Game1` as it's now handled by `UIManager`.

#### Class Components:

1. **Fields**:
   * `_panels`: A list that holds all the `PanelElement` objects managed by the `UIManager`.
   * `_renderer`: An instance of `PanelElementRenderer` used to draw the panels.
2. **Constructor**:
   * `UIManager(GraphicsDevice graphicsDevice)`: The constructor initializes the `_renderer` field with a new instance of `PanelElementRenderer`, passing the `GraphicsDevice` object to it.
3. **Methods**:
   * `AddPanel(PanelElement panel)`: Adds a new `PanelElement` to the `_panels` list.
   * `Draw(SpriteBatch spriteBatch)`: Iterates through all the `PanelElement` objects in the `_panels` list and calls `_renderer.DrawPanel()` to draw each one.

#### Functionality Breakdown:

* **Initialization**: When a `UIManager` object is created, it initializes a `PanelElementRenderer` object, which will be used to render panels.
* **Panel Management**:
  * `AddPanel` allows for adding new panels to the UI manager.
  * `Draw` iterates through these panels, rendering each one using the `PanelElementRenderer`.
* **Rendering**: The actual rendering is delegated to the `PanelElementRenderer` object, keeping the rendering logic separate from the management logic.

You can then register this method to the `ClientSizeChanged` event within `Game1.cs`. This way, the `UIManager` is responsible for updating the sizes of the panels when the window size changes, which centralizes the UI management logic within the `UIManager` class.

### Implementing the PanelElement in Game1.cs

Now that we have our `PanelElement` class defined, let's implement it in your `Game1.cs` file.

### Fully Updated Game1.cs

```csharp
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;

namespace YourNameSpaceHere
{
	public class Game1 : Game
	{
		GraphicsDeviceManager graphicsManager;
		SpriteBatch spriteBatch;
		TextElement textElement;
		PhysicsEngine physicsEngine;
		ColorManager colorManager;
		MouseInputManager mouseInputManager;

		private UIManager uiManager;
		private PanelElement panelMain;
		private PanelElement panel2;

		public Game1()
		{
			graphicsManager = new GraphicsDeviceManager(this);
			Content.RootDirectory = "Content";
		}

		protected override void Initialize()
		{
			// Window Attributes
			IsMouseVisible = true;
			Window.AllowUserResizing = true;

			physicsEngine = new PhysicsEngine(graphicsManager);
			colorManager = new ColorManager();
			mouseInputManager = new MouseInputManager();


			// Panels
			panelMain = new PanelElement(Color.LightGray, layer: 1, isVisible: true);
			panel2 = new PanelElement(Color.LightGreen, layer: 2, isVisible: false);

			// TextElements
			textElement = new TextElement(GraphicsDevice, Content, "Click Me!", position: new Vector2(100, 50), panel: panelMain);

			// Instantiate UIManager and add panels to list
			uiManager = new UIManager(GraphicsDevice);
			this.Window.ClientSizeChanged += uiManager.Window_ClientSizeChanged;

			uiManager.AddPanel(panelMain);
			uiManager.AddPanel(panel2);

			base.Initialize();
		}

		protected override void LoadContent()
		{
			spriteBatch = new SpriteBatch(GraphicsDevice);
			textElement.GenerateTextureFromText(spriteBatch);
		}

		protected override void Update(GameTime gameTime)
		{
			float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
			mouseInputManager.update();
			physicsEngine.ApplyWallBouncingToText(textElement, elapsed, 200, 50);
			physicsEngine.ApplyMouseEventHandlingToText(textElement, mouseInputManager, isPrecisionClicks: true);
			colorManager.UpdateTextColor(textElement, elapsed, changeRed: false, changeGreen: true, changeBlue: true);
			base.Update(gameTime);
		}

		protected override void Draw(GameTime gameTime)
		{
			GraphicsDevice.Clear(Color.CornflowerBlue);
			spriteBatch.Begin();

			// Delegate panel drawing to UIManager
			uiManager.Draw(spriteBatch);

			spriteBatch.End();
			base.Draw(gameTime);
		}
	}
}

```

Let's break down the significant sections of this code:

#### Field Declarations:

```csharp
GraphicsDeviceManager graphicsManager;
SpriteBatch spriteBatch;
TextElement textElement;
PhysicsEngine physicsEngine;
ColorManager colorManager;
MouseInputManager mouseInputManager;

private UIManager uiManager;
private PanelElement panelMain;
private PanelElement panel2;
```

These fields are various members of `Game1` class, representing managers, engine instances, and UI elements that are used throughout the game's lifecycle.

#### Constructor:

```csharp
csharpCopy codepublic Game1()
{
    graphicsManager = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";
}
```

The constructor initializes `graphicsManager` and sets the root directory for content loading.

#### Initialize Method:

```csharp
protected override void Initialize()
{
    //...
}
```

The `Initialize` method is where various game components and UI elements are instantiated and configured. This includes setting some window attributes, creating instances of `PhysicsEngine`, `ColorManager`, `MouseInputManager`, and some UI elements like `PanelElement` and `TextElement`. It also sets up the `UIManager` and binds a method to the `ClientSizeChanged` event of the window.

#### LoadContent Method:

```csharp
protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    textElement.GenerateTextureFromText(spriteBatch);
}
```

`LoadContent` is where content is loaded into the game. In this code, a new `SpriteBatch` is created, and a texture is generated for `textElement` from text.

#### Update Method:

```csharp
protected override void Update(GameTime gameTime)
{
    //...
}
```

`Update` is called every frame and is where the game's logic is updated. In this method, input is processed, and physics and color updates are applied to `textElement`.

#### Draw Method:

```csharp
protected override void Draw(GameTime gameTime)
{
    //...
}
```

`Draw` is also called every frame, and is where the game's rendering occurs. In this method, the graphics device is cleared, `SpriteBatch` is begun, the UI is drawn by delegating to `UIManager`, and `SpriteBatch` is ended.

### Updating the PhysicsEngine.cs

You may have noticed that we do not always need to be within the rectangle/on the text when clicking our `TextElement` this is due to the management of our IsMouseOnNonTransparentPixel() function. To resolve this, we need to update the if condition to the below

```csharp
private bool IsMouseOnNonTransparentPixel(TextElement textElement, Point mousePosition)
{
	Color[] pixelColorData = new Color[textElement.Texture.Width * textElement.Texture.Height];
	textElement.Texture.GetData(pixelColorData);

	int x = mousePosition.X - (int)textElement.Position.X;
	int y = mousePosition.Y - (int)textElement.Position.Y;

	if (x >= 0 && x < textElement.Texture.Width && y >= 0 && y < textElement.Texture.Height)
	{
		int index = x + y * textElement.Texture.Width;
		return pixelColorData[index].A > 0;
	}

	return false;
}
```

In this corrected version, we first check whether the `x` and `y` coordinates are within the bounds of the texture before attempting to calculate the `index`. This avoids accessing invalid indices and ensures the function works as intended. If the mouse position is outside the texture bounds, the function returns `false`.



### Updating the TextElement.cs

In this updated version we are updating the constructor to handle the passing of the position and a specific panel so the user can specify a location for their TextElement.

```csharp
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace YourNameSpaceHere
{
	public class TextElement
	{
		public Vector2 Position { get; set; }
		public Vector2 Velocity { get; set; }
		public SpriteFont Font { get; set; }
		public Color Color { get; set; }
		public string Text { get; set; }
		public Texture2D Texture { get; private set; }
		public PanelElement Panel { get; set; }



		public TextElement(
			GraphicsDevice graphicsDevice,
			ContentManager content,
			string text,
			PanelElement panel = null,
			SpriteFont font = null,
			Vector2? velocity = null,
			Vector2? position = null,
			Color? color = null)
		{
			Text = text;
			Panel = panel;
			if (Panel != null)
			{
				Panel.TextElements.Add(this);  // Automatically add this TextElement to the panel's list of TextElements
			}

			Font = font ?? content.Load<SpriteFont>("Fonts/MyFont");	// Default font if none provided
			Velocity = velocity ?? new Vector2(2, 1);                   // Default velocity if none provided
			Position = position ?? new Vector2(100, 100);				// Default location is none provided
			Color = color ?? Color.White;								// Default color if none provided
			Texture = new Texture2D(graphicsDevice, (int)Font.MeasureString(text).X, (int)Font.MeasureString(text).Y);
		}

		public void GenerateTextureFromText(SpriteBatch spriteBatch)
		{
			var textSize = Font.MeasureString(Text);
			RenderTarget2D renderTarget = new RenderTarget2D(spriteBatch.GraphicsDevice, (int)textSize.X, (int)textSize.Y);
			spriteBatch.GraphicsDevice.SetRenderTarget(renderTarget);
			spriteBatch.GraphicsDevice.Clear(Color.Transparent);

			spriteBatch.Begin();
			spriteBatch.DrawString(Font, Text, Vector2.Zero, Color);
			spriteBatch.End();

			spriteBatch.GraphicsDevice.SetRenderTarget(null);
			Texture = renderTarget;
		}
	}
}

```

##

## [Current Results](https://drive.google.com/drive/folders/1SFOiptr-MSxayVFN2RFxWXybFdCZvtxH?usp=drive_link)

Remember you will likely need to adjust your Namespace&#x20;

<figure><img src="https://2337211560-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8LR6Nd8AdneenwoowRAg%2Fuploads%2F8MSQY7LcWjMrlYdAnXNG%2FOutput.gif?alt=media&#x26;token=77e6a7b3-b008-4845-b030-09e436874084" alt=""><figcaption></figcaption></figure>

## Knowledge Check

1. What is the primary purpose of the PanelElement class in the MonoGame project?
2. In the PanelElement constructor, what is the default value for the position parameter if it is not provided, and how is this default value set?
3. What are the key aspects of UI (User Interface) as mentioned in the article?
4. What is the role of the PanelElementRenderer class in rendering the UI elements?
5. In the UIManager class, what method is responsible for updating the size of all managed panels when the window size changes, and how is this method triggered?
6. In the provided PanelElement.cs code, what does the ternary operator (?:) do in the constructor?

\
Interactive Challenges
----------------------

#### Challenge 1: Extend the `PanelElement` Class

The `PanelElement` class is a fundamental part of the UI system described in the article. Your challenge is to extend this class to create a new type of panel element, such as a button.

1. Create a new class called `ButtonElement` that inherits from `PanelElement`.
2. Add new properties to `ButtonElement`, such as `Label` (a string to be displayed on the button) and `IsClicked` (a boolean that tracks whether the button has been clicked).
3. Override the `Draw` method to include code that changes the color of the button when `IsClicked` is true.
4. Instantiate a `ButtonElement` in your main program, add it to the `UIManager`, and test its functionality.
