January 2011

Volume 26 Number 01

UI Frontiers - A Color Scroll for XNA

By Charles Petzold | January 2011

image: Charles PetzoldOne of the first Windows programs I wrote for publication was called COLORSCR (“color scroll”), and it appeared in the May 1987 issue of Microsoft Systems Journal, the predecessor to this magazine.

Over the years, I’ve frequently found it instructive to rewrite this program for other APIs and frameworks. Although the program is simple—you manipulate three scrollbars or sliders for red, green and blue values to create a custom color—it involves crucial tasks such as layout and event handling. In functionality, as well, the program is not strictly a simple, meaningless demo. If you ever found yourself creating a color-selection dialog, this program would be a good place to start.

In this article, I want to show you how to write a color-scroll program using the XNA Framework as implemented in Windows Phone 7. The result is shown in Figure 1.

image: The Xnacolorscroll Program

Figure 1 The Xnacolorscroll Program

XNA is Microsoft .NET Framework-based managed code for games programming, but if you start searching through the Microsoft.Xna.Framework namespaces, you won’t find any scrollbars or sliders or even buttons among the classes! XNA is simply not that kind of framework. If you want a slider in your program, it must be specially created for that job.

Showing you how to write an XNA color-scroll program isn’t just an academic exercise, however. My intention is actually much broader.

As you know, Windows Phone 7 supports both Silverlight and XNA, and generally for any particular application the choice between the two is obvious. However, you might find that the graphical demands of your application are simply too much for Silverlight, but at the same time you might be hesitant about using XNA instead because it’s missing familiar amenities, such as buttons and sliders.

My response is: Do not be afraid. Writing simple controls in XNA is easier than you probably think.

You can also consider this article as an introduction to XNA programming for Windows Presentation Foundation (WPF) and Silverlight programmers. I found myself applying certain concepts from WPF and Silverlight to this job, so the XNA color-scroll program is a celebration of synergy as well.

All the downloadable source code is in one Visual Studio solution called XnaColorScroll. This solution includes an XnaColorScroll project for Windows Phone 7 and a library called Petzold.Xna.Controls containing the four XNA “controls” that I created for this program. (Obviously, loading the solution into Visual Studio requires that you have the Windows Phone Development Tools installed.) Feel free to steal these controls and supplement them with your own for your own XNA controls library.

Games and Components

Just as the main class of a WPF, Silverlight or Windows Forms program is a class that derives from Window, Page or Form, the main class of an XNA program derives from Game. An XNA program uses this Game derivative to draw bitmaps, text and 3D graphics on the screen.

To help you organize your program into discrete functional entities, XNA supports the concept of a component, which is perhaps the closest that XNA comes to a control. Components come in two varieties: GameComponent derivatives and DrawableGameComponent derivatives. DrawableGameComponent itself derives from GameComponent, and the names suggest the difference. I’ll be using both base classes in this exercise.

The Game class defines a property named Components of type GameComponentCollection. Any components that a game creates must be added to this collection. Doing so ensures that the component gets the same calls to various overridden methods that the game itself gets. A DrawableGameComponent draws on top of whatever the Game draws.

Let’s begin the analysis of the XnaColorScroll program—not with the Game derivative, but with the GameComponent derivatives. Figure 2 shows the source code for a component I call TextBlock, and it’s used in much the same way as a WPF or Silverlight TextBlock.

Figure 2 The TextBlock Game Component

public class TextBlock : DrawableGameComponent
{
  SpriteBatch spriteBatch;
  Vector2 textOrigin;
  public TextBlock(Game game) : base (game)
  {
    // Initialize properties
    this.Color = Color.Black;
  }
  public string Text { set; get; }
  public SpriteFont Font { set; get; }
  public Color Color { set; get; }
  public Vector2 Position { set; get; }
  public HorizontalAlignment HorizontalAlignment { set; get; }
  public VerticalAlignment VerticalAlignment { set; get; }
  public Vector2 Size
  {
    get
    {
      if (String.IsNullOrEmpty(this.Text) || this.Font == null)
          return Vector2.Zero;
      return this.Font.MeasureString(this.Text);
    }
  }
  protected override void LoadContent()
  {
    spriteBatch = new SpriteBatch(this.GraphicsDevice);
    base.LoadContent();
  }
  public override void Update(GameTime gameTime)
  {
    if (!String.IsNullOrEmpty(Text) && Font != null)
    {
      Vector2 textSize = this.Size;
      float xOrigin = (int)this.HorizontalAlignment * textSize.X / 2;
      float yOrigin = (int)this.VerticalAlignment * textSize.Y / 2;
      textOrigin = new Vector2((int)xOrigin, (int)yOrigin);
    }
    base.Update(gameTime);
  }
  public override void Draw(GameTime gameTime)
  {
    if (!String.IsNullOrEmpty(Text) && Font != null)
    {
      spriteBatch.Begin();
      spriteBatch.DrawString(this.Font, this.Text, this.Position, this.Color,
                            0, textOrigin, 1, SpriteEffects.None, 0);
      spriteBatch.End();
    }
    base.Draw(gameTime);
  }
}

Notice the public properties following the constructor in Figure 2. These indicate the text itself, and the font, color and position of the text relative to the screen. Two additional properties, HorizontalAlignment and VerticalAlignment, allow that position to reference the side or center of the text string. In XnaColorScroll, these properties are handy for centering the text relative to the sliders.

The class also includes a get-only Size property, which returns the size of the rendered text with that particular font. (The XNA Vector2 structure is often used for representing two-dimensional positions and sizes as well as vectors.)

The TextBlock component overrides both the Update and Draw methods, which is normal for a drawable component and normal for Game derivatives as well. These two methods constitute the program’s “game loop,” and they’re called at the same rate as the video display—30 times per second for Windows Phone 7. You can think of the Draw method like a WM_PAINT message or an OnPaint override, except that it’s called for every screen refresh. The Update method is called before each Draw call and is intended to be used for performing all the necessary calculations and preparation for the Draw method.

TextBlock as written has lots of room for performance improvements. For example, it could cache the value of the Size property or only recalculate the textOrigin field whenever any property that contributes to it changes. These enhancements might take priority if you ever discover that the program has become sluggish because all the Update and Draw calls in the program are taking longer than 0.03 seconds to execute.

Components and Child Components

As you can see in Figure 1, XnaColorScroll has six TextBlock components but also three sliders, which are probably going to be a little more challenging.

Very many years ago, I might have tried coding the entire slider in a single class. With my experience in WPF and Silverlight control templates, however, I realize that the Slider control might best be constructed by assembling a Thumb control and RepeatButton controls. It seems reasonable to try to create an XNA Slider component from similar child components.

A Game class adds child components to itself by adding them to its Components property of type GameComponentCollection. The big question now becomes: Does GameComponent itself have a Components property for its own child components?

Unfortunately, the answer is no. However, a component has access to its parent Game class, and if the component needs to instantiate its own child components, it can add those additional components to the same Components collection of the Game class. Do this in the right order and you won’t even need to mess around with the DrawOrder property—defined by DrawableGameComponent—that can help with the order in which components are rendered. By default, the Draw method in the Game class is called first, then all the Draw methods in the components as they appear in the Components collection.

So let’s begin. Like TextBlock, the Thumb class derives from DrawableGameComponent. The entire Thumb class except for the touch processing is shown in Figure 3. Thumb defines the same three events as the WPF and Silverlight Thumb controls.

Figure 3 Most of the Thumb Class

public class Thumb : DrawableGameComponent
{
  SpriteBatch spriteBatch;
  Texture2D tinyTexture;
  int? touchId;
  public event EventHandler<DragEventArgs> DragStarted;
  public event EventHandler<DragEventArgs> DragDelta;
  public event EventHandler<DragEventArgs> DragCompleted;
  public Thumb(Game game) : base(game)
  {
    // Initialize properties
    this.Color = Color.White;
  }
  public Rectangle Position { set; get; }
  public Color Color { set; get; }
  protected override void LoadContent()
  {
    spriteBatch = new SpriteBatch(this.GraphicsDevice);
    tinyTexture = new Texture2D(this.GraphicsDevice, 1, 1);
    tinyTexture.SetData<Color>(new Color[] { Color.White });
    base.LoadContent();
  }
  ...
  public override void Draw(GameTime gameTime)
  {
    spriteBatch.Begin();
    spriteBatch.Draw(tinyTexture, this.Position, this.Color);
    spriteBatch.End();
    base.Draw(gameTime);
  }
}

In the TextBlock component in Figure 2, the Position property is a point of type Vector2; in Thumb, the Position property is a Rectangle object, and defines not only the location of the Thumb, but also its rectangular size. The Thumb visuals consist entirely of a white 1 x 1 pixel bitmap that’s displayed at the location and size indicated by the Position property. (Using single-pixel bitmaps stretched to a particular size is one of my favorite XNA programming techniques.)

The Thumb processes touch input but doesn’t move itself. The Slider component is responsible for handling DragDelta events from the Thumb and setting a new Position property.

Besides the Thumb, my Slider also contains two RepeatButton components, one on each side of the Thumb. Like the WPF and Silverlight RepeatButton controls, these components generate a series of Click events if you hold your finger on them.

In my XNA Slider, the RepeatButton components occupy a particular area of the screen but are actually invisible. (The thin vertical track down the center is drawn by Slider itself.) This means that RepeatButton can derive from GameComponent rather than DrawableGameComponent. Figure 4 shows the whole RepeatButton control except for the touch processing.

Figure 4 Most of the RepeatButton Class

public class RepeatButton : GameComponent
{
  static readonly TimeSpan REPEAT_DELAY = TimeSpan.FromMilliseconds(500);
  static readonly TimeSpan REPEAT_TIME = TimeSpan.FromMilliseconds(100);
  int? touchId;
  bool delayExceeded;
  TimeSpan clickTime; 
  public event EventHandler Click;
  public RepeatButton(Game game) : base(game)
  {
  }
  public Rectangle Position { set; get; }
  ...
}

At this point, we’re ready to build the Slider component. To keep it reasonably simple, I restricted myself to a vertical orientation. Slider has the usual public properties—Minimum, Maximum and Value, among others—and also defines a ValueChanged event. In its Initialize override, Slider creates the three child components (and saves them as fields), sets the event handlers and adds them to the Components collection of its parent Game class accessible through its Game property:

public override void Initialize()
{
  thumb = new Thumb(this.Game);
  thumb.DragDelta += OnThumbDragDelta;
  this.Game.Components.Add(thumb);
  btnDecrease = new RepeatButton(this.Game);
  btnDecrease.Click += OnRepeatButtonClick;
  this.Game.Components.Add(btnDecrease);
  btnIncrease = new RepeatButton(this.Game);
  btnIncrease.Click += OnRepeatButtonClick;
  this.Game.Components.Add(btnIncrease);
  base.Initialize();
}

XNA Game derivatives and GameComponent derivatives have three occasions to perform initialization—the class constructor, an override of the Initialize method and an override of the LoadContent method—and it might be a little confusing when to use which. As the name suggests, LoadContent is great for loading content (such as fonts or bitmaps) and should be used for all other initialization that depends on the existence of the GraphicsDevice object.

I prefer creating components and adding them to the Components collection during the Initialize override, as shown in the previous code snippet. In XnaColorScroll, the Game derivative class also creates six TextBlock components and three Slider components in its Initialize override. When the Initialize method in the base class is called at the end, then all the Initialize methods for the child components are called. The Initialize method in each Slider then creates the Thumb and two RepeatButton components. This scheme ensures that all these components are added to the Components collection in a proper order for rendering.

Processing Touch Input

The primary means of user input in most Windows Phone 7 programs is multi-touch, and XNA is no exception. (Keyboard input is also available to XNA programs, and programs can also use the phone’s accelerometer as an input device.)

An XNA program obtains touch input during the Update override. There are no touch events. All touch input is polled and is accessible through the TouchPanel class. The static TouchPanel.GetState method provides low-level touch input that consists of TouchLocation objects indicating Pressed, Moved and Released activity with position information and integer ID numbers to distinguish between multiple fingers. The TouchPanel.ReadGesture method (which I don’t use in this program) provides higher-level gesture support.

When I first started working with game components, I thought that each component could obtain touch input on its own, take what it needed and ignore the rest. This didn’t seem to work very well; it was as if the game class and the components were all competing for touch input rather than sharing it like polite children.

I decided to work out another system for processing touch input. Only the Game class calls TouchPanel.GetState. Then, each TouchLocation object is passed to child components through a method I call ProcessTouch. This allows the components to get first dibs on the input. A component that makes use of a TouchLocation object returns true from the ProcessTouch method, and further processing for that particular TouchLocation object ends. (Returning true from ProcessTouch is like setting the Handled property of RoutedEventArgs to true in WPF or Silverlight.)

In XnaColorScroll, the Update method in the main Game class calls TouchPanel.GetState to get all the touch input and then calls the ProcessTouch method in each Slider component as shown here:

TouchCollection touches = TouchPanel.GetState();
foreach (TouchLocation touch in touches)
{
  for (int primary = 0; primary < 3; primary++)
    if (sliders[primary].ProcessTouch(gameTime, touch))
      break;
}

Notice how further processing of each TouchLocation object stops whenever ProcessTouch returns true.

The ProcessTouch method in each Slider control then calls the ProcessTouch methods in the two RepeatButton components and the Thumb component:

public bool ProcessTouch(GameTime gameTime, TouchLocation touch)
{
  if (btnIncrease.ProcessTouch(gameTime, touch))
    return true;
  if (btnDecrease.ProcessTouch(gameTime, touch))
    return true;
  if (thumb.ProcessTouch(gameTime, touch))
    return true;
  return false;
}

If any of these components (or the Game class itself) wished to perform its own touch processing, it would first call ProcessTouch in each child. Any TouchLocation object that remained unprocessed by the children could then be examined for the class’s own use. In effect, this scheme allows the visually topmost children to have first access to touch input, which is often exactly what you want. It’s much like the routed event handling implemented in WPF and Silverlight.

Both the RepeatButton and the Thumb are interested in touch input if a finger is first touched within the area indicated by the component’s Position rectangle. The component then “captures” that finger by saving the touch ID. All other touch input with that ID belongs to that particular component until the finger is released.

Propagating Events

The touch processing of the Thumb and RepeatButton components is really the crux of the Slider control, but that’s something you can study on your own if you’re interested. When the Thumb component detects finger movement on its surface, it generates DragDelta events; when the RepeatButton component detects taps or sustained presses, it fires Click events.

Both these events are handled by the parent Slider component, which adjusts its Value property accordingly, and this fires a ValueChanged event from the Slider. The Slider is also responsible for setting the Position properties of the Thumb and two RepeatButton components based on the new Value.

The Game class handles the ValueChanged events from the three Slider components in a single event handler, so the method simply sets new values of the three TextBlock components and a new color:

void OnSliderValueChanged(object sender, EventArgs args)
{
  txtblkValues[0].Text = sliders[0].Value.ToString("X2");
  txtblkValues[1].Text = sliders[1].Value.ToString("X2");
  txtblkValues[2].Text = sliders[2].Value.ToString("X2");
  selectedColor = new Color(sliders[0].Value, 
                            sliders[1].Value, 
                            sliders[2].Value);
}

That leaves the Draw override in the Game class with just two jobs: Draw the black rectangle that serves as background behind the components, and draw the rectangle with a new selectedColor on the right. Both jobs involve a 1 x 1 pixel bitmap stretched to fill half the screen.

Better than Silverlight?

I recently also wrote a Silverlight version of the color-scroll program for Windows Phone 7, and I discovered that the program only allowed me to manipulate one Slider at a time. However, if you deploy the XnaColorScroll program to a phone, you can manipulate all three Slider controls independently. I find that difference very interesting. It illustrates once again how high-level interfaces such as Silverlight may make our lives much simpler, but often something else needs to be sacrificed. I believe the concept is called TANSTAAFL: There ain’t no such thing as a free lunch.

XNA is so low-level in some respects that it might remind us of the Wild West. But with a little judicious use of components (including components built from other components) and mimicking routed event handling, even XNA can be tamed and made to seem more like Silverlight—perhaps even better.    


Charles Petzold is a longtime contributing editor to MSDN Magazine*. His new book, “Programming Windows Phone 7” (Microsoft Press, 2010), is available as a free download at bit.ly/cpebookpdf.*

Thanks to the following technical expert for reviewing this article: Shawn Hargreaves