Game development with SharpDX Toolkit: Content loading and drawing a sprite

Now that we know the bare minimum on how to configure a SharpDX Toolkit project from scratch, it’s time to reveal more about what kind of game we are going to develop: a Flappy Bird clone. You can love or hate it, but you have to admit that it’s a good candidate to learn the basics of game development: 2D drawing, input, playing sounds and collision detection.

In this tutorial, we are going to focus on how to load our first texture and draw it with the SpriteBatch class. So go ahead and clone or download the source code of the previous article if you don’t have it, because we are going to extend it.

The ContentManager class

Much like XNA, SharpDX Toolkit has a ContentManager class for managing the assets (content files for textures, sounds, etc.) that a game has. It has a generic method, Load, which receives a string with the path of the asset file and returns an object of the specified type that represents the asset loaded into memory, throwing different kinds of exceptions if any error happens (asset not found, format not supported…).

The ContentManager also acts as a cache system, since it saves references to assets already loaded and returns them without having to re-read the data if they are requested. After all, if two game objects of the same type (for example, an enemy) are instantiated, they will end sharing the same texture when being drawn.

Like in XNA, the Game class already has a ContentManager property named Content, just for convenience. But one new feature is the addition of the Unload function, which allows us to unload individual asset files instead of having to dispose of all of them.

Loading a texture

It’s time to get our hands dirty with code. First, download the free Tappy Plane asset pack made by the awesome Kenney. Open the ZIP file and extract one of the PNG images of a plane of your liking.

Next, add a Content folder to the game project from the Solution Explorer window. Copy the plane texture inside it (we will be renaming it to “Plane.png”) and, in the Properties window that shows when selecting it, change its Build Action to ToolkitTexture. If this new option doesn’t show, close the solution and reopen it so the new build actions added by SharpDX Toolkit are reloaded.

Back to our Game class, we need to define a field of type Texture2D (careful! Use the one inside the SharpDX.Toolkit.Graphics namespace, and NOT the one inside SharpDX.Direct3D11) to hold the reference of our texture used for drawing. Now we can proceed to load it, but first we have to define the default directory of our existing ContentManager, so it knows where to look for assets when loading them. This can be done in the constructor, just by setting the property Content.RootDirectory.

public CrazyPlanesGame()
{
    deviceManager = new GraphicsDeviceManager(this);
    deviceManager.PreferredBackBufferWidth = 480;
    deviceManager.PreferredBackBufferHeight = 800;
    this.Content.RootDirectory = “Content”;
}


Now, our Game class has a specific function where we can put our content loading code. It’s got the descriptive name LoadContent, and you can override it and safely call any asset loading code from there since it’s executed just after the graphics device is initialized but before any actual drawing is performed. This way, any Direct3D internal objects needed for loading and sending the data to the GPU are already available.

So, let’s just call Content.Load passing the path to our plane texture relative to the Content directory. Also note that like in XNA you don’t need to specify the extension of the asset since all of them are compiled to an optimized format stored in a container file with the extension .tkb. The only downside to this approach is that you can’t have two assets with the same name (even if they have different extensions, like a PNG texture and an OBJ model) in the same directory.

protected override void LoadContent()
{
    this.planeTexture = this.Content.Load<Texture2D>(“Plane”);
    base.LoadContent();
}


Now you can try running your project to check that everything is fine. The Content.Load line will throw an AssetNotFoundException if it has any problems finding the asset file, and a NotSupportedException in case anything goes wrong when trying to load it.

Drawing with SpriteBatch

When drawing 2D elements to the screen we have a very helpful class that will take care of a lot of boilerplate (creating the vertex/index buffers, loading the shaders…) for us. It’s the SpriteBatch, and by instantiating a copy of it we can draw our textures to the screen just by specifying the position in pixels and the texture to draw. For future reference, the representation of a texture object (or a part of it) on screen is called a sprite, so the SpriteBatch class is named that way since it groups commands for drawing sprites into batches and then executes them for better performance.

So let’s go ahead and declare a field for holding our SpriteBatch. To initialize it we need for the graphics device to be available like when loading content, but to keep our code cleaner the Game class provides another function where we can create all our non-asset data: Initialize.

protected override void Initialize()
{
    this.spriteBatch = new SpriteBatch(this.GraphicsDevice);
    base.Initialize();
}


Now let’s get back to the Draw function that we added in the previous tutorial, and draw our sprite after clearing the screen. When you want to draw something with SpriteBatch, you must first call its Begin method so it enters into a valid state; after this, feel free to make any Draw or DrawString (for drawing text) calls you want. When finished, make sure you call End so the commands you entered are sent to the GPU and drawn on screen. Failing to follow this sequence will likely raise an InvalidOperationException.

We want to draw our plane perfectly centered, so we will need to access the GraphicsDevice.BackBuffer properties named Width and Height. These store the dimensions of our back buffer, in other words, the texture where everything is drawn to before the GPU presents it on screen. But since the default origin for positioning a sprite drawn with SpriteBatch is the top left corner, we have to take into account the size of the plane texture too:

Vector2 planePosition = new Vector2(
    (this.GraphicsDevice.BackBuffer.Width / 2.0f) – (this.planeTexture.Width / 2.0f),
    (this.GraphicsDevice.BackBuffer.Height / 2.0f) – (this.planeTexture.Height / 2.0f));


Having everything we need, let’s draw our sprite. The third parameter for SpriteBatch.Draw is a SharpDX.Color that will be applied as a tint to the sprite; in our case we want to see the texture as is, so we specify it to be white.

this.spriteBatch.Begin();
this.spriteBatch.Draw(this.planeTexture, planePosition, Color.White);
this.spriteBatch.End();


Now you can run the project and see that the plane sprite finally shows! However, there are weird pixels around its borders. This is due to SharpDX Toolkit using premultiplied alpha blending (transparency), much like XNA did, but the sprite doesn’t! Thankfully this can be easily changed by modifying the call to SpriteBatch.Begin and changing its BlendState parameter:

this.spriteBatch.Begin(SpriteSortMode.Deferred, this.GraphicsDevice.BlendStates.NonPremultiplied);

Plane texture with wrong alpha on the left, correct one on the right.

Now the sprite displays adequately! And with this ends this tutorial. Feel free to fiddle with the source code and try loading more textures, playing with the different overloads of the SpriteBatch.Draw function or even making the plane move in preparation for the next one.

Source code

You can find the source code for this tutorial and the following ones in this GitHub repository, inside the Chapter2 folder.

Game development with SharpDX Toolkit: Creating a base project

Since the announcement that Microsoft wasn’t going to update XNA anymore, lots of frameworks have tried to fill the void it left as a friendly way of being introduced to game development. Perhaps the most well known of them is MonoGame, which started as a way to expand XNA compatibility to non-Windows platforms by using managed OpenGL and the Xamarin products. Other alternatives based on C# are Unity, OpenTK and SharpDX, but they have totally different APIs or are just graphical libraries with a more low level approach.

But time passed and the people behind SharpDX launched their own solution: SharpDX Toolkit, a higher-level library that mimicked XNA. While the namespaces are named differently, almost all classes have matching names and even their structure is identical. With this, you can bring the power of DirectX 11 to desktop, Windows Store and Windows Phone games while maintaining the philosophy that XNA introduced.

Starting with this post, there will be a (hopefully regular!) series of tutorials on how to use SharpDX Toolkit for game development. They will be intertwined with the regular, more low level Direct2D and Direct3D posts using bare SharpDX.

Creating a new Windows desktop project

This time we are going to focus on game development for the desktop platform, and in future tutorials we will be porting the existing code to Windows Store and Windows Phone Universal Apps. So, open Visual Studio and go to select the New > Project… menu option. Inside the New Project window, select the template Templates > Visual C# > Windows Desktop > Empty Project. Give it a name and a location and click OK.

Create new project window

This will give us an empty project that by default is meant for console applications; this means that a console window will pop up when our app runs! You can change this by right clicking your project file from the Solution Explorer window and selecting Properties > Application > Output Type: Windows Application. However, you can keep it this way if you want to print debug messages via Console.Write.

Now, let’s add the references to the SharpDX Toolkit assemblies that we are going to need. Instead of downloading the package and manually adding it (which you can do if you want), we are going to take advantage of NuGet to download and reference the latest version of the libraries. So right click again your project file and select Manage NuGet Packages…. In this new window search for SharpDX.Toolkit and install only the SharpDX.Toolkit.Game package; every other library (including vanilla SharpDX) will be automatically added since it’s marked as a dependency.

Installing SharpDX Toolkig packages from NuGet window

Adding the game class

Let’s get started with the Game class. Add a new source file and define a class inside it (we will call it CrazyPlanesGame) that inherits from SharpDX.Toolkit.Game. Like in XNA, this class will be used to abstract the game loop and run the update and draw logic when it is needed.

Next, we need to perform a little initialization before the game class runs properly. Add a private member field of type GraphicsDeviceManager and instantiate it inside the constructor of CrazyPlanesGame. This will take care of initializing the graphics device (Direct3D device, context and application window) and will raise the proper events during the application life cycle. For now we will only modify its PreferredBackBufferWidth and PreferredBackBufferHeight properties because we want a portrait window for our game, although you can change other properties, like running the window in full screen mode or specifying a custom colour format for the back buffer.

internal class CrazyPlanesGame : Game
{
    private GraphicsDeviceManager deviceManager;

    public CrazyPlanesGame()
    {
        deviceManager = new GraphicsDeviceManager(this);
        deviceManager.PreferredBackBufferWidth = 480;
        deviceManager.PreferredBackBufferHeight = 800;
    }
}


Creating our GraphicsDeviceManager is mandatory; not doing so will throw an InvalidOperationException with the error message No GraphicsDeviceManager found when the game starts running.

Now, and to properly run, our game needs an entry point so the operating system knows what code should be called first. In C# this is achieved by declaring a class with a static function named Main of type void. Inside it, we will need to instantiate a copy of our CrazyPlanesGame and call its Run method. In addition, the Main function must be marked with the STAThread attribute because internally SharpDX uses Windows Forms to create the game window and this is one of its requisites.

public static class Program
{
    [STAThread]
    public static void Main()
    {
        using (CrazyPlanesGame game = new CrazyPlanesGame())
        {
            game.Run();
        }
    }
}

At last, go back to the CrazyPlanesGame class and override the Draw method. This will allow us to insert our own drawing code, which for now will be just clearing the screen to a cornflower blue colour:

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

    base.Draw(gameTime);
}


Now you can run the project and take a look at the results of our coding session.

Our first SharpDX Toolkit game window

Source code

Unlike previously, the code for this series of tutorials will be all hosted in the same repository, with each solution inside its own folder. Here it is on GitHub, and you can find the one for this article inside the folder named Chapter1.

Styling the CheckBox control with Segoe UI Symbol glyphs

With Windows 8.1 Microsoft updated the Segoe UI Symbol font and added a nice amount of icons for the most commonly actions used in application bar buttons. The reason behind this was to replace the old method of creating small PNG images to use as the content of the buttons and directly use font glyphs that would scale nicely across all target resolutions.

Character Map tool showing Segoe UI Symbol

This font also introduces some other symbols like back/forward arrows, hearts for favourited items and even the tick parts of CheckBox and RadioButton controls. However, the standard XAML controls still don’t use it so I’m assuming they were thought for using inside HTML/WinJS apps.

Today, we are going to dissect the CheckBox control to see what parts it is composed of, and will be switching standard XAML shapes and paths for glyphs when applicable.

Understanding the parts of the CheckBox control

Let’s start by creating a new Universal App project and adding a CheckBox control to both the MainPage of the Windows 8.1 and Windows Phone projects. We have to do it this way since the styles for both platforms are a little different, and we want to make them as accurate to the original version as possible.

Now, to better work with control styles, open the project in Blend. Right click the CheckBox control and select Edit Template > Edit a Copy… . Set a name in the new dialog (we will be using CheckBoxGlyphStyle) and make sure it’s defined at the document level so there aren’t any duplicate names in App.xaml. Now, if you have never customized control templates or styles, what it does is copy all the default XAML for the control so you can edit it and override whatever you want, including animations and visual states.

If you examine the generated templates for both controls, you will see that they have some things in common:

  • Both have a grid that encompasses the ContentPresenter (where the text or custom content will be displayed) and another grid for holding the tick/check part.
  • Inside that grid, both have a Path named CheckGlyph to the represent the tick mark.

However, other parts are platform specific.

Windows Phone
  • The indeterminate mark is a Rectangle called NormalRectangle.
  • A Border named CheckBackground is used as the background of the tick. Its BorderThickness is directly bound to the same property of the CheckBox control.
  • The root container of the control is a Grid.
  • Display of parts depending on the visual state is controlled through the Visibility property.
Windows 8.1
  • The indeterminate mark is a Rectangle too, but this time it’s called IndeterminateGlyph.
  • The NormalRectangle exists, too, but in this case is a Rectangle that acts as the background of the tick part.
  • The root container of the control is a Border, whose BorderThickness is bound to the same property of the CheckBox control.
  • Display of parts depending on the visual state is controlled through the Opacity property.
  • There are two other rectangles, FocusVisualWhite and FocusVisualBlack, for displaying that the control has focus while tab navigating.

Take a deep look at the default templates generated for both controls and make you comfortable with them. When you are ready, let’s start customizing them.

Switching from shapes to glyphs

To achieve our goal we must replace some of the existing XAML shapes with TextBlock controls containing a single glyph. Since the process has some differences depending on the target platform, we are going to detail them separately:

Windows Phone
  • Give a name to the Grid that contains the tick parts, since we will set some of its properties via a visual state later. We will be using CheckGrid.
  • Create three TextBlock controls inside this grid and name them as the existing control parts.
  • Set their font family to Segoe UI Symbol and size to the system resource called TextStyleLargeFontSize.
  • Match the HorizontalAlignment and VerticalAlignment with those of the old controls; in some instances the glyphs will appear cut off due to font padding/kerning. You can try fixing this for perfection or leave it as is.
  • Set their Text values to the matchings glyphs (this is more easily achieved by writing the escaped value in XAML code). The escaped values for each one are as follows:
Usage Escaped Unicode Glyph
Border &#xE003;
Tick &#xE001;
Indeterminate &#xE004;
  • Match the IsHitTestVisible and Visibility properties with those of the old controls.
  • Now, since the brush properties of the old and new controls are different, we have to try and match the bindings as closely as possible:
Control and property Binding value
CheckBackground Foreground TemplateBinding BorderBrush
Parent Grid Background TemplateBinding Background
NormalRectangle Foreground ThemeResource CheckBoxForegroundThemeBrush
CheckGlyph Foreground ThemeResource CheckBoxForegroundThemeBrush

WARNING: by default, the indeterminate state of the CheckBox is broken! The theme background brush is incorrectly assigned to NormalRectangle instead of the foreground one, so if you declare the control as IsThreeState you can see it behave erroneously.

<Grid x:Name=”CheckGrid” Grid.Column=”0″ VerticalAlignment=”Top” Background=”{TemplateBinding Background}“>

    <TextBlock x:Name=”CheckBackground” HorizontalAlignment=”Center” Text=”&#xE003;” VerticalAlignment=”Top” FontFamily=”Segoe UI Symbol” IsHitTestVisible=”False” Foreground=”{TemplateBinding BorderBrush} FontSize=”{StaticResource TextStyleLargeFontSize}“/>

    <TextBlock x:Name=”NormalRectangle” HorizontalAlignment=”Center” Text=”&#xE004;” VerticalAlignment=”Top” FontFamily=”Segoe UI Symbol” IsHitTestVisible=”False” Visibility=”Collapsed” Foreground=”{ThemeResource CheckBoxForegroundThemeBrush} FontSize=”{StaticResource TextStyleLargeFontSize}” />

    <TextBlock x:Name=”CheckGlyph” HorizontalAlignment=”Center” Text=”&#xE001;” VerticalAlignment=”Top” FontFamily=”Segoe UI Symbol” IsHitTestVisible=”False” Visibility=”Collapsed” Foreground=”{ThemeResource CheckBoxForegroundThemeBrush} FontSize=”{StaticResource TextStyleLargeFontSize}” />

</Grid>

Windows 8.1
  • Create three TextBlock controls inside this grid and name them as the existing control parts that aren’t focus visuals.
  • Set their font family to Segoe UI Symbol and size to 11 points.
  • Again, match the HorizontalAlignment and VerticalAlignment with those of the old controls. Fix the small displacements adjusting margins if you want.
  • Set their Text values to the matchings glyphs. They are almost the same, except that in this instance the tick has a solid background instead of a border:
Usage Escaped Unicode Glyph
Background &#xE002;
Tick &#xE001;
Indeterminate &#xE004;
  • Match the Opacity properties with those of the old controls.
  • Again, match the brush properties using the following table:
Control and property Binding value
NormalRectangle Foreground ThemeResource CheckBoxBackgroundThemeBrush
CheckGlyph Foreground ThemeResource ComboBoxForegroundThemeBrush
IndeterminateGlyph Foreground TemplateBinding ComboBoxForegroundThemeBrush

<Grid VerticalAlignment=”Top”>

    <TextBlock x:Name=”NormalRectangle” HorizontalAlignment=”Center” Text=”&#xE002;” VerticalAlignment=”Center” FontFamily=”Segoe UI Symbol” Foreground=”{ThemeResource CheckBoxBackgroundThemeBrush}“/>

    <TextBlock x:Name=”CheckGlyph” HorizontalAlignment=”Center” Text=”&#xE001;” VerticalAlignment=”Center” FontFamily=”Segoe UI Symbol” Foreground=”{ThemeResource ComboBoxForegroundThemeBrush} Opacity=”0″/>

    <TextBlock x:Name=”IndeterminateGlyph” HorizontalAlignment=”Center” Text=”&#xE004;” VerticalAlignment=”Center” FontFamily=”Segoe UI Symbol” Foreground=”{ThemeResource ComboBoxForegroundThemeBrush} Opacity=”0″/>

    <Rectangle x:Name=”FocusVisualWhite” Height=”27″ Opacity=”0″ StrokeDashOffset=”0.5″ StrokeEndLineCap=”Square” Stroke=”{ThemeResource FocusVisualWhiteStrokeThemeBrush} StrokeDashArray=”1,1″ Width=”27″/>

    <Rectangle x:Name=”FocusVisualBlack” Height=”27″ Opacity=”0″ StrokeDashOffset=”1.5″ StrokeEndLineCap=”Square” Stroke=”{ThemeResource FocusVisualBlackStrokeThemeBrush} StrokeDashArray=”1,1″ Width=”27″/>

</Grid>

Fixing the visual states

Now you could run the project in both platforms and check that it displays correctly. However, interacting with the CheckBox controls in any way will crash the application. This is due to changing the parts that compose the control, so when a new visual state tries to change a property that doesn’t exist an exception is thrown. We are going to fix this by modifying the default visual states so they match our new layout.

Windows Phone

In this case we only need to modify the Pressed and Disabled visual states, since they are the ones that change the conflicting values. We need to match the Storyboard.TargetProperty and Storyboard.TargetName accordingly to the names and properties of the new control parts.

<VisualState x:Name=”Pressed”>

    <Storyboard>

        <PointerDownThemeAnimation Storyboard.TargetName=”Grid”/>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Background” Storyboard.TargetName=”CheckGrid”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPressedBackgroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”CheckGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPressedForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”NormalRectangle”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPressedBackgroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

    </Storyboard>

</VisualState>

<VisualState x:Name=”Disabled”>

    <Storyboard>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”CheckBackground”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledBorderThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”CheckGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”NormalRectangle”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledBackgroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”ContentPresenter”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

    </Storyboard>

</VisualState>

Windows 8.1

As before, but this time the visual states to be modified are PointerOver, Pressed and Disabled. Also, setting the Stroke property of NormalRectangle should be deleted since we don’t have an equivalent for it.

<VisualState x:Name=”PointerOver”>

    <Storyboard>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”NormalRectangle”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPointerOverBackgroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”CheckGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPointerOverForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”IndeterminateGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPointerOverForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

    </Storyboard>

</VisualState>

<VisualState x:Name=”Pressed”>

    <Storyboard>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”NormalRectangle”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPressedBackgroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”CheckGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPressedForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”IndeterminateGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxPressedForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

    </Storyboard>

</VisualState>

<VisualState x:Name=”Disabled”>

    <Storyboard>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”NormalRectangle”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledBackgroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”CheckGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”IndeterminateGlyph”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxDisabledForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=”Foreground” Storyboard.TargetName=”ContentPresenter”>

            <DiscreteObjectKeyFrame KeyTime=”0″ Value=”{ThemeResource CheckBoxContentDisabledForegroundThemeBrush}“/>

        </ObjectAnimationUsingKeyFrames>

    </Storyboard>

</VisualState>

And that’s it. Now you can run both projects and test that they work.

Custom CheckBox styles on Windows 8.1 and Windows Phone

Further customization

While you can leave the controls as they are right now, the purpose of this article is to give an entry point so you can customize them further. What about styling the RadioButton in a similar way, too? Or using the CheckBox to mark favourite items with a heart icon? Now you can use whatever glyphs you like to give your app a more personalized look. This is just the entry point.

Showing the CheckBox using glyphs and with a new custom style with hearts

Source code

You can download the source code as a Visual Studio 2013 C#/XAML Universal App project from the GitHub repository. Feel free to use in any way you like since it’s MIT licensed, and if you have any questions or improvements contact me via the contact page or the repository’s issues page.